/* Fit Pro CSM Hub -- a compact white/light-grey utility interface.
   Density is the point: a CSM scans a roster, they do not read a page. */

:root {
  color-scheme: light;

  /* ---------------------------------------------------------- surfaces
     Warm paper, not grey. A neutral grey page under white cards is the
     single biggest reason an interface reads as a document rather than a
     product -- there is no temperature in it, so nothing feels placed. */
  --bg:        #f7f6f3;
  --panel:     #ffffff;
  --panel-2:   #fbfaf8;
  --field:     #ffffff;
  --sidebar:   #ffffff;

  /* Washes, not fixed colours. A lift darkens on paper and lightens on ink,
     so one rule works in both themes and hairlines never have to be guessed
     at twice. */
  --lift-1: rgba(24, 24, 20, .022);
  --lift-2: rgba(24, 24, 20, .045);
  --lift-3: rgba(24, 24, 20, .075);
  --lift-4: rgba(24, 24, 20, .12);
  --line:        var(--lift-3);
  --line-strong: var(--lift-4);
  /* One hover tint for the whole app. Ink at low alpha on paper,
     white at low alpha on ink -- never a fixed colour, so it works in
     both themes without a second rule. */
  --hover-tint: rgba(24, 24, 20, .045);
  --hero-bg: linear-gradient(155deg, #0e4a30 0%, #0a3a26 55%, #093322 100%);
  --hero-ink: #e9f6ef;
  --hero-dim: rgba(233, 246, 239, .68);
  --hero-line: rgba(255, 255, 255, .14);
  --hero-badge: #1fb877;
  --hero-badge-ink: #06281a;
  --hover-lift: -2px;      /* cards */
  --hover-lift-sm: -1px;   /* buttons: a smaller object, a smaller move */

  /* ------------------------------------------------------------- ink */
  --ink:    #14181d;
  --ink-2:  #55606f;
  --ink-3:  #626c7a;   /* 4.93:1 on paper, was 2.84 */

  /* ---------------------------------------------------------- accent
     Emerald. Fit Pro's mark is black and white, so the accent carries the
     identity on its own -- which means it has to be dark enough to hold
     white text and to be legible on paper, not the brighter green that
     works on a dark ground. */
  --accent:      #0d8a5f;
  --accent-hi:   #0aa06d;
  --accent-ink:  #ffffff;
  --accent-soft: rgba(13, 138, 95, .10);
  --accent-line: rgba(13, 138, 95, .28);

  --blue:   #2f6fed;  --blue-soft:   rgba(47, 111, 237, .10);
  --green:  #0d8a5f;  --green-soft:  rgba(13, 138, 95, .10);
  --amber:  #9a6512;  --amber-soft:  rgba(198, 132, 24, .13);
  --red:    #c0392f;  --red-soft:    rgba(192, 57, 47, .10);
  /* Folder spines. Eight hues, no red -- red means something is wrong
     everywhere else here, and a folder is not wrong. */
  --fold-slate: #5b6b7a;
  --fold-green: #0d8a5f;
  --fold-teal: #0f8a86;
  --fold-blue: #2c6fd1;
  --fold-violet: #6d4bd6;
  --fold-plum: #a3418c;
  --fold-amber: #a8720f;
  --fold-rust: #b1552a;
  --violet: #6d4bd6;  --violet-soft: rgba(109, 75, 214, .10);
  /* Hypercare, and nothing else. The yellow lives in the FILL and the
     border, not the text: yellow type on white is unreadable, and a text
     colour dark enough to read is the brown this first shipped as. So the
     pill is a real yellow and the letters on it are a deep gold. */
  --gold:   #7a5300;
  --gold-soft: rgba(250, 204, 21, .38);
  --gold-line: #eab308;

  /* --------------------------------------------------------- elevation
     Two-part shadows: a tight contact shadow that sits the card on the
     page, and a wide soft one that lifts it off. One alone always looks
     either pasted on or floating. */
  --shadow-1: 0 1px 1px rgba(20, 24, 29, .04), 0 1px 3px rgba(20, 24, 29, .05);
  --shadow-2: 0 1px 2px rgba(20, 24, 29, .06), 0 6px 16px rgba(20, 24, 29, .07);
  --shadow-3: 0 2px 4px rgba(20, 24, 29, .07), 0 18px 44px rgba(20, 24, 29, .13);
  --shadow-4: 0 4px 8px rgba(20, 24, 29, .08), 0 34px 76px rgba(20, 24, 29, .20);
  --ring: 0 0 0 3px rgba(13, 138, 95, .18);

  /* ------------------------------------------------------------ shape */
  --r-sm: 8px;    /* chips, small controls */
  --r-md: 12px;   /* cards, inputs, buttons */
  --r-lg: 16px;   /* panels */
  --r-xl: 22px;   /* overlays */
  --r-pill: 999px;

  /* ----------------------------------------------------------- motion
     One curve. Decelerate: fast to start, settling at the end, which is
     what makes a thing feel like it has weight rather than like it teleported.
     Colour is quicker than movement because the eye forgives a fast tint and
     notices a fast slide. */
  --ease: cubic-bezier(.22, .61, .36, 1);
  --t-colour: 120ms var(--ease);
  --t-move:   180ms var(--ease);
  --t-panel:  240ms var(--ease);

  /* ------------------------------------------------------------ scale */
  --step-1: 4px;  --step-2: 8px;   --step-3: 12px; --step-4: 16px;
  --step-5: 24px; --step-6: 32px;  --step-7: 48px;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:      #10141a;
  --panel:   #171d25;
  --panel-2: #1c232d;
  --field:   #0e131a;
  --sidebar: #141a22;
  --lift-1: rgba(255, 255, 255, .03);
  --lift-2: rgba(255, 255, 255, .06);
  --lift-3: rgba(255, 255, 255, .09);
  --lift-4: rgba(255, 255, 255, .15);
  --hover-tint: rgba(255, 255, 255, .055);
  --ink:   #eef2f7;
  --ink-2: #a4b0c0;
  --ink-3: #808d9e;    /* 5.02:1 on panel, was 3.99 */
  /* Brighter on ink: the same emerald that reads confident on paper goes
     muddy on a dark ground. */
  --accent:      #35c98f;
  --accent-hi:   #4ad9a0;
  --accent-ink:  #06231a;
  --accent-soft: rgba(53, 201, 143, .14);
  --accent-line: rgba(53, 201, 143, .34);
  --blue:   #6ea8fe;  --blue-soft:   rgba(110, 168, 254, .14);
  --green:  #35c98f;  --green-soft:  rgba(53, 201, 143, .14);
  --amber:  #e0b45f;  --amber-soft:  rgba(224, 180, 95, .14);
  --red:    #f2726f;  --red-soft:    rgba(242, 114, 111, .14);
  --fold-slate: #8fa3b5;
  --fold-green: #35c98f;
  --fold-teal: #3fc4bd;
  --fold-blue: #6ea6f5;
  --fold-violet: #a78bfa;
  --fold-plum: #e07ac7;
  --fold-amber: #e0b45f;
  --fold-rust: #e59166;
  --violet: #a78bfa;  --violet-soft: rgba(167, 139, 250, .14);
  --gold:   #fde047;
  --gold-soft: rgba(250, 204, 21, .22);
  --gold-line: #facc15;
  /* Depth on a dark ground comes from the lighter surface, not the shadow --
     a drop shadow over near-black is invisible. These stay for the contact
     edge only. */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .34);
  --shadow-2: 0 2px 6px rgba(0, 0, 0, .40), 0 8px 22px rgba(0, 0, 0, .30);
  --shadow-3: 0 4px 12px rgba(0, 0, 0, .46), 0 20px 50px rgba(0, 0, 0, .40);
  --shadow-4: 0 8px 20px rgba(0, 0, 0, .52), 0 40px 90px rgba(0, 0, 0, .52);
  --ring: 0 0 0 3px rgba(53, 201, 143, .26);
  /* Deeper on a dark page: the light-mode hero would glow against near-black. */
  --hero-bg: linear-gradient(155deg, #0d3f29 0%, #09301f 60%, #072a1b 100%);
  --hero-badge: #35c98f;
}

/* Follows the system unless the reader has chosen. Light is the default
   here, deliberately: Stefan's CSMs work on this all day and light is
   easier to read for long stretches. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #10141a; --panel: #171d25; --panel-2: #1c232d; --field: #0e131a;
    --sidebar: #141a22;
    --lift-1: rgba(255,255,255,.03); --lift-2: rgba(255,255,255,.06);
    --lift-3: rgba(255,255,255,.09); --lift-4: rgba(255,255,255,.15);
    --hover-tint: rgba(255,255,255,.055);
    --ink: #eef2f7; --ink-2: #a4b0c0; --ink-3: #808d9e;
    --accent: #35c98f; --accent-hi: #4ad9a0; --accent-ink: #06231a;
    --accent-soft: rgba(53,201,143,.14); --accent-line: rgba(53,201,143,.34);
    --blue: #6ea8fe;  --blue-soft: rgba(110,168,254,.14);
    --green: #35c98f; --green-soft: rgba(53,201,143,.14);
    --amber: #e0b45f; --amber-soft: rgba(224,180,95,.14);
    --red: #f2726f;   --red-soft: rgba(242,114,111,.14);
    --fold-slate: #8fa3b5;
    --fold-green: #35c98f;
    --fold-teal: #3fc4bd;
    --fold-blue: #6ea6f5;
    --fold-violet: #a78bfa;
    --fold-plum: #e07ac7;
    --fold-amber: #e0b45f;
    --fold-rust: #e59166;
    --violet: #a78bfa; --violet-soft: rgba(167,139,250,.14);
    --gold: #fde047;
    --gold-soft: rgba(250,204,21,.22);
    --gold-line: #facc15;
    --shadow-1: 0 1px 2px rgba(0,0,0,.34);
    --shadow-2: 0 2px 6px rgba(0,0,0,.40), 0 8px 22px rgba(0,0,0,.30);
    --shadow-3: 0 4px 12px rgba(0,0,0,.46), 0 20px 50px rgba(0,0,0,.40);
    --shadow-4: 0 8px 20px rgba(0,0,0,.52), 0 40px 90px rgba(0,0,0,.52);
    --ring: 0 0 0 3px rgba(53,201,143,.26);
    /* Same deepening as the chosen-dark block above. A token defined in only
       one of the two dark paths is the bug that leaves system-dark readers on
       the light-mode value. */
    --hero-bg: linear-gradient(155deg, #0d3f29 0%, #09301f 60%, #072a1b 100%);
    --hero-badge: #35c98f;
  }
}

/* Legacy names still referenced by component rules. Aliases rather than a
   find-and-replace across 976 lines, so the visual pass and the tidy-up can
   be separate changes and either can be reverted on its own. */
:root, :root[data-theme="dark"] {
  --surface: var(--panel);
  --line-soft: var(--lift-2);
  --backdrop: none;
  --grade-1: var(--green);  --grade-1-soft: var(--green-soft);
  --grade-2: var(--blue);   --grade-2-soft: var(--blue-soft);
  --grade-3: var(--amber);  --grade-3-soft: var(--amber-soft);
  --grade-4: #c2701a;       --grade-4-soft: rgba(194, 112, 26, .13);
  --grade-5: var(--red);    --grade-5-soft: var(--red-soft);
}

* { box-sizing: border-box; }

/* The `hidden` attribute has to actually hide things.
   It works by a UA rule of `display: none`, which ANY author rule setting
   display beats -- so `.notifhead { display: flex }` left a panel head on
   screen with hidden="hidden" set on it, and JS that "hid" an element did
   nothing at all. There was already a one-off patch for this on one row of
   the payments form; this is that fix, made general. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  /* The system stack, but asking for the optical-size and grade features the
     platform faces carry. It is the difference between text that is set and
     text that is merely displayed. */
  font: 400 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text",
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-feature-settings: "cv11", "ss01";
}

/* ---------------------------------------------------------- typography
   A real scale. The old file had fifteen sizes including 11.5, 12.5 and
   13.5px -- nudges rather than decisions, and the reason everything read at
   the same volume. Seven steps, each with a weight and a tracking that suit
   it: large text wants to be tighter, small text wants to be looser, and
   uniform tracking is what makes a page look typed rather than set. */
h1, h2, h3 { margin: 0; color: var(--ink); }
h1 { font-size: 25px; font-weight: 680; letter-spacing: -.021em; line-height: 1.15; }
h2 { font-size: 16px; font-weight: 620; letter-spacing: -.011em; line-height: 1.3; }
h3 { font-size: 14px; font-weight: 620; letter-spacing: -.006em; }

/* The small uppercase label above a number or a field. Tracking is what
   stops it reading as shouting. */
.alabel, .metric > span, .statlabel, .eyebrow {
  font-size: 11px; font-weight: 620; letter-spacing: .055em;
  text-transform: uppercase; color: var(--ink-3);
}

.dim  { color: var(--ink-2); }
.small, .sub { font-size: 12.5px; }
.mono { font-variant-numeric: tabular-nums; }
/* Anything that gets compared down a column is tabular, always. Ragged
   digits in a list of money is the tell of a page nobody looked at. */
td.num, .metric b, .statcard b, .nwhen, .calldate, .avalue { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- motion
   The old file had three transitions against thirty-two hover rules, so
   almost everything snapped. This is most of what separates an interface
   that feels considered from one that feels like a document with links. */
a, button, select, input, textarea, summary,
button.tb, a.tb, .mini, .pill, .tag, .chipbtn, .statcard, .panel, .cicard, .taskcard,
.notifrow, .paygroup, tbody tr {
  transition: background-color var(--t-colour), border-color var(--t-colour),
              color var(--t-colour), box-shadow var(--t-move),
              transform var(--t-move), opacity var(--t-colour);
}
/* Pressed states. Nothing in the old file responded to being clicked at all,
   which is the loudest "this is a web page, not an app" signal there is. */
button:active, button.tb:active, .mini:active, .chipbtn:active, a.tb:active {
  transform: translateY(1px);
}
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 1ms !important; animation-duration: 1ms !important; }
}

/* ---------------------------------------------- retired: top bar */
.topbar {
  display: flex; align-items: center; gap: 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  padding: 0 20px; height: 52px;
  /* Pinned. Scrolling down a long roster used to take the entire navigation
     with it, so from halfway down the Admin page there was no way to reach
     Status, Overview or anything else without scrolling back to the top. */
  position: sticky; top: var(--asbar, 0px); z-index: 20;
}
.brand { font-weight: 700; font-size: 15px; color: var(--ink); text-decoration: none; letter-spacing: -.01em; }
.brand span { font-weight: 500; color: var(--ink-3); }
.topbar nav { display: flex; gap: 4px; flex: 1; }
.topbar nav a {
  color: var(--ink-2); text-decoration: none; font-weight: 500;
  padding: 6px 11px; border-radius: 6px; font-size: 13.5px;
}
.topbar nav a:hover { background: var(--line-soft); color: var(--ink); }
.who { display: flex; align-items: center; gap: 14px; }
.who .name { font-weight: 500; color: var(--ink-2); font-size: 13.5px; }
.linkish {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--ink-3); font: inherit; font-size: 13.5px; text-decoration: underline;
}
.linkish:hover { color: var(--ink); }
.who form { display: flex; align-items: center; margin: 0; }
/* Belt and braces: nothing in the header bar carries form spacing. */
.topbar .linkish { margin: 0; padding: 0; line-height: 1; }

/* --------------------------------------------------------------- pages */
.page { max-width: 1240px; margin: 0 auto; padding: 26px 20px 64px; }
.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-bottom: 20px;
}


.metrics { display: flex; gap: 26px; }
.metric { display: flex; flex-direction: column; }
.metric b { font-size: 22px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.metric span { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); margin-top: 5px; }

.banner {
  background: var(--blue-soft); border: 1px solid var(--blue);
  border-radius: 8px; padding: 11px 14px; margin-bottom: 18px;
  font-size: 13px; color: var(--ink);
}
.empty { color: var(--ink-3); background: var(--surface); border: 1px solid var(--line);
         border-radius: 8px; padding: 22px; }
code { font-family: ui-monospace, Menlo, monospace; font-size: .9em;
       background: var(--line-soft); padding: 1px 5px; border-radius: 4px; }

/* --------------------------------------------------------------- cards */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-1);
  padding: 18px; text-decoration: none; color: inherit; display: block;
}
.card h2 { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
.card .counts { display: flex; gap: 14px; margin: 12px 0 10px; font-size: 13px; color: var(--ink-2); }
.card .counts b { font-variant-numeric: tabular-nums; color: var(--ink); }
/* The two halves an invoice is built from, told apart at a glance rather
   than read as one more statistic each. */
.tiercount { display: inline-flex; align-items: baseline; gap: 4px; }
.tiercount.elite b { color: var(--accent); }
.tiercount.core b { color: var(--ink-2); }
.card .counts { flex-wrap: wrap; }
.card .go { font-size: 12.5px; color: var(--accent); font-weight: 600; }
.tiers { display: flex; gap: 5px; }

/* ---------------------------------------------------------------- tags */
/* A pill is a label, not a paragraph. Letting the text inside one wrap turned
   "Never signed in" into three lines of amber in a narrow column, which reads
   as a coloured background behind the row rather than as a tag. */
.tag { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 7px;
       border-radius: 4px; letter-spacing: .02em; white-space: nowrap; }
.tag.elite { background: var(--blue-soft); color: var(--blue); }
.tag.core  { background: var(--line-soft); color: var(--ink-2); }

.health { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 7px;
          border-radius: 4px; white-space: nowrap; }
/* More than one pill in a table cell. They sit in a row with air between them
   and wrap as whole pills; inline-block alone butts them together and then
   breaks the longest one mid-label. Wide enough that the pair somebody reads
   together -- Active and whether they have ever signed in -- stays on one
   line, with anything further dropping underneath. */
td.statuscell { display: flex; flex-wrap: wrap; align-items: center; gap: 5px;
                min-width: 158px; }
/* Status colours run from calm to loud, so a roster sorts itself by urgency
   before anything is read. Hypercare is blue rather than red: a new client
   needs attention but is not a problem, and colouring it like one teaches
   people to ignore red. */
.health.all_good           { background: var(--green-soft); color: var(--green); }
.health.needs_focus        { background: var(--amber-soft); color: var(--amber); }
.health.requires_attention { background: var(--red-soft);   color: var(--red); }
.health.hypercare_first_30 { background: var(--blue-soft);  color: var(--blue); }
.health.hypercare_transfer { background: var(--blue-soft);  color: var(--blue); }
.health.first_30           { background: var(--line-soft);  color: var(--ink-2); }
/* Not a client status -- the plain grey pill used elsewhere, e.g. on the
   system status page for "not run yet". Named separately so renaming a client
   status can never restyle it by accident. */
.health.neutral            { background: var(--line-soft);  color: var(--ink-2); }

/* -------------------------------------------------------------- tables */
.tablewrap { background: var(--surface); border: 1px solid var(--line);
             border-radius: 10px; overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 720px; }
th {
  text-align: left; font-size: 11px; font-weight: 620; text-transform: uppercase;
  letter-spacing: .055em; color: var(--ink-3); padding: 11px 14px;
  border-bottom: 1px solid var(--line); white-space: nowrap;
  background: var(--panel-2);
}
th:first-child { border-top-left-radius: var(--r-md); }
th:last-child  { border-top-right-radius: var(--r-md); }
/* Rows are taller than they were. Density is not the same as cramped, and a
   roster read every morning should not need care to stay on the right line. */
td { padding: 11px 14px; border-bottom: 1px solid var(--lift-1);
     color: var(--ink-2); vertical-align: middle; }
tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--accent-soft); }
td.strong { color: var(--ink); font-weight: 500; }
td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.dim { display: block; color: var(--ink-3); font-weight: 400; font-size: 12px; margin-top: 2px; }

/* -------------------------------------------------------------- signin */
.signin {
  max-width: 340px; margin: 12vh auto; background: var(--surface);
  border: 1px solid var(--line); border-radius: 12px; padding: 30px 28px 32px;
}
.signin h1 { text-align: center; margin-bottom: 6px; }
.signin .sub { text-align: center; color: var(--ink-3); font-size: 13px; margin: 0 0 22px; }
.signin label { display: block; font-size: 12px; font-weight: 600; color: var(--ink-2);
                margin: 14px 0 5px; }
.signin input {
  width: 100%; padding: 9px 11px; border: 1px solid var(--line);
  border-radius: 7px; font: inherit; background: var(--bg); color: var(--ink);
}
.signin input:focus { outline: 2px solid var(--accent); outline-offset: 1px;
                      border-color: var(--accent); }
.signin button {
  width: 100%; margin-top: 20px; padding: 10px; border: none; border-radius: 7px;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink); box-shadow: 0 1px 2px rgba(13,138,95,.24), 0 4px 12px rgba(13,138,95,.20); font: inherit; font-weight: 600; cursor: pointer;
}
.signin button:hover { filter: brightness(1.08); }
.error { background: var(--red-soft); color: var(--red); border-radius: 7px;
         padding: 9px 11px; font-size: 13px; margin: 0 0 4px; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }

/* ------------------------------------------------------- admin & account */
.page.narrow { max-width: 620px; }

.panel {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 20px 22px; margin-bottom: var(--step-4);
  box-shadow: var(--shadow-1);
}
.panel h2 { margin: 0 0 var(--step-3); }

h2.sec {
  font-size: 15px; font-weight: 600; margin: 30px 0 10px;
  letter-spacing: -.005em;
}

.facts { display: grid; grid-template-columns: auto 1fr; gap: 8px 18px; margin: 0; }
.facts dt { font-size: 12px; font-weight: 600; color: var(--ink-3);
            text-transform: uppercase; letter-spacing: .04em; padding-top: 2px; }
.facts dd { margin: 0; font-size: 14px; color: var(--ink); }

.banner.warn { background: var(--amber-soft); border-color: var(--amber); }
.banner b { display: block; margin-bottom: 3px; }

form label { display: block; font-size: 12px; font-weight: 600;
             color: var(--ink-2); margin: 14px 0 5px; }
form label .opt { font-weight: 400; color: var(--ink-3); text-transform: none;
                  letter-spacing: 0; }
/* Matches an input with no type attribute too -- `input[type=text]` does not,
   which silently left every typeless field at its default narrow width. */
form input:not([type=checkbox]):not([type=radio]),
form select {
  width: 100%; padding: 9px 12px; border: 1px solid var(--line);
  border-radius: var(--r-md); font: inherit; font-size: 14px;
  /* background-color, not the `background` shorthand: the shorthand resets
     background-image to none, and on a <select> that image IS the dropdown
     caret -- appearance:none having already taken the real one away. */
  background-color: var(--field); color: var(--ink);
  box-shadow: inset 0 1px 2px var(--lift-1);
}
form input:hover:not(:focus), form select:hover:not(:focus) {
  border-color: var(--line-strong);
}
form input:focus, form select:focus {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}
/* Scoped away from .linkish. This rule is for the forms in panels and
   dialogs; unqualified it also caught the sign-out button in the header,
   whose form is a submit form too, and its margin-top pushed that button
   below the baseline of the name beside it. */
form button[type=submit]:not(.linkish) {
  margin-top: 18px; padding: 9px 18px; border: none; border-radius: 7px;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink); box-shadow: 0 1px 2px rgba(13,138,95,.24), 0 4px 12px rgba(13,138,95,.20); font: inherit; font-weight: 600;
  font-size: 14px; cursor: pointer;
}
form button[type=submit]:not(.linkish):hover { filter: brightness(1.08); }

.row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 620px) { .row { grid-template-columns: 1fr; } }

.checks { display: flex; gap: 14px; flex-wrap: wrap; padding-top: 4px; }
.check { display: flex; align-items: center; gap: 6px; font-size: 13.5px;
         font-weight: 500; color: var(--ink-2); margin: 0; text-transform: none;
         letter-spacing: 0; }
.check input { width: auto; margin: 0; }

.hint { font-size: 12.5px; color: var(--ink-3); margin: 12px 0 0; line-height: 1.5; }

.adder {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; margin: 12px 0 8px; padding: 0 6px;
}
.adder[open] { padding: 0 20px; }
/* A button, not a disclosure triangle. These two summaries are the whole of
   "change this contract" and "add a payment", and drawn as faint text with a
   marker beside them the page reads as something you can only look at. */
.adder summary {
  cursor: pointer; padding: 11px 14px; font-weight: 600; font-size: 13.5px;
  color: var(--accent); list-style: none;
  display: flex; align-items: center; gap: 8px;
  border-radius: 9px; transition: background var(--t-colour);
}
.adder summary::-webkit-details-marker { display: none; }
/* Its own chevron, so it turns when the thing opens. */
.adder summary::before {
  content: ""; flex: 0 0 auto; width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform .14s var(--ease);
}
.adder[open] summary::before { transform: rotate(90deg) translateX(1px); }
.adder summary:hover { background: var(--accent-soft); }
.adder summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.adder[open] summary { border-bottom: 1px solid var(--line-soft); margin-bottom: 6px; }
.adder form { padding-bottom: 20px; }

.msg { font-size: 13.5px; border-radius: 7px; margin: 12px 0 0; }
.msg:empty { display: none; }
.msg.good { background: var(--green-soft); color: var(--green); padding: 9px 12px; }
.msg.bad  { background: var(--red-soft);   color: var(--red);   padding: 9px 12px; }
.msg.note { background: var(--amber-soft); color: var(--amber); padding: 9px 12px; }

.mini {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 5px 10px; font: inherit; font-size: 12.5px; font-weight: 550;
  color: var(--ink-2); cursor: pointer; white-space: nowrap;
  text-decoration: none; display: inline-flex; align-items: center; gap: 5px;
}
/* A small button that undoes something across the whole roster, which
   nothing else its size does. Amber border and ink so it reads as a caution
   before anyone gets as far as the confirm behind it. */
.mini.caution { border-color: var(--amber); color: var(--amber);
                background: var(--amber-soft); margin-left: 6px; }
.mini.caution .ico { width: 13px; height: 13px; }
.mini.caution:hover { border-color: var(--amber); color: var(--amber);
                      background: var(--amber-soft); filter: brightness(.95); }
.mini:hover { border-color: var(--line-strong); color: var(--ink);
              background: var(--panel-2); }
td.actions { white-space: nowrap; display: flex; gap: 6px; }
td.dimcell { color: var(--ink-3); }
.mono { font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
.small { font-size: 11.5px; line-height: 1.5; }
.tag.warn { background: var(--amber-soft); color: var(--amber); }
/* The name is a shortcut to the same page, not the only way there. Dotted
   underline so it reads as interactive without competing with the nav. */
a.name { color: var(--ink-2); text-decoration: none;
         border-bottom: 1px dotted var(--ink-3); }
a.name:hover { color: var(--ink); border-bottom-color: var(--accent); }

/* ------------------------------------------------------- system status */
.sysbanner {
  background: var(--red-soft); color: var(--red);
  border-bottom: 1px solid var(--red);
  padding: 10px 20px; font-size: 13.5px;
  /* top: 0 now the navigation is a left rail. It was offset by the height
     of a pinned top bar that no longer exists, so it sat over the first
     line of every page. */
  position: sticky; top: var(--asbar, 0px); z-index: 19;
}
.sysbanner b { margin-right: 6px; }
.sysbanner a { color: var(--red); font-weight: 600; margin-left: 8px; }

.statushead { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.statushead h2 { margin: 0; }

.mini-table { width: 100%; border-collapse: collapse; font-size: 13px;
              margin-top: 14px; }
.mini-table th { text-align: left; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3);
  padding: 0 12px 8px 0; border-bottom: 1px solid var(--line); }
.mini-table td { padding: 8px 12px 8px 0;
                 border-bottom: 1px solid var(--line-soft); color: var(--ink-2); }
.mini-table tr:last-child td { border-bottom: none; }

.warnline { background: var(--amber-soft); color: var(--amber);
            border-radius: 7px; padding: 10px 12px; font-size: 13px;
            margin: 12px 0 0; }
.mini.big { padding: 8px 14px; font-size: 13px; }
.dim { color: var(--ink-3); }

.subform { border-top: 1px solid var(--line-soft); margin-top: 20px; padding-top: 6px; }
.check.big { font-size: 14px; margin-top: 12px; }
.check.big b { font-weight: 600; color: var(--ink); }

/* Tag strings are single values, not prose. Wrapping them word-by-word made
   "elite csm holly" four lines tall and the row unreadable. */
td.tags { white-space: nowrap; }

.brand { display: flex; align-items: center; gap: 9px; }
.brand img { border-radius: 5px; display: block; }

/* ------------------------------------------------------------- edit dialog */
dialog {
  border: 1px solid var(--line); border-radius: 12px; padding: 24px 26px 22px;
  background: var(--surface); color: var(--ink); max-width: 620px; width: 92vw;
  box-shadow: 0 20px 60px -20px rgba(0,0,0,.4);
  /* A dialog must not be a scroll container.

     Tooltips are pseudo-elements on the controls inside it, and a bubble that
     reaches past the right edge counts toward scrollWidth. So hovering
     "+ Add another link" grew a horizontal scrollbar, and the bubble was then
     cut off by the scroll it had just caused -- which reads as a broken
     tooltip rather than as a scrolled box. Seven dialogs had one, and not one
     of them because any actual content was too wide to fit.

     It has to be the whole box: `overflow-x: visible` with `overflow-y: auto`
     is not a combination CSS allows -- the visible axis computes back to auto.
     A dialog that genuinely needs to scroll therefore scrolls on an inner
     element instead, the way #ctdlg does. */
  overflow: visible;
}
dialog::backdrop { background: rgba(10,16,22,.45); }
dialog h2 { font-size: 16px; font-weight: 600; margin: 0 0 4px; }
dialog .row { margin-top: 4px; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 10px;
                  margin-top: 22px; align-items: center; }
/* Pushes a destructive action to the far left, away from the one somebody is
   reaching for. Nothing sits between Cancel and Save. */
.dlgspacer { flex: 1; }
/* A consequence of the choice above it, not an error. Amber: this is going
   to happen, and it changes money. */
/* Whose commission a contract earns. Quiet when it is yours and all of it;
   named when it is somebody else's or shared, because that is the case worth
   reading off the page. */
.tag.earns { background: var(--accent-soft); color: var(--accent); }
/* What GoHighLevel holds, under what is on file here. Separated because one
   is a copy we have and the other is a place to go and look. */
.crmfound { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
.tag.earns.split { background: var(--amber-soft); color: var(--amber); }
.splitrow { align-items: end; }

.tierwarn { background: var(--amber-soft); color: var(--amber);
            border-radius: var(--r-sm); padding: 9px 12px; margin-top: 12px; }
/* The months somebody reads off when they invoice. Numbers line up, and the
   month still filling is marked rather than looking like a short one. */
.payoutpanel { margin-top: 20px; }
.payouttable td, .payouttable th { white-space: nowrap; }
.payouttable td { font-variant-numeric: tabular-nums; }
.payouttable td .dim { font-size: 12px; margin-left: 4px; }
/* On the cells, not the row -- a <tr> background is painted under them and
   shows through only where they do not reach -- and scoped the long way on
   purpose: the panel's own "tbody td" rule carries four classes, so anything
   shorter than this loses to it and the row stays the colour of every other. */
.panel > .tablewrap table.payouttable tbody tr.thismonth td {
  background: var(--amber-soft); }
/* The confirming button in any dialog. Keyed to shape rather than to one id,
   because every dialog has exactly one of these and half of them were coming
   out as bare browser buttons. `.primary` keeps its own rule; `.mini` is the
   quiet way out and stays quiet. */
.dialog-actions button[type=button]:not(.mini):not(.primary):not(.linkish) {
  padding: 9px 18px; border: none; border-radius: var(--r-sm);
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink); box-shadow: 0 1px 2px rgba(13,138,95,.24), 0 4px 12px rgba(13,138,95,.20); font: inherit; font-weight: 600; font-size: 14px; cursor: pointer;
}
.dialog-actions button[type=button]:not(.mini):not(.primary):not(.linkish):hover {
  filter: brightness(1.08); }
.dialog-actions .msg { margin: 0; }

/* A real button, not a bordered label. */
button.primary {
  padding: 10px 20px; border: none; border-radius: var(--r-sm);
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink); box-shadow: 0 1px 2px rgba(13,138,95,.24), 0 4px 12px rgba(13,138,95,.20); font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  white-space: nowrap;
}
button.primary:hover { filter: brightness(1.08); }
button.primary:disabled { opacity: .55; cursor: default; }
.panel.action { display: flex; align-items: center; justify-content: space-between;
                gap: 24px; flex-wrap: wrap; }
.panel.action h2 { margin-bottom: 4px; }
.panel.action .hint { margin: 0; max-width: 52ch; }

/* ------------------------------------------------------- client profile */
.crumb { margin: 0 0 14px; font-size: 13px; }
.crumb a { color: var(--ink-3); text-decoration: none; }
.crumb a:hover { color: var(--accent); }
.headactions { display: flex; gap: 8px; }
.cols { display: grid; grid-template-columns: 1fr 320px; gap: 16px; align-items: start; }
@media (max-width: 900px) { .cols { grid-template-columns: 1fr; } }
.page-head h1 .tag { vertical-align: 5px; margin-left: 6px; }

textarea {
  width: 100%; padding: 9px 11px; border: 1px solid var(--line);
  border-radius: 7px; font: inherit; font-size: 14px; background: var(--bg);
  color: var(--ink); resize: vertical;
}
textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px;
                 border-color: var(--accent); }

.entry { border-top: 1px solid var(--line-soft); padding: 14px 0 2px; }
.entry-head { display: flex; gap: 10px; align-items: baseline; font-size: 13px;
              margin-bottom: 5px; }
.entry-head b { font-weight: 600; }
.entry-body { white-space: pre-wrap; font-size: 14px; color: var(--ink-2);
              margin: 0 0 8px; }
.suggested { margin: 6px 0 4px; padding-left: 20px; }
.suggested li { font-size: 13.5px; color: var(--ink-2); margin-bottom: 4px; }
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { display: flex; justify-content: space-between; font-size: 13px;
               padding: 6px 0; border-bottom: 1px solid var(--line-soft); }
.timeline li:last-child { border-bottom: none; }
.empty.small { padding: 12px 0; border: none; background: none; font-size: 13px; }
td.due { color: var(--red); font-weight: 600; }

/* Contract urgency. Three bands rather than one threshold, so six weeks out
   does not look the same as three months out. */
td.band-urgent { color: var(--red); font-weight: 600; }
td.band-soon   { color: var(--amber); font-weight: 600; }
td.band-ahead  { color: var(--ink-2); }
.days { font-size: 11.5px; opacity: .75; margin-left: 4px; }
.overdue { color: var(--red); font-weight: 600; }
th[title] { cursor: help; text-decoration: underline dotted var(--line);
            text-underline-offset: 3px; }

/* Elite and Core as separate lists. A coach works them differently, and an
   empty Core section says something a missing one does not. */
.tiergroup { margin-bottom: 22px; }
.tierhead { display: flex; align-items: center; gap: 9px; font-size: 14px;
            font-weight: 600; margin: 0 0 8px; }
.tierhead .count { color: var(--ink-3); font-weight: 500; }
.tierhead .dim { font-weight: 400; font-size: 13px; }
/* A badge rather than a line of red text: a client with no email cannot be
   sent a check-in at all, so it is a fault on the record, not a note. */
.noemail {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 3px;
  padding: 2px 7px 2px 5px; border-radius: 999px;
  background: var(--red-soft); border: 1px solid var(--red);
  font-size: 11.5px; font-weight: 600; color: var(--red);
}
.noemail .ico { width: 12px; height: 12px; }
a.mini { text-decoration: none; display: inline-block; }
.warntext { color: var(--red); }
.sec .count { color: var(--ink-3); font-weight: 500; }
.timeline .notemeta { display: block; color: var(--ink-3); font-size: 12px;
                      padding: 0 0 6px; border-bottom: 1px solid var(--line-soft); }

/* Utility strip under the heading. These are occasional actions -- the roster
   is what the page is for, and it should not compete with a button you press
   twice a month. */
.toolbar { display: flex; gap: 4px; flex-wrap: wrap; margin: -6px 0 16px;
           padding-bottom: 12px; border-bottom: 1px solid var(--line-soft); }
.toolbar a { font-size: 12.5px; font-weight: 500; color: var(--ink-2);
             text-decoration: none; padding: 5px 10px; border-radius: 6px; }
.toolbar a:hover { background: var(--line-soft); color: var(--ink); }

.note-strong { background: var(--amber-soft); border: 1px solid var(--amber);
               border-radius: 8px; padding: 16px 18px; margin-top: 16px; }
.note-strong h3 { margin: 0 0 6px; font-size: 14px; color: var(--amber); }
.note-strong p { font-size: 13px; color: var(--ink-2); margin: 0 0 10px; }
.note-strong pre { font-family: ui-monospace, Menlo, monospace; font-size: 14px;
                   line-height: 1.9; margin: 0; background: var(--surface);
                   border-radius: 6px; padding: 12px 14px; color: var(--ink); }

/* ===================================================== CSM dashboard ===== */
.page.wide { max-width: 1500px; }

.viewingas {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  background: var(--blue-soft); border: 1px solid var(--blue); border-radius: 8px;
  padding: 10px 14px; margin-bottom: 14px; font-size: 13px; flex-wrap: wrap;
}
.viewingas a { color: var(--blue); font-weight: 600; text-decoration: none; white-space: nowrap; }

/* toolbar */
.hubbar { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; }
.who-hub { font-size: 17px; font-weight: 700; letter-spacing: -.015em; margin-right: 4px; }
.hubbar .spacer { flex: 1; }
/* Every search box that is not inside a <form>. The form rule above does not
   reach these, so each one that got added came out as a raw browser input --
   grey inset border, square corners -- next to styled ones. Listed together so
   the next one added is a selector, not a fourth copy of these declarations. */
.hubbar input[type=search],
.filters input[type=search],
.helpsearchbar input[type=search] {
  padding: 7px 12px; border: 1px solid var(--line); border-radius: 8px;
  font: inherit; font-size: 13.5px; background: var(--surface); color: var(--ink);
  min-width: 210px;
}
.hubbar input[type=search]:focus,
.filters input[type=search]:focus,
.helpsearchbar input[type=search]:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
button.tb, a.tb {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 8px 13px; font: inherit; font-size: 13px; font-weight: 550;
  color: var(--ink-2); cursor: pointer; text-decoration: none; white-space: nowrap;
  box-shadow: var(--shadow-1);
  display: inline-flex; align-items: center; gap: 7px;
}
button.tb:hover, a.tb:hover { border-color: var(--line-strong); color: var(--ink);
            box-shadow: var(--shadow-2); transform: translateY(var(--hover-lift-sm)); }
button.tb:active, a.tb:active { box-shadow: var(--shadow-1); }
/* The primary sits on a gradient so it has a light source, and keeps its own
   coloured shadow -- a flat rectangle of accent looks like a placeholder. */
button.tb.primary, a.tb.primary {
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  border-color: transparent; color: var(--accent-ink); font-weight: 600;
  box-shadow: 0 1px 2px rgba(13,138,95,.24), 0 4px 12px rgba(13,138,95,.20);
}
button.tb.primary:hover, a.tb.primary:hover { color: var(--accent-ink); filter: brightness(1.05);
                    box-shadow: 0 2px 4px rgba(13,138,95,.26), 0 8px 20px rgba(13,138,95,.26); }
button.tb.green, a.tb.green { background: linear-gradient(180deg, var(--accent-hi), var(--accent));
            border-color: transparent; color: var(--accent-ink); }
/* Asking for a reach-out is the one button in this row that puts a job on
   somebody else's desk, and it was reading as another grey button beside Edit
   details. It borrows the red the HQ reach-out panels already use, so the ask
   and the thing it lands in are recognisably the same colour. Deliberately
   not `primary`: green is "the normal next thing", and this is not. */
button.tb.reachask {
  background: var(--red-soft); border-color: var(--red); border-width: 1.5px;
  color: var(--red); font-weight: 600; box-shadow: none;
}
button.tb.reachask:hover {
  border-color: var(--red); color: var(--red);
  background: color-mix(in srgb, var(--red) 20%, var(--panel));
  box-shadow: var(--shadow-2);
}
button.tb.reachask:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

/* summary cards */
.cards4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
          gap: 12px; margin-bottom: 16px; }
.statcard { background: var(--panel); border: 1px solid var(--line);
            border-radius: var(--r-lg); padding: 16px 18px 17px;
            box-shadow: var(--shadow-1); }
a.statcard.clickable:hover { border-color: var(--accent-line); }
.statlabel { display: flex; align-items: center; gap: 6px; }
.info {
  display: inline-grid; place-items: center; width: 14px; height: 14px;
  border-radius: 50%; background: var(--line); color: var(--ink-3);
  font-size: 9px; font-weight: 700; font-style: italic; cursor: help;
}
.statcard b { display: block; font-size: 26px; font-weight: 700; letter-spacing: -.03em;
              line-height: 1.1; margin: 6px 0 0; font-variant-numeric: tabular-nums; }
.statcard b .of { font-size: 15px; font-weight: 500; color: var(--ink-3); }
.statcard b.money { color: var(--green); }
.substats { display: flex; align-items: center; gap: 6px; margin-top: 8px;
            font-size: 12px; color: var(--ink-3); }
.substats .pct { font-weight: 700; color: var(--ink); font-size: 13px; }

/* attention */
.attnbar {
  display: flex; width: 100%; justify-content: space-between; align-items: center;
  background: var(--red-soft); border: 1px solid var(--red); border-radius: 8px;
  padding: 11px 14px; margin-bottom: 10px; font: inherit; font-size: 13.5px;
  color: var(--red); cursor: pointer; text-align: left;
}
.attnbar:hover { filter: brightness(.98); }
.attnbar b { font-weight: 700; }
.attnaction { font-weight: 600; font-size: 12.5px; }

.alertpanel {
  background: var(--amber-soft); border: 1px solid var(--amber);
  border-radius: 8px; margin-bottom: 10px; padding: 0 14px;
}
.alertpanel summary { cursor: pointer; padding: 11px 0; font-size: 13.5px;
                      font-weight: 600; color: var(--amber); }
.alertlist { list-style: none; margin: 0; padding: 0 0 12px; }
.alertlist li { display: flex; justify-content: space-between; gap: 16px;
                padding: 4px 0; font-size: 13px; color: var(--ink-2); }
.alertlist a { color: var(--ink); text-decoration: none; font-weight: 500; }
.alertlist a:hover { text-decoration: underline; }
.alertlist .band-urgent { color: var(--red); font-weight: 600; }
.alertlist .band-soon { color: var(--amber); font-weight: 600; }

/* roster header and filters */
.rosterhead { display: flex; justify-content: space-between; align-items: flex-end;
              gap: 16px; margin: 22px 0 10px; flex-wrap: wrap; }
.rosterhead h2 { font-size: 16px; font-weight: 700; margin: 0; }
.rosterhead .dim { font-size: 12.5px; }
.filters { display: flex; gap: 5px; flex-wrap: wrap; }
.chipbtn {
  background: var(--surface); border: 1px solid var(--line); border-radius: 20px;
  padding: 5px 12px; font: inherit; font-size: 12.5px; font-weight: 500;
  color: var(--ink-2); cursor: pointer;
  /* A chip is one object. Without this its count badge wraps under the label
     and the row becomes a grid of two-line pills; the row should wrap between
     chips, never inside one. */
  display: inline-flex; align-items: center; gap: 2px; white-space: nowrap;
  text-decoration: none;
}
.chip-apart { margin-left: auto; }
.chipbtn:hover { border-color: var(--accent-line); }
.chipbtn.active { background: var(--accent); border-color: var(--accent);
                  color: var(--accent-ink); }
.chipbtn.active .tbcount { background: rgba(255, 255, 255, .22);
                           color: var(--accent-ink); }
/* A chip for the thing that is wrong -- overdue money. It reads as a warning
   before it is pressed, because the point of it is to be noticed, and keeps
   the red when active rather than turning accent like the rest. */
.chipbtn.warnchip { color: var(--red); border-color: var(--red-soft); }
.chipbtn.warnchip:hover { border-color: var(--red); }
.chipbtn.warnchip.active { background: var(--red); border-color: var(--red);
                           color: #fff; }

/* The date window over the master sheet. Labels sit inline with their field
   so the whole thing reads as one sentence across a row, and wraps as pairs
   rather than orphaning a label above its input. */
.paydates { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
            margin: 0 0 12px; }
.paydates .alabel { margin: 0 0 0 4px; font-size: 12px; }
.paydates select, .paydates input[type=date] {
  padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px;
  font: inherit; font-size: 13px; background: var(--surface); color: var(--ink);
}
.paydates select:focus, .paydates input[type=date]:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
.pcfilters select {
  padding: 5px 10px; border: 1px solid var(--line); border-radius: 8px;
  font: inherit; font-size: 12.5px; background: var(--surface); color: var(--ink);
}

/* the roster itself */
table.roster { font-size: 13px; min-width: 1180px; }
/* Header and body share one padding rule. They were 12px apart, which put
   every column's data out of step with the heading above it. */
table.roster th,
table.roster td { padding: 12px 14px; vertical-align: middle; text-align: left; }

/* Each client as its own card inside the table.
   A plain row separated by a hairline is hard to stay on across ten columns
   -- your eye drifts a line between the name and the tick at the far end,
   which is how a tick lands on the wrong person. Separated rows with their
   own ground make each client a single object.
   border-spacing needs border-collapse: separate, which is also what lets a
   row carry a shadow at all. */
table.roster { border-collapse: separate; border-spacing: 0 6px; }
table.roster thead th { border-bottom: none; }
table.roster tbody tr { background: var(--panel); }
table.roster tbody td {
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 1px rgba(20, 24, 29, .03);
}
table.roster tbody td:first-child {
  border-left: 1px solid var(--line);
  border-radius: var(--r-md) 0 0 var(--r-md);
}
table.roster tbody td:last-child {
  border-right: 1px solid var(--line);
  border-radius: 0 var(--r-md) var(--r-md) 0;
}
/* The whole row answers, not just the cell under the cursor. */
table.roster tbody tr:hover td {
  background: var(--accent-soft); border-color: var(--accent-line);
  box-shadow: 0 1px 2px rgba(13, 138, 95, .10);
}
table.roster tbody tr:hover td.strong { color: var(--accent); }
table.roster tbody tr:hover td.strong a { color: var(--accent); }
/* Beats table.roster td, which sets text-align on every cell. The tick has to
   sit under the middle of its heading or it reads as belonging to the column
   beside it -- which is how a tick lands on the wrong row. */
table.roster td.tb { text-align: center; }
table.roster th { padding-bottom: 9px; }
/* The checkbox column reads as a column of ticks, so it centres. */
table.roster th:nth-child(6), table.roster td:nth-child(6) { text-align: center; }
/* Flex on a <td> takes the cell out of the table layout, so its width stops
   agreeing with its header. The layout goes inside the cell instead. */
.cnamewrap { display: flex; align-items: flex-start; gap: 9px; }
td.cname { min-width: 240px; }
td.cname a { color: var(--ink); font-weight: 600; text-decoration: none; }
td.cname a:hover { color: var(--accent); }
td.cname .dim { display: block; font-size: 11.5px; color: var(--ink-3); margin-top: 1px; }
.dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; flex: none; }
.dot.all_good           { background: var(--green); }
.dot.needs_focus        { background: var(--amber); }
.dot.requires_attention { background: var(--red); }
.dot.hypercare_first_30 { background: var(--blue); }
.dot.hypercare_transfer { background: var(--blue); }
.dot.first_30           { background: var(--ink-3); }
.rowchips { display: flex; gap: 4px; margin-top: 4px; flex-wrap: wrap; }
/* Two hypercare windows, told apart at a glance: a new contract is routine
   and blue, a transfer is the one that needs a person on it and is amber. */
.tag.hc { background: var(--blue-soft); color: var(--blue); }
.tag.hc.transfer { background: var(--amber-soft); color: var(--amber); }

/* The hypercare countdown, on its own class rather than sharing .tag.hc --
   which has quietly become the generic blue tag and also carries "3
   follow-ups" and "2 check-ins".

   Violet, and the only violet on a roster row. It was blue: the same blue as
   .tag.elite, sitting directly beside it, so the one badge with a clock
   running on it read as part of the tier label. Bordered and a shade heavier
   than its neighbours, because 30 days is the window where a client decides
   whether this was a good idea and it should catch the eye before the tier
   does. */
.tag.hcbadge {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--gold-soft); color: var(--gold);
  border: 1px solid var(--gold-line); font-weight: 700;
  padding: 1px 7px 1px 6px;
}
/* 11px and scaling properly now the sprite uses <symbol viewBox>. At 10px in
   a cropped <g> the star lost its bottom two points and read as a blob. */
.tag.hcbadge .ico { width: 11px; height: 11px; flex: 0 0 11px; }
/* A transfer is the same clock for the same reason -- somebody new to them --
   so it is the same badge. The label already says which it is. */
.tag.hcbadge.transfer { background: var(--gold-soft); color: var(--gold); }

.pill { display: inline-block; font-size: 11.5px; font-weight: 500;
        padding: 3px 8px; border-radius: 5px; white-space: nowrap; }
.pill.grey   { background: var(--line-soft); color: var(--ink-2); }
.pill.plain  { background: var(--line-soft); color: var(--ink-2); }
.pill.green  { background: var(--green-soft); color: var(--green); }
.pill.amber  { background: var(--amber-soft); color: var(--amber); }
.pill.red    { background: var(--red-soft); color: var(--red); }
.pill.urgent { background: var(--red-soft); color: var(--red); font-weight: 600; }
.pill.soon   { background: var(--amber-soft); color: var(--amber); font-weight: 600; }
.pill.ahead  { background: var(--line-soft); color: var(--ink-2); }
.sub { display: block; font-size: 11px; color: var(--ink-3); margin-top: 1px; }
.sub.overdue { color: var(--red); font-weight: 600; }
/* The tick and its date sit under the middle of the column heading. Left
   aligned they read as belonging to the "Last call" column beside them,
   which is how a tick gets put on the wrong row. */
th.colcentre { text-align: center; }
th.colcentre .colfilter { margin: 0 auto; }
td.tb { text-align: center; }
td.tb input { width: 17px; height: 17px; margin: 0; cursor: pointer;
              accent-color: var(--accent); vertical-align: middle; }
td.tb .sub { text-align: center; }

/* ------------------------------------------------------- status picker
   A styled button and menu rather than a <select>. A native select drops an
   operating-system menu that no stylesheet can reach, so the colour that
   makes a status readable at a glance disappears at exactly the moment you
   are choosing one. */
.statuspick { position: relative; display: inline-block; }
.statusnow {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 9px 5px 8px; border-radius: var(--r-pill);
  border: 1px solid transparent; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 600; white-space: nowrap;
  max-width: 175px;
}
.statuslabel { overflow: hidden; text-overflow: ellipsis; }
.statusdot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 7px;
             background: currentColor; }
.statuscaret { width: 12px; height: 12px; flex: 0 0 12px; opacity: .55;
               transform: rotate(90deg); color: currentColor; }
.statusnow:hover { filter: brightness(.97); }
.statusnow[aria-expanded="true"] { box-shadow: var(--ring); }

.statusmenu {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0;
  min-width: 190px; padding: 5px; border-radius: var(--r-md);
  background: var(--panel); border: 1px solid var(--line);
  box-shadow: var(--shadow-3); animation: rise 140ms var(--ease);
}
.statusmenu.dropup { top: auto; bottom: calc(100% + 6px); }
.statusopt {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 10px; border: 0; border-radius: var(--r-sm);
  background: none; cursor: pointer; font: inherit; font-size: 13px;
  font-weight: 550; text-align: left; color: var(--ink-2);
}
.statusopt:hover { background: var(--lift-2); color: var(--ink); }
.statusopt.chosen { font-weight: 650; }
.statusopt.chosen::after { content: ""; margin-left: auto; width: 6px;
                           height: 6px; border-radius: 50%; background: currentColor; }
/* Each option wears its own status colour, so the menu teaches the palette. */
.statusopt.all_good           { color: var(--green); }
.statusopt.needs_focus        { color: var(--amber); }
.statusopt.requires_attention { color: var(--red); }
.statusnow.all_good           { background: var(--green-soft); color: var(--green); }
.statusnow.needs_focus        { background: var(--amber-soft); color: var(--amber); }
.statusnow.requires_attention { background: var(--red-soft);   color: var(--red); }

.statuspick.first_30           { background: var(--line-soft);  color: var(--ink-2); border-color: transparent; }

/* ======================================================== payments ====== */
.payhero { background: var(--hero-bg); border-radius: var(--r-lg);
           padding: 22px 24px; margin-bottom: var(--step-5);
           color: var(--hero-ink); border: 1px solid var(--hero-line);
           box-shadow: var(--shadow-2); }
.payfigs { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
           gap: 20px; align-items: start; }
.paylabel { font-size: 11px; font-weight: 600; letter-spacing: .07em;
            text-transform: uppercase; opacity: .72;
            display: flex; align-items: center; gap: 6px; }
.info.light { background: rgba(255,255,255,.22); color: var(--hero-ink); }
.payfig b { display: block; font-size: 27px; font-weight: 700; letter-spacing: -.03em;
            margin: 8px 0 5px; font-variant-numeric: tabular-nums; }
.payfig span { font-size: 12px; color: var(--hero-dim); }
.paytotal { background: var(--hero-badge); color: var(--hero-badge-ink);
            border-radius: var(--r-md); padding: 15px 18px;
            box-shadow: 0 4px 14px rgba(0, 0, 0, .18); }
.paytotal .paylabel { opacity: .8; }
.paytotal b { display: block; font-size: 27px; font-weight: 700; letter-spacing: -.03em;
              margin: 8px 0 5px; font-variant-numeric: tabular-nums; }
.paytotal span { display: block; font-size: 12px; opacity: .8; }
/* The one number on the page somebody is actually looking for. It sits in
   a row of four and read like a fourth statistic; when it is the sum of two
   things it has to win the row outright, or you go hunting for the total
   somewhere further down the page and find a different one (Stefan,
   2026-09-27). */
.paytotal.split { padding: 17px 20px; }
.paytotal.split b { font-size: 34px; margin: 6px 0 8px; }
/* The arithmetic, said once, in the box carrying the answer. */
.paysplit { font-weight: 600; opacity: 1 !important; font-size: 12.5px !important;
            font-variant-numeric: tabular-nums; margin-bottom: 4px; }
.paysplit i { font-style: normal; opacity: .65; padding: 0 2px; }
.collectedfrom { margin-top: 20px; padding-top: 16px;
                 border-top: 1px solid var(--hero-line); }
.chiprow { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 10px; }
.cashchip { background: rgba(255,255,255,.12); border-radius: 6px;
            padding: 5px 10px; font-size: 12.5px; }
.cashchip b { font-weight: 700; }

.chipbtn .n { opacity: .6; margin-left: 3px; }
.paygroup { background: var(--panel); border: 1px solid var(--line);
            border-radius: var(--r-lg); margin-bottom: 10px;
            box-shadow: var(--shadow-1); }
.paygroup[open] { border-color: var(--accent-line); box-shadow: var(--shadow-2); }
.paygroup summary { display: flex; align-items: center; gap: 10px; cursor: pointer;
                    padding: 13px 16px; font-size: 14px; flex-wrap: wrap; }
.pgname { font-weight: 600; }
.pgshape, .pgplan { font-size: 12.5px; color: var(--ink-3); }
.pgplan { font-style: italic; }
.pgspacer { flex: 1; }
.pgtotal { font-weight: 700; font-variant-numeric: tabular-nums; }
.paygroup[open] summary { border-bottom: 1px solid var(--line-soft); }

table.paytable { min-width: 620px; font-size: 13px; }
table.paytable td { padding: 11px 14px; border-bottom: 1px solid var(--line-soft);
                    vertical-align: middle; }
table.paytable tr:last-child td { border-bottom: none; }
td.seq { color: var(--ink-3); white-space: nowrap; width: 74px; }
td.amt b { font-weight: 600; font-variant-numeric: tabular-nums; }
td.amt.paidamt, td.amt.paidamt b { color: var(--green); }
td.dates { color: var(--ink-3); font-size: 12px; }
.paidline { display: block; color: var(--green); font-size: 12px; }
td.payactions { text-align: right; white-space: nowrap; }
td.payactions .mini { margin-left: 6px; }
.insthead { display: flex; align-items: center; gap: 8px; margin: 14px 0 8px;
            font-size: 12px; font-weight: 600; color: var(--ink-2); }
.instspacer { flex: 1; }
.instrow { display: grid; grid-template-columns: 1fr 1fr 34px; gap: 8px; margin-bottom: 7px; }
.instrow input { margin: 0; }
.instrow .i_del { color: var(--ink-3); }
.instrow .i_del:hover { color: var(--red); border-color: var(--red); }
/* Wraps. A filter row that runs off the right edge hides the filters at the
   end of it, and the last one here is "Needs attention". */
.viewtabs { display: flex; flex-wrap: wrap; align-items: center;
            gap: 6px; margin: 4px 0 18px; }
a.statcard { text-decoration: none; color: inherit; display: block; }
.golink { color: var(--accent); font-weight: 600; }
.instrow { grid-template-columns: 26px 1fr 1fr 34px 34px; align-items: center; }
/* The backfill row: amount and date on top, "already collected" beneath them,
   so the common case (a plan going forward) is not cluttered by the case that
   only comes up when an existing client is being entered. */
.instrow .i_paidwrap { grid-column: 2 / 4; display: flex; align-items: center;
                       gap: 6px; font-size: 12px; color: var(--ink-2);
                       margin: -2px 0 2px; }
.instrow .i_paidwrap input { width: auto; }
.instrow .i_paidon { grid-column: 2 / 4; }
.instrow.wasPaid .i_amt { border-color: var(--green); }
.instrow.wasPaid .i_paidwrap { color: var(--green); font-weight: 600; }
.i_num { font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.instrow .i_copy { color: var(--accent); }
.instrow .i_copy:hover { border-color: var(--accent-line); }
.insttotal { font-size: 13px; font-weight: 600; color: var(--green);
             margin: 6px 0 0; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- tasks */
button.tb.green, a.tb.green { background: var(--green); border-color: var(--green); color: #fff; }
button.tb.green:hover, a.tb.green:hover { filter: brightness(1.08); color: #fff; }

.pill.prio-urgent    { background: var(--red-soft);   color: var(--red); font-weight: 600; }
.pill.prio-this_week { background: var(--amber-soft); color: var(--amber); font-weight: 600; }
.pill.prio-whenever  { background: var(--line-soft);  color: var(--ink-2); }
.taskcell { background: none; border: none; padding: 0; cursor: pointer; font: inherit; }
.taskcell:hover .pill { filter: brightness(.95); }

.prios { display: flex; gap: 7px; margin-top: 4px; flex-wrap: wrap; }
.prio { display: flex; align-items: center; gap: 6px; padding: 6px 11px;
        border: 1px solid var(--line); border-radius: 20px; font-size: 13px;
        font-weight: 500; cursor: pointer; margin: 0; text-transform: none;
        letter-spacing: 0; color: var(--ink-2); }
.prio input { width: auto; margin: 0; }
/* How long a task takes: the same chips, one colour. */
.prio.est:has(input:checked) { background: var(--blue-soft); color: var(--blue); border-color: var(--blue); }
.estcustom { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink-2); }
.estcustom input { width: 72px; margin: 0; padding: 5px 8px; }
.taskest { display: inline-flex; align-items: center; font: inherit; font-size: 11.5px; font-weight: 600;
           padding: 1px 8px; border-radius: 999px; border: 1px solid var(--blue-soft);
           background: var(--blue-soft); color: var(--blue); cursor: pointer; }
.taskest.ro { cursor: default; }
.taskest.unset { background: transparent; color: var(--ink-3); border: 1px dashed var(--line-strong); font-weight: 500; }
.taskest.unset:hover { color: var(--ink-2); border-color: var(--ink-3); }
.taskcopy { display: inline-flex; align-items: center; font: inherit; font-size: 11.5px; font-weight: 600;
            padding: 1px 8px; border-radius: 999px; border: 1px solid var(--line);
            background: transparent; color: var(--ink-3); cursor: pointer; }
.taskcopy:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }
.taskcopy.copied { color: var(--green); border-color: var(--green); }

.grouptime { font-size: 12.5px; font-weight: 500; margin-left: 4px; }
.fitin { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 12.5px; }
.fitin .chipbtn { padding: 4px 10px; font-size: 12px; }
/* The words before each run of chips -- Urgency, Fits in -- as labels:
   dark and set in small caps, so they read as the name of the control
   and not as one more grey chip (Stefan, 2026-09-17). */
.fitin > .dim:first-child { color: var(--ink); font-weight: 700; font-size: 11.5px;
                            letter-spacing: .07em; text-transform: uppercase; margin-right: 2px; }
/* The urgency chips wear their bucket's colour once pressed, the same
   red, amber and grey as the pills on the groups below. */
.priochip.active.prio-urgent    { background: var(--red-soft);   border-color: var(--red);   color: var(--red); }
.priochip.active.prio-this_week { background: var(--amber-soft); border-color: var(--amber); color: var(--amber); }
.priochip.active.prio-whenever  { background: var(--line-soft);  border-color: var(--ink-3); color: var(--ink); }
.priochips { margin-right: 6px; }
.estpop { position: absolute; z-index: 40; display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
          max-width: 330px; padding: 10px 12px; background: var(--panel); border: 1px solid var(--line);
          border-radius: var(--r-md); box-shadow: var(--shadow-2); font-size: 12.5px; }
.estpop .estcustom input { width: 64px; }
.prio.urgent:has(input:checked)    { background: var(--red-soft);   color: var(--red);   border-color: var(--red); }
.prio.this_week:has(input:checked) { background: var(--amber-soft); color: var(--amber); border-color: var(--amber); }
.prio.whenever:has(input:checked)  { background: var(--line-soft);  color: var(--ink);   border-color: var(--ink-3); }
/* A subtask row and a link row are the same shape: one field, and a way to
   take it back off again. */
.subrow, .linkrow { grid-template-columns: 1fr 34px; }
.linkrow .l_del { color: var(--ink-3); }
/* At five the plus stops working, so it has to stop looking like it works --
   nothing else in the app disables a button, so there was no styling for it. */
button.mini:disabled { opacity: .45; cursor: default; box-shadow: none; }
button.mini:disabled:hover { border-color: var(--line); color: var(--ink-3);
                             background: var(--panel); transform: none; }
.linkrow .l_del:hover { color: var(--red); border-color: var(--red); }
/* Several links on one task read as a list of places, so they need to be
   separable at a glance rather than running into each other. */
.taskmeta .tasklink { font-variant-numeric: tabular-nums; }
.taskmeta .tasklink + .tasklink { margin-left: 2px; }

.taskview { min-width: min(560px, 86vw); }
.taskitem { border-left: 3px solid var(--line); padding-left: 12px; margin-bottom: 14px; }
.taskitem.prio-urgent    { border-left-color: var(--red); }
.taskitem.prio-this_week { border-left-color: var(--amber); }
.taskline { display: flex; gap: 9px; align-items: flex-start; font-size: 14px;
            margin: 0 0 4px; cursor: pointer; text-transform: none; letter-spacing: 0;
            font-weight: 400; color: var(--ink-2); }
.taskline input { width: 16px; height: 16px; margin: 2px 0 0; flex: none; }
.taskline b { color: var(--ink); font-weight: 600; }
.taskline.sub { margin-left: 24px; font-size: 13px; }
.taskmeta { display: flex; gap: 8px; align-items: center; margin-top: 4px;
            font-size: 12px; flex-wrap: wrap; }
.tasknotes { display: block; font-size: 12.5px; color: var(--ink-3); margin-top: 4px;
             white-space: pre-wrap; }
table.roster th { position: relative; }
table.roster th span { display: block; }
.colfilter {
  display: block; margin-top: 4px; width: 100%; max-width: 130px;
  border: 1px solid var(--line); border-radius: 5px; padding: 2px 4px;
  font: inherit; font-size: 11px; font-weight: 500; text-transform: none;
  letter-spacing: 0; color: var(--ink-3); background: var(--surface);
  cursor: pointer;
}

/* ----------------------------------------------------------- task board */
.tbcount { background: var(--accent); color: var(--accent-ink); border-radius: 10px;
           padding: 1px 6px; font-size: 11px; font-weight: 700; margin-left: 4px; }
.taskgroup { margin-bottom: 24px; }
.taskgroup .tierhead .count { color: var(--ink-3); font-weight: 500; }
.taskcard { background: var(--surface); border: 1px solid var(--line);
            border-left: 3px solid var(--line); border-radius: 9px;
            padding: 13px 16px; margin-bottom: 8px; }
.taskgroup.prio-urgent    .taskcard { border-left-color: var(--red); }
.taskgroup.prio-this_week .taskcard { border-left-color: var(--amber); }
.taskcard.isdone { opacity: .55; }
/* Clicking the card opens it, so it has to look like something you can
   click. This is the whole affordance -- there is no Edit button, because
   one would sit on every row of a list whose job is being scanned. The
   checkbox, the links and the buttons keep their own cursors. */
.taskcard.editable { cursor: pointer; transition: border-color .12s ease, background .12s ease; }
.taskcard.editable:hover { border-color: var(--line-strong); background: var(--hover-tint); }
.taskcard.editable:hover .taskbody > b { text-decoration: underline;
                                         text-decoration-color: var(--line-strong);
                                         text-underline-offset: 3px; }
.taskcard.editable input[type=checkbox], .taskcard.editable a,
.taskcard.editable button { cursor: pointer; }
.taskcard.editable .tasknotes, .taskcard.editable .taskbody > b { cursor: pointer; }
/* Folding away after a tick: height, padding and margin run down together. */
.taskcard.leaving { overflow: hidden; opacity: 0; pointer-events: none;
                    transition: height .24s ease, margin .24s ease, padding .24s ease, opacity .18s ease; }
@media (prefers-reduced-motion: reduce) { .taskcard.leaving { transition: none; } }
.taskcard .taskline.isdone > span { text-decoration: line-through; }
.taskbody { display: block; }
.taskmeta a { color: var(--accent); text-decoration: none; font-weight: 500; }
.taskmeta a:hover { text-decoration: underline; }
.duetoday { color: var(--amber); font-weight: 600; }
.metric b.overdue { color: var(--red); }
.toolbar input[type=search] {
  padding: 5px 10px; border: 1px solid var(--line); border-radius: 7px;
  font: inherit; font-size: 12.5px; background: var(--surface); color: var(--ink);
  min-width: 200px;
}
.toolbar .spacer { flex: 1; }

/* The row of controls under a contract's payments. Deliberately quiet: these
   are corrections, not the thing you came to the page to do. */
.payfoot { display: flex; align-items: center; gap: 8px; padding: 10px 2px 2px;
           border-top: 1px solid var(--line-soft); margin-top: 6px;
           flex-wrap: wrap; }
.mini.danger { color: var(--ink-3); }
.mini.danger:hover { color: var(--red); border-color: var(--red); }
tr[data-state="void"] .amt b, tr[data-state="void"] .seq {
  text-decoration: line-through; color: var(--ink-3); }
tr[data-state="void"] { opacity: .65; }
/* A removed payment: struck through, said in red, and greyed as a row.
   Grey "removed" in the collected column looked like a state the payment
   was still in, and the commission beside it looked like money owed. */
table.paytable tr[data-state="void"] td { color: var(--ink-3); background: var(--panel-2); }
table.paytable tr[data-state="void"] s { text-decoration: line-through; text-decoration-color: var(--red);
                                          text-decoration-thickness: 1.5px; color: var(--ink-3); }
.tag.removed { background: var(--red-soft); color: var(--red); border: 1px solid rgba(192, 57, 47, .25);
               font-weight: 700; letter-spacing: .02em; }
.removedwhy { display: block; font-size: 11.5px; color: var(--ink-3); margin-top: 3px;
              max-width: 26ch; margin-left: auto; text-align: right; }

/* The balance line: what is owed on the deal, next to what is still invoiced.
   These agree until somebody pays more than their plan said, and the moment
   they part company is the moment somebody asks. */
.balanceline { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
               padding: 9px 2px 0; font-size: 13px; color: var(--ink-2);
               font-variant-numeric: tabular-nums; }
.balanceline b { color: var(--ink); }
.balanceline b.good { color: var(--green); }
.bsep { color: var(--ink-3); }
.gapnote { font-size: 12px; padding: 2px 8px; border-radius: 999px; }
.gapnote.over  { background: var(--blue-soft); color: var(--blue); }
.gapnote.under { background: var(--amber-soft); color: var(--amber); }
.pgbalance { font-variant-numeric: tabular-nums; font-weight: 600;
             color: var(--ink-2); margin-right: 14px; }
.editstamp { display: block; font-size: 11px; color: var(--ink-3); margin-top: 2px; }
.editstamp b { color: var(--ink-2); }
.editstamp b.over { color: var(--blue); }
.balanceline b.over, .pgbalance .over { color: var(--blue); }

/* ------------------------------------------------------------ check-ins */
.tbcount.amber { background: var(--amber); }
.cicard { background: var(--surface); border: 1px solid var(--line);
          border-radius: 12px; margin-bottom: 12px; padding: 0 16px;
          box-shadow: var(--shadow-1);
          transition: box-shadow .16s ease, transform .16s ease,
                      border-color .16s ease; }
/* The check-in being read should not look like the ones that are not. */
.cicard[open] { border-color: var(--accent-line); box-shadow: var(--shadow-2); }
.cicard[open]:hover { transform: none; }
.cicard.answered { opacity: .9; box-shadow: none; }
.cicard.answered:hover { box-shadow: var(--shadow-1); }
.cicard summary { display: flex; align-items: center; gap: 8px; padding: 12px 0;
                  cursor: pointer; list-style: none; flex-wrap: wrap; }
.cicard summary::-webkit-details-marker { display: none; }
.ciname { font-weight: 600; }
.cidate { font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.tag.grade-1 { background: var(--grade-1-soft); color: var(--grade-1); }
.tag.grade-2 { background: var(--grade-2-soft); color: var(--grade-2); }
.tag.grade-3 { background: var(--grade-3-soft); color: var(--grade-3); }
.tag.grade-4 { background: var(--grade-4-soft); color: var(--grade-4); }
.tag.grade-5 { background: var(--grade-5-soft); color: var(--grade-5); }
.matchnote { font-size: 12px; color: var(--amber); background: var(--amber-soft);
             padding: 7px 10px; border-radius: 8px; margin: 0 0 10px; }
/* history rather than a doubt: how it got here, nothing to do about it */
.matchnote.past { color: var(--ink-3); background: var(--lift-2); }
/* The short structured answers as a row of tiles, the written ones as their
   own cards underneath. A check-in is scanned before it is read: which area,
   how supported, what score -- then the paragraphs. */
/* 190px, not 158: the longest real answer on this form is
   "Management/Leadership", which has nothing to break at and looked broken
   when it wrapped mid-word in a narrower tile. Sized to the content that
   actually exists rather than to a round number. */
.answergrid { display: grid; gap: 8px; margin: 0 0 8px;
              grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.acard { background: var(--bg); border: 1px solid var(--line-soft);
         border-radius: 10px; padding: 10px 12px; }
.alabel { display: block; font-size: 11px; font-weight: 600; color: var(--ink-3);
          text-transform: uppercase; letter-spacing: .03em; }
/* "Management/Leadership" has no space to break at, so it has to be allowed
   to break anywhere -- otherwise it runs off the edge of its tile. */
.avalue { display: block; margin-top: 3px; font-weight: 600;
          overflow-wrap: anywhere; line-height: 1.3; }
.acard.score .avalue { font-size: 22px; font-variant-numeric: tabular-nums;
                       line-height: 1.15; }
.acard.support .avalue { color: var(--ink); }
/* Five answers, five steps. A scale, not good-or-bad: "self-sufficient" and
   "feel lost" are not two states with a line between them. */
.acard.grade-1 { background: var(--grade-1-soft); border-color: var(--grade-1); }
.acard.grade-1 .avalue { color: var(--grade-1); }
.acard.grade-2 { background: var(--grade-2-soft); border-color: var(--grade-2); }
.acard.grade-2 .avalue { color: var(--grade-2); }
.acard.grade-3 { background: var(--grade-3-soft); border-color: var(--grade-3); }
.acard.grade-3 .avalue { color: var(--grade-3); }
.acard.grade-4 { background: var(--grade-4-soft); border-color: var(--grade-4); }
.acard.grade-4 .avalue { color: var(--grade-4); }
.acard.grade-5 { background: var(--grade-5-soft); border-color: var(--grade-5);
                 box-shadow: var(--shadow-1); }
.acard.grade-5 .avalue { color: var(--grade-5); }
.chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.chip { font-size: 11px; font-weight: 600; padding: 2px 7px; border-radius: 999px;
        background: var(--surface); border: 1px solid var(--line); }
.acard.written { margin-bottom: 8px; background: var(--surface);
                 border-color: var(--line); box-shadow: var(--shadow-1); }
.atext { margin: 5px 0 0; white-space: pre-wrap; line-height: 1.5; }
.atext:empty::after { content: "\2014"; color: var(--ink-3); }
.exrow { flex-wrap: wrap; gap: 8px; }
.exactions { display: flex; align-items: center; gap: 6px; }
.exactions select { margin: 0; width: auto; }

dialog { box-shadow: var(--shadow-3); }
dialog::backdrop { background: rgba(16, 32, 48, .38); backdrop-filter: blur(2px); }
input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible { outline: none; box-shadow: var(--ring); }

/* The webhook URLs. Wide, monospaced and selectable: they are pasted, not
   read, and a URL that wraps mid-token invites a half-copied paste. */
.hooklist { display: grid; gap: 8px; margin: 10px 0; }
.hookrow { display: grid; grid-template-columns: 130px 1fr auto; gap: 8px;
           align-items: center; }
.hooklabel { font-size: 12px; font-weight: 600; color: var(--ink-2); }
.hookurl { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
           font-size: 12px; margin: 0; }
@media (max-width: 640px) {
  .hookrow { grid-template-columns: 1fr auto; }
  .hooklabel { grid-column: 1 / -1; }
}

/* ------------------------------------------------- notification centre */
.notifpanel { background: var(--surface); border: 1px solid var(--line);
              border-radius: 12px; box-shadow: var(--shadow-1);
              padding: 12px 14px; margin: 0 0 16px; }
.notifhead { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.notifhead h2 { margin: 0; font-size: 15px; }
.notiflist { list-style: none; margin: 0; padding: 0; }
.notifrow { display: grid; gap: 10px; align-items: center; padding: 8px 2px;
            grid-template-columns: 104px 1fr auto auto auto;
            border-top: 1px solid var(--line-soft); }
.notifrow:first-child { border-top: none; }
/* Unread is carried by a bar and weight, not by colour alone. */
.notifrow.unread { box-shadow: inset 2px 0 0 var(--blue); padding-left: 8px; }
.notifrow:not(.unread) .nbody b { font-weight: 500; color: var(--ink-2); }
.nkind { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px;
         text-align: center; }
.k-checkin { background: var(--blue-soft); color: var(--blue); }
.k-onboarding_call { background: var(--green-soft); color: var(--green); }
.k-support_call { background: var(--amber-soft); color: var(--amber); }
/* A reply. Blue: not a call and not a form -- a person waiting on an answer. */
.k-sms { background: var(--blue-soft); color: var(--blue); }
.nbody { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.nbody b { overflow-wrap: anywhere; }
.nwhen { font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.upcoming { display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
            margin-top: 10px; padding-top: 10px;
            border-top: 1px solid var(--line-soft); }
.upcall { font-size: 12px; }
@media (max-width: 720px) {
  .notifrow { grid-template-columns: 1fr auto; }
  .nkind, .nwhen { grid-column: 1; justify-self: start; }
}

.scanbtns { display: flex; align-items: center; gap: 8px; }

/* ------------------------------------------------------------- tooltips */
/* data-tip on any element. Preferred over a native title=: the browser's own
   tooltip waits about a second, cannot be styled, and never appears for
   somebody on a touch screen or navigating by keyboard. This one shows on
   hover AND on focus, so the explanation is reachable either way. */
[data-tip] { position: relative; }
/* While a tooltip is showing, its HOST rises above its siblings. The bubble
   having a high z-index is not enough on its own: inside a table it competes
   cell by cell, which is how a tip ended up drawn over one column and under
   the next. Raising the host settles the whole thing in one place. */
[data-tip]:hover, [data-tip]:focus-visible { z-index: 60; }
[data-tip]::after,
[data-tip]::before { opacity: 0; pointer-events: none;
                     transition: opacity .14s ease, transform .14s ease; }
[data-tip]::after {
  content: attr(data-tip);
  /* Set by the aligner in base.html when anchoring to an edge is not enough --
     a control scrolled half out of its own container has an edge that is
     already outside, so the bubble is nudged back in rather than lined up
     with it. `translate` rather than `transform` so it composes with the
     slide-in animation instead of replacing it. */
  translate: var(--tipdx, 0px) 0;
  position: absolute; bottom: calc(100% + 9px); left: 50%;
  transform: translateX(-50%) translateY(3px);
  width: max-content; max-width: 260px; white-space: normal; text-align: left;
  background: var(--ink); color: var(--surface);
  font-size: 12px; font-weight: 500; line-height: 1.45; letter-spacing: 0;
  padding: 8px 10px; border-radius: 8px; box-shadow: var(--shadow-2);
  z-index: 40;
}
/* The little arrow, so it reads as attached to the thing it explains. */
[data-tip]::before {
  content: ""; position: absolute; bottom: calc(100% + 4px); left: 50%;
  transform: translateX(-50%) translateY(3px);
  border: 5px solid transparent; border-top-color: var(--ink); z-index: 41;
}
[data-tip]:hover::after, [data-tip]:hover::before,
[data-tip]:focus-visible::after, [data-tip]:focus-visible::before {
  opacity: 1; transform: translateX(-50%) translateY(0);
}
/* Near the right edge of a panel a centred bubble runs off. */
[data-tip-align="end"]::after { left: auto; right: 0; transform: translateY(3px); }
[data-tip-align="end"]:hover::after,
[data-tip-align="end"]:focus-visible::after { transform: translateY(0); }
[data-tip-align="end"]::before { left: auto; right: 14px; transform: translateY(3px); }
[data-tip-align="end"]:hover::before,
[data-tip-align="end"]:focus-visible::before { transform: translateY(0); }

/* The mirror of it, for the other edge. A tag in the client-name cell is at
   the far LEFT of the page, so a centred bubble runs off that side and gets
   cut in half -- and the longer the explanation, the more of it goes. This
   pins the bubble's left edge to the tag and lets it open rightwards, where
   there is a whole table's worth of room. */
[data-tip-align="start"]::after { left: 0; right: auto; transform: translateY(3px); }
[data-tip-align="start"]:hover::after,
[data-tip-align="start"]:focus-visible::after { transform: translateY(0); }
[data-tip-align="start"]::before { left: 14px; right: auto; transform: translateY(3px); }
[data-tip-align="start"]:hover::before,
[data-tip-align="start"]:focus-visible::before { transform: translateY(0); }

/* Tooltips that have to open DOWNWARD, because there is nothing above them.
   A profile opened from the roster is this page inside an iframe, with the
   card's own bar directly over it: the header buttons sit at document top 0,
   so a bubble drawn upward is 95px above the frame and the iframe simply cuts
   it off. It reads as the bar swallowing the tooltip, which is what it looks
   like. There is always room below -- that is the page.

   Applied by position rather than by id: anything that lands in the header
   row of a panel has the same problem, including buttons added later. `th`
   already flipped this way for its own reason and keeps its own rule. */
.headactions [data-tip]::after,
.factstrip [data-tip]::after,
.topbar [data-tip]::after { bottom: auto; top: calc(100% + 9px); }
.headactions [data-tip]::before,
.factstrip [data-tip]::before,
.topbar [data-tip]::before {
  bottom: auto; top: calc(100% + 4px);
  border-top-color: transparent; border-bottom-color: var(--ink);
}
/* The slide-in starts from above now, so the bubble still moves toward the
   thing it belongs to rather than away from it. */
.headactions [data-tip]::after, .headactions [data-tip]::before,
.factstrip [data-tip]::after, .factstrip [data-tip]::before,
.topbar [data-tip]::after, .topbar [data-tip]::before {
  transform: translateX(-50%) translateY(-3px);
}
.headactions [data-tip-align="end"]::after,
.factstrip [data-tip-align="end"]::after,
.topbar [data-tip-align="end"]::after { transform: translateY(-3px); }
.headactions [data-tip-align="end"]::before,
.factstrip [data-tip-align="end"]::before,
.topbar [data-tip-align="end"]::before { transform: translateY(-3px); }
.topbar [data-tip-align="start"]::after,
.topbar [data-tip-align="start"]::before { transform: translateY(-3px); }

/* the Merge into... picker: a filter box over a short list */
#mergedlg #mg_find { width: 100%; margin-bottom: 6px; }
#mergedlg #mg_into { width: 100%; font: inherit; font-size: 13px; padding: 4px;
                     border: 1px solid var(--line); border-radius: var(--r-sm);
                     background: var(--surface); color: var(--ink); }
#mergedlg #mg_into option { padding: 5px 8px; border-radius: 6px; }
#mergedlg #mg_into option[hidden] { display: none; }
#mergedlg #mg_why { width: 100%; }

/* the check-ins history, by week */
.civiews .chipbtn { margin-right: 2px; }
.ciweek { font-size: 12px; font-weight: 600; letter-spacing: .04em;
          text-transform: uppercase; color: var(--ink-3); margin: 18px 0 8px; }
.ciweek:first-of-type { margin-top: 4px; }

/* past check-ins panel on the GHL sync page */
.citools { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: end; margin-top: 10px; }
.cilabel { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; color: var(--ink-2); }
.cilabel select, .cilabel input { min-width: 220px; }

/* the Check-ins panel on a profile: the same card shape as the call log */
.checkinhistory .cientry {
  padding: 0 14px; margin: 0 0 8px; border: 1px solid var(--line);
  border-radius: 12px; background: var(--panel-2);
}
.checkinhistory .cientry:hover { border-color: var(--line-strong); }
.checkinhistory .cientry[open] { background: var(--panel); border-color: var(--accent-line); }
.checkinhistory .entry-head { display: flex; align-items: center; gap: 6px; padding: 10px 0;
                              cursor: pointer; list-style: none; flex-wrap: wrap; }
.checkinhistory .entry-head::-webkit-details-marker { display: none; }
.checkinhistory .callpeek { flex: 0 1 auto; }
.cibody { padding: 0 0 14px; }
.cibody .answergrid { margin-top: 2px; }
.ciacts { display: flex; gap: 6px; margin-top: 10px; }
/* the score line above the list: latest, and the average over everything */
.npsline { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
           margin: -4px 0 10px; font-size: 12.5px; color: var(--ink-2); }
.npsline b { font-size: 18px; color: var(--ink); font-variant-numeric: tabular-nums; }
.npsline .npsavg { color: var(--ink-3); }

/* --------------------------------------------------- call log + review */
.callentry { padding: 0 12px; }
.callentry summary { display: flex; align-items: center; gap: 8px; padding: 10px 0;
                     cursor: pointer; list-style: none; flex-wrap: wrap; }
.callentry summary::-webkit-details-marker { display: none; }
.calldate { font-size: 12px; font-weight: 600; color: var(--ink-2);
            font-variant-numeric: tabular-nums; }
.callpeek { color: var(--ink-3); font-size: 13px; overflow: hidden;
            text-overflow: ellipsis; white-space: nowrap; max-width: 46%; }
.callentry[open] .callpeek { display: none; }
.pastsug { background: var(--bg); border-radius: 9px; padding: 9px 11px;
           margin: 8px 0 12px; }
.suggested { list-style: none; margin: 5px 0 0; padding: 0; }
.suggested li { display: flex; align-items: center; gap: 8px; padding: 3px 0;
                font-size: 13px; }
.suggested li span { flex: 1; }
/* Wrapping flex rather than a fixed grid. A native date input has a minimum
   width Chrome will not go below, so a grid track narrower than that overflows
   however the other columns are sized -- and this dialog is ~560px wide inside
   a card while the window behind it is 1100px, so a viewport media query never
   fires to help. Wrapping copes with both without needing to know either. */
.sugrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
          padding: 9px 0; border-top: 1px solid var(--line-soft); }
.sugrow > .sugpick { flex: 0 0 auto; }
.sugtext { flex: 1 1 200px; }
.sugrow .prioset { flex: 0 0 auto; margin-left: auto; }
.sugrow input[type=date] { flex: 0 0 138px; }
.sugrow:first-child { border-top: none; }
.sugrow input[type=checkbox] { width: auto; margin: 0; }
.sugrow input[type=date] { margin: 0; min-width: 0; max-width: 100%; }
.sugtext { line-height: 1.35; min-width: 0; overflow-wrap: anywhere; }

/* How urgent, as three buttons rather than a dropdown: one click instead of
   two, and the colour says it at a glance where a closed select says nothing
   until you open it. The colours are the app's own severity set, so they mean
   the same here as everywhere else. */
.prioset { display: inline-flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
.priochip {
  padding: 4px 9px; font: inherit; font-size: 11.5px; font-weight: 600;
  border-radius: 999px; cursor: pointer; white-space: nowrap;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--ink-3);
  transition: color var(--t-colour), border-color var(--t-colour),
              background var(--t-colour);
}
.priochip:hover { color: var(--ink); border-color: var(--line-strong); }
.priochip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.priochip.urgent.on {
  background: var(--red-soft); border-color: var(--red); color: var(--red); }
.priochip.this_week.on {
  background: var(--amber-soft); border-color: var(--amber); color: var(--amber); }
.priochip.whenever.on {
  background: var(--panel-2); border-color: var(--ink-3); color: var(--ink-2); }


/* Add a task can grow: five links and a run of subtasks. Same shell as #ctdlg
   and #sugdlg -- the box stops at the window and the form inside it scrolls,
   rather than the dialog growing past the bottom edge and taking Add task
   with it. Sideways it does not scroll at all; tooltips are kept inside the
   box by the aligner in base.html, so nothing reaches the edge to push on it. */
#taskdlg { max-height: 92vh; overflow: hidden; }
#taskdlg[open] { display: flex; flex-direction: column; }
#taskdlg form { display: flex; flex-direction: column; min-height: 0;
                overflow-y: auto; overflow-x: hidden; padding-right: 4px; }
#taskdlg .dialog-actions {
  position: sticky; bottom: 0; margin-top: auto; padding: 12px 0 2px;
  background: var(--surface); border-top: 1px solid var(--line-soft);
}

/* Nothing in this dialog scrolls sideways either -- a tooltip on the button
   at its right edge was being clipped by the scroll container. */
#sugdlg { width: min(680px, 94vw); max-width: min(680px, 94vw);
          max-height: 92vh; overflow: hidden; }
#sugdlg[open] { display: flex; flex-direction: column; }
#sugdlg form { display: flex; flex-direction: column; min-height: 0;
               overflow-y: auto; overflow-x: hidden; padding-right: 4px; }
#sugdlg .dialog-actions {
  position: sticky; bottom: 0; margin-top: auto; padding: 12px 0 2px;
  background: var(--panel); border-top: 1px solid var(--line-soft);
}

/* ------------------------------------------------------------ your week
   Quieter than the panels it summarises. It is the first thing on the page and
   the first thing read, which is exactly why it must not shout -- a coloured
   box at the top of every roster every day stops being seen inside a week. */
.weekcard { background: var(--panel); border: 1px solid var(--line);
            border-radius: var(--r-lg); padding: 14px 18px 6px;
            margin: 0 0 var(--step-4); box-shadow: var(--shadow-1); }
/* The summary is the whole header, so the entire line is the hit target rather
   than a caret somebody has to aim at. */
.weekcard > summary { display: flex; align-items: baseline; gap: 10px;
                      cursor: pointer; list-style: none; padding: 2px 0 8px; }
.weekcard > summary::-webkit-details-marker { display: none; }
.weekcard > summary h2 { font-size: 13px; font-weight: 620; letter-spacing: .04em;
                         text-transform: uppercase; color: var(--ink-3); margin: 0; }
.weekcard > summary .dim { font-size: 12px; }
.weekcard > summary::after { content: "\25BE"; color: var(--ink-3); font-size: 11px;
                             margin-left: auto; transition: transform var(--t-move); }
.weekcard[open] > summary::after { transform: rotate(180deg); }
.weekcard > summary:focus-visible { outline: 2px solid var(--accent);
                                    outline-offset: 3px; border-radius: 4px; }
/* Shut, the card is just its own header, so the padding that gives the list
   room underneath would read as a gap below the line. */
.weekcard:not([open]) { padding-bottom: 12px; }
.weeklist { list-style: none; margin: 0; padding: 0; }
.weekrow { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
           padding: 7px 0; border-top: 1px solid var(--line-soft); }
.weekrow:first-child { border-top: none; }
/* The number and the sentence are one target, because "6 touch bases" is one
   thought and clicking the 6 rather than the words is not a distinction
   anybody makes. */
.weekgo { display: inline-flex; align-items: baseline; gap: 7px; padding: 0;
          border: none; background: none; font: inherit; font-size: 14px;
          color: var(--ink); text-align: left; text-decoration: none;
          cursor: pointer; border-bottom: 1px solid transparent; }
.weekgo b { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums;
            min-width: 1.2em; }
.weekgo:hover { color: var(--accent); border-bottom-color: var(--accent-line); }
.weekgo:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px;
                        border-radius: 3px; }
.weekgo.flat { cursor: default; }
.weekgo.flat:hover { color: var(--ink); border-bottom-color: transparent; }
.weekwhy { font-size: 12.5px; color: var(--ink-3); }
.weekempty { font-size: 13.5px; color: var(--ink-2); margin: 0 0 10px; }
@media (max-width: 700px) {
  /* The reason goes under the line rather than beside it; side by side they
     were two half-width columns of wrapped text. */
  .weekrow { flex-direction: column; gap: 2px; }
}

/* The reconcile report. Sections only appear when they have rows, so a clean
   run is a short page rather than four empty tables. */
.recsec { margin-bottom: var(--step-4); }
.recsec .hint { margin: 0 0 10px; }
.recsec table { min-width: 0; }

/* ------------------------------------------------------------ search bar
   Across the top of the working area, not in the rail: the results list wants
   more width than a sidebar has, and the box has to stay where it is when the
   rail collapses to icons. */
.topbar { position: sticky; top: var(--asbar, 0px); z-index: 40; display: flex;
          align-items: center; gap: 12px; padding: 10px var(--step-4);
          background: var(--bg); border-bottom: 1px solid var(--line-soft); }
/* The pill is the .gsearch box itself; the input inside it has no border of
   its own. The magnifying glass sits in the pill's left padding, the Fit Pro
   AI segment closes the right end, and the whole thing lights up as one when
   the box has focus. */
.gsearch { position: relative; flex: 1 1 auto; max-width: 640px;
           display: flex; align-items: center; gap: 6px;
           padding: 0 4px 0 12px; border: 1px solid var(--line); border-radius: 999px;
           background: var(--panel);
           transition: border-color var(--t-colour), box-shadow var(--t-colour); }
.gsearch:focus-within { border-color: var(--accent-line); box-shadow: var(--ring); }
.gsicon { flex: none; width: 16px; height: 16px; color: var(--ink-3); pointer-events: none; }
/* Beats `form input:not([type=checkbox])` at (0,2,2). */
.gsearch input[type=search].gsbox,
.topbar .gsearch input[type=search] {
  flex: 1 1 auto; min-width: 0; width: auto; margin: 0; padding: 9px 6px; font: inherit;
  font-size: 13.5px; border: none; border-radius: 0;
  background: transparent; color: var(--ink); box-shadow: none;
}
.topbar .gsearch input[type=search]::placeholder { color: var(--ink-3); }
.topbar .gsearch input[type=search]:focus { outline: none; box-shadow: none; }
.topbar .gsearch input[type=search]::-webkit-search-cancel-button { -webkit-appearance: none; }

.gsresults {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0;
  max-height: min(60vh, 460px); overflow-y: auto; z-index: 70;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-3); padding: 6px;
}
.gsgroup + .gsgroup { border-top: 1px solid var(--line-soft); margin-top: 4px;
                      padding-top: 4px; }
.gskind { font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
          text-transform: uppercase; color: var(--ink-3); padding: 6px 8px 3px; }
.gsitem { display: flex; align-items: baseline; gap: 8px; padding: 7px 8px;
          border-radius: var(--r-sm); text-decoration: none; color: var(--ink); }
/* One highlight, driven by the arrow keys, and hover sets the same look so
   the mouse and the keyboard never disagree about which row is next. */
.gsitem:hover, .gsitem.on { background: var(--accent-soft); color: var(--accent); }
.gslabel { font-size: 13.5px; font-weight: 550; overflow: hidden;
           text-overflow: ellipsis; white-space: nowrap; }
.gssub { font-size: 11.5px; color: var(--ink-3); margin-left: auto;
         white-space: nowrap; flex: none; }
.gsitem:hover .gssub, .gsitem.on .gssub { color: var(--accent); }
.gsnone { margin: 0; padding: 12px 10px; font-size: 13px; color: var(--ink-3); }
/* The assistant's row at the foot of the results: set apart, because it is
   the one row that does something rather than goes somewhere. */
.gsitem.gsask { background: var(--accent-soft); color: var(--accent); }
.gsitem.gsask .gssub { color: var(--accent); opacity: .8; }
.gsitem.gsask:hover, .gsitem.gsask.on { background: var(--accent); color: var(--accent-ink); }
.gsitem.gsask:hover .gssub, .gsitem.gsask.on .gssub { color: var(--accent-ink); }

/* The segment at the right end of the pill. A hairline divides it from the
   box, and it keeps the pill's curve on its outer side only. */
.askbtn { display: inline-flex; align-items: center; gap: 6px; flex: none;
          margin: 3px 0; padding: 6px 12px 6px 11px; border-radius: 999px;
          font: inherit; font-size: 12.5px; font-weight: 600;
          color: var(--accent); background: var(--accent-soft);
          border: none; border-left: 1px solid var(--line); border-top-left-radius: 0;
          border-bottom-left-radius: 0; cursor: pointer;
          white-space: nowrap; transition: background var(--t-colour), color var(--t-colour); }
.askbtn .ico { width: 14px; height: 14px; }
.askbtn:hover, .askbtn[aria-expanded="true"] { background: var(--accent); color: var(--accent-ink); }
.askbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 600px) { .askbtn { padding: 6px 9px; } }

@media (max-width: 720px) {
  /* The mobile menu button already lives up here. */
  .topbar { padding-left: 58px; }
}

/* Missing contract dates, on the roster row. It was the quietest tag there
   was -- a grey dashed outline -- because it sat on most of the roster and
   a warning on every row reads as texture. Stefan wants it noticed and the
   dates entered, so it is amber now: a real fill and border, a clock, one
   step below a payment overdue, which is money. */
/* Their message is the newest thing in the thread: somebody owes them a
   reply. Blue, not amber -- it is not late, it is waiting. */
.tag.texted { background: var(--blue-soft); color: var(--blue); text-decoration: none; }
.tag.texted:hover { filter: brightness(.97); }
.tag.texted .ico { width: 11px; height: 11px; }
.chipbtn.bluechip { border-color: var(--blue); color: var(--blue); background: var(--blue-soft); }
.tag.nodates { display: inline-flex; align-items: center; gap: 3px;
               background: var(--amber-soft); color: var(--amber);
               border: 1px solid var(--amber); text-decoration: none;
               font-weight: 650; }
.tag.nodates .ico { width: 11px; height: 11px; flex: 0 0 11px; }
.tag.nodates:hover { background: var(--amber); color: #fff; border-color: var(--amber); }

/* ------------------------------------------------------- Fit Pro AI
   A panel on the right, over the page rather than instead of it. Styled as a
   conversation: the person's messages on the right in the accent, the
   assistant's on the left in the panel colour, the way every messaging app
   has taught everybody to read a thread. */
.brainpanel {
  /* Below the workspace bar when there is one (--asbar is set by the bar
     itself), so the panel's own header -- New chat, close -- is never
     under it. */
  position: fixed; top: var(--asbar, 0px); right: 0; bottom: 0; width: min(440px, 100vw);
  z-index: 65; display: flex; flex-direction: column;
  background: var(--surface); border-left: 1px solid var(--line);
  box-shadow: -12px 0 40px -18px rgba(0, 0, 0, .28);
}
.brainpanel[hidden] { display: none; }
.brainhead { display: flex; align-items: center; gap: 8px; padding: 12px 14px;
             border-bottom: 1px solid var(--line); flex: none; }
.brainhead > .ico { width: 18px; height: 18px; color: var(--accent); }
.brainhead b { font-size: 14.5px; }
.brainscope { font-size: 11.5px; }
.brainclose { border: none; background: none; font-size: 22px; line-height: 1;
              color: var(--ink-3); cursor: pointer; padding: 2px 6px; border-radius: var(--r-sm); }
.brainclose:hover { color: var(--ink); background: var(--lift-2); }
.brainthread { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 14px;
               display: flex; flex-direction: column; gap: 10px; }
.brainempty { margin: auto 0; text-align: center; color: var(--ink-2);
              display: flex; flex-direction: column; align-items: center; gap: 12px; }
.brainempty[hidden] { display: none; }
.brainempty .ico.big { width: 34px; height: 34px; color: var(--accent); }
.brainempty p { margin: 0; font-size: 13.5px; max-width: 34ch; }
.brainhints { display: flex; flex-direction: column; gap: 6px; width: 100%; margin-top: 4px; }
.brainhint { font: inherit; font-size: 13px; text-align: left; padding: 9px 12px;
             border: 1px solid var(--line); border-radius: var(--r-md);
             background: var(--panel); color: var(--ink-2); cursor: pointer; }
.brainhint:hover { border-color: var(--accent-line); color: var(--accent); }

.brainmsg { max-width: 88%; padding: 10px 13px; font-size: 14px; line-height: 1.5;
            border-radius: 16px; word-wrap: break-word; }
.brainmsg.user { align-self: flex-end; background: var(--accent); color: var(--accent-ink);
                 border-bottom-right-radius: 5px; }
.brainmsg.assistant { align-self: flex-start; background: var(--panel); color: var(--ink);
                      border: 1px solid var(--line); border-bottom-left-radius: 5px; }
.brainmsg p { margin: 0 0 7px; } .brainmsg p:last-child { margin-bottom: 0; }
.brainmsg ul { margin: 4px 0 6px; padding-left: 18px; } .brainmsg li { margin: 2px 0; }
.brainlooked { margin-top: 8px; padding-top: 6px; border-top: 1px dashed var(--line);
               font-size: 11px; color: var(--ink-3); }
/* Three dots while it reads. */
.brainmsg.thinking { display: flex; gap: 4px; padding: 13px 15px; }
.brainmsg.thinking span { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3);
                          animation: brainpulse 1.1s infinite ease-in-out; }
.brainmsg.thinking span:nth-child(2) { animation-delay: .15s; }
.brainmsg.thinking span:nth-child(3) { animation-delay: .3s; }
@keyframes brainpulse { 0%, 80%, 100% { opacity: .3; transform: translateY(0); }
                        40% { opacity: 1; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .brainmsg.thinking span { animation: none; opacity: .6; } }

.braincompose { flex: none; display: flex; align-items: flex-end; gap: 8px;
                padding: 10px 12px 12px; border-top: 1px solid var(--line);
                background: var(--surface); }
.braincompose textarea { flex: 1; margin: 0; resize: none; min-height: 40px; max-height: 160px;
                         padding: 10px 12px; border-radius: 20px; font-size: 14px;
                         border: 1px solid var(--line); background: var(--panel); }
.braincompose textarea:focus { border-color: var(--accent-line); box-shadow: var(--ring); outline: none; }
.brainsend { flex: none; width: 40px; height: 40px; border-radius: 50%; border: none;
             background: var(--accent); color: var(--accent-ink); cursor: pointer;
             display: inline-flex; align-items: center; justify-content: center; }
.brainsend .ico { width: 16px; height: 16px; transform: rotate(-90deg); }
.brainsend:disabled { opacity: .5; cursor: default; }
.brainsend:hover:not(:disabled) { filter: brightness(1.06); }
/* With the panel open, the page keeps a right margin so nothing important
   sits under it on a wide screen. On a narrow one the panel is the screen. */
@media (min-width: 1100px) { body.brainopen .shellmain { padding-right: 440px; } }

/* --------------------------------------------------------------- ask
   A thread, not a search box. Questions and answers alternate down the page
   and the box stays at the bottom, because the second question is usually
   about the first answer. */
.askpage { max-width: 860px; }
.askthread { display: flex; flex-direction: column; gap: 12px; margin: 0 0 16px; }
.askmsg { max-width: 78ch; padding: 12px 16px; border-radius: var(--r-lg);
          font-size: 14.5px; line-height: 1.55; }
.askmsg.user { align-self: flex-end; background: var(--accent-soft); color: var(--ink);
               border-bottom-right-radius: 4px; }
.askmsg.assistant { align-self: flex-start; background: var(--panel);
                    border: 1px solid var(--line); border-bottom-left-radius: 4px;
                    box-shadow: var(--shadow-1); }
.askmsg.thinking { color: var(--ink-3); font-style: italic; }
.askmsg p { margin: 0 0 8px; } .askmsg p:last-child { margin-bottom: 0; }
.askmsg ul { margin: 4px 0 8px; padding-left: 20px; }
.askmsg li { margin: 3px 0; }
/* Which tools it used. Quiet, but present: an answer that says where it
   looked is one you can check, and one that does not is one you have to
   take on trust. */
.asklooked { margin-top: 8px; padding-top: 6px; border-top: 1px dashed var(--line);
             font-size: 11.5px; color: var(--ink-3); }
.askform { position: sticky; bottom: 0; background: var(--bg); padding: 10px 0 12px;
           border-top: 1px solid var(--line-soft); }
.askform textarea { width: 100%; margin: 0 0 8px; font-size: 14.5px; resize: vertical; }
.askfoot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.askfoot .dim { font-size: 12px; }
.askhints { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; }
.askhints[hidden] { display: none; }
.askhint { font: inherit; font-size: 13px; padding: 7px 12px; border-radius: 999px;
           border: 1px dashed var(--line-strong); background: var(--panel);
           color: var(--ink-2); cursor: pointer; text-align: left; }
.askhint:hover { border-color: var(--accent); color: var(--accent); border-style: solid; }

/* Admin: the write-up prompt. Monospace, because a prompt is read for its
   line breaks and its numbered rules, and proportional type hides both. */
.writeuppanel { margin-top: var(--step-5); }
.writeuppanel textarea { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                         font-size: 12px; line-height: 1.5; }
.writeuppanel select { max-width: 460px; }

/* HQ: calls not written up, by CSM. A list, not a chart -- the owners want
   the names, and "who" is the whole question. */
.unloggedpanel { margin-bottom: var(--step-5); }
.unloggedlist { list-style: none; margin: 8px 0 0; padding: 0; }
.unloggedrow { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
               padding: 8px 0; border-top: 1px solid var(--line-soft); font-size: 13.5px; }
.unloggedrow:first-child { border-top: none; }
.unloggedrow > b { min-width: 130px; }
.unloggedn { font-family: inherit; font-weight: 700; font-variant-numeric: tabular-nums;
             background: var(--accent-soft); color: var(--accent);
             padding: 1px 8px; border-radius: 999px; font-size: 12px; }
/* Not a second count -- the age of the oldest, which is the part that says
   whether a coach is behind or just hasn't got to this morning yet. */
.unloggedage { font-size: 12px; font-variant-numeric: tabular-nums;
               white-space: nowrap; }
.unloggedwho { color: var(--ink-2); }
.unloggedwho a { color: var(--ink); text-decoration: none; border-bottom: 1px dotted var(--ink-3); }
.unloggedwho a:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* A call the calendar says happened and nobody has written up. On the roster
   it is a tag that opens the profile with the form ready; on the profile it
   is the line above the transcript box. Accent rather than a warning colour:
   it is a job waiting, not something wrong. */
/* Red, like the Calls to write up chip and the rail badge it feeds: it
   means something is owed, and in green it read as good news beside Paid
   up (Stefan, 2026-09-15). */
.tag.callwaiting { background: var(--red-soft); color: var(--red);
                   text-decoration: none; display: inline-flex;
                   align-items: center; gap: 4px; }
.tag.callwaiting .ico { width: 11px; height: 11px; color: inherit; }
.tag.callwaiting:hover { background: var(--red); color: #fff; }
/* Past the ten days: still owed, still counted, but the backlog should not
   shout at the same pitch as the call that ended an hour ago -- otherwise a
   roster of old ones drowns out today's (Stefan, 2026-09-24). */
.tag.callwaiting.owedold { background: var(--lift-2); color: var(--ink-3); }
.tag.callwaiting.owedold:hover { background: var(--lift-4); color: var(--ink); }
/* The Hub did the writing; the person does the reading. Amber: owed, but
   most of the work is done. */
.tag.draftready { background: var(--amber-soft); color: var(--amber);
                  text-decoration: none; display: inline-flex;
                  align-items: center; gap: 4px; }
.tag.draftready .ico { width: 11px; height: 11px; color: inherit; }
.tag.draftready:hover { background: var(--amber); color: #fff; }
#fathompanel .steps { padding-left: 18px; margin: 6px 0 12px; }
#fathompanel .steps li { margin: 3px 0; }
#fathompanel #fathomkey, #fathompanel #firefliskey { width: 100%; }
/* Two note takers side by side until one is chosen. */
.ntchoice { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; }
.ntoption { border: 1px solid var(--line); border-radius: var(--r-md); padding: 12px 14px 8px; background: var(--panel-2); }
.ntoption h3 { margin: 0 0 6px; font-size: 15px; }
.ntswitch { margin-top: 10px; }
.ntswitch summary, .ffsetup summary { cursor: pointer; color: var(--ink-2); font-size: 13px; }
.ffsetup { margin: 8px 0 12px; padding: 10px 14px; border: 1px solid var(--amber); border-radius: var(--r-md); background: var(--amber-soft); }
.ffsetup:not([open]) { border-color: var(--line); background: transparent; }
.ffsetup summary { font-weight: 600; color: var(--ink); }
.ffsetup .steps li { margin: 6px 0; }
.fathomcatch { margin: 4px 0 10px; }
/* Filing a recording that matched nobody: a picker and two buttons under
   the note, on their own line so the table cell does not sprawl. */
.fileto { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; }
.fileto select { font: inherit; font-size: 13px; padding: 4px 8px; max-width: 260px; }
.pastebox { display: inline-flex; align-items: center; gap: 8px; max-width: 100%; margin: 4px 0 0; }
.pastebox code { display: inline-block; padding: 5px 8px; border-radius: var(--r-sm); background: var(--field);
                 border: 1px solid var(--line); font-size: 12px; word-break: break-all; user-select: all; }
.stepsub { display: block; }
#fathompanel table.plain { width: 100%; min-width: 0; margin: 10px 0 4px; font-size: 12.5px; }
#fathompanel table.plain td { padding: 8px 10px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
#fathompanel table.plain th { padding: 8px 10px; }
.pendingcall { display: flex; align-items: center; gap: 8px; margin: 0 0 10px;
               padding: 9px 12px; border-radius: var(--r-md);
               background: var(--accent-soft); color: var(--ink);
               font-size: 13.5px; }
.pendingcall .ico { color: var(--accent); flex: none; width: 15px; height: 15px; }
.pendingcall > span { flex: 1; }
.pendingcall .mini { flex: none; }

/* ------------------------------------------------- the two steps
   Write it up, then Save call -- side by side, numbered, each with a big
   button, because this is pressed every day and the order was not
   obvious when one was a small button in a row of controls. The step
   that is next is lit from what is in the box (a transcript lights 1, a
   breakdown lights 2); the other steps back. */
.callsteps { display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
             margin-top: 14px; }
.callstep { border: 1px solid var(--line); border-radius: 12px;
            padding: 12px 14px 12px; background: var(--panel-2);
            display: flex; flex-direction: column; gap: 8px; min-width: 0;
            transition: border-color var(--t-colour), background var(--t-colour),
                        opacity var(--t-colour); }
.stephead { display: flex; align-items: center; gap: 8px; font-size: 14px;
            flex-wrap: wrap; }
.stephead b { font-weight: 650; color: var(--ink); }
.stepsub { margin: -4px 0 0; font-size: 12.5px; color: var(--ink-3); }
/* The two cards share their rows, so the buttons sit level with each
   other whatever the text above them wraps to. */
@supports (grid-template-rows: subgrid) {
  .callstep { display: grid; grid-template-rows: subgrid; grid-row: span 5;
              align-items: start; }
}
.stepnum { display: grid; place-items: center; width: 22px; height: 22px;
           border-radius: 50%; background: var(--line-strong); color: var(--ink);
           font-size: 12px; font-weight: 700; flex: none; }
.callstep .stepbtn, form .callstep button[type=submit]:not(.linkish) {
  margin: 0; width: 100%; padding: 11px 16px; font-size: 14.5px;
  border-radius: var(--r-md); }
.stephint { margin: 0; font-size: 12.5px; color: var(--ink-3); line-height: 1.45; }
.writeopts { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.writeopts select { width: auto; max-width: 22ch; font-size: 12.5px;
                    padding: 4px 26px 4px 8px; }
.nudges { display: flex; gap: 5px; flex-wrap: wrap; }
.nudges[hidden] { display: none; }
/* Which step is next. */
.callsteps[data-stage="transcript"] #step1,
.callsteps[data-stage="draft"] #step2 {
  border-color: var(--accent-line); background: var(--accent-soft); }
.callsteps[data-stage="transcript"] #step1 .stepnum,
.callsteps[data-stage="draft"] #step2 .stepnum,
.callsteps[data-stage="writing"] #step1 .stepnum {
  background: var(--accent); color: var(--accent-ink); }
.callsteps[data-stage="transcript"] #step2,
.callsteps[data-stage="writing"] #step2 { opacity: .6; }
.callsteps[data-stage="writing"] #step1 { border-color: var(--accent-line); }
/* Working. The brain breathes -- scale and glow -- and three dots walk,
   so the half minute a write-up takes reads as the Hub thinking rather
   than the page hanging. Reduced motion: a steady brain, no walk. */
.writing { display: flex; align-items: flex-start; gap: 10px; font-size: 13px;
           color: var(--ink-2); line-height: 1.4; padding: 2px 0; }
.writing[hidden] { display: none; }
.writing .brain { width: 26px; height: 26px; flex: none; color: var(--accent);
                  animation: brainbreathe 1.6s ease-in-out infinite;
                  filter: drop-shadow(0 0 6px rgba(13, 138, 95, .35)); }
.writing b { color: var(--ink); }
.writing .dots::after { content: ""; display: inline-block; width: 1.2em; text-align: left;
                        animation: walkdots 1.2s steps(4, end) infinite; }
@keyframes brainbreathe { 0%, 100% { transform: scale(1); opacity: .75; }
                          50% { transform: scale(1.18); opacity: 1; } }
@keyframes walkdots { 0% { content: ""; } 25% { content: "."; } 50% { content: ".."; }
                      75% { content: "..."; } 100% { content: ""; } }
.stepbtn.busy { opacity: .85; cursor: progress; }
/* Copy for Slack: the first thing step 2 is for, above Save call -- a
   real button, outlined in the accent, full width, with the copy mark. */
.copyslack { display: flex; align-items: center; justify-content: center; gap: 8px;
             width: 100%; padding: 9px 14px; font: inherit; font-size: 14px;
             font-weight: 600; cursor: pointer; border-radius: var(--r-md);
             color: var(--accent); background: var(--panel);
             border: 1.5px solid var(--accent); }
.copyslack .ico { width: 15px; height: 15px; }
.copyslack:hover { background: var(--accent-soft); }
.copyslack.copied { background: var(--accent); color: var(--accent-ink); }
.copyslack:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.copyslack[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) {
  .writing .brain { animation: none; opacity: 1; }
  .writing .dots::after { animation: none; content: "\u2026"; }
}
.writestate { margin: 10px 0 0; font-size: 13px; color: var(--ink-2);
              min-height: 1.4em; }
.writestate:empty { display: none; }
@media (max-width: 640px) { .callsteps { grid-template-columns: 1fr; } }

/* Subfolders. Indented under their parent, with the parent's spine running
   down the left so the nesting is read as a shape rather than worked out
   from the indent alone. A subfolder is otherwise the same row -- same
   grip-less handle, same caret, same drop zone -- because it IS a folder. */
/* A subfolder reads as inside its parent the way every file tree says it:
   by indentation, and nothing else. A connecting line was tried and looked
   like a stray mark. So: set in from the parent, a lighter and smaller bar,
   the parent's colour on its spine so it belongs to the group, and the
   parent's bar left alone as the header. */
/* Both classes, because the nested list carries both and `.libfolders`
   zeroes the margin further down the sheet -- which is how the first
   version shipped with no indent at all and only the line showing. */
.libfolders.subfolders { list-style: none; margin: 6px 0 4px 40px; padding: 0; }
.subfolder { margin-bottom: 8px; --fold: inherit; }
.subfolder > .libfolderrow { padding: 7px 12px; background: var(--bg); }
.subfolder .libfoldername { font-size: 13px; font-weight: 600; }
.subfolder .libgrip.ghost { display: none; }

/* ---------------------------------------------------- folder colours
   A spine and a tinted icon, not a filled card. On a shelf of a dozen folders
   the job is telling them apart at a glance while scrolling -- a wall of
   coloured blocks does the opposite, and the items inside are what people are
   actually reading. Every folder gets one from the start, derived from its id,
   because a colour scheme nobody opts into leaves everything grey. */
.libfolder { --fold: var(--fold-slate); }
.libfolder[data-colour="slate"]  { --fold: var(--fold-slate); }
.libfolder[data-colour="green"]  { --fold: var(--fold-green); }
.libfolder[data-colour="teal"]   { --fold: var(--fold-teal); }
.libfolder[data-colour="blue"]   { --fold: var(--fold-blue); }
.libfolder[data-colour="violet"] { --fold: var(--fold-violet); }
.libfolder[data-colour="plum"]   { --fold: var(--fold-plum); }
.libfolder[data-colour="amber"]  { --fold: var(--fold-amber); }
.libfolder[data-colour="rust"]   { --fold: var(--fold-rust); }

.libfolder > .libfolderrow { border-left: 3px solid var(--fold); }
.libfolder .foldico { color: var(--fold); }

/* A training on the shelf: a pill on the name, the description, and its
   parts listed with their labels. The parts are what the student gets. */
.libtitle.training { display: flex; align-items: center; gap: 8px; font-weight: 600;
                     color: var(--ink); flex-wrap: wrap; }
.libtitle.training .presenter { font-weight: 500; font-size: 12.5px; }
.tag.training { background: var(--violet-soft, rgba(124, 58, 237, .12));
                color: var(--violet, #6d28d9); text-transform: uppercase;
                letter-spacing: .04em; font-size: 10.5px; }
.libparts { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 3px;
            font-size: 12.5px; }
.libparts li { display: flex; gap: 8px; align-items: baseline; }
.libparts b { color: var(--ink-2); font-weight: 600; min-width: 72px; }
.libparts a { color: var(--accent); text-decoration: none; overflow-wrap: anywhere; }
.libparts a:hover { text-decoration: underline; }
.mini.primaryish { border-color: var(--accent-line); color: var(--accent);
                   background: var(--accent-soft); font-weight: 600; }
.partsbox { margin-top: 4px; }
.partsbox > label { margin-bottom: 6px; }
.partrow { display: grid; grid-template-columns: 130px 1fr auto; gap: 8px;
           align-items: center; margin-bottom: 8px; }
.partrow select, .partrow input { margin: 0; }
.partrow .p_drop { padding: 6px 9px; }
@media (max-width: 640px) { .partrow { grid-template-columns: 1fr; } }

/* The star on a row. Grey and quiet until it is yours, then gold. */
.libstar { flex: none; border: 0; background: none; padding: 3px; margin: 0 -4px 0 -6px;
           cursor: pointer; color: var(--ink-3); opacity: .45; border-radius: var(--r-sm);
           display: inline-flex; transition: opacity var(--t-colour), color var(--t-colour); }
/* The icon takes the button's colour. The generic .ico rule paints every
   icon ink-grey directly, which is why a starred star stayed grey: the
   button went gold and the star inside it did not. */
.libstar .ico { width: 15px; height: 15px; color: inherit; }
.libitem:hover .libstar, .libstar:focus-visible { opacity: .9; }
.libstar.on { color: var(--gold-line); opacity: 1; }
.libstar:hover { color: var(--gold-line); opacity: 1; }
.libstar:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Your favourites: one shelf, gold, with a star for a folder icon, so it
   never reads as one of the team's folders. */
.libfolder.favs { --fold: var(--gold-line); margin-bottom: 14px; }
.libfolder.favs > .libfolderrow { background: var(--gold-soft); border-color: var(--gold-line);
                                  border-left-width: 3px; }
.libfolder.favs .foldico { color: var(--gold); }
.libfolder.favs .libfoldername { color: var(--gold); }
.libfolder.favs .libcount, .libfolder.favs .favnote { color: var(--gold); opacity: .8; }
.libfolder.favs .favnote { font-size: 12px; }
.libfolder.favs[hidden] { display: none; }
.libitem.favrow .libgrip { visibility: hidden; }
/* The icon doubles as the button that changes the colour, so it has to look
   pressable without becoming another boxy control in a row that has four. */
button.foldico {
  border: none; background: none; padding: 2px 3px; margin: 0;
  cursor: pointer; border-radius: var(--r-sm); display: inline-flex;
  transition: background var(--t-colour);
}
button.foldico:hover { background: var(--lift-3); }
button.foldico:focus-visible { outline: 2px solid var(--fold); outline-offset: 1px; }

.foldpalette {
  position: absolute; z-index: 80; display: flex; gap: 6px; padding: 8px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-3);
}
.foldpalette[hidden] { display: none; }
.swatch {
  width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--line);
  padding: 0; cursor: pointer; transition: transform var(--t-move);
}
.swatch:hover { transform: scale(1.14); }
.swatch:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.swatch[data-colour="slate"]  { background: var(--fold-slate); }
.swatch[data-colour="green"]  { background: var(--fold-green); }
.swatch[data-colour="teal"]   { background: var(--fold-teal); }
.swatch[data-colour="blue"]   { background: var(--fold-blue); }
.swatch[data-colour="violet"] { background: var(--fold-violet); }
.swatch[data-colour="plum"]   { background: var(--fold-plum); }
.swatch[data-colour="amber"]  { background: var(--fold-amber); }
.swatch[data-colour="rust"]   { background: var(--fold-rust); }

/* ------------------------------------------------------- contract dates
   A queue, not a table. One row per client, the next box already focused, and
   the row stays put once saved so the page never reshuffles under somebody
   typing -- a list that removes what you just did makes you lose your place
   every time you finish one. */
.dateprog { display: flex; align-items: center; gap: 12px; margin: 0 0 12px; }
.datebar { flex: 1; height: 7px; border-radius: 99px; background: var(--line);
           overflow: hidden; }
.datebar span { display: block; height: 100%; background: var(--accent);
                border-radius: 99px; transition: width var(--t-move); }
.dateprog .dim { font-size: 12.5px; font-variant-numeric: tabular-nums;
                 white-space: nowrap; }
.dateshelp { margin: 0 0 16px; }

.datelist { list-style: none; margin: 0; padding: 0; }
.daterow { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
           padding: 10px 14px; border: 1px solid var(--line);
           border-radius: var(--r-md); background: var(--panel);
           margin-bottom: 8px; transition: opacity var(--t-colour),
                                           border-color var(--t-colour); }
/* Done rows stay legible rather than vanishing -- seeing the ones behind you
   is the other half of a progress bar -- but they stop competing for the eye. */
.daterow.saved { opacity: .55; border-color: var(--green); }
/* Contracts to find: a row takes a dropped PDF. The outline says it will. */
.daterow.cfdrag { border-color: var(--accent); outline: 2px dashed var(--accent);
                  outline-offset: -4px; }
.cfpick { cursor: pointer; }
.cfstate:empty { display: none; }
.datewho { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
           min-width: 260px; flex: 1 1 260px; }
.datewho > a { font-weight: 600; color: var(--ink); text-decoration: none; }
.datewho > a:hover { color: var(--accent); }
.datemail { font-size: 12px; }
.datecrm { font-size: 11px; }

.datefields { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }
.datefield { display: flex; flex-direction: column; gap: 2px; margin: 0;
             font-size: 11px; font-weight: 600; color: var(--ink-3);
             text-transform: none; letter-spacing: 0; }
/* Beats `form input:not([type=checkbox])`, which is (0,2,2) and was setting
   these to the full-width form field. */
.datefields .datefield input[type=date] {
  width: 132px; margin: 0; padding: 5px 8px; font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.dateterms { display: flex; gap: 4px; padding-bottom: 3px; }
.dateterms .d_term { font-size: 11px; padding: 4px 8px; }
.dateflag { font-size: 11.5px; color: var(--ink-3); min-width: 70px; }
/* Contracts to enter: what is known about each, then the one way in. */
.needgroup { margin: 18px 0 6px; }
.needgroup .groupnote { font-size: 12.5px; font-weight: 500; }
.pill.need-both { background: var(--amber-soft); color: var(--amber); }
.pill.need-payments { background: var(--blue-soft); color: var(--blue); }
.pill.need-dates { background: var(--line-soft); color: var(--ink-2); }
.pill.need-with_will { background: var(--accent-soft); color: var(--accent); }
.dateknown { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
             font-size: 12.5px; font-variant-numeric: tabular-nums; }
.dateknown .known { color: var(--ink); font-weight: 550; cursor: default; }
.dateknown .known .dim { font-weight: 500; margin-left: 3px; }
.dateacts { margin-left: auto; display: flex; align-items: center; gap: 6px; }
/* Handing a batch to Will: the ticks, and the bar that acts on them. */
.sendbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
           margin: 0 0 8px; padding: 8px 12px; border: 1px solid var(--line);
           border-radius: var(--r-md); background: var(--lift-2); }
.sendbar .check { margin: 0; font-size: 13px; }
.sendpick { margin: 0 2px 0 0; display: flex; align-items: center; }
.sendpick input { width: 16px; height: 16px; margin: 0; cursor: pointer; }
.sendpick.nosend { width: 16px; justify-content: center; color: var(--ink-3);
                   font-size: 12px; cursor: help; }
.need-with_will .daterow { opacity: .72; }
.wantnote { flex-basis: 100%; margin: 4px 0 0; font-size: 12.5px; color: var(--ink-2); }
/* On a contract with no plan: the way to hand it to Will, and what it says
   once it has been. */
.planmissing .sendone { white-space: nowrap; }
.planmissing .withwill { white-space: nowrap; color: var(--accent); cursor: help;
                         border: 1px solid var(--accent-line); border-radius: var(--r-pill);
                         padding: 3px 9px; font-size: 12px; font-weight: 600; }
.dateacts .d_enter { text-decoration: none; }
.dateflag.good { color: var(--green); font-weight: 600; }
.dateflag.bad  { color: var(--red);   font-weight: 600; }

@media (max-width: 760px) {
  /* Stacked, the fields get the whole width rather than three 90px boxes. */
  .daterow { flex-direction: column; align-items: stretch; }
  .dateacts { margin-left: 0; }
  .datefields { justify-content: space-between; }
}

/* ---------------------------------------------------- client overlay */
/* Big enough to work in, not so big it hides that the roster is behind it. */
.clientpanel { width: 94vw; max-width: 1180px; height: 92vh; padding: 0;
               border: none; border-radius: 14px; overflow: hidden;
               box-shadow: var(--shadow-3); background: var(--surface); }
.clientpanel::backdrop { background: rgba(16, 32, 48, .42);
                         backdrop-filter: blur(2px); }
.cpbar { display: flex; align-items: center; gap: 8px; padding: 10px 14px;
         border-bottom: 1px solid var(--line); background: var(--surface); }
.cpbar b { font-size: 15px; }
/* Both overlay frames -- the roster's client panel and the payments one.
   Keyed to one id, the second panel got no sizing at all and collapsed to an
   iframe's default 300x150 in the corner of its own dialog. */
#cpframe, #ppframe { width: 100%; height: calc(92vh - 47px); border: 0; display: block;
           background: var(--bg); }
/* Inside the frame the page is the whole window, so it loses the outer
   margins it would otherwise keep for the site chrome. */
.panelmode .page { padding-top: 14px; }
.panelmode { background: var(--bg); }

/* Tighter cards on the profile. The call log keeps its room; the rest were
   using a page's worth of padding for a few lines of content each. */
.panel.compact { padding: 13px 15px; margin-bottom: 11px; }
.panel.compact h2 { font-size: 14px; margin: 0 0 9px; }
/* The plain entries only. The call and check-in cards carry their own
   inner padding, and this rule used to beat it on specificity -- which put
   their pills and buttons a pixel from the card border. */
.panel.compact .entry:not(.callentry):not(.cientry) { padding: 7px 0; }
.panel.compact dl { margin: 0; }
.panel.compact dt { margin-top: 8px; }
@media (max-width: 900px) {
  .clientpanel { width: 100vw; height: 100vh; max-width: none; border-radius: 0; }
  #cpframe, #ppframe { height: calc(100vh - 47px); }
}

/* One primary per screen. "Add client" is the thing this page is for; every
   other control in the toolbar is a way of getting somewhere, and dressing
   them all as buttons is why the row reads as noise. */
button.tb.green, a.tb.green { background: var(--panel); border-color: var(--line);
            color: var(--ink-2); box-shadow: var(--shadow-1); }
button.tb.green:hover, a.tb.green:hover { border-color: var(--accent-line); color: var(--accent);
                  background: var(--accent-soft); }
/* Navigation chips in the toolbar sit back until wanted. */
.toolbar a.tb, .rosterhead a.tb { background: transparent; box-shadow: none;
                                  border-color: transparent; color: var(--ink-2); }
.toolbar a.tb:hover, .rosterhead a.tb:hover {
  background: var(--lift-2); border-color: transparent;
  color: var(--ink); transform: none; box-shadow: none; }
.tbcount { background: var(--accent); color: var(--accent-ink);
           font-size: 11px; font-weight: 650; padding: 1px 7px;
           border-radius: var(--r-pill); font-variant-numeric: tabular-nums; }
.golink { color: var(--accent); font-weight: 600; text-decoration: none; }
.golink:hover { text-decoration: underline; }
.statcard b.money { color: var(--accent); }
.pct { color: var(--accent); font-weight: 650; }

/* ------------------------------------------------------- page header
   Every page opens the same way: where you are, what this is, and one line
   saying what it is for. The roster had none of this -- its title was a
   <span> inside a toolbar -- which is most of why the app read as a
   document. A page with no first line has no hierarchy for the rest to
   hang from. */
.pagehead { margin: 4px 0 var(--step-5); }
.pagehead .eyebrow { display: block; color: var(--accent); margin-bottom: 7px; }
.pagehead h1 { margin: 0; }
.pagehead .lede { margin: 7px 0 0; color: var(--ink-2); font-size: 14.5px;
                  max-width: 62ch; line-height: 1.55; }
.pagehead .headrow { display: flex; align-items: flex-start; gap: var(--step-4);
                     flex-wrap: wrap; }
.pagehead .headrow > div:first-child { flex: 1; min-width: 260px; }
.pagehead .headactions { display: flex; gap: var(--step-2); align-items: center;
                         flex-wrap: wrap; padding-top: 4px; }

/* The toolbar under it is navigation, not the headline. */
.hubbar { display: flex; align-items: center; gap: var(--step-2);
          flex-wrap: wrap; margin-bottom: var(--step-4); }
.hubbar .who-hub { display: none; }
.hubbar input[type=search] { width: 260px; }

/* Metrics sit beside the title rather than under it, so the page opens with
   one line of identity and its numbers on the same eye level. */
.pagehead .metrics { padding-top: 6px; }
.pagehead .headrow > .metrics { flex: 0 0 auto; }

/* ------------------------------------------------- client profile head */
.clienthead .chiprow { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.clienthead h1 { margin-bottom: 0; }

/* The facts you need before speaking to somebody, on one line. Recessed
   against the page so it reads as context rather than as another card
   competing with the call log. */
/* The dividers used to be the strip's own background showing through 1px
   gaps. Neat, until the last row is short: five facts across four columns
   left a grey rectangle where the fifth and sixth cells would have been, and
   it read as a broken card rather than as an empty grid area.

   So the lines are drawn BY the cells instead -- a hairline off the top and
   left of each one, clipped at the strip's edge by overflow, which leaves
   internal dividers only. Any cell that does not exist now shows the panel
   colour, because that is what is behind it. */
.factstrip {
  display: grid; gap: 0; margin-bottom: var(--step-4);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden;
}
.fact { background: var(--panel); padding: 12px 14px; display: flex;
        flex-direction: column; gap: 3px;
        box-shadow: -1px 0 0 var(--line), 0 -1px 0 var(--line); }
.fact b { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; }
.fact b.breaks { font-size: 13.5px; font-weight: 550; overflow-wrap: anywhere; }
.fact .mini { align-self: flex-start; margin-top: 4px; }
.fact.wide { grid-column: span 2; }
@media (max-width: 700px) { .fact.wide { grid-column: span 1; } }

/* Two columns that actually differ in weight: the call log is the work, the
   rest is reference. Previously both were 1fr and read as equals. */
.cols { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
        gap: var(--step-4); align-items: start; }
@media (max-width: 1000px) { .cols { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------------- icons
   16px, currentColor, and shifted down a hair: an icon sitting on the text
   baseline reads as floating above the word next to it. */
.ico { width: 16px; height: 16px; flex: 0 0 16px; vertical-align: -3px;
       color: var(--ink-3); }
button.tb:hover .ico, a.tb:hover .ico, .mini:hover .ico { color: inherit; }
button.tb.primary .ico, a.tb.primary .ico { color: currentColor; opacity: .9; }
.topbar nav a .ico { margin-right: 6px; }
.chipbtn .ico { margin-right: 5px; }

/* --------------------------------------------------------------- shell
   A left rail rather than a top bar. A CSM moves between roster, tasks,
   check-ins and payments all day; down the side those are always visible and
   always in the same place, where a horizontal bar makes them compete with
   the page title for the same line. */
.shell { display: grid; grid-template-columns: 232px minmax(0, 1fr);
         min-height: 100vh; }
.shellmain { min-width: 0; }
.sidebar {
  background: var(--sidebar); border-right: 1px solid var(--line);
  padding: 16px 12px 12px; display: flex; flex-direction: column;
  gap: var(--step-3); position: sticky; top: 0; height: 100vh;
  /* position:sticky creates a stacking context whatever the z-index, so
     without this the whole sidebar paints at level 0 and its tooltips are
     trapped inside it -- the roster's sticky header (19) and anything
     positioned after it in the document painted straight over them. Below the
     mobile drawer at 60, above everything in the page column. */
  z-index: 50;
}
.sidebar .brand { display: flex; align-items: center; gap: 9px;
                  padding: 4px 8px 8px; text-decoration: none; color: var(--ink); }
.brandname { font-size: 14.5px; font-weight: 550; color: var(--ink-2);
             letter-spacing: -.01em; }
.brandname b { color: var(--ink); font-weight: 680; }

/* The nav scrolls; the brand above it and the Renewals card and Sign out
   below it do not. Before this the rail was a fixed 100vh column with no
   overflow, so once the links outgrew the screen everything under them --
   Renewals, the theme toggle, Sign out -- was simply cut off. min-height: 0
   is what lets a flex child shrink below its content and scroll instead. */
.sidenav { display: flex; flex-direction: column; gap: 1px; flex: 1;
           min-height: 0; overflow-y: auto; overflow-x: hidden;
           scrollbar-width: thin; margin: 0 -4px; padding: 0 4px;
           /* Room for the scrollbar is always reserved. Without it, a nav
              that just fits gains a scrollbar the moment a hover nudges a
              row, the rows narrow, a label wraps, the row under the pointer
              moves, the hover ends, the scrollbar goes -- and round again,
              which is a rail flickering as fast as the browser can paint. */
           scrollbar-gutter: stable; }
/* A label never wraps. Hovering a link opens a gutter for the drag grip,
   and a label that wrapped to make room would change the row's height
   under the pointer. */
.sidenav .navlabel { white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
                     min-width: 0; }
.navsec { display: flex; flex-direction: column; gap: 1px; }
/* A section header is a button: click folds the section. The chevron says
   so, and turns. */
.navgroup { font-size: 11px; font-weight: 750; letter-spacing: .09em;
            text-transform: uppercase; color: var(--ink-2);
            padding: 16px 8px 6px 6px; border: none; background: none;
            text-align: left; cursor: pointer; font-family: inherit;
            display: flex; align-items: center; gap: 6px; width: 100%; }
/* Chevron on the left, where the eye starts a line, and it turns to say
   which way the section is. A real element, not ::before: anything with a
   tooltip has its ::before taken by the tooltip's arrow, which is how the
   first version of this rendered at opacity 0. */
.navchev { display: inline-block; width: 12px; font-size: 13px; line-height: 1;
           transition: transform var(--t-move); }
.navgroup.shut .navchev { transform: rotate(-90deg); }
.navgroup:hover { color: var(--ink); }
.navgroup:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px;
                          border-radius: var(--r-sm); }
.navgroup.shut + .navsec { display: none; }

/* Reordering. A grip that only shows on hover, at the left of the icon, so
   a rail nobody is rearranging looks exactly as it did. Dragging moves a
   link and its sub-links as one block. */
.navitem { position: relative; display: flex; flex-direction: column; gap: 1px; }
/* The link's content slides right on hover to make a gutter, and the grip
   sits in the gutter -- not on top of the icon, which is where it landed
   first. Off hover there is no gutter and no grip: the rail looks exactly as
   it did until somebody reaches for it. */
.navitem > a:first-child { transition: padding-left var(--t-move); }
.navitem:hover > a:first-child { padding-left: 26px; }
.navgrip { display: none; position: absolute; left: 6px; top: 50%;
           transform: translateY(-50%); width: 14px; height: 14px;
           align-items: center; justify-content: center;
           color: var(--ink-3); cursor: grab; z-index: 1; }
.navgrip:active { cursor: grabbing; }
.navgrip .ico { width: 12px; height: 12px; }
.navitem:hover > a:first-child > .navgrip { display: inline-flex; }
/* Sub-views share the item but keep their own indent. */
.navitem > a.subnav { transition: none; }
.navitem.dragging { opacity: .45; }
.navitem.over { box-shadow: 0 -2px 0 var(--accent); }
.railshut .navgrip { display: none !important; }
.navreset { margin: 14px 4px 4px; padding: 8px 10px; border: 1px dashed var(--line-strong);
            border-radius: var(--r-sm); background: none; font: inherit;
            font-size: 10.5px; font-weight: 700; letter-spacing: .08em;
            text-transform: uppercase; color: var(--ink-3); cursor: pointer;
            width: calc(100% - 8px); }
.navreset:hover { color: var(--ink); border-color: var(--ink-3); }
.navreset[hidden] { display: none; }
.railshut .navreset { display: none; }
.sidenav a {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--r-sm);
  color: var(--ink-2); text-decoration: none;
  font-size: 13.5px; font-weight: 550;
}
.sidenav a:hover { background: var(--lift-2); color: var(--ink); }
.sidenav a:hover .ico { color: var(--ink-2); }
/* The page you are on is stated, not merely tinted -- the bar on the left
   survives being looked at out of the corner of your eye. */
.sidenav a.on { background: var(--accent-soft); color: var(--accent);
                font-weight: 620; box-shadow: inset 2px 0 0 var(--accent); }
.sidenav a.on .ico { color: var(--accent); }

.sidefoot { border-top: 1px solid var(--line); padding-top: 10px;
            display: flex; flex-direction: column; gap: 8px; }
.sidetools, .sideperson { display: flex; align-items: center; gap: 8px; }
/* The install button: a proper button, not an icon somebody has to guess
   at. Accent-tinted so it reads as the one thing in the footer to do. */
.installfoot { flex: 1; display: inline-flex; align-items: center; gap: 8px;
               padding: 7px 10px; border-radius: var(--r-sm); text-decoration: none;
               font-size: 12.5px; font-weight: 600; color: var(--accent);
               background: var(--accent-soft); border: 1px solid var(--accent-line); }
.installfoot .ico { flex: none; }
.installfoot:hover { background: var(--accent); color: var(--accent-ink);
                     border-color: var(--accent); }
.installfoot[hidden] { display: none; }
.whoami { display: flex; align-items: center; gap: 8px; flex: 1;
          text-decoration: none; color: var(--ink-2); padding: 4px;
          border-radius: var(--r-sm); font-size: 13px; min-width: 0; }
.whoami:hover { background: var(--lift-2); color: var(--ink); }
.whoname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.avatar { display: grid; place-items: center; width: 26px; height: 26px;
          flex: 0 0 26px; border-radius: 50%; font-size: 12px; font-weight: 650;
          background: var(--accent); color: var(--accent-ink); }
.sidefoot form { margin: 0; }

.navtoggle { display: none; }
/* ------------------------------------------------------------- phones
   The page must never be wider than the screen. On a phone a page that
   overflows sideways does not just scroll: the browser widens the layout
   viewport to fit the content, and everything position:fixed -- the Fit Pro
   AI panel, the drawer -- is placed against THAT width and hangs off the
   right edge. Two things caused it. The tooltip bubbles: max-content
   pseudo-elements that count toward the document's width even at opacity
   zero, and which a finger cannot hover anyway. And the roster: a
   thirteen-column table, which now scrolls inside its own wrapper with the
   client's name held at the left edge. */
@media (hover: none) {
  [data-tip]::before, [data-tip]::after { display: none !important; }
}
/* Nowhere, at any width, does the page itself scroll sideways. A Mac
   trackpad swipes horizontally as easily as vertically, and a page that
   can move that way shows a version of itself with the rail cut off. Wide
   content scrolls inside its own wrapper (the roster, the payments table);
   the rest of what reached past the edge was tooltip bubbles at rest --
   invisible, and still counted. `clip` rather than `hidden`: it forbids the
   scrolling without making the viewport a different kind of scroller, so
   sticky headers and the rail stay pinned exactly as before. The `hidden`
   line is the fallback for a browser without `clip`. */
html { overflow-x: hidden; overflow-x: clip; }
/* And on body, where `clip` is a real clipping box rather than a viewport
   setting: whatever still pokes past the right edge -- a tooltip bubble at
   rest -- is clipped there, so the document has no sideways overflow left
   for even a script to scroll into. Not `hidden` on body: that would make
   body a scroll container and un-stick the rail and the top bar. */
@supports (overflow: clip) { body { overflow-x: clip; } }
@media (max-width: 900px) {
  table.roster th:first-child, table.roster td:first-child {
    position: sticky; left: 0; z-index: 2; background: var(--surface);
    box-shadow: 1px 0 0 var(--line);
  }
}
@media (max-width: 640px) {
  .row { grid-template-columns: 1fr; gap: 10px; }
  dialog { width: calc(100vw - 20px); padding: 18px 16px 16px; }
}

@media (max-width: 900px) {
  .shell { grid-template-columns: 1fr; }
  .sidebar { position: fixed; left: 0; top: 0; width: 232px; z-index: 60;
             transform: translateX(-100%); transition: transform var(--t-panel);
             box-shadow: var(--shadow-3); }
  .sidebar.open { transform: none; }
  .navtoggle { display: inline-flex; align-items: center; justify-content: center;
               position: sticky; top: 10px; left: 10px; margin: 10px 0 0 10px;
               width: 38px; height: 38px; z-index: 55;
               background: var(--panel); border: 1px solid var(--line);
               border-radius: var(--r-md); box-shadow: var(--shadow-1);
               cursor: pointer; }
}
/* The overlay frames a page; it must not draw a second sidebar inside itself. */
.panelmode .sidebar, .panelmode .navtoggle { display: none; }
/* Embedded under a card that already says what this is and how many. The
   crumb goes with it: the card is the context, and a link back to a page you
   are not on is one to nowhere. */
.nohead .pagehead, .nohead .crumb { display: none; }
.nohead .page { padding-top: 6px; }
.panelmode .shell { grid-template-columns: 1fr; min-height: 0; }

/* The count sits at the far end of its row so the numbers line up down the
   rail and can be read as a column. */
.sidenav a { position: relative; }
.navcount { margin-left: auto; background: var(--lift-3); color: var(--ink-2);
            font-size: 11px; font-weight: 650; padding: 1px 7px;
            border-radius: var(--r-pill); font-variant-numeric: tabular-nums; }
.sidenav a.on .navcount { background: var(--accent); color: var(--accent-ink); }
.sidenav a:hover .navcount { background: var(--lift-4); color: var(--ink); }

/* Rail tooltips point right, not up.
   Keyed on the rail itself rather than on a list of components -- that list
   was patched three times as things were added to the sidebar, and each time
   the newest control was the one hanging off the left edge of the window. */
/* Fixed rather than absolute, with the spot set by the aligner in base.html
   on hover: the nav scrolls now, and a scrolling box clips anything drawn
   past its edge -- which is the entire tooltip, since these open to the
   right. A fixed element is positioned against the viewport and no
   ancestor's overflow can touch it. */
.sidebar [data-tip]::after {
  position: fixed; bottom: auto; right: auto;
  left: var(--tipx, -9999px); top: var(--tipy, 0);
  transform: translateY(-50%) translateX(-3px); max-width: 240px;
}
.sidebar [data-tip]:hover::after, .sidebar [data-tip]:focus-visible::after {
  transform: translateY(-50%) translateX(0);
}
.sidebar [data-tip]::before {
  position: fixed; bottom: auto; right: auto;
  left: calc(var(--tipx, -9999px) - 10px); top: var(--tiparrowy, var(--tipy, 0));
  border-color: transparent; border-right-color: var(--ink);
  transform: translateY(-50%) translateX(-3px);
}
.sidebar [data-tip]:hover::before, .sidebar [data-tip]:focus-visible::before {
  transform: translateY(-50%) translateX(0);
}

/* The new-client action at the top of the rail. */
.railnew {
  display: flex; align-items: center; gap: 9px; margin: 2px 0 4px;
  padding: 9px 11px; border-radius: var(--r-md); text-decoration: none;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  color: var(--accent-ink); font-size: 13.5px; font-weight: 600;
  box-shadow: 0 1px 2px rgba(13,138,95,.24), 0 4px 12px rgba(13,138,95,.18);
}
.railnew:hover { filter: brightness(1.05); }
/* The second action. Same shape, none of the weight -- it is the one you
   reach for occasionally, not the one you reach for all day. */
.railsecond {
  display: flex; align-items: center; gap: 9px; margin: 0 0 4px;
  padding: 8px 11px; border-radius: var(--r-md); text-decoration: none;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--ink-2); font-size: 13px; font-weight: 550;
}
.railsecond:hover { border-color: var(--accent-line); color: var(--accent);
                    background: var(--accent-soft); }
.railsecond:hover .ico { color: var(--accent); }
.railsecond:active { transform: translateY(1px); }
.railshut .railsecond { justify-content: center; padding: 8px 0; }
.railnew:active { transform: translateY(1px); }
.railnew .ico { color: currentColor; }

/* Search sits with the filters it drives, not on the toolbar above. */
.filters { display: flex; align-items: center; gap: var(--step-2);
           flex-wrap: wrap; }
.filters input[type=search] { width: 210px; margin: 0; }

/* An email you can take away with you. */
/* cursor: copy renders as an arrow with a green plus, which reads as "add
   this" rather than "take this". A copy glyph on hover says it without
   relying on a cursor nobody can name. */
.copyable { background: none; border: none; padding: 0; font: inherit;
            font-size: 12px; color: var(--ink-3); cursor: pointer;
            text-align: left; max-width: 100%; display: flex;
            align-items: center; gap: 5px; }
.copytext { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.copyico { width: 13px; height: 13px; flex: 0 0 13px; opacity: 0;
           color: var(--accent); transition: opacity var(--t-colour); }
.copyable:hover { color: var(--accent); }
.copyable:hover .copytext { text-decoration: underline; }
.copyable:hover .copyico, .copyable:focus-visible .copyico { opacity: 1; }
.copyable.copied { color: var(--accent); font-weight: 600; }
.copyable.copied .copytext { text-decoration: none; }

/* ------------------------------------------------- collapsing the rail
   Collapsed shows icons only. The labels do not disappear -- they become
   the tooltips, which already point right for exactly this reason. */
.railtop { display: flex; align-items: center; gap: 4px; }
.railtop .brand { flex: 1; min-width: 0; }
.railtoggle { background: none; border: 1px solid transparent; cursor: pointer;
              padding: 5px; border-radius: var(--r-sm); color: var(--ink-3);
              display: inline-flex; }
.railtoggle:hover { background: var(--lift-2); color: var(--ink-2); }
.railtoggle .ico { transform: rotate(180deg); transition: transform var(--t-move); }

.subnav { padding-left: 34px !important; font-size: 13px !important;
          font-weight: 500 !important; color: var(--ink-3) !important; }
.subnav:hover { color: var(--ink-2) !important; }
.navcount.warnish { background: var(--red-soft); color: var(--red); }
/* Still red on the page it belongs to: the active item's green badge used
   to win, so the one place the count was meant to alarm, it did not. */
.sidenav a.on .navcount.warnish { background: var(--red); color: #fff; }

.shell.railshut { grid-template-columns: 64px minmax(0, 1fr); }
.railshut .sidebar { padding: 16px 8px 12px; align-items: stretch; }
.railshut .brandname,
.railshut .navgroup,
.railshut .subnav,
.railshut .whoname,
.railshut .sidefoot form,
/* The labels. These were written as `span:not(.ico)` before the labels were
   spans at all -- they sat in the markup as bare text nodes after the icon,
   which is the one thing a stylesheet cannot hide. So the rail narrowed to
   64px and every label stayed, wrapping over the content beside it. */
.railshut .navlabel { display: none; }
.railshut .railnew { justify-content: center; padding: 9px 0; }
.railshut .sidenav a { justify-content: center; padding: 9px 0; }
.railshut .sidenav a.on { box-shadow: inset 2px 0 0 var(--accent); }
.railshut .brand { justify-content: center; }
.railshut .railtop { flex-direction: column-reverse; gap: 8px; }
.railshut .railtoggle .ico { transform: none; }
.railshut .whoami { justify-content: center; padding: 4px 0; }
/* A count still has to be visible when the label is not, so it moves onto the
   icon as a badge.

   Drawn with the count element itself, NOT a pseudo-element. [data-tip] has
   already taken both ::before and ::after on every one of these links -- the
   bubble and its arrow -- so the dot this rule used to draw with ::after was
   silently overwriting the tooltip on Tasks, Check-ins and GHL sync: the only
   three items that have counts, and collapsed, the only three whose label is
   gone and whose tooltip is therefore the whole explanation.

   It also keeps the number rather than reducing it to "something is there".
   The comment here used to claim the number was in the tooltip. It never was. */
.railshut .sidenav a { position: relative; }
.railshut .navcount {
  position: absolute; top: 3px; right: 3px; margin: 0;
  min-width: 14px; height: 14px; padding: 0 3px;
  display: flex; align-items: center; justify-content: center;
  font-size: 9.5px; font-weight: 700; line-height: 1;
  border-radius: var(--r-pill);
  background: var(--accent); color: var(--accent-ink);
  box-shadow: 0 0 0 2px var(--sidebar);
}
.railshut .navcount.warnish { background: var(--red); color: #fff; }
@media (max-width: 900px) { .shell.railshut { grid-template-columns: 1fr; } }

/* The renewal reminder. Sits above the account footer, always present, so
   the space is worth glancing at rather than something that appears and
   disappears. One row tall, the same height as a nav link: the rail
   scrolls, and every line this card takes is a line the menu loses. The
   "ending in 60 days" caption lives in the tooltip. */
.railcard { display: flex; align-items: center; gap: 10px;
            text-decoration: none; margin-top: auto;
            padding: 7px 10px; border-radius: var(--r-sm);
            border: 1px solid var(--line); background: var(--panel-2);
            color: var(--ink-2); }
.railcard .ico { flex: none; color: var(--ink-3); }
.railcard .alabel { margin: 0; font-size: 13px; font-weight: 550;
                    letter-spacing: 0; text-transform: none;
                    color: var(--ink-2); flex: 1; min-width: 0; }
.railcard b { font-size: 14px; font-weight: 700; line-height: 1;
              font-variant-numeric: tabular-nums; color: var(--ink);
              margin-right: 16px; }
.railcard:hover { background: var(--lift-2); color: var(--ink); }
.railcard:hover .ico, .railcard:hover .alabel { color: var(--ink); }
.railcard.live { border-color: var(--accent-line); background: var(--accent-soft); }
.railcard.live b, .railcard.live .alabel, .railcard.live .ico { color: var(--accent); }
.railshut .railcard { padding: 8px 4px; justify-content: center; gap: 0; }
.railshut .railcard .alabel, .railshut .railcard .ico:not(.railcaret) { display: none; }
.railshut .railcard b { font-size: 13px; margin: 0; }

/* "Install the app". Shown by install.js only while the browser is
   offering to install, so most of the time this row is not there at all.
   Tinted so the one person who has not installed yet notices it once. */
.railinstall { display: flex; align-items: center; gap: 10px;
               text-decoration: none;
               padding: 7px 10px; border-radius: var(--r-sm);
               border: 1px dashed var(--accent-line); background: var(--accent-soft);
               color: var(--accent); font-size: 13px; font-weight: 550; }
.railinstall .ico { flex: none; color: var(--accent); }
.railinstall:hover { border-style: solid; background: var(--lift-2); }
.railshut .railinstall { justify-content: center; padding: 8px 4px; gap: 0; }
.railinstallrow { display: flex; align-items: stretch; gap: 4px; }
.railinstallrow[hidden] { display: none; }
.railinstallrow .railinstall { flex: 1; min-width: 0; }
.railinstallx { flex: none; background: none; border: 0; cursor: pointer;
                color: var(--ink-3); opacity: .6; padding: 0 6px;
                font: inherit; font-size: 17px; line-height: 1;
                border-radius: var(--r-sm); }
.railinstallx:hover { opacity: 1; background: var(--lift-2); color: var(--ink); }
.railshut .railinstallx { display: none; }

/* The account page's install instructions: the route for THIS browser
   first and in full, the rest smaller below it. */
.installhelp p { margin: 0 0 8px; opacity: .6; font-size: 12.5px; }
.installhelp p:last-child { margin-bottom: 0; }
:root[data-browser="chrome"] .installhelp .for-chrome,
:root[data-browser="edge"] .installhelp .for-edge,
:root[data-browser="safari"] .installhelp .for-safari,
:root[data-browser="ios"] .installhelp .for-ios,
:root[data-browser="firefox"] .installhelp .for-firefox { opacity: 1; font-size: 13.5px;
                                                          order: -1; }
.installhelp { display: flex; flex-direction: column; }

/* Loud on purpose. A filtered roster that looks like a whole one is how
   somebody concludes a client has been deleted. */
.viewlabel { display: flex; align-items: center; gap: 10px;
             margin: 0 0 14px; font-size: 13.5px; font-weight: 500;
             color: var(--ink); background: var(--amber-soft);
             border: 1px solid var(--amber);
             padding: 10px 12px; border-radius: var(--r-md); }
.viewlabel .ico { width: 15px; height: 15px; flex: 0 0 15px; color: var(--amber); }
.viewlabel > span { flex: 1; }
.viewlabel b { color: var(--amber); }
.viewlabel a.tb { flex: 0 0 auto; padding: 6px 12px; font-size: 12.5px;
                  text-decoration: none; }

/* A heading's tooltip hangs below it -- above would sit off the top of the
   table, and often off the window when the roster is scrolled to the top. */
th[data-tip]::after { bottom: auto; top: calc(100% + 9px); }
th[data-tip]::before { bottom: auto; top: calc(100% + 4px);
                       border-color: transparent; border-bottom-color: var(--ink); }
th[data-tip] { cursor: help; }

/* The client's name at roster weight, above the task rather than beside it. */
.taskclient { display: flex; align-items: center; gap: 7px; margin-bottom: 3px; }
.taskclient a { font-size: 14px; font-weight: 620; color: var(--ink);
                text-decoration: none; letter-spacing: -.008em; }
.taskclient a:hover { color: var(--accent); text-decoration: underline; }
.taskcard.isdone .taskclient a { color: var(--ink-3); font-weight: 550; }
/* Saved without saying who it is for: the list itself goes red, and stays
   red until a choice is made. The browser's bubble says why, but it is
   gone in a few seconds, and the form's own message line is out of view
   once the list has focus. */
#taskdlg #t_client:invalid { border-color: var(--red); }
/* A task about nobody. The word sits where the name would, at the same
   size so the column lines up, but a step lighter: it is a category, not
   a person, and it should not read as one when the board is scanned for
   names. */
.taskclient .general { font-size: 14px; font-weight: 600; color: var(--ink-3);
                       letter-spacing: -.008em; cursor: help; }
/* The task itself is the line that matters on this page, so it is the
   darkest and heaviest thing on the card; the client's name sits a step
   behind it. It was a light grey that did not read as the title
   (Stefan, 2026-09-17). */
.taskbody > b { display: block; font-weight: 640; color: var(--ink);
                font-size: 14.5px; line-height: 1.4; }
.taskcard.isdone .taskbody > b { color: var(--ink-3); font-weight: 500; }

/* ====================================================== premium accents
   Small things, each nearly invisible on its own. Together they are most of
   the difference between an interface that is tidy and one that feels made. */

/* 1. The rail is its own plane. A flat panel colour beside a flat page reads
      as two rectangles; a very slight vertical wash gives it a light source
      and lets the border almost disappear. */
.sidebar { background-image: linear-gradient(180deg,
             var(--lift-1) 0%, transparent 34%, transparent 100%); }

/* 2. Cards catch light along their top edge. One pixel, barely there, and it
      is what stops a white card on a white page looking like a cut-out. */
.panel, .statcard, .cicard, .taskcard, .paygroup, .notifpanel, .factstrip {
  background-image: linear-gradient(180deg, var(--lift-1) 0, transparent 42px);
}

/* 3. Numbers get room to breathe and the tight tracking large type wants.
      A metric set at body tracking reads as text; set tighter it reads as a
      figure. */
.statcard b, .metric b, .fact b, .railcard b {
  letter-spacing: -.028em; font-feature-settings: "tnum", "cv11";
}

/* 4. Focus is a ring AND a border, so it survives on a coloured ground where
      a ring alone washes out. */
:focus-visible { outline: none; }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  border-color: var(--accent) !important;
}

/* 5. Section headings get a hairline that stops at the text rather than
      running the width of the page -- a full-width rule cuts a page in half,
      a short one introduces what follows. */
h2.sec { display: flex; align-items: center; gap: 10px; }
h2.sec::after { content: ""; flex: 1; height: 1px; background: var(--line); }

/* 6. Empty states say what would be here, centred in their own space rather
      than sitting flush left like a forgotten paragraph. */
.empty {
  text-align: center; color: var(--ink-3); font-size: 13.5px;
  padding: 28px 20px; border: 1px dashed var(--line);
  border-radius: var(--r-lg); background: var(--panel-2);
}

/* 7. Pills and tags stop being flat fills: a hairline of their own colour
      makes them read as objects rather than highlighter marks. */
.tag, .pill { border: 1px solid transparent; }
.tag.elite { border-color: rgba(47, 111, 237, .18); }
.tag.core  { border-color: var(--lift-3); }
.pill.green, .health.all_good { border-color: rgba(13, 138, 95, .18); }
.pill.amber { border-color: rgba(198, 132, 24, .22); }
.pill.red   { border-color: rgba(192, 57, 47, .18); }

/* 8. The badge on a rail item settles rather than appearing. */
@keyframes settle { from { opacity: 0; transform: translateY(-2px); }
                    to   { opacity: 1; transform: none; } }
.navcount, .tbcount { animation: settle 220ms var(--ease) both; }

/* 9. A dialog arrives rather than blinking into place. */
@keyframes rise { from { opacity: 0; transform: translateY(8px) scale(.985); }
                  to   { opacity: 1; transform: none; } }
dialog[open] { animation: rise 200ms var(--ease); }

/* 10. Scrollbars, so a long roster does not put a slab of system grey down
       the side of a designed page. */
* { scrollbar-width: thin; scrollbar-color: var(--lift-4) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--lift-3); border-radius: 999px;
                             border: 3px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: var(--lift-4);
                                   background-clip: content-box; }

/* 11. Text selection in the brand colour rather than the browser's blue. */
::selection { background: var(--accent-soft); color: var(--ink); }

/* The theme switch shows the theme you would GET, not the one you are in --
   a sun while it is dark. Showing the current state makes people press it to
   find out what it does. */
.themebtn { background: none; border: 1px solid transparent; cursor: pointer;
            padding: 5px; border-radius: var(--r-sm); color: var(--ink-3);
            display: inline-flex; flex: 0 0 auto; }
.themebtn:hover { background: var(--lift-2); color: var(--ink-2); }

.darkicon { display: none; }
:root[data-theme="dark"] .lighticon { display: none; }
:root[data-theme="dark"] .darkicon { display: inline-block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .lighticon { display: none; }
  :root:not([data-theme="light"]) .darkicon { display: inline-block; }
  :root[data-theme="light"] .lighticon { display: inline-block; }
  :root[data-theme="light"] .darkicon { display: none; }
}
.railshut .sidefoot { gap: 6px; }
.railshut .sidetools, .railshut .sideperson { flex-direction: column; gap: 6px; }
.railshut .installfoot { flex: none; padding: 7px; }

/* The copy notice. One line at the foot of the screen, dark on light and
   light on dark, that says what was just copied and goes away by itself. */
.hubtoast {
  position: fixed; left: 50%; bottom: 22px; transform: translate(-50%, 12px);
  z-index: 200; max-width: min(92vw, 520px);
  padding: 9px 14px 9px 12px; border-radius: 999px;
  background: var(--ink); color: var(--bg); font-size: 13px; font-weight: 550;
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, .45);
  opacity: 0; pointer-events: none;
  transition: opacity .18s var(--ease), transform .18s var(--ease);
  display: flex; align-items: center; gap: 8px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.hubtoast::before { content: "\2713"; font-weight: 700; color: var(--accent); }
.hubtoast.bad::before { content: "!"; color: var(--red); }
.hubtoast.on { opacity: 1; transform: translate(-50%, 0); }
button.copied, .mini.copied { color: var(--accent); border-color: var(--accent-line);
                              background: var(--accent-soft); }
@media (prefers-reduced-motion: reduce) { .hubtoast { transition: none; } }

/* ============================================== hover, in one place
   Keyed on what is already clickable rather than on component names, so a
   page built next month inherits it without anybody remembering to. If a
   thing navigates or opens, it lifts.

   .16s, a 2px rise, a stronger edge and a shadow. Any more reads as a bounce;
   a roster of forty rows that bounces is unusable. */
a.statcard, a.card, .railcard, .cicard, .taskcard, .paygroup > summary,
.notifrow, tr[data-id], table.roster tbody tr,
details.entry > summary, .exrow, .acard.written {
  transition: transform .16s ease, box-shadow .16s ease,
              border-color .16s ease, background-color .16s ease;
}
a.statcard:hover, a.card:hover, .railcard:hover, .cicard:hover,
.taskcard:hover, details.entry > summary:hover, .acard.written:hover {
  transform: translateY(var(--hover-lift));
  border-color: var(--line-strong);
  box-shadow: var(--shadow-2);
}
/* Rows and menu items tint rather than lift: a table row that rises drags
   its neighbours' borders with it. */
.notifrow:hover, .statusopt:hover, .sidenav a:hover,
.alertlist li:hover, .suggested li:hover {
  background: var(--hover-tint);
}
/* Anything already open is where you are. It should not move under the
   cursor while it is being read. */
.cicard[open]:hover, details.entry[open] > summary:hover,
.paygroup[open] > summary:hover { transform: none; }

@media (prefers-reduced-motion: reduce) {
  a.statcard:hover, a.card:hover, .railcard:hover, .cicard:hover,
  .taskcard:hover, details.entry > summary:hover, .acard.written:hover,
  button.tb:hover, a.tb:hover, .railnew:hover, .railsecond:hover {
    transform: none;
  }
}

/* The info button on a metric. Was a 14px circle with a lower-case "i" in it,
   which at that size reads as a question mark -- and it carried a native
   title=, so hovering it produced nothing at all. */
.infobtn {
  background: none; border: 0; padding: 0; cursor: help; line-height: 0;
  color: var(--ink-3); display: inline-flex; vertical-align: -2px;
  border-radius: 50%;
}
.infobtn .ico { width: 14px; height: 14px; flex: 0 0 14px; color: currentColor; }
.infobtn:hover { color: var(--accent); }
.infobtn.light { color: rgba(255, 255, 255, .6); }
.infobtn.light:hover { color: #fff; }
/* Metric labels sit near the top of a card, so their tooltip drops below.
   The pagehead metrics are higher still -- they are the first row on the page
   -- so a tooltip above them opens off the top of the window. */
.statlabel .infobtn::after, .paylabel .infobtn::after,
.metric .infobtn::after { bottom: auto; top: calc(100% + 9px); }
.statlabel .infobtn::before, .paylabel .infobtn::before,
.metric .infobtn::before {
  bottom: auto; top: calc(100% + 4px);
  border-color: transparent; border-bottom-color: var(--ink); }
/* The metrics sit at the right edge of the pagehead, so their tooltips are
   anchored to their right or they run off the side of the page. */
.metric .infobtn::after { left: auto; right: -8px; }
.metric .infobtn::before { left: auto; right: 2px; }

/* ------------------------------------------------- held check-ins panel
   A card like every other card, with the amber carried by one icon and one
   tag rather than by a brown wash across the whole thing. A panel that
   colours its own background shouts at the same volume whether it holds one
   held check-in or twelve. */
.heldpanel { border-color: var(--amber); }
.heldhead { display: flex; gap: 12px; align-items: flex-start;
            margin-bottom: var(--step-3); }
.heldhead > .ico { width: 20px; height: 20px; flex: 0 0 20px;
                   color: var(--amber); margin-top: 2px; }
.heldhead h2 { margin: 0; }
.heldhead .lede { margin: 5px 0 0; font-size: 13px; color: var(--ink-2);
                  max-width: 70ch; line-height: 1.5; }
.heldrow { display: flex; align-items: center; gap: var(--step-2);
           flex-wrap: wrap; padding: 11px 12px; border-radius: var(--r-md);
           border: 1px solid var(--line); background: var(--panel-2);
           margin-top: 8px; }
.heldwho { display: flex; flex-direction: column; gap: 1px; min-width: 170px; }
.heldwho b { font-size: 13.5px; font-weight: 620; }
.heldwho .dim { font-size: 12px; }
.heldspacer { flex: 1; }

/* --------------------------------------------------------- client picker
   Same component as the status picker on the roster, plus a filter: a native
   select with thirty-odd names in it can only be searched by first letter. */
.clientpick { position: relative; }
.clientnow {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  padding: 6px 10px; border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--panel);
  font: inherit; font-size: 12.5px; font-weight: 550; color: var(--ink-3);
  min-width: 168px; white-space: nowrap;
}
.clientnow:hover { border-color: var(--line-strong); color: var(--ink-2); }
.clientnow[aria-expanded="true"] { box-shadow: var(--ring);
                                   border-color: var(--accent); }
.clientnow.chosen { color: var(--ink); border-color: var(--accent-line);
                    background: var(--accent-soft); }
.clientlabel { flex: 1; text-align: left; overflow: hidden;
               text-overflow: ellipsis; }
.clientmenu {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0;
  width: 250px; padding: 6px; border-radius: var(--r-md);
  background: var(--panel); border: 1px solid var(--line);
  box-shadow: var(--shadow-3); animation: rise 140ms var(--ease);
}
.clientfilter { width: 100%; margin: 0 0 6px; padding: 7px 9px;
                border: 1px solid var(--line); border-radius: var(--r-sm);
                font: inherit; font-size: 13px; background: var(--field);
                color: var(--ink); }
.clientfilter:focus { outline: none; border-color: var(--accent);
                      box-shadow: var(--ring); }
.clientopts { max-height: 232px; overflow-y: auto; }
.clientopt { display: block; width: 100%; text-align: left; border: 0;
             background: none; cursor: pointer; font: inherit; font-size: 13px;
             font-weight: 550; color: var(--ink-2); padding: 7px 9px;
             border-radius: var(--r-sm); }
.clientopt:hover { background: var(--hover-tint); color: var(--ink); }
.clientnone { margin: 6px 9px; font-size: 12.5px; color: var(--ink-3); }

/* ------------------------------------------------------- native selects
   The ones left are form fields inside dialogs, plus the roster's column
   filters. Those do not need replacing -- a select in a form is the right
   control -- but the browser's default chrome does not belong next to
   everything else here. Custom caret, our border, our radius.

   appearance: none removes the OS arrow; without a replacement the control
   reads as a text box, so the caret is drawn as a background image. */
select {
  appearance: none; -webkit-appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 13px top 55%, right 8px top 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 26px !important;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background-color: var(--field); color: var(--ink);
  font: inherit; font-size: 13px; cursor: pointer;
}
select:hover { border-color: var(--line-strong); }
select:focus-visible { outline: none; border-color: var(--accent);
                       box-shadow: var(--ring); }
select:disabled { opacity: .55; cursor: not-allowed; }

/* The roster's column filters sit under their heading and should read as
   part of it, not as a form. */
.colfilter {
  font-size: 11.5px; font-weight: 550; padding: 3px 22px 3px 7px;
  border-radius: var(--r-sm); background-color: transparent;
  border-color: transparent; color: var(--ink-3); width: 100%;
  background-position: right 7px top 55%, right 3px top 55%;
}
.colfilter:hover { background-color: var(--hover-tint); color: var(--ink-2); }
.colfilter.on { background-color: var(--accent-soft); color: var(--accent);
                border-color: var(--accent-line); font-weight: 650; }

/* Payment rows read as rows of a card, not of a spreadsheet -- the same
   change the roster got. */
table.paytable { border-collapse: separate; border-spacing: 0 4px; }
table.paytable td { border: none; background: var(--bg); }
table.paytable td:first-child { border-radius: var(--r-sm) 0 0 var(--r-sm); }
table.paytable td:last-child  { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
table.paytable tbody tr:hover td { background: var(--hover-tint); }
table.paytable tr[data-state="paid"] td { background: var(--green-soft); }
table.paytable tr[data-state="overdue"] td { background: var(--red-soft); }
.payhero .infobtn { color: var(--hero-dim); }
.payhero .infobtn:hover { color: var(--hero-ink); }

/* Admin and Overview tables get the roster's treatment: rows as cards, so a
   person or a workspace is a single object rather than a line of a ledger.
   Scoped to tables inside a panel so dialogs and mini-tables are untouched. */
.panel > .tablewrap table:not(.paytable):not(.mini-table),
.tablewrap > table:not(.paytable):not(.mini-table):not(.roster) {
  border-collapse: separate; border-spacing: 0 5px;
}
.panel > .tablewrap table:not(.paytable):not(.mini-table) thead th,
.tablewrap > table:not(.paytable):not(.mini-table):not(.roster) thead th {
  border-bottom: none; background: transparent;
}
.panel > .tablewrap table:not(.paytable):not(.mini-table) tbody td,
.tablewrap > table:not(.paytable):not(.mini-table):not(.roster) tbody td {
  background: var(--panel-2); border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line); vertical-align: middle;
}
.panel > .tablewrap table:not(.paytable):not(.mini-table) tbody td:first-child,
.tablewrap > table:not(.paytable):not(.mini-table):not(.roster) tbody td:first-child {
  border-left: 1px solid var(--line);
  border-radius: var(--r-md) 0 0 var(--r-md);
}
.panel > .tablewrap table:not(.paytable):not(.mini-table) tbody td:last-child,
.tablewrap > table:not(.paytable):not(.mini-table):not(.roster) tbody td:last-child {
  border-right: 1px solid var(--line);
  border-radius: 0 var(--r-md) var(--r-md) 0;
}
.panel > .tablewrap table:not(.paytable):not(.mini-table) tbody tr:hover td,
.tablewrap > table:not(.paytable):not(.mini-table):not(.roster) tbody tr:hover td {
  background: var(--hover-tint); border-color: var(--line-strong);
}

/* ------------------------------------------- the one-to-one book
   Will coaches a handful of people directly. A dense scanning table is built
   for reading forty rows fast; this is built for reading one card properly,
   so the note is the body of it rather than a column squeezed between dates. */
.onelist { display: flex; flex-direction: column; gap: 14px; }
.onecard {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-1); padding: 18px 20px;
}
.onehead { display: flex; flex-wrap: wrap; gap: 10px 18px;
           align-items: baseline; justify-content: space-between; }
.onename { font-size: 17px; font-weight: 650; color: var(--ink);
           text-decoration: none; }
.onename:hover { color: var(--accent); }
.onehead .copymail {
  display: block; margin-top: 2px; background: none; border: 0; padding: 0;
  font: inherit; font-size: 12.5px; color: var(--ink-3); cursor: copy;
}
.onehead .copymail:hover { color: var(--accent); }
.onefacts { display: flex; flex-wrap: wrap; gap: 14px; font-size: 13px;
            color: var(--ink-2); align-items: baseline; }
.onefacts b { font-weight: 650; color: var(--ink); }

.onenote {
  margin-top: 13px; padding: 12px 14px; background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--r-md);
  font-size: 14px; line-height: 1.55;
}
.onenote p { margin: 0 0 6px; white-space: pre-wrap; }
.onenote .dim { font-size: 12px; margin-right: 10px; }

.onelinks { display: flex; flex-wrap: wrap; gap: 7px; align-items: center;
            margin-top: 12px; }
.linkchip {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid var(--accent-line); background: var(--accent-soft);
  border-radius: var(--r-pill); padding: 3px 5px 3px 11px; font-size: 12.5px;
}
.linkchip a { color: var(--accent); font-weight: 600; text-decoration: none; }
.linkchip a:hover { text-decoration: underline; }
.linkx {
  background: none; border: 0; cursor: pointer; color: var(--ink-3);
  font-size: 15px; line-height: 1; padding: 0 4px; border-radius: 50%;
}
.linkx:hover { color: var(--red); }
.linkadd { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
/* form.linkadd, not .linkadd: the global field rule is
   `form input:not([type=checkbox]):not([type=radio])`, which outranks a plain
   class selector and stretches these to 100%. Matching its specificity is
   what keeps them inline. */
form.linkadd input {
  font: inherit; font-size: 12.5px; padding: 5px 9px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--field); color: var(--ink); box-shadow: none;
}
form.linkadd input[name="label"] { width: 120px; }
form.linkadd input[name="url"] { width: 230px; max-width: 100%; }
form.linkadd input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The first-term / re-sign picker in the contract import. Sized to its own
   content: the global field rule stretches every select to 100%, which in a
   table column makes a dropdown as wide as the widest cell in it. */
table select.cdkind {
  width: auto; min-width: 116px; padding: 4px 8px; font-size: 12.5px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--field); color: var(--ink); box-shadow: none;
}
table select.cdkind:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ------------------------------------------------ the renewals card
   Ordered by how soon each contract runs out, which is the only order this
   list is useful in -- the roster below it is alphabetical because that is
   how you look somebody up, and they are different questions. */
.renewcard {
  background: var(--accent-soft); border: 1px solid var(--accent-line);
  border-radius: var(--r-md); padding: 0 16px; margin: 0 0 16px;
}
.renewcard > summary {
  cursor: pointer; list-style: none; padding: 12px 0;
  display: flex; align-items: center; gap: 8px;
  font-size: 14px; color: var(--ink);
}
.renewcard > summary::-webkit-details-marker { display: none; }
.renewcard > summary .ico { color: var(--accent); flex: 0 0 16px; }
.renewcard > summary b { font-weight: 700; }
.renewcard .renewhint {
  margin-left: auto; font-size: 12px; color: var(--ink-3);
}
/* The marker is drawn rather than relied on: list-style is off so the arrow
   can sit with the text instead of outside the padding. */
.renewcard > summary::after {
  content: "\25BE"; color: var(--ink-3); font-size: 11px;
  transition: transform var(--t-move);
}
.renewcard[open] > summary::after { transform: rotate(180deg); }

/* The reach-out bar, directly under the renewals one and built the same way so
   the pair read as one stack. Amber rather than the renewals green: this is
   somebody waiting on a person, not a date approaching. */
.reachcard {
  background: var(--amber-soft); border: 1px solid var(--amber);
  border-radius: var(--r-md); padding: 0 16px; margin: 0 0 16px;
}
.reachcard > summary {
  cursor: pointer; list-style: none; padding: 12px 0;
  display: flex; align-items: center; gap: 8px;
  font-size: 14px; color: var(--ink);
}
.reachcard > summary::-webkit-details-marker { display: none; }
.reachcard > summary .ico { color: var(--amber); flex: 0 0 16px; }
.reachcard > summary b { font-weight: 700; }
.reachcard > summary::after {
  content: "\25BE"; color: var(--ink-3); font-size: 11px;
  transition: transform var(--t-move);
}
.reachcard[open] > summary::after { transform: rotate(180deg); }

.reachrows { list-style: none; margin: 0; padding: 0 0 12px; }
.reachrow { padding: 10px 0; border-top: 1px solid var(--amber); }
.reachrow:first-child { border-top: none; }
.reachrowhead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
                font-size: 13.5px; }
.reachrowhead > a { font-weight: 600; color: var(--ink); text-decoration: none; }
.reachrowhead > a:hover { color: var(--accent); text-decoration: underline; }
/* The note is the reason somebody is being asked, so it is the part that has
   to be readable without opening anything. */
.reachrow .reachnote { margin: 4px 0 0; font-size: 13px; color: var(--ink-2);
                       white-space: pre-wrap; max-width: 78ch; }

.renewlist { padding-bottom: 12px; }
.renewlist li {
  padding: 6px 0; border-top: 1px solid var(--accent-line);
  font-size: 13.5px;
}
.renewlist li:first-child { border-top: none; }
.renewlist .renewdate {
  color: var(--ink-3); font-weight: 400; margin-left: 8px;
  font-variant-numeric: tabular-nums;
}
.renewlist .band-urgent { color: var(--red); font-weight: 650; }
.renewlist .band-soon   { color: var(--amber); font-weight: 650; }
.renewlist .band-later  { color: var(--ink-2); font-weight: 500; }
.renewcard .renewpast {
  background: var(--red-soft); color: var(--red); font-weight: 650;
  font-size: 11.5px; padding: 2px 8px; border-radius: var(--r-pill);
}

/* The Renewals card in the rail doubles as the switch for the card on the
   roster, so it has to say which way the next click goes. The arrow points
   right when the card is shut and turns down once it is open -- the same
   language as the chevron on the card's own summary. */
.railcard { position: relative; }
.railcard .railcaret {
  position: absolute; top: 50%; right: 10px; margin-top: -6px;
  width: 12px; height: 12px; color: var(--ink-3);
  transform: rotate(-45deg); transition: transform var(--t-move);
}
.railcard.live .railcaret { color: var(--accent); }
.railcard.open .railcaret { transform: rotate(90deg); }
.railcard:hover .railcaret { color: var(--accent); }
/* Collapsed, the rail is 64px wide and there is no room for it. */
.railshut .railcaret { display: none; }

/* ------------------------------------------- last call, set by hand
   The date sits under the elapsed days rather than replacing them: the days
   are what the column is scanned for, and the date is what you change on the
   rare occasion you need to. */
/* position: relative so the hidden date input below, which is absolutely
   positioned, stays inside this cell -- and so inside the table's scroll
   wrapper. Without it the input's containing block was the page, it sat at
   x=510 on a 375px phone, and the browser widened the whole layout viewport
   to fit an invisible 1px box. */
.lastcall { display: inline-flex; flex-direction: column; align-items: flex-end;
            gap: 3px; position: relative; }
/* Centred over the control below it, not right-aligned against the column
   edge. `stretch` overrides the flex-end above so the label spans the width
   the date input sets, and the input centres its own text -- otherwise the
   two agree on a right edge while the date reads left-of-centre inside its
   box, and "6d ago" sits visibly off to the side of the date it describes. */
.lastcall .lcwhen { font-variant-numeric: tabular-nums;
                    align-self: stretch; text-align: center; }
.lastcall .lcwhen.overdue { color: var(--red); font-weight: 600; }
td .lastcall input.lcdate {
  width: 104px; padding: 2px 6px; font: inherit; font-size: 11.5px;
  border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; color: var(--ink-3); box-shadow: none;
  font-variant-numeric: tabular-nums; cursor: pointer; text-align: center;
}
/* The picker icon is a flex sibling of the digits inside the input, so it eats
   width on the right and `text-align: center` then centres digits-plus-icon,
   leaving the digits themselves sitting left of the label above them. It does
   not take `position`, so it goes instead: the click handler opens the picker
   from anywhere in the input, which is what the pointer cursor already
   promised, and the digits get the whole box to centre in. */
td .lastcall input.lcdate::-webkit-calendar-picker-indicator { display: none; }
/* An empty date input draws "mm/dd/yyyy" and a calendar icon, so a roster
   nobody has called would read as a column of placeholder text. It used to be
   hidden until hover, which left nothing to aim at -- somebody looking for
   where to type found blank space. So: a date once there is one, a small Set
   button until then, and the input itself only exists to be opened.

   The swap is driven by a class on the wrapper, not by [value=""]. Picking a
   date changes the property and not the attribute, so an attribute selector
   goes on matching an input that visibly holds a date. */
.lastcall:not(.hasdate) input.lcdate {
  position: absolute; left: 0; top: 0; width: 1px; height: 1px; padding: 0;
  opacity: 0; pointer-events: none;
}
.lastcall.hasdate .lcset { display: none; }
.lcset {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px 2px 6px; font: inherit; font-size: 11.5px; font-weight: 500;
  color: var(--ink-3); background: transparent; cursor: pointer;
  border: 1px dashed var(--line-strong); border-radius: 999px;
  transition: color var(--t-colour), border-color var(--t-colour),
              background var(--t-colour);
}
.lcset .ico { width: 11px; height: 11px; }
.lcset:hover { color: var(--accent); border-color: var(--accent);
               background: var(--accent-soft); border-style: solid; }
.lcset:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
tr:hover td .lastcall input.lcdate,
td .lastcall input.lcdate:focus {
  border-color: var(--line); background: var(--field); color: var(--ink-2);
}
td .lastcall input.lcdate:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
td .lastcall input.lcdate:disabled { opacity: .5; }

/* --------------------------------------------- the padding GHL attaches
   Every webhook carries the contact's whole custom-field set. It is kept so a
   question genuinely added to the form is not silently dropped, and shut so a
   five-line check-in does not read as forty. */
.extras { margin: 14px 0 0; border-top: 1px solid var(--line); padding-top: 10px; }
.extras > summary {
  cursor: pointer; list-style: none; font-size: 12.5px; color: var(--ink-3);
  display: flex; align-items: baseline; gap: 6px;
}
.extras > summary::-webkit-details-marker { display: none; }
.extras > summary::after { content: "\25BE"; font-size: 10px; margin-left: auto; }
.extras[open] > summary::after { transform: rotate(180deg); display: inline-block; }
.extras > summary:hover { color: var(--ink-2); }
.extralist { margin: 10px 0 0; display: grid; gap: 8px; }
.extralist dt {
  font-size: 11px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-3);
}
.extralist dd { margin: 2px 0 0; font-size: 13px; color: var(--ink-2);
                white-space: pre-wrap; }

/* ------------------------------------------- the import table's columns
   The tier picker was being clipped at the table's right edge. Two causes,
   both worth naming: the global field rule stretches every select to 100%,
   and the tags column has no ceiling -- a contact carrying nine tags was
   taking half the table and squeezing everything after it.

   `table select.tier` rather than `.tier`, to outrank `form select`. */
table select.tier {
  width: auto; min-width: 92px; padding: 5px 8px; font-size: 12.5px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--field); color: var(--ink); box-shadow: none;
}
table select.tier:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
th.col-tier, td.col-tier { width: 1%; white-space: nowrap; }
/* Tags wrap inside their own column instead of widening the table. */
td.col-tags { max-width: 340px; }
/* The contract coming across with them: term, a line of the payment terms,
   and first term or re-sign under it. The terms are clipped to a line and
   read in full from the tooltip. */
td.col-contract { min-width: 210px; max-width: 300px; }
td.col-contract .cterms { display: block; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; max-width: 280px; }
table select.kind {
  display: block; margin-top: 6px; width: auto; min-width: 110px;
  padding: 4px 8px; font-size: 12px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--field); color: var(--ink); box-shadow: none;
}
table select.kind:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
/* One line per imported client saying what came with them. */
.msg .importout { margin: 8px 0 0; padding-left: 18px; font-weight: 400; }
.msg .importout li { margin: 3px 0; }

/* ------------------------------------------------- roster density
   Stefan reads this list all day and could see four clients at a time on a
   950px screen; he was zooming the browser to 85% to get a workable number.
   Zoom is the wrong lever -- it shrinks the navigation and the type along
   with the rows -- so the rows themselves got tighter instead.

   Name and chips now share a line and the email sits under them, which took
   a three-line cell to two, and the vertical padding came down with it. */
.cnametop { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.cnametop .rowchips { display: inline-flex; gap: 4px; }
table.roster td { padding-top: 8px; padding-bottom: 8px; }
table.roster td.cname { line-height: 1.35; }
table.roster .copyable { margin-top: 1px; }

/* The CSM picker on a held check-in, and the coach's name beside a client in
   the picker -- an admin sees every roster here, so two Jessica Marcuses have
   to be told apart by something. */
select.sendcsm {
  width: auto; min-width: 132px; padding: 5px 9px; font-size: 12.5px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--field); color: var(--ink-2); box-shadow: none;
}
select.sendcsm:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.clientopt .optcsm {
  margin-left: 8px; font-size: 11.5px; color: var(--ink-3); font-weight: 400;
}

/* The CRM link panel. One row per contact carrying this client's name, with
   enough about each -- email, when it appeared, whether it has an owner or
   the contract dates -- to tell a ghost from the real one. */
#crmlist { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.crmrow {
  display: flex; align-items: center; gap: 10px; justify-content: space-between;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--panel-2); font-size: 12.5px;
}
.crmrow.current { border-color: var(--accent-line); background: var(--accent-soft); }
.crmrow b { display: block; font-size: 12px; }
.crmrow .dim { display: block; }
.crmrow .mono { font-size: 10.5px; }

/* ------------------------------------- filing a held check-in
   Two pickers that answer "where does this go", then the two things you can
   do about it. They used to be interleaved -- picker, buttons, then a second
   picker stranded on the far side -- which read as three unrelated controls
   rather than one decision. */
.heldrow .heldgap { width: 14px; flex: 0 0 14px; }
.heldrow .clientpick + select.sendcsm { margin-left: 6px; }
/* Filing is the thing you came here to do, so it looks like it. */
.mini.green {
  display: inline-flex; align-items: center; gap: 5px;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent));
  border-color: transparent; color: var(--accent-ink); font-weight: 600;
  box-shadow: 0 1px 2px rgba(13,138,95,.24);
}
.mini.green:hover { filter: brightness(1.05); color: var(--accent-ink); }
.mini.green .ico { width: 13px; height: 13px; }
/* A client just picked on a held row: the next press is this one. */
.mini.green.nudge {
  box-shadow: 0 0 0 3px rgba(13,138,95,.22), 0 1px 2px rgba(13,138,95,.24);
}
.mini.quiet { color: var(--ink-3); border-color: var(--line); background: transparent; }
.mini.quiet:hover { color: var(--red); border-color: var(--red); }
.heldactions {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  justify-content: flex-end;
}

/* Two records for one person. Amber rather than red: it is not broken, it is
   a decision waiting -- but it sits at the top of the profile because a coach
   who does not see it logs a call against the wrong half. */
.twinpanel { border-color: var(--amber); background: var(--amber-soft); }
.twinlist { list-style: none; margin: 10px 0 0; padding: 0;
            display: flex; flex-direction: column; gap: 8px; }
.twinlist li {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 11px; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-sm); font-size: 13px;
}
.twinlist a { text-decoration: none; color: var(--ink); }
.twinlist a:hover b { color: var(--accent); }
.twinlist .dim { display: block; font-size: 11.5px; }

/* ------------------------------------------- one client's money
   A page about payments and nothing else. Opening a name from the payments
   list used to land on the roster profile -- calls, notes, check-ins -- which
   is the wrong half of the app when the question in hand is what they paid. */
.paycontract { margin-bottom: 16px; }
.paycontract .statushead { flex-wrap: wrap; gap: 8px; }
.paycontract .statushead h2 { margin-right: 4px; }
/* Which term. Loud on purpose: a first term and a re-sign are the two
   things somebody has to tell apart before touching either. */
.tag.kindtag { font-size: 12px; padding: 3px 9px; border-radius: 999px;
               letter-spacing: .03em; text-transform: uppercase; }
.tag.kindtag.first { background: var(--blue-soft); color: var(--blue); }
.tag.kindtag.resign { background: var(--accent-soft); color: var(--accent); }
/* Settled: over, or paid up. Read as history -- a strip across the top
   saying so, the card itself a shade quieter -- and the folds ask before
   opening. */
.paycontract.settled { background: var(--panel-2); }
.settledbar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
              margin: -4px 0 12px; padding: 7px 12px; border-radius: var(--r-sm);
              font-size: 12.5px; color: var(--ink-2);
              background: var(--lift-2); border: 1px solid var(--line); }
.settledbar .ico { width: 14px; height: 14px; color: var(--ink-3); flex: none; }
.settledbar b { color: var(--ink); }
.settledbar .dim { font-size: 12px; }
.earlier { font-size: 12px; font-weight: 700; letter-spacing: .08em;
           text-transform: uppercase; color: var(--ink-3);
           margin: 26px 0 10px; display: flex; align-items: baseline; gap: 10px;
           flex-wrap: wrap; }
.earlier .opt { font-weight: 500; letter-spacing: 0; text-transform: none;
                font-size: 12.5px; }
.factstrip.tight { margin: 10px 0 0; }
.factstrip.tight .fact b { font-size: 17px; }

/* ------------------------------------ acting in somebody else's workspace
   Full width, above everything, and sticky. The earlier version was a tinted
   box inside the page column, which you scroll past in a second and forget --
   and the moment it matters is the moment you have scrolled to the bottom of
   somebody else's roster and are about to change something on it.

   Violet on purpose. Emerald is "normal", amber is "look at this", red is
   "something is wrong" -- none of those is true here. This is a mode, and it
   needed a colour that means nothing else in the app. */
.asbar {
  position: sticky; top: 0; z-index: 70;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 18px; font-size: 13px; line-height: 1.4;
  background: var(--violet); color: #fff;
  box-shadow: 0 1px 0 rgba(0, 0, 0, .12);
}
.asbar b { font-weight: 700; }
.asbar .ico { flex: 0 0 16px; opacity: .9; }
.asbar .asgap { flex: 1; }
.asbar .asback {
  flex: 0 0 auto; white-space: nowrap; text-decoration: none;
  background: rgba(255, 255, 255, .17); color: #fff; font-weight: 650;
  padding: 4px 12px; border-radius: var(--r-pill);
  border: 1px solid rgba(255, 255, 255, .28);
}
.asbar .asback:hover { background: rgba(255, 255, 255, .28); color: #fff; }
/* The sidebar is 100vh and sticky at top:0; with a bar above it, both have to
   come down by its height or the rail runs off the bottom of the window. */
.shell.hasasbar .sidebar { top: var(--asbar, 38px); height: calc(100vh - var(--asbar, 38px)); }
@media (max-width: 900px) { .asbar { padding: 8px 12px; } }

/* Editing on the client's payments page. The forms sit inside the contract
   they belong to rather than in a dialog, so what you are changing and what
   it currently says stay on screen together. */
.payedit { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0 0; }
/* Renew client. The same row as the adders beside it, but it is an action
   rather than a fold -- it opens the contract form -- so it is filled in
   where they are outlined, and carries an arrow instead of a chevron. */
.payedit .renew {
  flex: 1 1 100%; margin: 0; cursor: pointer; text-align: left;
  padding: 11px 14px; font: inherit; font-weight: 600; font-size: 13.5px;
  color: var(--accent-ink); background: var(--accent);
  border: 1px solid var(--accent); border-radius: 10px;
  display: flex; align-items: center; gap: 8px;
}
/* A real element for the arrow, not ::before: the tooltip draws its own
   arrow through ::before on anything with data-tip, and the two fought --
   the refresh glyph popped up over the tooltip text on hover. */
.payedit .renew .renewicon { font-size: 15px; line-height: 1; }
.payedit .renew:hover { filter: brightness(1.06); }
.payedit .renew:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Full width, stacked. Side by side they were 300px columns, and a two-field
   row inside one of those squeezes a date input to nothing. */
.payedit .adder { flex: 1 1 100%; margin: 0; }
tr.editrow > td { background: var(--panel-2); padding: 12px 14px; }
tr.editrow .row { margin-bottom: 6px; }
tr.editrow .dialog-actions { margin-top: 10px; }
tr.editrow .e_note.needed { border-color: var(--red); }

/* ------------------------------------------------------- the library
   A shelf of rows. Everything that can be picked up carries the same six-dot
   handle on the left, folders included -- that handle is the whole reason a
   folder reads as something you can move rather than a label printed on the
   page. Folders are a solid bar so the grouping is visible at a glance; their
   contents are indented under them. What somebody scans for is a name they
   half-remember, so the name is the loud part and the right-hand column is
   reduced to where the thing is: a host for a link, a type and size for a
   file. */
.libbar { margin-bottom: 16px; }
.libsearch { margin: 0; }
form.libsearch input { min-width: 280px; width: auto; }

.libfolders { list-style: none; margin: 0; padding: 0; }
.libfolder { margin-bottom: 16px; }
.libfolder.dragging { opacity: .45; }

/* The folder bar. Tinted rather than white so it reads as a header for the
   rows beneath it, not as another one of them. */
.libfolderrow {
  display: flex; align-items: center; gap: 10px; position: relative;
  padding: 10px 14px;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.libfoldername { font-size: 14px; font-weight: 600; color: var(--ink); }
/* The name as a button: looks like the name until you hover, when a faint
   underline says it will take a click. */
button.libfoldername { border: none; background: none; padding: 2px 4px; margin: -2px -4px;
                       font: inherit; font-size: 14px; font-weight: 600; color: var(--ink);
                       cursor: text; border-radius: var(--r-sm); text-align: left; }
button.libfoldername:hover { background: var(--lift-2); }
button.libfoldername:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
input.libfolderedit { font: inherit; font-size: 14px; font-weight: 600; color: var(--ink);
                      padding: 2px 6px; margin: -3px 0; border: 1px solid var(--accent-line);
                      border-radius: var(--r-sm); background: var(--panel); width: min(320px, 60%);
                      box-shadow: var(--ring); }
/* The bar's actions -- subfolder, sort, remove -- stay out of the way until
   the bar is hovered, the same as Edit on the rows beneath. At rest a folder
   bar is a name and a count; three buttons on every bar were what made the
   shelf read as a control panel. */
.libfolderrow .folderact { opacity: 0; transition: opacity .12s ease; }
.libfolderrow:hover .folderact, .libfolderrow:focus-within .folderact { opacity: 1; }
@media (hover: none) { .libfolderrow .folderact { opacity: 1; } }
/* The caret is the affordance; the whole bar is the hit area. */
.libcaret {
  flex: 0 0 auto; background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--ink-3); font-size: 10px; line-height: 1; width: 11px;
  transition: transform .14s var(--ease), color var(--t-colour);
}
.libcaret:hover { color: var(--ink); }
.libfolder.shut .libcaret { transform: rotate(-90deg); }
/* The same caret is reused by HQ's cards and the client payments card, where
   shut is the absence of .open rather than a .shut class. */
[data-card]:not(.open) .libcaret { transform: rotate(-90deg); }
.libfolderrow { cursor: pointer; }
.libfolderrow .mini, .libfolderrow .libgrip { cursor: default; }
.libfolderrow .libgrip { cursor: grab; }
/* A shut folder keeps its bar and loses its rule -- there is nothing hanging
   off it to connect to. */
.libfolder.shut { margin-bottom: 8px; }
.foldico { width: 15px; height: 15px; flex: 0 0 15px; color: var(--ink-3); }
.libcount { font-size: 12px; color: var(--ink-3); white-space: nowrap; }

/* One handle, everywhere, so picking a thing up is one gesture to learn. */
.libgrip {
  flex: 0 0 16px; width: 16px; height: 16px; cursor: grab;
  color: var(--ink-2); opacity: .7; transition: opacity var(--t-colour);
  display: inline-flex; align-items: center; justify-content: center;
}
.libgrip .ico { width: 16px; height: 16px; }
.libgrip:hover { opacity: 1; }
.libgrip:active { cursor: grabbing; }
/* Keeps the names in one column for people who cannot rearrange the shelf. */
.libgrip.ghost { visibility: hidden; cursor: default; }

/* Everything in a folder hangs off a rule running down from its bar. Without
   it an empty folder reads as a box floating under a heading rather than as
   the inside of that folder -- the indent alone is not enough to say so when
   there is only one thing to indent. The rule starts under the folder's own
   handle, which is where the eye already is. */
.liblist { list-style: none; margin: 0 0 0 21px; padding: 8px 0 0 18px;
           border-left: 2px solid var(--line-strong);
           display: flex; flex-direction: column; gap: 6px; }
.liblist.over { border-left-color: var(--accent); }
.liblist.over > .libdrop { border-color: var(--accent);
                           background: var(--accent-soft); }

.libitem {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px; font-size: 13.5px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-1);
  transition: border-color var(--t-colour), box-shadow var(--t-colour);
}
.libitem:hover { border-color: var(--line-strong); box-shadow: var(--shadow-2); }
.libitem.dragging { opacity: .4; }

/* Basis 0, not auto: with auto the name-and-description column carries its
   content width into the shrink calculation, so a two-line description pulled
   width off the host column beside it and turned "PDF - 44B" into "PD...".
   From zero it simply absorbs whatever is left once the host has its own. */
.libmain { flex: 1 1 0; min-width: 0; }
.libtitle { font-weight: 600; color: var(--ink); text-decoration: none; }
.libtitle:hover { color: var(--accent); text-decoration: underline; }
.libnote { display: block; font-size: 12.5px; color: var(--ink-2); margin-top: 2px; }

/* Sits exactly where the description would be, so a shelf reads as a column of
   descriptions with gaps in it rather than a column of names. Quiet -- it is an
   invitation, not a warning, and a resource without one still works. */
.libdescribe {
  display: block; margin-top: 2px; padding: 0;
  font-size: 12.5px; font-family: inherit; color: var(--ink-3);
  background: none; border: 0; border-bottom: 1px dashed var(--line-strong);
  cursor: pointer; text-align: left;
  transition: color var(--t-colour), border-color var(--t-colour);
}
.libdescribe:hover { color: var(--blue); border-bottom-color: var(--blue); }
.libdescribe:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* Only speaks near the limit, so it is silent for a normal description. */
.charleft { display: block; margin-top: 4px; font-size: 11.5px; color: var(--ink-3); }
.charleft.tight { color: var(--red); font-weight: 600; }

#linkdlg textarea {
  width: 100%; resize: vertical; min-height: 62px;
  font: inherit; line-height: 1.45;
}

/* Monospaced so hosts line up down the column and read as addresses. */
.libhost {
  flex: 0 1 auto; max-width: 34%; font-size: 12px; color: var(--ink-3);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-align: right;
}
/* A file we hold is a different thing from a link out, and the column is
   where that shows. */
.libhost.isfile { font-family: inherit; letter-spacing: .02em;
                  font-size: 11.5px; font-weight: 600; color: var(--ink-2); }

.libitem .libedit { flex: 0 0 auto; opacity: .5; transition: opacity .12s ease; }
.libitem:hover .libedit, .libitem:focus-within .libedit { opacity: 1; }
/* Keeps the right-hand column straight when a row has no Edit of its own. */
.libedit-gap { flex: 0 0 45px; }

/* Matches .empty.small's specificity, which zeroes the border and the ground.
   Drawn firmly: it is a target somebody is meant to aim a file at, and a hint
   too faint to read is not a target. */
.empty.libdrop {
  margin: 0; padding: 18px; text-align: center;
  color: var(--ink-2); font-weight: 500;
  border: 2px dashed var(--accent-line); border-radius: var(--r-md);
  background: var(--accent-soft);
}
.libitem ~ .libdrop { display: none; }
/* A folder with rows in it has no placeholder to light up, so the list says
   it is a target itself. An empty one already has the placeholder doing that,
   and two dashed boxes inside each other say it twice. */
.liblist.over:has(.libitem) { outline: 2px dashed var(--accent);
                              outline-offset: 4px; border-radius: var(--r-md); }

/* Dragging a file in from the desktop. Without this the browser navigates to
   the file and the page is gone. */
.libdropveil {
  position: fixed; inset: 0; z-index: 200; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(2px);
}
.libdropveil span {
  padding: 18px 26px; font-size: 15px; font-weight: 600; color: var(--accent);
  background: var(--surface); border: 2px dashed var(--accent);
  border-radius: var(--r-md); box-shadow: var(--shadow-3);
}
#linkdlg { width: 460px; max-width: 92vw; }

/* ------------------------------------------------------------- Will's HQ
   A dashboard, not a stack. Shut, every card carries its own numbers so the
   page answers most questions without being touched; opened, a card takes the
   full width and holds the real page, so working in one is the same as being
   on it. The two states are the whole design: dense when you are reading,
   wide when you are doing. */
.hqpage .sec { margin: 26px 0 10px; }
.asof { font-size: 12px; color: var(--ink-3); white-space: nowrap; }

/* --- the numbers across the top --- */
.kpirow { display: grid; gap: 12px;
          grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.kpicard {
  display: flex; flex-direction: column; gap: 2px;
  padding: 15px 16px 13px; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-1);
  transition: border-color var(--t-colour), box-shadow var(--t-colour);
}
.kpicard:hover { border-color: var(--line-strong); box-shadow: var(--shadow-2); }
.kpilabel { display: flex; align-items: center; gap: 5px;
            font-size: 10.5px; font-weight: 700; letter-spacing: .08em;
            text-transform: uppercase; color: var(--ink-3); }
.kpivalue { font-size: 30px; font-weight: 700; line-height: 1.15; color: var(--ink);
            font-variant-numeric: tabular-nums; }
.kpicard.bad .kpivalue { color: var(--red); }
.kpisub { font-size: 12px; color: var(--ink-2); }
.kpigo { margin-top: 8px; font-size: 12px; font-weight: 600; color: var(--accent); }

/* --- the modules --- */
.modgrid { display: grid; gap: 12px;
           grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.modcard {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-1);
  transition: border-color var(--t-colour), box-shadow var(--t-colour);
}
.modcard:hover { border-color: var(--line-strong); }
/* Open means full width, wherever it sits in the grid. A page squeezed into
   one column of three is a page nobody can work in. */
.modcard.open { grid-column: 1 / -1; box-shadow: var(--shadow-2); }
.modhead { padding: 14px 16px 0; cursor: pointer; }
.modtitle { font-size: 14.5px; font-weight: 600; color: var(--ink); }
.modchip {
  padding: 2px 9px; border-radius: 999px; white-space: nowrap;
  font-size: 10.5px; font-weight: 700; letter-spacing: .02em;
  color: var(--ink-2); background: var(--panel-2); border: 1px solid var(--line);
}
.modchip.ok { color: var(--green); background: var(--green-soft); border-color: transparent; }
.modchip.warn { color: var(--amber); background: var(--amber-soft); border-color: transparent; }
.modblurb { margin: 4px 16px 0 42px; font-size: 12.5px; color: var(--ink-2); }
.modstats { display: flex; gap: 22px; padding: 12px 16px 0 42px; }
.modstat { display: flex; flex-direction: column; }
.modstat b { font-size: 21px; font-weight: 700; line-height: 1.2; color: var(--ink);
             font-variant-numeric: tabular-nums; }
.modstat span { font-size: 11px; color: var(--ink-3); }
.modopen { display: inline-block; margin: 12px 16px 14px 42px;
           font-size: 12.5px; font-weight: 600; color: var(--accent);
           text-decoration: none; }
.modopen:hover { text-decoration: underline; }
/* Opened, the summary has done its job and the page below it is the point. */
.modcard.open .modblurb, .modcard.open .modstats, .modcard.open .modopen {
  display: none;
}
.modcard.open .modhead { padding-bottom: 12px; }

.hqbody { border-top: 1px solid var(--line); background: var(--bg);
          overflow: hidden; border-radius: 0 0 var(--r-md) var(--r-md); }
.hqframe { display: block; width: 100%; height: 420px; border: 0; }

.hqjump { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
          margin-top: 22px; font-size: 12.5px; }

/* ------------------------------------------------ how clients feel
   The check-in already asks all of this and nothing ever showed it. Bars
   rather than a chart: there are four or five answers and the only question
   is which is biggest, which a length answers faster than a legend. */
.feelwrap { padding: 16px; }
.feelsplit { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
             gap: 20px; margin-top: 18px; }
.feelbars { list-style: none; margin: 8px 0 0; padding: 0;
            display: flex; flex-direction: column; gap: 7px; }
.feelbars li { display: grid; grid-template-columns: minmax(0, 1fr) 90px 26px;
               align-items: center; gap: 10px; font-size: 12.5px; }
.feelname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
            color: var(--ink-2); }
.feelbar { height: 7px; border-radius: 999px; background: var(--panel-2);
           overflow: hidden; }
.feelbar i { display: block; height: 100%; background: var(--accent);
             border-radius: 999px; }
.feelbars b { text-align: right; font-variant-numeric: tabular-nums;
              color: var(--ink); }

/* ------------------------------------------- asked for you to reach out
   Somebody wrote this after a call and is waiting on a person, not on a
   page, so it sits above everything else and is not behind a shut card. */
/* Outlined in red the whole way round rather than a stripe down one edge: a
   left border reads as a category marker and got skimmed past. Red because it
   is the only thing on this page asking the reader personally to do something
   -- everything below it is information. Tightened up at the same time, since
   it sits above the metrics and should not push them off the fold. */
/* One card per tier, side by side. Elite and Core are different products
   answered for by different people, and a single list meant reading tier tags
   a row at a time to work out which was which. auto-fit rather than two fixed
   columns: when only one tier has anything waiting, its card takes the full
   width instead of leaving a hole where the other would have been. */
.reachhint { margin: 0 0 8px; max-width: 78ch; }

.reachgrid {
  display: grid; gap: var(--step-3);
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  margin-bottom: var(--step-5);
}
.reachpanel {
  border: 1.5px solid var(--red); background: var(--red-soft);
  padding: 14px 16px; margin: 0;
}
.reachpanel .statushead h2 { font-size: 15px; }
.reachpanel .hint { margin: 4px 0 0; font-size: 12.5px; }
.reachlist { list-style: none; margin: 10px 0 0; padding: 0;
             display: flex; flex-direction: column; gap: 8px; }
.reachitem { padding: 9px 12px; border: 1px solid var(--line);
             border-radius: var(--r-md); background: var(--surface); }
.reachwho { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
            font-size: 13px; }
.reachclient { font-weight: 600; color: var(--ink); text-decoration: none; }
.reachclient:hover { color: var(--accent); text-decoration: underline; }
.reachnote { margin: 4px 0 0; font-size: 13px; color: var(--ink);
             white-space: pre-wrap; max-width: 78ch; }
.reachacts { margin-top: 8px; }

/* Whether they are behind on payments, on the roster rather than two clicks
   away. Silent when there is no plan recorded at all: a green "Paid up" on
   somebody nobody has entered payments for would be good news the app
   invented. */
.paytag {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 0 6px 0 5px; border-radius: 999px;
  font-size: 10px; font-weight: 700; letter-spacing: .02em;
  white-space: nowrap; border: 1px solid transparent;
}
/* flex-basis reset too: .ico sets `flex: 0 0 16px`, so width alone left an
   11px tick sitting in a 16px slot, pushed off-centre against the word. */
.paytag .ico { width: 11px; height: 11px; flex: 0 0 11px; }
.paytag.paidup { color: var(--green); background: var(--green-soft); }
.paytag.overdue { color: var(--red); background: var(--red-soft);
                  border-color: var(--red); }


/* The name cell grew four kinds of chip over this session and rows went from
   66px to 92. The words are only labels -- the sentence is in the tooltip --
   so they are set tight, and the row is allowed to be as short as it was. */
.cname .rowchips { gap: 4px; }
.cname .tag, .cname .paytag, .cname .ghlchip, .cname .tagwarn, .cname .noemail {
  font-size: 10px; padding-top: 0; padding-bottom: 0; line-height: 1.7;
}
.cname .noemail { margin-top: 0; gap: 3px; }
.cname .tagwarn { gap: 3px; padding: 0 7px 0 5px; }


/* ---------------------------------------------------------------- rules
   the HQ rebuild removed by accident. It replaced a slice of this file by
   string position, which took 20-odd unrelated groups with it. Templates
   still reference all of these: Back to HQ, the update notice, the client
   payments card, the contract dialog, the call editor, filter bars. */

.hqcard {
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); box-shadow: var(--shadow-1);
  margin-bottom: 12px;
  transition: border-color var(--t-colour), box-shadow var(--t-colour);
}

.hqcard:hover { border-color: var(--line-strong); }

.hqcard.open { box-shadow: var(--shadow-2); }


.hqhead {
  display: flex; align-items: center; gap: 6px 11px; flex-wrap: wrap;
  padding: 14px 16px 0; cursor: pointer;
}

.hqhead .pgspacer { flex: 1 0 auto; }

.hqtitle { font-size: 15px; font-weight: 600; color: var(--ink); }

/* The figure is the point of a shut card, so it is the loud part. */
.hqcount {
  font-size: 13px; font-weight: 700; color: var(--accent);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

.hqnote { font-size: 12px; color: var(--ink-3); white-space: nowrap; }

.hqcard.open .hqnote, .hqcard.open .hqcount { opacity: .75; }

.hqcard.open .hqblurb { margin-bottom: 8px; }


/* A fold-card sitting inside a page column rather than on HQ: it is already
   a .panel, so it brings its own border and ground. */
.panel.hqcard { padding: 0; }

.panel.hqcard .hqhead { padding: 14px 16px; }

.panel.hqcard.open .hqhead { padding-bottom: 12px; }

.cpay .hqbody { border-radius: 0 0 var(--r-md) var(--r-md); overflow: hidden; }


/* Finding one person on All clients. Sits above the table, apart from the
   tier and stage chips, which are a different kind of question. */
.acfilters { margin: 14px 0 12px; align-items: center; gap: 12px; }

.acfilters input { min-width: 260px; }


/* --------------------------------------------------------- back to HQ
   The owner's way home, in the same place on every page. Loud enough to find
   without looking, quiet enough not to compete with the page's own title --
   it is a way back, not the thing you came to do. */
.backbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
           margin: 18px 0 -6px 28px; }

.backhq, .backlink {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px 7px 10px;
  font-size: 13px; font-weight: 600; text-decoration: none;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid var(--accent-line); border-radius: 999px;
  transition: background var(--t-colour), border-color var(--t-colour),
              transform var(--t-colour);
}

.backhq:hover { background: var(--accent-line); border-color: var(--accent);
                transform: translateX(-2px); }

/* The second one is the page you came off rather than home, so it is quieter
   -- two buttons shouting the same volume make you read both to pick. */
.backlink { color: var(--ink-2); background: var(--panel-2);
            border: 1px solid var(--line-strong); }

.backlink:hover { color: var(--ink); border-color: var(--ink-3);
                  transform: translateX(-2px); }

.backhq:focus-visible, .backlink:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; }

.backhq .ico, .backlink .ico { width: 14px; height: 14px; }

th.sorted { color: var(--accent); }

th.sorted .sortarrow { opacity: 1; }


/* Copy does not hide until hover the way Edit does: sharing what is on the
   shelf is what people open this page to do, so it should not need finding. */
.libitem .copybtn { flex: 0 0 auto; opacity: 1; font-weight: 600;
                    color: var(--accent); }

.libitem .copybtn:hover { border-color: var(--accent); }

.libitem .copybtn.copied {
  color: var(--accent-ink); background: var(--accent); border-color: var(--accent);
}


/* Finding one client on the payments page. */
.pcfilters { margin: 14px 0 12px; align-items: center; gap: 8px; flex-wrap: wrap; }

.pcfilters input { min-width: 240px; margin-right: 4px; }

.pcfilters .chipbtn .count { margin-left: 5px; opacity: .7; }


/* Adding a contract from the client's own money page. */
.payaddbar { display: flex; justify-content: flex-end; margin: 4px 0 14px; }


/* What the numbers do not say, on the contract itself. */
.ctnotes {
  margin: 10px 0 0; padding: 10px 12px; font-size: 13px; color: var(--ink-2);
  background: var(--panel-2); border-left: 3px solid var(--line-strong);
  border-radius: 0 var(--r-sm) var(--r-sm) 0; white-space: pre-wrap;
}

#ctdlg .rowacts { display: flex; gap: 2px; justify-content: flex-end; }

/* Time on top of a contract. A box of its own inside the dialog, because
   it is the one thing on the form that is NOT the contract: the term stays
   what was signed, this is what was added and why. Quiet until something is
   chosen, then tinted so the fact that an extension is going in is visible
   from across the form. */
.extbox { border: 1px solid var(--accent-line); border-radius: var(--r-md);
          padding: 10px 14px 12px; margin: 6px 0 0; min-width: 0;
          background: var(--accent-soft); }
.extbox[hidden] { display: none; }
.extbox .row { margin-top: 0; }
.extbox .hint { margin: 8px 0 0; }
.tag.ext { background: var(--accent-soft); color: var(--accent); }
.extlist { list-style: none; margin: 10px 0 0; padding: 0; font-size: 13px;
           color: var(--ink-2); display: grid; gap: 4px; }
.extlist li { padding: 6px 12px; background: var(--accent-soft);
              border-left: 3px solid var(--accent-line);
              border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.extlist b { color: var(--accent); margin-right: 4px; }

/* A column this narrow cannot hold the word "Remove" -- it was two pixels
   wider than the space for it, which is all it takes to start a sideways
   scrollbar and clip a tooltip against it. */
#ctdlg .ct_copy, #ctdlg .ct_drop {
  width: 22px; height: 22px; padding: 0; line-height: 1; font-size: 15px;
  border: 1px solid transparent; border-radius: 6px; cursor: pointer;
  background: none; color: var(--ink-3); opacity: .6;
  transition: color var(--t-colour), border-color var(--t-colour), opacity var(--t-colour);
}

#ctdlg tr:hover .ct_copy, #ctdlg tr:hover .ct_drop { opacity: 1; }

#ctdlg .ct_copy:hover { color: var(--accent); border-color: var(--accent); }

#ctdlg .ct_drop:hover { color: var(--red); border-color: var(--red); }

#ctdlg .ct_copy:focus-visible, #ctdlg .ct_drop:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; }


/* Fixing a call that was logged wrong. Quiet until you want them: the common
   thing to do with a call is read it. */
.callacts { display: flex; gap: 6px; margin-top: 8px; opacity: .55;
            transition: opacity var(--t-colour); }

.callentry:hover .callacts, .callentry:focus-within .callacts { opacity: 1; }

.calledform { margin-top: 10px; padding: 12px 14px; background: var(--panel-2);
              border: 1px solid var(--line); border-radius: var(--r-md); }

.calledform textarea { width: 100%; }


/* ------------------------------------------------------ a new version
   Bottom of the window, not the top: the top is where the system banner and
   the impersonation bar already live, and a third thing up there would push
   the page somebody is reading. Quiet, dismissible, and it never appears
   twice for the same build. */
.updatebar {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 120; display: flex; align-items: center; gap: 14px;
  padding: 11px 12px 11px 16px; font-size: 13px;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
  animation: rise 220ms var(--ease);
}

.updatebar .uptext { display: flex; flex-direction: column; gap: 1px; }

.updatebar .upmeta { font-size: 11.5px; color: var(--ink-3); }


.updatebar button {
  padding: 7px 14px; font: inherit; font-size: 12.5px; font-weight: 600;
  border-radius: var(--r-sm); cursor: pointer; border: none;
  background: var(--accent); color: var(--accent-ink);
  white-space: nowrap; flex: 0 0 auto;
}

.updatebar .uptext { min-width: 0; }

.updatebar .upmeta { white-space: nowrap; overflow: hidden;
                     text-overflow: ellipsis; }

.updatebar button:hover { filter: brightness(1.08); }

.updatebar .uplater {
  background: none; color: var(--ink-3); font-weight: 500;
  padding: 0; width: 24px; height: 24px; font-size: 17px; line-height: 1;
}

.updatebar .uplater:hover { color: var(--ink); filter: none; }

.updatebar button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.libitem a.mini { flex: 0 0 auto; }

#ctdlg .row { display: flex; gap: 12px; flex-wrap: wrap; }

#ctdlg .tablewrap { max-height: 260px; overflow-y: auto; margin: 6px 0 4px; }

.scanbtns { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

#ctdlg .tablewrap { overflow: visible; max-height: none; border-radius: 8px; }

.insttotal.ok { color: var(--green); font-weight: 600; }

/* Straight into GoHighLevel from a list. Half of what a coach does about a
   client -- fix a tag, check a field, read the conversation -- happens there,
   and the alternative was searching for them by name in another tab. */
.ghlchip {
  display: inline-flex; align-items: center; padding: 1px 7px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
  color: var(--blue); background: var(--blue-soft);
  border: 1px solid transparent; border-radius: 999px;
  text-decoration: none; white-space: nowrap;
  transition: border-color var(--t-colour), background var(--t-colour);
}

.ghlchip:hover { border-color: var(--blue); background: transparent; }

.ghlchip:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }

/* Both tier tags on one contact. Amber rather than red: nothing is broken and
   nobody is at risk -- GoHighLevel is simply still treating an upgraded client
   as the tier they left, and somebody has to go and take the old tag off. */
.tagwarn {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 8px 1px 6px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .02em; white-space: nowrap; text-decoration: none;
  color: var(--amber); background: var(--amber-soft);
  border: 1px solid var(--amber); border-radius: 999px;
  transition: filter var(--t-colour);
}

.tagwarn:hover { filter: brightness(1.06); text-decoration: none; }

.tagwarn:focus-visible { outline: 2px solid var(--amber); outline-offset: 1px; }

/* The sortable roster headers and the whole contract dialog, also lost when
   the HQ rebuild cut this file by string position. Order matters here: the
   second #ctdlg pass is the one that stops the dialog scrolling sideways. */

/* ------------------------------------------------------ sortable columns
   Every roster column sorts both ways. The arrow is faint until you hover or
   until that column is the one in use, so ten headers do not turn into ten
   pieces of furniture competing with the data underneath. */
table.roster th[data-sortable] {
  cursor: pointer; user-select: none; white-space: nowrap;
}

table.roster th[data-sortable]:hover { color: var(--ink); }

table.roster th[data-sortable]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 4px;
}

/* Specificity: `table.roster th span { display: block }` is (0,1,3) and beats
   a bare `.sortarrow`, which dropped every arrow onto a line of its own under
   the header -- and under the filter dropdown on the three columns that have
   one. Matched here so the arrow sits beside the label it belongs to. */
table.roster th .sortarrow, .sortarrow {
  display: inline-block; width: 0; height: 0; margin-left: 5px;
  vertical-align: middle; opacity: 0; transition: opacity var(--t-colour);
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-bottom: 5px solid currentColor;
}

th[data-sortable]:hover .sortarrow { opacity: .45; }

/* Pointing the way the column is going: up for A–Z and smallest first. */
th[aria-sort="descending"] .sortarrow {
  border-bottom: 0; border-top: 5px solid currentColor;
}

/* Sized to whatever viewport it lands in -- on its own page that is the
   window, inside a card it is the frame. The form scrolls rather than the
   dialog growing past the bottom edge and taking Save with it. */
/* [open], not bare: `display` on a <dialog> overrides the browser's own
   `dialog:not([open]) { display: none }`, and the dialog then sits in the page
   permanently -- which is exactly the blank gap and the duplicate "Add a
   contract" card that appeared below the button meant to open it. */
#ctdlg {
  /* max-width too: the base `dialog` rule sets 620px and a bare width cannot
     beat it, so this asked for 720 and got 620. */
  width: min(720px, 94vw); max-width: min(720px, 94vw);
  max-height: 92vh; overflow: hidden;
}

#ctdlg[open] { display: flex; flex-direction: column; }

#ctdlg form { display: flex; flex-direction: column; min-height: 0;
              overflow-y: auto; padding-right: 4px; }

#ctdlg .row > div { flex: 1 1 150px; min-width: 0; }

/* Closing commission: the section on a closer's Payments page, and the
   tag on a contract somebody closed. Gold, the colour the Hub already uses
   for "yours" -- the favourites shelf -- because that is what it is. */
.closingpanel { margin: 0 0 var(--step-5); padding: 18px 20px 6px;
                border: 1px solid var(--gold-line); border-radius: var(--r-lg);
                background: linear-gradient(180deg, var(--gold-soft), transparent 140px);
                box-shadow: var(--shadow-1); }
.closingpanel .rosterhead { margin-bottom: 14px; }
.closingpanel .rosterhead .dim { max-width: 72ch; }
.closingfigs { margin-bottom: 14px; }
.closingfigs .fact span { font-size: 11px; font-weight: 600; letter-spacing: .07em;
                          text-transform: uppercase; color: var(--ink-3);
                          display: flex; align-items: center; gap: 6px; }
.closingfigs .fact b { font-size: 19px; letter-spacing: -.02em; }
.closingfigs .fact.total { background: var(--gold-soft); }
.closingfigs .fact small { font-size: 11.5px; color: var(--ink-3); }
.closingpanel .tablewrap { margin-bottom: 12px; }
.closingpanel.none { padding-bottom: 16px; background: var(--panel);
                     border-style: dashed; box-shadow: none; }
.closingpanel.none .rosterhead { margin-bottom: 0; }
.closingpanel.none h2 { color: var(--ink-2); }
/* The chip that must not be missed: light red with a solid count while
   there is anything in it, solid red when it is the active filter. */
.chipbtn.redchip { border-color: var(--red); color: var(--red); background: var(--red-soft); }
.chipbtn.redchip .n { opacity: 1; margin-left: 5px; background: var(--red); color: #fff;
                      border-radius: var(--r-pill); padding: 0 6px; font-size: 11px;
                      font-weight: 700; font-variant-numeric: tabular-nums; }
.chipbtn.redchip.active { background: var(--red); border-color: var(--red); color: #fff; }
.chipbtn.redchip.active .n { background: rgba(255, 255, 255, .25); }
.chipbtn.amberchip { border-color: var(--amber); color: var(--amber); background: var(--amber-soft); }
.chipbtn.amberchip.active { background: var(--amber); border-color: var(--amber); color: #fff; }
.chipbtn.amberchip .n { opacity: 1; margin-left: 5px; background: var(--amber); color: #fff;
                        border-radius: var(--r-pill); padding: 0 6px; font-size: 11px;
                        font-weight: 700; font-variant-numeric: tabular-nums; }
.chipbtn.amberchip.active .n { background: rgba(255, 255, 255, .25); }
.chipbtn .ico { width: 13px; height: 13px; margin-right: 4px; color: inherit; }
.closingpanel td.rowacts { white-space: nowrap; }
.closingpanel td.rowacts .mini + .mini { margin-left: 4px; }
#closingdlg .hint.warn { color: var(--amber); }
#closingdlg .hint.warn a { color: inherit; font-weight: 600; }
.tag.closer { background: var(--gold-soft); color: var(--gold); border: 1px solid var(--gold-line); }
.closebox { border-color: var(--gold-line); }

.cdoutcome { margin: 10px 0 0; padding: 10px 14px; border-radius: var(--r-md); font-size: 13.5px; }
.cdoutcome.good { background: var(--green-soft); color: var(--green); border: 1px solid rgba(13, 138, 95, .25); }
.cdoutcome.bad { background: var(--red-soft); color: var(--red); border: 1px solid rgba(192, 57, 47, .25); }

/* The contract-dates import: the tools above its table, and its sortable
   headings. The column filters are the roster's. */
.cdtools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0 10px; }
.cdtools input[type="search"] { flex: 1 1 160px; max-width: 240px; }
.cdbulk { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-3); }
.cdbulk select { width: auto; font-size: 12.5px; padding: 4px 26px 4px 8px; }
#cdtable th[data-sort] span { cursor: pointer; }
#cdtable th[data-sort] span:hover { color: var(--ink); }
#cdtable th.sorted span::after { content: " \2191"; color: var(--accent); }
#cdtable th.sorted[data-dir="desc"] span::after { content: " \2193"; }
#cdtable th .colfilter { max-width: 150px; min-width: 88px; }
#cdtable th[data-sort="csm"] { min-width: 120px; }

/* A contract with no payments behind it: said in amber on the card, with
   the way to fix it beside it. In plan mode the contract dialog loses its
   term fields -- they belong to the contract that already exists. */
.planmissing { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
               padding: 12px 14px; margin: 0 0 6px; border-radius: var(--r-md);
               background: var(--amber-soft); border: 1px solid var(--amber);
               color: var(--ink-2); font-size: 13.5px; line-height: 1.45; }
.planmissing > .ico { flex: none; width: 18px; height: 18px; color: var(--amber); }
.planmissing > div { flex: 1 1 260px; }
.planmissing b { color: var(--amber); }
.planbtn { flex: none; font: inherit; font-size: 13px; font-weight: 600; padding: 7px 12px;
           border-radius: var(--r-sm); border: 1px solid var(--amber); color: var(--amber);
           background: var(--panel); cursor: pointer; }
.planbtn:hover { background: var(--amber); color: #fff; }
#ctdlg.planmode .termonly { display: none; }
#ctdlg #ct_planfor { margin: -4px 0 8px; }

/* The deposit date before there is a deposit: present, greyed, waiting. */
#ctdlg .row > div.off label, #ctdlg .row > div.off label .opt { color: var(--ink-3); }
#ctdlg .row > div.off input { opacity: .55; cursor: not-allowed; }

#ctdlg table input { width: 100%; min-width: 0; }

#ctdlg table th, #ctdlg table td { padding-left: 6px; padding-right: 6px; }

#ctdlg textarea { width: 100%; }

/* Save stays put while the form above it scrolls. */
#ctdlg .dialog-actions {
  position: sticky; bottom: 0; margin-top: auto; padding: 12px 0 2px;
  background: var(--panel); border-top: 1px solid var(--line-soft);
}

/* The two GoHighLevel actions. Both used to be `mini`, which in this app is
   the quietest control there is -- and neither is a footnote: one is the only
   way into the contract-date panel, the other is what people reach for every
   time tags are fixed in bulk. */
.statushead .tb.primary { padding: 8px 16px; font-size: 13px; }

.scanbtns .tb { padding: 9px 16px; }

/* The schedule builder on a client's contract dialog.

   Nothing here scrolls sideways. Tables are min-width:720px globally and
   .tablewrap scrolls to cope, which inside a ~560px dialog meant the schedule
   ran off to the right -- taking the Add a payment tooltip with it, clipped by
   the very container that was scrolling. So in here the table is laid out to
   the width available and the form only ever scrolls up and down. */
#ctdlg form { overflow-x: hidden; }

#ctdlg table { min-width: 0; width: 100%; table-layout: fixed; font-size: 12.5px; }

#ctdlg table th:first-child, #ctdlg table td:first-child { width: 26px; }

#ctdlg table th:last-child,  #ctdlg table td:last-child  { width: 58px; }

#ctdlg table th, #ctdlg table td { padding-left: 5px; padding-right: 5px; }

#ctdlg table input { width: 100%; min-width: 0; padding-left: 6px; padding-right: 2px; }

#ctdlg .insthead { display: flex; align-items: center; gap: 10px; margin: 10px 0 4px;
                   font-size: 12px; font-weight: 600; color: var(--ink-2); }

#ctdlg .instspacer { flex: 1; }

.insttotal.off { color: var(--amber); font-weight: 600; }

.tagwarn .ico { width: 11px; height: 11px; }

.hqjump { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
          margin-top: 20px; font-size: 12.5px; }

.insttotal { margin: 6px 0 2px; font-size: 12.5px; font-variant-numeric: tabular-nums; }

@media (max-width: 640px) {
  .updatebar { width: calc(100vw - 24px); }
  .updatebar .upmeta { white-space: normal; }
}

/* On a phone the row has more columns than it has width: name, description,
   host and two buttons do not fit across 375px, and squeezed together they
   wrapped the description one word per line. Let it wrap instead -- the name
   and its description take the full width, and the host and buttons sit under
   them on a line of their own. */
@media (max-width: 640px) {
  .libitem { flex-wrap: wrap; row-gap: 8px; }
  .libmain { flex: 1 1 100%; }
  .libhost { order: 2; max-width: none; text-align: left; }
  .libitem .copybtn, .libitem a.mini, .libitem .libedit { order: 3; }
  .libedit-gap { display: none; }
  /* The grip is for dragging, which is a desktop gesture on this page. */
  .libgrip { display: none; }
  /* Same problem one level up: the folder bar carries a name, a count and
     three buttons, which is wider than a phone. The name keeps its own line
     and the buttons drop beneath it rather than pushing the page sideways. */
  .libfolderrow { flex-wrap: wrap; row-gap: 8px; }
  .libfoldername { flex: 1 1 100%; min-width: 0; }
  /* Edit fades in on hover, and a phone has no hover -- so on a touch screen
     it sat at half opacity looking disabled. Show it. */
  .libitem .libedit { opacity: 1; }
}


/* --------------------------------------------------------------- help
   A page of prose, so it is set like prose: one column, measured line length,
   and enough space between topics that the eye can find the next heading
   without hunting. */
.helpsearchbar { display: flex; align-items: center; gap: 12px;
                 margin: 0 0 14px; }
.helpsearchbar input { min-width: 320px; flex: 0 1 420px; }

.helpjump { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 18px; }
.helpjump a {
  padding: 5px 11px; font-size: 12.5px; font-weight: 600;
  color: var(--ink-2); text-decoration: none;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 999px;
  transition: border-color var(--t-colour), color var(--t-colour);
}
.helpjump a:hover { color: var(--accent); border-color: var(--accent-line); }
/* The open section. Filled, not just outlined, so the eye finds it in a row
   of nine without reading them. */
.helpjump a.on { background: var(--accent); border-color: var(--accent);
                 color: var(--accent-ink); }
.helpjump a.on:hover { color: var(--accent-ink); }
.helpjump[hidden] { display: none; }

.helpsection { margin-bottom: 14px; scroll-margin-top: 18px; }
.helpsection > h2 { margin: 0 0 4px; }
.helptopic { padding: 14px 0 2px; border-top: 1px solid var(--line); }
.helpsection > .helptopic:first-of-type { border-top: none; padding-top: 10px; }
.helptopic h3 {
  margin: 0 0 6px; font-size: 14.5px; font-weight: 650; color: var(--ink);
}
.helptopic p {
  margin: 0 0 8px; font-size: 14px; line-height: 1.6;
  color: var(--ink-2); max-width: 68ch;
}

/* The walkthrough videos at the top of a section. A row each -- play mark,
   title, what it shows -- that opens into the player in place. */
.sopstrip { margin: 8px 0 12px; padding: 10px 12px 6px;
            background: var(--panel-2); border: 1px solid var(--line);
            border-radius: var(--r-md); }
.sopstriphead { display: flex; align-items: center; gap: 10px; margin: 0 0 4px; }
.sopstriphead .alabel { margin: 0; }
.sopstriphead .sopadd { margin-left: auto; }
.sopempty { margin: 4px 0 6px; }
.sopvideo { border-top: 1px solid var(--line-soft); }
.sopvideo:first-of-type { border-top: none; }
.sopvideo > summary { list-style: none; cursor: pointer; display: flex;
                      align-items: flex-start; gap: 10px; padding: 9px 4px;
                      border-radius: var(--r-sm); }
.sopvideo > summary::-webkit-details-marker { display: none; }
.sopvideo > summary:hover { background: var(--lift-2); }
.sopplay { flex: none; width: 26px; height: 26px; border-radius: 50%;
           display: grid; place-items: center; font-size: 10px;
           background: var(--accent); color: var(--accent-ink); padding-left: 2px; }
.sopvideo[open] .sopplay { background: var(--ink-3); }
.soptext { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.soptitle { font-size: 14px; color: var(--ink); }
.sopdesc { font-size: 13px; color: var(--ink-2); line-height: 1.45; max-width: 68ch; }
.sophost { flex: none; font-size: 12px; padding-top: 4px; }
.sopbody { padding: 2px 4px 10px 40px; }
.sopframe { position: relative; width: 100%; max-width: 760px;
            aspect-ratio: 16 / 9; background: #000; border-radius: var(--r-md);
            overflow: hidden; }
.sopframe iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.sopfoot { display: flex; align-items: center; gap: 8px; margin: 8px 0 0;
           font-size: 13px; max-width: 760px; }
.sopfoot a { color: var(--accent); font-weight: 600; text-decoration: none; }
.sopfoot a:hover { text-decoration: underline; }
.sopplay.planned { background: var(--line-strong); color: var(--ink-2); font-size: 11px;
                   padding-left: 0; font-weight: 700; }

/* ------------------------------------------------------------ the course
   A classroom, not a list: modules and numbered lessons down the left,
   the lesson on the right, a tick when it is watched. */
.coursetab .coursepct { margin-left: 6px; font-size: 11px; opacity: .75;
                        font-variant-numeric: tabular-nums; }
.coursehead { display: flex; gap: 24px; align-items: flex-start; justify-content: space-between;
              flex-wrap: wrap; margin-bottom: 14px; }
.coursehead h2 { margin: 0 0 4px; }
.coursehead .hint { margin: 0; max-width: 60ch; }
.courseprog { flex: 0 0 220px; display: grid; grid-template-columns: auto 1fr; gap: 2px 8px;
              align-items: baseline; font-size: 13px; color: var(--ink-2); }
.courseprog b { font-size: 26px; font-weight: 700; color: var(--ink); line-height: 1;
                font-variant-numeric: tabular-nums; }
.coursebar { grid-column: 1 / -1; height: 6px; border-radius: 999px; background: var(--lift-3);
             overflow: hidden; margin-top: 4px; }
.coursebar i { display: block; height: 100%; background: var(--accent); border-radius: 999px;
               transition: width .3s var(--ease); }
.courseprog .dim { grid-column: 1 / -1; font-size: 12px; }
.coursegrid { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 18px;
              align-items: start; }
.courselist { display: flex; flex-direction: column; gap: 12px; position: sticky; top: 64px; }
.coursemod { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--panel-2);
             padding: 6px; }
.coursemodhead { display: flex; justify-content: space-between; align-items: baseline;
                 padding: 6px 8px 4px; font-size: 11px; font-weight: 750; letter-spacing: .08em;
                 text-transform: uppercase; color: var(--ink-3); }
.lesson { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
          padding: 7px 8px; border: 0; background: none; font: inherit; font-size: 13px;
          color: var(--ink-2); border-radius: var(--r-sm); cursor: pointer; }
.lesson:hover { background: var(--lift-2); color: var(--ink); }
.lesson.on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.lesson:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.lessonno { font-family: ui-monospace, Menlo, monospace; font-size: 11px; color: var(--ink-3);
            flex: none; width: 20px; }
.lesson.on .lessonno { color: var(--accent); }
.lessontitle { flex: 1; min-width: 0; line-height: 1.3; }
.lessonmark { flex: none; width: 16px; text-align: center; color: var(--accent); font-weight: 700; }
.lesson.planned .lessontitle { color: var(--ink-3); }
.lesson.planned .lessonmark { color: var(--ink-3); }
.lesson.done .lessontitle { color: var(--ink-2); }
.coursepane { min-width: 0; }
.lessonpane h3 { margin: 4px 0 8px; font-size: 20px; font-weight: 700; line-height: 1.25;
                 text-wrap: balance; }
.lessonpane .eyebrow { margin: 0; }
.lessondesc { font-size: 14.5px; line-height: 1.55; color: var(--ink-2); max-width: 68ch;
              margin: 0 0 14px; }
.lessonpane .sopframe { max-width: none; }
.lessonplanned { padding: 22px 20px; border: 1px dashed var(--line-strong); border-radius: var(--r-md);
                 background: var(--panel-2); font-size: 14px; color: var(--ink-2); line-height: 1.5; }
.lessonplanned .dim { display: block; margin-top: 6px; font-size: 12.5px; }
.lessonplanned b { color: var(--ink); }
.lessonplanned a { color: var(--accent); font-weight: 600; }
.lessonfoot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.lessonfoot .watchbtn[aria-pressed="true"] { color: var(--accent); border-color: var(--accent-line);
                                             background: var(--accent-soft); }
@media (max-width: 800px) {
  .coursegrid { grid-template-columns: 1fr; }
  .courselist { position: static; }
}


/* The HQ renewals bar. Same shell as the roster's, with the tier split inside
   it -- Elite and Core are different conversations, and reading a mixed list
   means sorting it in your head before it is any use. */
.hqrenew { margin-bottom: var(--step-5); }
.hqrenew .renewsplit {
  font-size: 12px; font-weight: 600; color: var(--ink-2);
  padding: 1px 8px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--accent-line);
}
.hqrenewtier { padding-bottom: 4px; }
.hqrenewtier h3 {
  margin: 10px 0 2px; font-size: 12px; font-weight: 700;
  display: flex; align-items: center; gap: 7px;
}
.hqrenewtier + .hqrenewtier { border-top: 1px solid var(--accent-line); }
/* Whose client it is. Present here and absent on a CSM's own roster, where the
   answer is always "yours". */
.hqrenewcsm { flex: 0 0 auto; font-size: 12px; }


/* ------------------------------------------------------- partnerships
   Two people, one business, one contract, one card, and a GoHighLevel contact
   each. The link table always allowed several contacts per client -- only the
   primary is unique -- so this is mostly about saying which of them is here
   and letting somebody add the second one. */
/* Deliberately not the tier tag's colours. A partnership is a fact about the
   shape of the card -- two people behind one contract -- and it has to be
   readable in a row of tags without being read as another tier. */
.partnertag {
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--blue); background: var(--blue-soft);
  border: 1px solid var(--blue);
}
.partnertag .ico { width: 11px; height: 11px; }

.crmlinks { list-style: none; margin: 0 0 10px; padding: 0; }
.crmlinks li {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  padding: 8px 0; border-top: 1px solid var(--line); font-size: 12.5px;
}
/* Name first and largest, then the email, then the id -- read in the order
   somebody needs them: who is this, which address, and only then the string
   they would paste into GoHighLevel. */
.crmwho { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.crmwho b { font-size: 13px; color: var(--ink); }
.crmwho .tiny { font-size: 11px; }
.crmlinks li:first-child { border-top: none; }
.crmlinks .crmdrop { margin-left: auto; }
.crmacts { display: flex; gap: 6px; flex-wrap: wrap; }

/* A checkbox whose label carries an explanation under it, so the box lines up
   with the first line rather than floating against the middle of two. */
.check.withhint { align-items: flex-start; margin-top: 10px; }
.check.withhint input { margin-top: 3px; }
.check.withhint .opt { display: block; margin-top: 2px; }


/* ------------------------------------------------------------ call log
   Written in every working day, usually by pasting a whole breakdown, so the
   writing area is the point of the card and everything else gets out of its
   way. */
.calldateline {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.calldateline label { margin: 0; flex: 0 0 auto; }
/* Not 100%. A date input is eight characters wide and the form rule was
   stretching it across the whole card, which cost a row and read as though
   something was missing from it.

   Qualified by the form's id on purpose: `form input:not([type=checkbox])
   :not([type=radio])` scores (0,2,2) and beat `.calldateline input[type=date]`
   at (0,2,1), so the plain version silently did nothing. */
#callform .calldateline input[type=date] {
  width: auto; min-width: 165px; flex: 0 0 auto;
}

#callform textarea.grows {
  /* 260px on a full page. Inside HQ's card this same page renders in a 420px
     iframe, where a fixed 260 would leave room for almost nothing else, so it
     takes the smaller of the two and the box scales to the space it is in. */
  min-height: min(260px, 40vh);
  line-height: 1.55; resize: vertical;
}


/* A payment plan read inside a card, not across a page.

   The client panel loads this page in an iframe about 590px wide, and the
   table carried min-width: 620px -- so five columns that nearly fit instead
   scrolled sideways, and the Edit button on the row you wanted was the part
   off the edge. The iframe has its own viewport, so a width query reaches it
   and the same rule covers a phone.

   Nothing is dropped: the columns compress and the type comes down a little
   rather than a column being hidden, because "which of these did we collect"
   needs all five. */
@media (max-width: 760px) {
  /* The columns fit -- they measure 511 in a 514 wrapper. What did not fit was
     the TOOLTIPS: [data-tip] draws a max-content bubble as a pseudo-element,
     and a pseudo-element counts toward its scroller's scrollWidth. So the row
     scrolled sideways because of something that is not even visible until you
     hover, and the bubble then got clipped by the scroll it had caused. Same
     trap the contract dialog set, same answer: let them escape. */
  .tablewrap:has(> table.paytable) { overflow: visible; }
  table.paytable { min-width: 0; font-size: 12.5px; }
  table.paytable th, table.paytable td { padding: 9px 8px; }
  /* Dates are the widest thing here and the year is the least surprising part
     of them, but truncating a date is worse than a narrow column, so they
     just stop wrapping and the row gives elsewhere. */
  table.paytable th { white-space: nowrap; letter-spacing: .03em; }
  table.paytable .rowacts { white-space: nowrap; }
}
/* A phone. The columns do not fit 335px however they compress, and a table
   that overflows the page widens the layout viewport (see the phones block
   above), so here the wrapper scrolls after all. Tooltips are not drawn on
   a touch screen, so nothing is lost to the clipping this time. */
@media (max-width: 600px) {
  .tablewrap:has(> table.paytable) { overflow-x: auto; }
  table.paytable { min-width: 560px; }
}


/* ------------------------------------------------------- touched base
   Built to sit beside Last call and read the same way. The tick is the fast
   path -- it means today -- and the clock opens a picker for the days you
   spoke on Tuesday and are writing up on Thursday.

   Narrower than Last call on purpose: this column carries a checkbox as well,
   and the date is the short form (09/05) because the year of a touch base
   inside the current week is never the question. */
.tbcell { display: inline-flex; align-items: center; gap: 6px; position: relative; }
.tbcell .tbwhen {
  font-size: 11.5px; color: var(--ink-2); font-variant-numeric: tabular-nums;
  min-width: 38px; text-align: right;
}
.tbcell .tbwhen.dim { color: var(--ink-3); }

/* The button and the input are the same control seen twice: the button is
   what you aim at, the input is what the browser hangs a calendar on. Only
   ever one of them takes space. */
.tbset {
  display: inline-flex; align-items: center; padding: 2px 5px;
  background: transparent; border: 1px dashed var(--line-strong);
  border-radius: 999px; color: var(--ink-3); cursor: pointer;
  transition: color var(--t-colour), border-color var(--t-colour),
              background var(--t-colour);
}
.tbset .ico { width: 11px; height: 11px; }
.tbset:hover { color: var(--accent); border-color: var(--accent);
               background: var(--accent-soft); border-style: solid; }
.tbset:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
td .tbcell input.tbdate {
  width: 0; opacity: 0; padding: 0; border: 0; position: absolute; left: 0; top: 0;
  pointer-events: none;
}


/* ------------------------------------------------- type it to confirm */
.cwdlg[open] { display: block; max-width: 440px; }
.cwdlg h2 { margin: 0 0 6px; font-size: 16px; }
.cwdlg .hint { margin: 0 0 14px; }
.cwdlg label { display: block; margin-bottom: 6px; }
.cwdlg label b { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                 color: var(--ink); }
.cwdlg input { width: 100%; }
/* Disabled until the word matches, so the button cannot be reached by
   momentum -- which is the whole reason this exists rather than a confirm(). */
.cwdlg #cw_go:disabled { opacity: .45; cursor: not-allowed; }
.cwdlg #cw_go { background: var(--red); border-color: var(--red); color: #fff; }
.cwdlg #cw_go:not(:disabled):hover { filter: brightness(1.06); }

.libitems { list-style: none; margin: 0; padding: 0;
            display: flex; flex-direction: column; gap: 8px; }
.libitem .tiny { font-size: 11.5px; }


/* Two lines, same column width. `th` is nowrap everywhere else so headings do
   not break mid-phrase, but this one is a phrase that SHOULD break -- saying
   "this week" in the heading is the whole point, and widening the column to
   fit it on one line would take the space from the names. */
th.tbhead { white-space: normal; }
th.tbhead > span { display: inline-block; line-height: 1.25; }


/* --------------------------------------------------- the call log list
   Lives in the right-hand column now, with the other history. Writing a call
   and reading old ones are different jobs; only one of them needs the width. */
/* Each call is its own card -- a bordered, tinted pill of a row -- so a
   run of them reads as a list of calls rather than as lines of the panel
   they sit in. Open, the card holds the write-up and its buttons. */
.callhistory .callentry {
  padding: 0 12px; margin: 0 0 8px; border: 1px solid var(--line);
  border-top: 1px solid var(--line); border-radius: 12px;
  background: var(--panel-2);
}
.callhistory .callentry:hover { border-color: var(--line-strong); }
.callhistory .callentry[open] { background: var(--panel); border-color: var(--accent-line); }
.callhistory .entry-head { gap: 6px; padding: 10px 0; }
.callhistory .calldate { flex: 0 0 auto; }
/* The peek is the first line of the call, and in a 320px column it has to
   give way to the date and the follow-up count rather than push them out. */
.callhistory .callpeek {
  flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
  font-size: 12px; color: var(--ink-3);
}
.callhistory .entry-body {
  font-size: 13px; line-height: 1.55; white-space: pre-wrap;
  margin: 2px 0 8px;
}
/* Turns to point down when the call is open, so the row says which it is
   before you read the date. */
.callcaret {
  width: 12px; height: 12px; flex: 0 0 12px; color: var(--ink-3);
  transition: transform var(--t-move);
}
.callentry[open] > summary .callcaret { transform: rotate(90deg); }
.callhistory .callacts { margin-bottom: 10px; }

/* ------------------------------------------------ who is using the Hub
   One row per person on the admin page; fourteen cells, one a day. Filled
   is "opened the Hub that day"; weekends dimmed because an empty Saturday
   is not a missed day. */
.usagepanel { margin-top: 18px; }
.usagetable td { vertical-align: middle; }
.usagetable .seencell b { font-weight: 600; }
.usagetable .seencell .dim { display: block; font-size: 12px; }
.daystrip { display: inline-flex; gap: 3px; }
.daycell {
  width: 13px; height: 20px; border-radius: 3px; display: inline-block;
  background: var(--line); cursor: default;
}
.daycell.weekend { opacity: .4; }
.daycell.used { background: var(--accent); opacity: 1; }
.daycell.used.weekend { opacity: .7; }
.daycell.today { box-shadow: 0 0 0 2px var(--panel), 0 0 0 3px var(--ink-3); }
.daycell.today.used { box-shadow: 0 0 0 2px var(--panel), 0 0 0 3px var(--accent); }
.daycell[data-tip]::after { white-space: nowrap; }
.usagerow.state-quiet td:first-child,
.usagerow.state-never td:first-child { box-shadow: inset 3px 0 0 var(--amber); }
.loginsrow td { background: var(--panel-2); padding: 8px 14px 10px; }
.loginlist { margin: 0; padding: 0 0 0 16px; columns: 2; column-gap: 24px; font-size: 13px; }
.loginlist li { break-inside: avoid; padding: 1px 0; }

/* ------------------------------------------------ end-of-month highlight
   A month told back to the student, in a box the CSM edits before copying.
   The picker sits in the panel head beside the button; the box is the
   same shape as the write-up box so the two read as the same kind of
   thing. */
#eomr .statushead { flex-wrap: wrap; gap: 8px 10px; }
#eomr #eomr_month { max-width: 100%; font-size: 13px; padding: 6px 28px 6px 10px; }
#eomr textarea { width: 100%; min-height: 120px; font-size: 14px; line-height: 1.5;
                 resize: vertical; overflow-y: auto; }
#eomr .hint b { color: var(--ink); }
#eomr .eomr-dates { display: inline-flex; align-items: center; gap: 6px; }
#eomr .eomr-dates input { font-size: 13px; padding: 5px 8px; }

/* ================================================================ phone
   The Hub on a phone is for three things -- add a task, see what is owed
   on a client, copy a breakdown into Slack -- and the layout below is those
   three things made easy. The desktop is untouched: every rule here is
   behind a width. Stefan, 2026-09-16. */

/* ---- the bar along the bottom */
.phonebar { display: none; }
@media (max-width: 640px) {
  .phonebar {
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 150;
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
    background: var(--panel); border-top: 1px solid var(--line);
    box-shadow: 0 -4px 16px rgba(20, 24, 29, .06);
  }
  .phonebar a, .phonebar button {
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 4px 2px 2px; border: 0; background: none; font: inherit;
    font-size: 11px; font-weight: 600; color: var(--ink-3); text-decoration: none;
    cursor: pointer; min-height: 48px; justify-content: center;
  }
  .phonebar a.on { color: var(--accent); }
  .phonebar .pbico { position: relative; display: inline-flex; width: 28px; height: 24px;
                     align-items: center; justify-content: center; }
  .phonebar .pbico .ico { width: 21px; height: 21px; color: inherit; }
  .phonebar .pbcount {
    position: absolute; top: -5px; right: -8px; min-width: 17px; height: 17px;
    padding: 0 5px; border-radius: 999px; background: var(--accent); color: var(--accent-ink);
    font-size: 10.5px; font-weight: 700; line-height: 17px; text-align: center;
    box-shadow: 0 0 0 2px var(--panel);
  }
  .phonebar .pbcount.warnish { background: var(--red); color: #fff; }
  /* Add task is the one press the bar exists for; it gets the green. */
  .phonebar .pbadd { color: var(--accent); }
  .phonebar .pbplus {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 32px; border-radius: 12px; margin-top: -8px;
    background: linear-gradient(180deg, var(--accent-hi), var(--accent));
    color: var(--accent-ink); box-shadow: 0 2px 8px rgba(13,138,95,.3);
  }
  .phonebar .pbplus .ico { width: 20px; height: 20px; }
  /* Room for the bar at the foot of every page, and the drawer above it. */
  body:has(.phonebar) .shellmain { padding-bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
  body:has(.phonebar) .sidebar { z-index: 160; padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
  .panelmode .phonebar { display: none; }
  /* Nothing sits on the bar. The "new version" notice and the copy toast
     both live at the foot of the screen; on a phone they move up above it.
     The notice was full-width over the bar, which is why Tasks, Add task
     and Check-ins could not be pressed after a deploy (Stefan, 2026-09-16). */
  body:has(.phonebar) .updatebar { bottom: calc(84px + env(safe-area-inset-bottom, 0px)); }
  body:has(.phonebar) .hubtoast { bottom: calc(86px + env(safe-area-inset-bottom, 0px)); }
}

/* ---- the roster as a list of cards */
@media (max-width: 640px) {
  .tablewrap { overflow-x: visible; background: transparent; border: 0; border-radius: 0; }
  .tablewrap table { min-width: 0; display: block; }
  .tablewrap thead { display: none; }
  .tablewrap tbody { display: flex; flex-direction: column; gap: 10px; }
  .tablewrap tbody tr {
    display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px;
    padding: 12px 14px 10px; background: var(--surface);
    border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow-1);
  }
  .tablewrap tbody tr[style*="display: none"] { display: none; }
  .tablewrap tbody td { display: block; padding: 0; border: 0; min-width: 0; }
  /* The name, tags and email run the full width; four facts sit under. */
  .tablewrap tbody td.cname { grid-column: 1 / -1; min-width: 0; padding-bottom: 4px;
                              border-bottom: 1px solid var(--line-soft); margin-bottom: 2px; }
  .tablewrap tbody td[data-label]::before {
    content: attr(data-label); display: block; font-size: 10.5px; font-weight: 600;
    letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 3px;
  }
  .tablewrap tbody td.num, .tablewrap tbody td[data-label],
  .tablewrap tbody td[data-label] > * { text-align: left; margin-left: 0; }
  .tablewrap tbody td[data-label] .lastcall { display: block; }
  .tablewrap tbody td.tb { justify-self: start; }
  /* What a phone does not need: programme month, contract end, last
     contact, check-in dates. All on the profile, one tap away. */
  .tablewrap tbody td:not(.cname):not([data-label]) { display: none; }
  .tablewrap tbody td.statuscol, .tablewrap tbody td.taskscol { grid-row: auto; }
  td.cname { min-width: 0; }
  /* Chips and small buttons at a thumb's size. */
  .chipbtn { padding: 8px 13px; font-size: 13px; }
  .mini { padding: 8px 12px; font-size: 13px; min-height: 36px; }
  .filters { gap: 8px; }
  .filters #search { width: 100%; }
}

/* ---- the profile in a phone's order: the write-ups first, then the form */
@media (max-width: 900px) {
  .cols { display: flex; flex-direction: column; }
  .col-main, .col-side { display: contents; }
  .cols .callhistory   { order: 1; }
  .cols #logcall       { order: 2; }
  .cols #eomr          { order: 3; }
  .cols .tbhistory     { order: 4; }
  .cols .checkinhistory{ order: 5; }
  .cols .notespanel    { order: 6; }
  .cols .cpay          { order: 7; }
  .cols .contractpanel { order: 8; }
  .cols .extpanel      { order: 9; }
  .cols .coachpanel    { order: 10; }
  .cols .twinpanel     { order: 11; }
  .cols #crmpanel      { order: 12; }
  /* A flex item is as wide as its widest unbreakable line unless told
     otherwise; a long call summary was pushing the panel past the edge. */
  .cols .panel { min-width: 0; width: 100%; max-width: 100%; }
}

/* ---- the profile's small parts, at a phone's width */
@media (max-width: 640px) {
  /* A call's summary line: the peek wraps to its own line rather than
     pushing the tags and the author off the edge. */
  .callentry summary { row-gap: 4px; }
  .callpeek, .callhistory .callpeek {
    max-width: 100%; flex: 1 1 100%; white-space: normal; order: 5; overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  .callentry summary .pgspacer { display: none; }
  /* The copy and edit buttons are always visible on a phone: there is no
     hover to reveal them. */
  .callacts { opacity: 1; flex-wrap: wrap; }
  /* The "not written up yet" note stacks its button under the words. */
  .pendingcall { flex-wrap: wrap; }
  .pendingcall > span { flex-basis: calc(100% - 26px); }
  .pendingcall .mini { margin-left: 26px; }
}

/* ---- the top of a phone's screen: no wasted rows. The menu button is in
   the bar now, the crumb repeats the eyebrow, and the pill that wrapped
   onto two lines does not. */
@media (max-width: 640px) {
  .navtoggle { display: none; }
  .topbar { height: 46px; padding: 0 10px; gap: 10px; }
  .backbar { margin: 10px 0 -2px 16px; }
  .backhq, .backlink { white-space: nowrap; padding: 6px 12px 6px 9px; font-size: 12.5px; }
  .page { padding: 12px 16px 64px; }
  .page .crumb { display: none; }
  .pagehead { margin: 0 0 14px; }
  .pagehead .eyebrow { margin-bottom: 4px; }
  .pagehead h1 { font-size: 23px; }
  .pagehead .lede { display: none; }
}

/* ---- a reload button, only where there is no address bar to have one */
.reloadbtn { display: none; }
/* Blue, and labelled: it sits beside the green Fit Pro AI segment and must
   not read as part of it (Stefan, 2026-09-17). */
@media (display-mode: standalone) { .reloadbtn { display: inline-flex; } }
html[data-standalone] .reloadbtn { display: inline-flex; }
.reloadbtn {
  align-items: center; justify-content: center; gap: 7px; flex: none;
  height: 34px; padding: 0 13px 0 11px; border-radius: 999px; cursor: pointer;
  background: var(--blue-soft); color: var(--blue);
  border: 1px solid color-mix(in srgb, var(--blue) 40%, transparent);
  font: inherit; font-size: 13px; font-weight: 600; white-space: nowrap;
}
.reloadbtn:hover { background: var(--blue); color: #fff; border-color: var(--blue); }
.reloadbtn .ico { width: 15px; height: 15px; color: inherit; }
@media (max-width: 640px) { .reloadbtn span { display: none; } .reloadbtn { padding: 0 10px; } }

/* Notes against one payout. A thread, so each line keeps who said it and
   when -- the point of it is that nobody is reconstructing the agreement
   from a chat on the 1st. */
.notespanel .notelist { list-style: none; margin: 10px 0 0; padding: 0;
                        display: flex; flex-direction: column; gap: 10px; }
/* Each note is a thing somebody submitted, so it reads as its own card
   rather than a run of paragraphs: a left bar alone disappeared against the
   panel once there were three or four of them (Stefan, 2026-10-12). The wash
   fades downward so the author line sits in the tinted part and the body in
   the clear part, which is what makes a stack legible at a glance. Both
   colours are tokens, so dark mode comes out on its own. */
.notespanel .notelist li {
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 10px;
  padding: 9px 12px 10px;
  background-color: var(--surface);
  background-image: linear-gradient(180deg, var(--accent-soft) 0%, transparent 72%);
}
.notespanel .notemeta { display: flex; align-items: center; gap: 8px;
                        font-size: 12.5px; }
.notespanel .notemeta b { font-weight: 600; }
.notespanel .notebody { white-space: pre-wrap; font-size: 14px;
                        color: var(--ink); margin-top: 2px; }
.notespanel textarea { width: 100%; padding: 8px 10px; border-radius: 8px;
                       border: 1px solid var(--line); font: inherit;
                       font-size: 14px; background: var(--surface);
                       color: var(--ink); resize: vertical; }
.notespanel textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.notespanel form { margin-top: 12px; }

/* A payout row opens what the figure is made of. One control: the chevron
   says it opens, the whole row is the hit area, and the button inside the
   first cell is what actually carries the state for a keyboard and a screen
   reader. */
.payoutrow { cursor: pointer; }
.payoutrow:hover > td { background: var(--accent-soft); }
.payoutopen {
  background: none; border: 0; padding: 0; margin: 0; font: inherit;
  color: inherit; cursor: pointer; display: inline-flex; align-items: center;
  gap: 6px; text-align: left;
}
.payoutopen:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px;
                            border-radius: 4px; }
.payoutopen .chev { width: 13px; height: 13px; color: var(--ink-3);
                    transition: transform .12s ease; flex: none; }
.payoutopen[aria-expanded="true"] .chev { transform: rotate(90deg);
                                          color: var(--accent); }

/* The opened half. Inset and on its own ground so it reads as belonging to
   the row above rather than as four more rows of the same table. */
.payoutbreak > td { padding: 0 0 12px; background: var(--surface); }
.breaktable { width: 100%; border-collapse: collapse; font-size: 13px;
              margin: 0; }
.breaktable th { text-align: left; font-weight: 600; font-size: 11.5px;
                 letter-spacing: .04em; text-transform: uppercase;
                 color: var(--ink-3); padding: 8px 12px 6px; }
.breaktable th.num, .breaktable td.num { text-align: right; }
.breaktable td { padding: 7px 12px; border-top: 1px solid var(--line); }
.breaktable tfoot td { border-top: 1px solid var(--ink-3); }
.breaknote { margin: 10px 12px 2px; }
/* On the client list the chevron sits beside the name, which is its own link
   to the card: two different destinations, so the chevron is its own small
   control rather than the whole row. The gap keeps names lined up on rows
   that have nothing to open. */
.payoutopen.inline { vertical-align: middle; margin-right: 2px; }
.chevgap { display: inline-block; width: 13px; margin-right: 8px; }
