/* ==========================================================================
   Simplify Communication Portal — the look
   ==========================================================================

   Loaded AFTER styles.css, and mostly it does not restyle anything. It
   redefines the tokens that styles.css was already built on — --bg, --panel,
   --text, --claim and the rest — so a change here reaches every screen at once
   without 4,000 lines of existing CSS being touched or risked.

   That is the whole design of this file. Two attributes on <html> decide how
   the portal looks:

     data-theme   light | dark | midnight     - what styles.css understands
     data-glass   day | night | (absent)      - the translucent treatment
     data-accent  blue | teal | violet | emerald | amber | rose

   GLASS IS NOT A THEME, AND THAT MATTERS
     It was, at first, and it was wrong. styles.css carries sixteen rules
     written as [data-theme="dark"] that put right the things it had hard-coded
     for a light screen - the logo, avatars, scrollbars, the highlight on a
     mention. A theme called "glass-night" matched none of them, so night glass
     inherited a light theme's corrections on a dark background and the text
     came out almost unreadable.

     So glass is a coat of paint on top of a theme, not a theme of its own.
     Night glass IS the dark theme, with data-glass="night" added, and it picks
     up all sixteen corrections for free - as will anything added to styles.css
     later without a thought for glass.

   Six accents across five themes is thirty combinations, and none of them are
   written out. The accent is one hue number; every shade the portal needs is
   computed from it, per theme, so a new accent is two lines and cannot produce
   an unreadable screen.

   WHY THE ACCENT IS A HUE AND NOT A PALETTE
     Letting people pick colours freely is how a portal ends up with grey text
     on a yellow panel. Here the choice is a hue; lightness and saturation stay
     under the theme's control, so contrast holds whatever anybody picks.

   ONE THING TO KNOW BEFORE EDITING
     --ah is already taken. styles.css uses it for the per-person avatar hue.
     The accent hue here is --acc-h. Reusing --ah tints every avatar in the
     portal to match the theme, which looks like a bug because it is one.
   ========================================================================== */

/* ==========================================================================
   0. The typefaces
   ==========================================================================

   THE BUG THIS FIXES. styles.css has asked for "Inter","IBM Plex Sans" since
   the portal was built, and there was no @font-face anywhere in it — so those
   names only ever resolved for somebody who happened to have the fonts
   installed, which on Windows is nobody. Everyone has been seeing whatever
   their operating system handed over: Segoe UI on the server, Roboto on
   Android, SF on a Mac. The portal was designed in a typeface almost none of
   its users received.

   SERVED FROM HERE, never from Google. Same reasoning as the signature face:
   a page that fetches a font from a third party tells that third party who is
   looking at what and when, and stops looking right the day they are down.

   Latin only, and only the weights actually used — 268KB for all four
   families, cached after the first visit.

   font-display:swap, deliberately. Text in the fallback face for a moment
   beats a portal that shows nothing while 24KB arrives on a train.
   ========================================================================== */

@font-face { font-family:"Inter"; font-style:normal; font-weight:400; font-display:swap; src:url(/fonts/inter-400.woff2) format("woff2"); }
@font-face { font-family:"Inter"; font-style:normal; font-weight:500; font-display:swap; src:url(/fonts/inter-500.woff2) format("woff2"); }
@font-face { font-family:"Inter"; font-style:normal; font-weight:600; font-display:swap; src:url(/fonts/inter-600.woff2) format("woff2"); }
@font-face { font-family:"Inter"; font-style:normal; font-weight:700; font-display:swap; src:url(/fonts/inter-700.woff2) format("woff2"); }

@font-face { font-family:"IBM Plex Sans"; font-style:normal; font-weight:400; font-display:swap; src:url(/fonts/ibm-plex-sans-400.woff2) format("woff2"); }
@font-face { font-family:"IBM Plex Sans"; font-style:normal; font-weight:500; font-display:swap; src:url(/fonts/ibm-plex-sans-500.woff2) format("woff2"); }
@font-face { font-family:"IBM Plex Sans"; font-style:normal; font-weight:600; font-display:swap; src:url(/fonts/ibm-plex-sans-600.woff2) format("woff2"); }
@font-face { font-family:"IBM Plex Sans"; font-style:normal; font-weight:700; font-display:swap; src:url(/fonts/ibm-plex-sans-700.woff2) format("woff2"); }

/* Atkinson Hyperlegible, from the Braille Institute. Its letterforms are drawn
   to be told apart rather than to look even — the capital I, lower-case l and
   figure 1 are three visibly different shapes, which matters on a screen full
   of ticket numbers and phone numbers. Offered because somebody will need it,
   not as decoration. */
@font-face { font-family:"Atkinson Hyperlegible"; font-style:normal; font-weight:400; font-display:swap; src:url(/fonts/atkinson-hyperlegible-400.woff2) format("woff2"); }
@font-face { font-family:"Atkinson Hyperlegible"; font-style:normal; font-weight:700; font-display:swap; src:url(/fonts/atkinson-hyperlegible-700.woff2) format("woff2"); }

@font-face { font-family:"IBM Plex Mono"; font-style:normal; font-weight:400; font-display:swap; src:url(/fonts/ibm-plex-mono-400.woff2) format("woff2"); }
@font-face { font-family:"IBM Plex Mono"; font-style:normal; font-weight:600; font-display:swap; src:url(/fonts/ibm-plex-mono-600.woff2) format("woff2"); }

/* Montserrat, for display type only — the headline face of the company's own
   posts and adverts (24 Sep). Headings, the home screen's banner and the
   sign-in page; never body text. Self-hosted like the rest, SIL OFL. */
@font-face { font-family:"Montserrat"; font-style:normal; font-weight:600; font-display:swap; src:url(/fonts/montserrat-600.woff2) format("woff2"); }
@font-face { font-family:"Montserrat"; font-style:normal; font-weight:700; font-display:swap; src:url(/fonts/montserrat-700.woff2) format("woff2"); }
@font-face { font-family:"Montserrat"; font-style:normal; font-weight:800; font-display:swap; src:url(/fonts/montserrat-800.woff2) format("woff2"); }

/* The stack, as a token, so one attribute on <html> changes every screen.
   The system fallbacks stay on every line: they are what is shown during
   swap, and what is shown if a font ever fails to arrive. */
:root {
  --font-ui:"Inter","IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
  --font-mono:"IBM Plex Mono",ui-monospace,"Cascadia Mono","Consolas",monospace;
  --font-display:"Montserrat","Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
}
/* Somebody who chose Hyperlegible or System chose it for a reason; headings
   follow their choice rather than the brand's. */
[data-font="legible"], [data-font="system"] { --font-display:var(--font-ui); }
[data-font="plex"]     { --font-ui:"IBM Plex Sans","Inter",system-ui,-apple-system,"Segoe UI",sans-serif; }
[data-font="legible"]  { --font-ui:"Atkinson Hyperlegible","Inter",system-ui,-apple-system,"Segoe UI",sans-serif; }
/* Nothing to download at all. Fastest, and the one to pick if a machine is
   struggling or somebody simply prefers how their own system looks. */
[data-font="system"]   { --font-ui:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
                         --font-mono:ui-monospace,"Cascadia Mono","Consolas",monospace; }

/* styles.css names the families directly in about a dozen places and was
   written before there was a token. Rather than edit all of them, the token
   wins from here — which also means a font choice reaches rules this file has
   never heard of. */
body, input, select, textarea, button { font-family:var(--font-ui); }
.mono, code, kbd, pre { font-family:var(--font-mono); }

/* --------------------------------------------------------------------------
   1. Accents — a hue, and how strongly to use it
   -------------------------------------------------------------------------- */

:root,
/* A HUE, A SATURATION, AND — added 21 Sep — A LIGHTNESS PER HUE.
   Why the third one is not a tidiness exercise: this file used to set every
   accent at the same 41% lightness on light themes, and lightness in HSL is
   not the same thing as how light a colour LOOKS. At 41%, blue is dark and
   cyan is almost pastel. Four of the six original accents were failing WCAG
   AA against white — teal at 3.04:1, emerald 3.20, olive 3.77, amber 3.88,
   where 4.5 is the floor for body text. Nobody had noticed because a teal
   link on white looks fine until you are tired, or outside, or fifty.

   --acc-l is the lightness each hue needs to clear the bar. The numbers come
   from a solver, not an eye: the test walks all 96 theme-and-accent pairs and
   fails anything under AA, and these are the values that pass it with a
   little room against Paper, which is the tightest background of the three
   light ones. */
[data-accent="blue"]    { --acc-h:213; --acc-s:62%; --acc-l:41%; }   /* the Simplify blue */
[data-accent="indigo"]  { --acc-h:243; --acc-s:56%; --acc-l:45%; }
[data-accent="violet"]  { --acc-h:263; --acc-s:58%; --acc-l:45%; }
[data-accent="magenta"] { --acc-h:316; --acc-s:52%; --acc-l:44%; }
[data-accent="rose"]    { --acc-h:344; --acc-s:60%; --acc-l:44%; }
[data-accent="orange"]  { --acc-h:22;  --acc-s:70%; --acc-l:40%; }
[data-accent="amber"]   { --acc-h:34;  --acc-s:72%; --acc-l:35%; }
/* Olive rather than a true lime: a yellow-green has to go so dark to clear
   the bar that it stops reading as the colour anybody picked. Pulled down in
   saturation and nudged towards green instead. */
[data-accent="olive"]   { --acc-h:96;  --acc-s:38%; --acc-l:34%; }
[data-accent="emerald"] { --acc-h:152; --acc-s:56%; --acc-l:31%; }
[data-accent="teal"]    { --acc-h:184; --acc-s:64%; --acc-l:31%; }
[data-accent="cyan"]    { --acc-h:196; --acc-s:66%; --acc-l:36%; }
[data-accent="slate"]   { --acc-h:215; --acc-s:18%; --acc-l:42%; }

/* WHAT AN ACCENT IS NOT
   It is decoration. --live, --warn and --signal below are MEANING: green is
   signed, amber is waiting, red is declined, and the Signing screen was fixed
   on 21 Sep precisely because two of those had drifted into looking alike.
   None of them derives from --acc-h, and none of them ever should — picking
   the Rose accent must not turn "waiting" into "refused". There is a test. */

/* --------------------------------------------------------------------------
   2. Themes
   --------------------------------------------------------------------------
   Each theme sets its own surfaces and then derives the accent from --acc-h at
   a lightness that works against those surfaces. A light theme needs a darker
   accent to stay legible; a dark one needs a lighter accent. That is why the
   accent shades are declared inside each theme rather than once at the top.
   -------------------------------------------------------------------------- */

/* --- the daylight family -------------------------------------------------- */

:root,
[data-theme="light"] {
  --bg:#FFFFFF; --panel:#F7F9FC; --raised:#EFF3F8;
  --line:#E3E9F1; --line-soft:#EDF1F6;
  --text:#14202C; --muted:#5A6B7D; --dim:#8A9AAB;

  --claim:        hsl(var(--acc-h) var(--acc-s) var(--acc-l, 41%));
  --claim-deep:   hsl(var(--acc-h) var(--acc-s) calc(var(--acc-l, 41%) - 9%));
  --accent-soft:  hsl(var(--acc-h) 58% 96%);
  --accent-line:  hsl(var(--acc-h) 48% 86%);
  --out:          hsl(var(--acc-h) 62% 94%);
  --on-accent:#FFFFFF;

  /* Darkened on 22 Sep, and not for the clocks that found it. Every one of
     these is drawn as text ON ITS OWN SOFT GROUND in about twenty places — the
     ops status badges, the signing chips, the dashboard's tone bars — and at
     the old values that combination came out at 3.95:1 for green and 3.95 for
     amber, which is under AA for small text and exactly the kind of fault
     nobody reports because it is only slightly too pale. Both now clear 4.5
     against their soft ground and 5.4 against the page. Paper inherits them. */
  --signal:#94600A; --live:#15793F; --warn:#BC402C;
  --warn-soft:#FDF0ED; --live-soft:#EBF7F0; --signal-soft:#FBF3E4;
  /* Night. Green says at their desk, amber says awake, and there was nothing
     left to say asleep with — --dim says "unimportant", which the middle of
     somebody's night is not. Each of these clears AA on its own soft ground. */
  --night:#4A5C8C; --night-soft:#EEF1F9;

  --brand-deep:  hsl(var(--acc-h) var(--acc-s) calc(var(--acc-l, 41%) + 2%));
  --brand-mid:   hsl(var(--acc-h) 60% 58%);
  --brand-light: hsl(var(--acc-h) 72% 74%);

  --sh-1:0 1px 2px hsl(var(--acc-h) 30% 20% / .06);
  --sh-2:0 4px 16px hsl(var(--acc-h) 30% 20% / .09);
  --sh-3:0 18px 50px hsl(var(--acc-h) 32% 18% / .18);
  color-scheme:light;
}

/* --- the night family ----------------------------------------------------- */

[data-theme="dark"] {
  --bg:#141A21; --panel:#19212A; --raised:#212B36;
  --line:#2A3540; --line-soft:#222C36;
  --text:#E8EEF4; --muted:#9BABBA; --dim:#6E7F8F;

  --claim:        hsl(var(--acc-h) 72% 68%);
  --claim-deep:   hsl(var(--acc-h) 76% 78%);
  --accent-soft:  hsl(var(--acc-h) 32% 18%);
  --accent-line:  hsl(var(--acc-h) 34% 34%);
  --out:          hsl(var(--acc-h) 38% 24%);
  --on-accent:#0E1620;

  --signal:#E0A63C; --live:#48C98A; --warn:#F0705C;
  --warn-soft:#33201D; --live-soft:#17301F; --signal-soft:#332A15;
  --night:#8FA3D6; --night-soft:#1E2436;

  --brand-deep:  hsl(var(--acc-h) 62% 52%);
  --brand-mid:   hsl(var(--acc-h) 68% 64%);
  --brand-light: hsl(var(--acc-h) 74% 78%);

  --sh-1:0 1px 2px rgba(0,0,0,.34);
  --sh-2:0 4px 18px rgba(0,0,0,.44);
  --sh-3:0 20px 56px rgba(0,0,0,.60);
  color-scheme:dark;
}

[data-theme="midnight"] {
  --bg:#0E1626; --panel:#131F33; --raised:#1A2941;
  --line:#243651; --line-soft:#1C2C44;
  --text:#E6EDF8; --muted:#98A9C4; --dim:#6B7C99;

  --claim:        hsl(var(--acc-h) 78% 72%);
  --claim-deep:   hsl(var(--acc-h) 82% 82%);
  --accent-soft:  hsl(var(--acc-h) 38% 20%);
  --accent-line:  hsl(var(--acc-h) 40% 38%);
  --out:          hsl(var(--acc-h) 44% 26%);
  --on-accent:#0A1120;

  --signal:#E3AC4A; --live:#4ACF92; --warn:#F47C68;
  --warn-soft:#33201E; --live-soft:#132F22; --signal-soft:#332B17;
  --night:#94A9DE; --night-soft:#1B2540;

  --brand-deep:  hsl(var(--acc-h) 66% 56%);
  --brand-mid:   hsl(var(--acc-h) 72% 68%);
  --brand-light: hsl(var(--acc-h) 80% 80%);

  --sh-1:0 1px 2px rgba(0,0,0,.40);
  --sh-2:0 4px 20px rgba(0,0,0,.50);
  --sh-3:0 20px 60px rgba(0,0,0,.66);
  color-scheme:dark;
}

/* --------------------------------------------------------------------------
   2b. Tones — a treatment over a family, not a theme of its own
   --------------------------------------------------------------------------
   THE LESSON THIS IS BUILT ON, already learned once and written down above
   glass in ui.js: styles.css corrects sixteen light-screen assumptions under
   [data-theme="dark"]. A dark theme given a NAME OF ITS OWN matches none of
   them, and comes out with a light theme's corrections on a dark background —
   which is how night glass was nearly unreadable before it became the dark
   theme wearing a treatment.

   So Paper and High contrast are the LIGHT theme wearing a tone, and Dim is
   the DARK theme wearing one. Every correction either family already has
   keeps applying, and these blocks only move the surfaces.
   -------------------------------------------------------------------------- */

/* --- Paper: warm, for a bright office ------------------------------------- */
/* The cool white above is right on a good monitor in a dim room and glares
   under strip lighting. This is the same theme with the blue taken out of the
   paper and a little put back into the ink. */
[data-tone="paper"] {
  --bg:#FBF8F3; --panel:#F5F0E8; --raised:#EFE9DE;
  --line:#E4DCCE; --line-soft:#EDE7DB;
  --text:#2B2620; --muted:#6B6153; --dim:#948B7C;
  --accent-soft:  hsl(var(--acc-h) 30% 94%);
  --accent-line:  hsl(var(--acc-h) 26% 82%);
  --out:          hsl(var(--acc-h) 30% 92%);
  --warn-soft:#FAEDE7; --live-soft:#E9F3E9; --signal-soft:#F8F0DC;
  --night:#55608A; --night-soft:#EFEFF6;
  --sh-1:0 1px 2px hsl(34 24% 30% / .07);
  --sh-2:0 4px 16px hsl(34 24% 30% / .10);
  --sh-3:0 18px 50px hsl(34 22% 26% / .18);
}

/* --- High contrast: an accessibility setting, not a style ----------------- */
/* Pure black on pure white, borders you can actually see, and the three signal
   colours darkened until each clears 7:1 against the background. Worth having
   for anyone with low vision, and worth having on a laptop outdoors. */
[data-tone="contrast"] {
  --bg:#FFFFFF; --panel:#FFFFFF; --raised:#F2F2F2;
  --line:#5A5A5A; --line-soft:#8A8A8A;
  --text:#000000; --muted:#262626; --dim:#4A4A4A;

  /* Darker than the light theme on purpose: somebody who picked High contrast
     asked for contrast, and an accent that merely scrapes AA is not it. */
  --claim:      hsl(var(--acc-h) 90% 24%);
  --claim-deep: hsl(var(--acc-h) 92% 17%);
  --accent-soft:#FFFFFF;
  --accent-line:hsl(var(--acc-h) 60% 30%);
  --out:        hsl(var(--acc-h) 70% 92%);
  --on-accent:#FFFFFF;

  --signal:#6B4200; --live:#0A5C2E; --warn:#8C1D08;
  --warn-soft:#FFF0EC; --live-soft:#EAF6EF; --signal-soft:#FFF6E2;
  --night:#1F2A5C; --night-soft:#EEF0FA;

  --brand-deep:hsl(var(--acc-h) 90% 26%);
  --brand-mid: hsl(var(--acc-h) 80% 34%);
  --brand-light:hsl(var(--acc-h) 60% 48%);

  /* Shadows carry no information and at this contrast they only smear an
     edge that is doing real work. Borders instead. */
  --sh-1:0 0 0 1px #5A5A5A; --sh-2:0 0 0 1px #5A5A5A; --sh-3:0 0 0 2px #000000;
}
/* A hairline border is invisible to the person who chose this setting. */
[data-tone="contrast"] .card,
[data-tone="contrast"] .rccard,
[data-tone="contrast"] .tvcard,
[data-tone="contrast"] table { border-width:2px; }
[data-tone="contrast"] :focus-visible { outline:3px solid #000; outline-offset:2px; }

/* --- Dim: the dark theme, quieter ----------------------------------------- */
/* Less distance between the background and the text than Dark, which is easier
   for a long evening and worse in daylight. Taste, and people ask for it. */
[data-tone="dim"] {
  /* Lifted off the dark theme's accent. Dim's background is four steps lighter
     than Dark's, so the same accent lands closer to it — indigo came out at
     3.95:1 and violet at 4.44 before this line existed. */
  --claim:      hsl(var(--acc-h) 76% 76%);
  --claim-deep: hsl(var(--acc-h) 80% 84%);

  --bg:#20262D; --panel:#262D36; --raised:#2E3742;
  --line:#3A454F; --line-soft:#313B45;
  --text:#D4DCE4; --muted:#9AA7B4; --dim:#77838F;
  --accent-soft:hsl(var(--acc-h) 24% 24%);
  --accent-line:hsl(var(--acc-h) 26% 38%);
  --out:        hsl(var(--acc-h) 28% 28%);
  --sh-1:0 1px 2px rgba(0,0,0,.26);
  --sh-2:0 4px 18px rgba(0,0,0,.32);
  --sh-3:0 20px 56px rgba(0,0,0,.46);
}

/* --------------------------------------------------------------------------
   2c. Density
   --------------------------------------------------------------------------
   Compact is for the ticket board and the invoice list, where the question is
   always "how many can I see at once". Deliberately a handful of selectors
   rather than a global squeeze: tightening everything makes the reading
   screens worse to save rows on the counting ones.
   -------------------------------------------------------------------------- */
[data-density="compact"] {
  --pad-row:6px;
}
[data-density="compact"] .opsrow td,
[data-density="compact"] .dirtable td,
[data-density="compact"] .invtable td,
[data-density="compact"] .rctable td,
[data-density="compact"] table.wide td { padding-top:5px; padding-bottom:5px; }
[data-density="compact"] .rccard { padding:8px 11px; }
[data-density="compact"] .rccards,
[data-density="compact"] .tvcards { gap:6px; }
[data-density="compact"] .rcgroup,
[data-density="compact"] .tvgroup { margin-bottom:16px; }
[data-density="compact"] .pad { padding-top:16px; padding-bottom:16px; }
[data-density="compact"] .padhead { margin-bottom:12px; }
[data-density="compact"] .nav button { padding-top:6px; padding-bottom:6px; }
/* Not on a phone. Compact exists to fit more on a big screen; on a small one
   it only makes things harder to hit, and a target under about 40px is a
   target somebody misses. */
@media (max-width: 700px) {
  [data-density="compact"] .nav button { padding-top:10px; padding-bottom:10px; }
}

/* --------------------------------------------------------------------------
   3. Glass
   --------------------------------------------------------------------------
   Glass is not a colour, it is a relationship: a translucent surface only looks
   like glass if there is something behind it worth seeing through to. So the
   glass themes put a soft accent-tinted wash on the page itself, and the
   panels above it become partly transparent and blurred.

   The blur is the expensive part. It is declared once, as a token, so the
   reduced-transparency and no-support fallbacks at the end of this file can
   switch the whole portal back to solid surfaces by changing two values.
   -------------------------------------------------------------------------- */

[data-glass] {
  --glass-blur:18px;
  --glass-sat:1.6;
  --radius:14px;
}

[data-glass="day"] {
  --glass:hsl(0 0% 100% / .62);
  --glass-strong:hsl(0 0% 100% / .80);
  --glass-line:hsl(var(--acc-h) 40% 62% / .22);
  --glass-hi:hsl(0 0% 100% / .70);     /* the lit top edge */
}

[data-glass="night"] {
  --glass:hsl(var(--acc-h) 22% 16% / .58);
  --glass-strong:hsl(var(--acc-h) 22% 17% / .78);
  --glass-line:hsl(var(--acc-h) 40% 70% / .16);
  --glass-hi:hsl(var(--acc-h) 40% 80% / .10);
}

/* The wash behind everything. Fixed, so it does not scroll with content and
   give the blur something moving to chew on. */
[data-glass="day"] body,
[data-glass="night"] body { background:var(--bg); }

[data-glass="day"] body::before,
[data-glass="night"] body::before {
  content:"";
  position:fixed; inset:0; z-index:-1; pointer-events:none;
  background:
    radial-gradient(58% 48% at 12% 8%,  hsl(var(--acc-h) 80% 62% / .16), transparent 70%),
    radial-gradient(52% 46% at 88% 12%, hsl(calc(var(--acc-h) + 18) 76% 60% / .10), transparent 72%),
    radial-gradient(64% 56% at 76% 92%, hsl(calc(var(--acc-h) - 16) 74% 56% / .10), transparent 70%),
    radial-gradient(48% 40% at 28% 86%, hsl(var(--acc-h) 70% 58% / .08), transparent 74%);
}

[data-glass="night"] body::before { opacity:.85; }

/* Surfaces.
   ----------------------------------------------------------------------------
   TRANSLUCENCY IS CHEAP. BLUR IS NOT. THAT IS THE WHOLE RULE HERE.

   The first version of this file blurred everything: the rail, the columns, the
   message list, the composer, the headers. Which meant .composer sat inside
   .msgs sat inside .col, all three with a backdrop-filter, each one having to
   read and re-blur the layer beneath it.

   Sending a message re-renders the whole thread, so every send invalidated all
   three layers at once. On a desktop that was a blank flash and a stutter. When
   I measured it by appending forty messages, the renderer stopped answering for
   forty-five seconds.

   So the big surfaces - the columns and the message river - are now only
   TRANSLUCENT. They let the wash behind show through with a plain semi-opaque
   colour, which costs the compositor nothing and looks nearly identical,
   because there is a soft gradient rather than sharp detail behind them.

   backdrop-filter is kept for small pieces of chrome that do not change as you
   work and are never nested inside each other: the rail, the two header bars,
   the composer, dialogs, the dialler.

   If you add a blurred surface, check first that it is not inside another one.
   -------------------------------------------------------------------------- */

/* Translucent only. No filter, no layer, no cost. */
[data-glass="day"] .col,
[data-glass="night"] .col,
[data-glass="day"] .msgs,
[data-glass="night"] .msgs,
[data-glass="day"] .calltile,
[data-glass="night"] .calltile,
[data-glass="day"] .emptystate,
[data-glass="night"] .emptystate {
  background:var(--glass);
  border-color:var(--glass-line);
}

/* The message river is the one place a re-render happens constantly, so it is
   also told to keep its painting to itself. */
[data-glass="day"] .msgs,
[data-glass="night"] .msgs {
  background:var(--glass-strong);
  contain:paint;
}

/* The headers and the composer: translucent, not blurred.
   ----------------------------------------------------------------------------
   These sit at the top and bottom of the message column, and I measured what
   blurring them costs. Appending messages with them blurred produced frames of
   67ms; with the blur gone the worst frame was 33ms and the spikes disappeared
   altogether. That spike is the flicker you see on every send.

   A shared inbox is used all day, on phones, and a message is sent every few
   seconds. A hitch on the most frequent action in the product is not worth an
   effect nobody would notice was missing. */
[data-glass="day"] .thead,
[data-glass="night"] .thead,
[data-glass="day"] .colhead,
[data-glass="night"] .colhead,
[data-glass="day"] .composer,
[data-glass="night"] .composer {
  background:var(--glass-strong);
  border-color:var(--glass-line);
}

/* Blurred, and only where what sits behind never changes while you work: the
   rail, which is static, and things that appear over the top for a moment. */
[data-glass="day"] .rail,
[data-glass="night"] .rail,
[data-glass="day"] .formbox,
[data-glass="night"] .formbox,
[data-glass="day"] .clockcard,
[data-glass="night"] .clockcard,
[data-glass="day"] .modalbox,
[data-glass="night"] .modalbox,
[data-glass="day"] .dialer,
[data-glass="night"] .dialer {
  background:var(--glass);
  -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-sat));
  backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-sat));
  border-color:var(--glass-line);
}

[data-glass="day"] .rail,
[data-glass="night"] .rail {
  border-right:1px solid var(--glass-line);
}

/* A single lit edge along the top of the larger panels. It is what stops a
   translucent rectangle reading as a washed-out one. */
[data-glass="day"] .formbox,
[data-glass="night"] .formbox,
[data-glass="day"] .modalbox,
[data-glass="night"] .modalbox,
[data-glass="day"] .dialer,
[data-glass="night"] .dialer,
[data-glass="day"] .clockcard,
[data-glass="night"] .clockcard {
  box-shadow:inset 0 1px 0 var(--glass-hi), var(--sh-2);
}

/* --------------------------------------------------------------------------
   4. Shape and motion
   --------------------------------------------------------------------------
   The portal was built on 5/8/12px corners. Softer corners suit glass, so the
   scale moves up — but only through the existing tokens, so every rounded
   thing in the portal moves together and nothing is left at the old radius
   looking like it was missed.
   -------------------------------------------------------------------------- */

:root { --r-1:8px; --r-2:12px; --r-3:16px; --ease:cubic-bezier(.32,.72,0,1); }

button, .fbtn, .primary, .callbtn, .bigbtn, .linkbtn, .notifybtn,
input, textarea, select, .tag, .pill {
  transition:background-color .18s var(--ease), color .18s var(--ease),
             border-color .18s var(--ease), box-shadow .18s var(--ease),
             transform .12s var(--ease), opacity .18s var(--ease);
}

/* Pressed, not just hovered. On a phone there is no hover, so without this a
   tap gives no feedback at all until the screen changes. */
button:active, .fbtn:active, .primary:active, .callbtn:active, .bigbtn:active {
  transform:scale(.975);
}

.view.on { animation:viewin .26s var(--ease) both; }
@keyframes viewin { from { opacity:0; transform:translateY(4px); } }

/* --------------------------------------------------------------------------
   5. Controls
   -------------------------------------------------------------------------- */

.primary, .fbtn.go, .bigbtn.on {
  background:linear-gradient(135deg, var(--brand-mid), var(--claim));
  color:var(--on-accent);
  border:0;
  box-shadow:var(--sh-1);
}
.primary:hover, .fbtn.go:hover { filter:brightness(1.06); box-shadow:var(--sh-2); }

input, textarea, select {
  background:var(--bg);
  border:1px solid var(--line);
  border-radius:var(--r-1);
  color:var(--text);
}
input:focus, textarea:focus, select:focus {
  border-color:var(--claim);
  box-shadow:0 0 0 3px hsl(var(--acc-h) 70% 55% / .16);
  outline:none;
}

[data-glass="day"] input, [data-glass="night"] input,
[data-glass="day"] textarea, [data-glass="night"] textarea,
[data-glass="day"] select, [data-glass="night"] select {
  background:var(--glass-strong);
  border-color:var(--glass-line);
}

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

/* The nav rail: the selected item gets a soft accent slab rather than a
   colour change alone, so it is findable at a glance across the room. */
.nav button {
  border-radius:var(--r-1);
  margin:1px 8px;
}
.nav button.sel {
  background:var(--accent-soft);
  color:var(--claim-deep);
  font-weight:600;
}
[data-glass="day"] .nav button.sel,
[data-glass="night"] .nav button.sel {
  background:hsl(var(--acc-h) 70% 56% / .16);
  box-shadow:inset 0 0 0 1px var(--glass-line);
}
.nav button:hover:not(.sel) { background:hsl(var(--acc-h) 40% 50% / .08); }

/* --------------------------------------------------------------------------
   6. Dialogs
   --------------------------------------------------------------------------
   These replace window.alert, window.confirm and window.prompt.

   Not only because "wa.simplifynetworks.co.uk says:" looks unfinished. A
   native dialog blocks the whole JavaScript thread, and inside the Android
   app's WebView that means the portal stops receiving anything at all —
   messages, calls, the lot — until somebody taps OK. The portal cannot afford
   a control that freezes it.
   -------------------------------------------------------------------------- */

.uiask {
  position:fixed; inset:0; z-index:4000;
  display:grid; place-items:center;
  padding:20px;
  background:hsl(var(--acc-h) 30% 12% / .42);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  animation:uiask-fade .18s var(--ease) both;
}
@keyframes uiask-fade { from { opacity:0; } }

.uiask-box {
  width:min(440px, 100%);
  background:var(--bg);
  border:1px solid var(--line);
  border-radius:var(--r-3);
  box-shadow:var(--sh-3);
  padding:24px 24px 18px;
  animation:uiask-in .26s var(--ease) both;
}
@keyframes uiask-in { from { opacity:0; transform:translateY(12px) scale(.98); } }

[data-glass="day"] .uiask-box, [data-glass="night"] .uiask-box {
  background:var(--glass-strong);
  -webkit-backdrop-filter:blur(24px) saturate(1.7);
  backdrop-filter:blur(24px) saturate(1.7);
  border-color:var(--glass-line);
  box-shadow:inset 0 1px 0 var(--glass-hi), var(--sh-3);
}

.uiask-box h3 { margin:0 0 8px; font-size:16px; font-weight:600; }
.uiask-box p  { margin:0 0 4px; color:var(--muted); font-size:13.5px; line-height:1.6;
             white-space:pre-wrap; overflow-wrap:anywhere; }
.uiask-box input {
  width:100%; margin-top:14px; padding:10px 12px; font-size:14px;
}
.uiask-row { display:flex; justify-content:flex-end; gap:8px; margin-top:20px; }
.uiask-row button {
  padding:9px 18px; border-radius:var(--r-1); font-size:13.5px; font-weight:600;
  border:1px solid var(--line); background:var(--panel); color:var(--text);
}
.uiask-row button:hover { background:var(--raised); }
.uiask-row .uiask-go {
  background:linear-gradient(135deg, var(--brand-mid), var(--claim));
  color:var(--on-accent); border-color:transparent;
}
.uiask-row .uiask-go:hover { filter:brightness(1.06); }
/* Destructive actions say so in the button, not only in the sentence above it.
   People read the button. */
.uiask-row .uiask-danger { background:var(--warn); color:#fff; border-color:transparent; }
.uiask-row .uiask-danger:hover { filter:brightness(1.06); }

/* A short-lived note for things that do not deserve a dialog at all — saved,
   copied, sent. It never blocks, and it never needs dismissing. */
.uitoasts {
  position:fixed; left:50%; bottom:26px; transform:translateX(-50%);
  z-index:4100; display:flex; flex-direction:column; gap:8px;
  align-items:center; pointer-events:none;
}
.uitoast {
  background:var(--text); color:var(--bg);
  padding:10px 16px; border-radius:999px; font-size:13px; font-weight:500;
  box-shadow:var(--sh-3); max-width:min(440px, 92vw);
  animation:uitoast-in .24s var(--ease) both;
}
.uitoast.uitoast-bad { background:var(--warn); color:#fff; }
@keyframes uitoast-in { from { opacity:0; transform:translateY(10px); } }
.uitoast.uitoast-going { animation:uitoast-out .2s var(--ease) both; }
@keyframes uitoast-out { to { opacity:0; transform:translateY(6px); } }

/* --------------------------------------------------------------------------
   7. Choosing a theme
   -------------------------------------------------------------------------- */

.themepanel { padding:4px 0 2px; }
.themerow { display:flex; flex-wrap:wrap; gap:7px; margin-bottom:12px; }

.themechip {
  display:flex; align-items:center; gap:7px;
  padding:7px 11px 7px 8px; border-radius:999px;
  border:1px solid var(--line); background:var(--panel);
  font-size:12.5px; color:var(--text);
}
.themechip:hover { border-color:var(--accent-line); }
.themechip[aria-pressed="true"] {
  border-color:var(--claim);
  box-shadow:0 0 0 2px hsl(var(--acc-h) 70% 55% / .18);
}
/* A small picture of the theme rather than its name alone: "Midnight" and
   "Dark" mean nothing until you have seen both. */
.themechip .themeprev {
  width:20px; height:20px; border-radius:6px; flex:none;
  border:1px solid hsl(0 0% 50% / .3);
}
.themeprev-glass       { background:linear-gradient(135deg,#EAF2FF,#FFFFFF 60%,#DCE9FA); }
.themeprev-glass-night { background:linear-gradient(135deg,#243247,#141A21 60%,#1C2838); }
.themeprev-light       { background:linear-gradient(135deg,#FFFFFF,#EFF3F8); }
.themeprev-dark        { background:linear-gradient(135deg,#212B36,#141A21); }
.themeprev-midnight    { background:linear-gradient(135deg,#1A2941,#0E1626); }
.themeprev-paper       { background:linear-gradient(135deg,#FBF8F3,#EFE9DE); }
.themeprev-contrast    { background:linear-gradient(135deg,#FFFFFF 50%,#000000 50%); }
.themeprev-dim         { background:linear-gradient(135deg,#2E3742,#20262D); }

.accentrow { display:flex; flex-wrap:wrap; gap:8px; }
.accentdot {
  width:28px; height:28px; border-radius:50%;
  border:2px solid transparent; padding:0;
  background:linear-gradient(135deg,
    hsl(var(--dot-h) 70% 62%), hsl(var(--dot-h) 66% 44%));
  box-shadow:var(--sh-1);
}
.accentdot[aria-pressed="true"] {
  border-color:var(--text);
  box-shadow:0 0 0 3px hsl(var(--dot-h) 70% 55% / .28);
}
.accentdot:hover { transform:scale(1.08); }

.themenote { color:var(--dim); font-size:11.5px; margin:10px 0 0; line-height:1.6; }

/* The two rows added on 21 Sep. A quiet label above each, because a grid of
   chips with no heading is a grid somebody has to work out. */
.themesub {
  margin:16px 0 7px; font-size:11px; letter-spacing:.05em; text-transform:uppercase;
  color:var(--dim); font-weight:600;
}
.fontrow { display:grid; gap:6px; grid-template-columns:repeat(auto-fit, minmax(140px, 1fr)); }
.fontchip {
  display:flex; flex-direction:column; align-items:flex-start; gap:1px; text-align:left;
  padding:8px 11px; border:1px solid var(--line); border-radius:9px;
  background:var(--panel); color:inherit; cursor:pointer; font:inherit;
}
.fontchip:hover { border-color:var(--accent-line); }
.fontchip[aria-pressed="true"] { border-color:var(--claim); box-shadow:0 0 0 2px var(--out); }
/* Each chip set in the face it offers. A list of font names in one font tells
   nobody anything — the whole decision is what it looks like. */
.fontchip.f-inter   .fontname { font-family:"Inter", sans-serif; }
.fontchip.f-plex    .fontname { font-family:"IBM Plex Sans", sans-serif; }
.fontchip.f-legible .fontname { font-family:"Atkinson Hyperlegible", sans-serif; }
.fontchip.f-system  .fontname { font-family:system-ui, -apple-system, "Segoe UI", sans-serif; }
.fontname { font-size:14px; font-weight:600; }
.fontnote { font-size:11px; color:var(--dim); }

.denrow { display:flex; gap:7px; flex-wrap:wrap; }
.denchip {
  padding:7px 14px; border:1px solid var(--line); border-radius:99px;
  background:var(--panel); color:inherit; cursor:pointer; font:inherit; font-size:13px;
}
.denchip:hover { border-color:var(--accent-line); }
.denchip[aria-pressed="true"] {
  border-color:var(--claim); background:var(--accent-soft); color:var(--claim-deep); font-weight:600;
}

/* The swatch on the rail button itself: the current theme and accent, small
   enough to sit next to a word. Its own class rather than the portal's existing
   .swatches, which sizes its children to 9px round dots and would squash the
   theme tile back into one. */
.themebtnsw { display:inline-flex; align-items:center; gap:5px; }
.themebtnsw i { flex:none; border:1px solid hsl(0 0% 50% / .3); }

/* --------------------------------------------------------------------------
   8. When glass is a bad idea
   --------------------------------------------------------------------------
   Three situations where the blur has to go, and in all three it goes by
   itself rather than by anybody finding a setting:

     - the browser cannot do backdrop-filter at all
     - the person has asked their system to reduce transparency
     - the person has asked to reduce motion (the animations go, glass stays)

   Each falls back to the solid surface underneath, which is why every glass
   rule above sets a background as well as a filter.
   -------------------------------------------------------------------------- */

@supports not ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))) {
  [data-glass] { --glass-blur:0px; }
  [data-glass="day"] .rail, [data-glass="day"] .col,
  [data-glass="day"] .thead, [data-glass="day"] .colhead,
  [data-glass="day"] .formbox, [data-glass="day"] .modalbox,
  [data-glass="day"] .dialer, [data-glass="day"] .msgs { background:var(--panel); }
  [data-glass="night"] .rail, [data-glass="night"] .col,
  [data-glass="night"] .thead, [data-glass="night"] .colhead,
  [data-glass="night"] .formbox, [data-glass="night"] .modalbox,
  [data-glass="night"] .dialer, [data-glass="night"] .msgs { background:var(--panel); }
}

@media (prefers-reduced-transparency:reduce) {
  [data-glass] { --glass-blur:0px; }
  [data-glass="day"] body::before,
  [data-glass="night"] body::before { opacity:.28; }
  [data-glass="day"] .rail, [data-glass="day"] .col, [data-glass="day"] .msgs,
  [data-glass="night"] .rail, [data-glass="night"] .col,
  [data-glass="night"] .msgs { background:var(--panel); }
}

@media (prefers-reduced-motion:reduce) {
  *, *::before, *::after {
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
  }
  button:active, .fbtn:active, .primary:active { transform:none; }
}

/* --------------------------------------------------------------------------
   9. Phones
   --------------------------------------------------------------------------
   The blur radius comes down on small screens. It is the same effect, but a
   phone GPU repainting an 18px blur behind a scrolling list is where a portal
   stops feeling quick — and the app is the surface most people use.
   -------------------------------------------------------------------------- */

@media (max-width:860px) {
  [data-glass] { --glass-blur:12px; }
  .uiask-box { width:100%; border-radius:var(--r-3) var(--r-3) 0 0; }
  .uiask { place-items:end center; padding:0; }
  .uiask-row button { flex:1; padding:12px 16px; }   /* thumbs, not cursors */
  .uitoasts { bottom:auto; top:16px; }
}


/* --------------------------------------------------------------------------
   10. The dialler
   --------------------------------------------------------------------------
   The status line under the keypad reports what the phone last did. It used to
   print the SIP identity the browser registered under — fifty random characters
   — with nothing stopping it, so it ran straight out of the card and across the
   call log beside it. Long words break, the line is clamped, and it can no
   longer be the widest thing on the screen.
   -------------------------------------------------------------------------- */

.calldebug {
  max-width:100%; overflow-wrap:anywhere; word-break:break-word;
  font-size:11px; line-height:1.5; color:var(--dim);
  text-align:center; margin:8px 0 0;
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden;
}
.dialer, .calllog { min-width:0; }
.dialer { overflow:hidden; }

/* The keypad reads as one object rather than twelve loose buttons. */
.keys button {
  border-radius:var(--r-2);
  background:var(--panel);
  border:1px solid transparent;
}
.keys button:hover { background:var(--raised); border-color:var(--line); }
[data-glass="day"] .keys button, [data-glass="night"] .keys button {
  background:hsl(var(--acc-h) 40% 60% / .07);
}
[data-glass="day"] .keys button:hover, [data-glass="night"] .keys button:hover {
  background:hsl(var(--acc-h) 50% 58% / .14);
}

.callgo {
  background:linear-gradient(135deg, var(--live), hsl(152 62% 32%));
  color:#fff; border-radius:999px; font-weight:600;
  box-shadow:0 4px 14px hsl(152 60% 30% / .28);
}
.callgo:hover { filter:brightness(1.07); }
.callend {
  background:linear-gradient(135deg, hsl(8 72% 56%), hsl(8 66% 44%));
  color:#fff; border-radius:999px; font-weight:600;
  box-shadow:0 4px 14px hsl(8 66% 44% / .3);
}

/* Not connected / outgoing only / ready — the three states, each unmistakable
   at a glance from across a desk. */
.callstate { border-radius:999px; padding:4px 12px; font-weight:600; }
.callstate.on   { background:var(--live-soft); border-color:transparent; color:var(--live); }
.callstate.warn { background:var(--warn-soft); border-color:transparent; color:var(--warn); }


/* --------------------------------------------------------------------------
   11. The rail's clocks, and the shift board
   --------------------------------------------------------------------------
   The rail used to repeat the six app shortcuts that already have their own
   screen. That space now carries something the screen cannot: what time it is
   for the rest of the team.
   -------------------------------------------------------------------------- */

/* The rail is a 100vh column and this sits at the bottom of it. Three short
   rows always fitted; six of these do not, and a block that grows instead of
   shrinking pushes the whole page taller — which puts a scrollbar on every
   screen in the portal and hides the last row of every list. So it shrinks,
   and the list inside it scrolls. */
.rail { min-height:0; }
.worldclocks {
  margin:auto 8px 6px; padding:8px 6px 6px;
  border-top:1px solid var(--line-soft);
  flex:0 1 auto; min-height:0; display:flex; flex-direction:column;
}
.clocklist {
  min-height:0; max-height:44vh; overflow-y:auto; overscroll-behavior:contain;
  scrollbar-width:thin;
}
.clockhead {
  display:flex; align-items:center; justify-content:space-between; gap:6px;
  padding:0 4px 6px; font-size:9.5px; font-weight:800;
  letter-spacing:.1em; text-transform:uppercase; color:var(--dim);
  white-space:nowrap;
}
.clockedit {
  background:none; border:0; padding:1px 4px; border-radius:5px; cursor:pointer;
  font:inherit; letter-spacing:.05em; color:var(--claim);
}
.clockedit:hover { background:var(--accent-soft); color:var(--claim-deep); }

/* THE COLOUR IS THE ANSWER, not decoration. Green while they are at their desk,
   amber while they are awake but it is early, late or their weekend, indigo
   while they are asleep. It is set from the local hour there, so the rail
   changes through the day on its own. */
.clockrow {
  --ctone:var(--dim); --ctint:var(--raised);
  display:flex; align-items:center; gap:7px;
  margin-bottom:4px; padding:5px 7px 5px 0;
  border-radius:8px; background:var(--ctint);
  overflow:hidden;
}
.clockrow[data-when="work"]  { --ctone:var(--live);   --ctint:var(--live-soft); }
.clockrow[data-when="edge"],
.clockrow[data-when="off"]   { --ctone:var(--signal); --ctint:var(--signal-soft); }
.clockrow[data-when="night"] { --ctone:var(--night);  --ctint:var(--night-soft); }

.clockbar { width:3px; align-self:stretch; background:var(--ctone); border-radius:0 3px 3px 0; }
.clockbody { flex:1; min-width:0; display:flex; flex-direction:column; gap:1px; }
.clockplace {
  font-size:11.5px; font-weight:650; color:var(--text); line-height:1.15;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.clocksub {
  font-size:9.5px; font-weight:600; color:var(--ctone); line-height:1.2;
  letter-spacing:.015em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.clockside { display:flex; flex-direction:column; align-items:flex-end; gap:0; }
.clocktime {
  font-variant-numeric:tabular-nums; font-weight:750; font-size:15px;
  color:var(--ctone); letter-spacing:-.01em; line-height:1.1;
}
/* Only shown when the day over there is not the day here, which is the only
   time the day is worth the room. */
.clockday { font-size:9px; font-weight:700; color:var(--ctone); opacity:.8; letter-spacing:.04em; }
.clocknone { font-size:11px; color:var(--dim); margin:2px 4px; line-height:1.4; }

/* --- the picker ----------------------------------------------------------- */
.clockminehead {
  display:flex; align-items:baseline; justify-content:space-between;
  margin:2px 0 6px; font-size:11px; font-weight:700; color:var(--muted);
  letter-spacing:.04em; text-transform:uppercase;
}
.clockcount { font-weight:600; color:var(--dim); text-transform:none; letter-spacing:0; }
.clockmine { display:flex; flex-direction:column; gap:5px; margin-bottom:14px; }
.clockpick {
  display:flex; align-items:center; gap:8px;
  padding:6px 8px; border:1px solid var(--line); border-radius:9px;
  background:var(--raised);
}
.clockpick.dragging { opacity:.45; }
.clockpick .navgrip { cursor:grab; color:var(--dim); font-size:13px; }
.clockname {
  flex:0 1 150px; min-width:80px;
  padding:4px 7px; font:inherit; font-size:13px; font-weight:600;
  color:var(--text); background:var(--bg);
  border:1px solid var(--line); border-radius:6px;
}
.clockname:focus { outline:2px solid var(--claim); outline-offset:1px; border-color:var(--claim); }
.clockzone {
  flex:1; min-width:0; font-size:11.5px; color:var(--dim);
  font-variant-numeric:tabular-nums;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.clockdrop {
  flex:none; width:24px; height:24px; border-radius:6px; cursor:pointer;
  border:1px solid var(--line); background:var(--bg);
  color:var(--muted); font-size:15px; line-height:1;
}
.clockdrop:hover { background:var(--warn-soft); border-color:var(--warn); color:var(--warn); }

.clockhits { display:flex; flex-direction:column; gap:3px; margin-top:7px; max-height:230px; overflow:auto; }
.clockhit {
  display:flex; align-items:baseline; gap:9px; width:100%; text-align:left;
  padding:7px 9px; border:1px solid transparent; border-radius:8px;
  background:none; cursor:pointer; font:inherit; color:var(--text);
}
.clockhit:hover, .clockhit:focus-visible {
  background:var(--accent-soft); border-color:var(--accent-line);
}
.clockhit b { font-size:13px; font-weight:650; }
.clockhit span { font-size:11px; color:var(--dim); }
.clockhittime {
  margin-left:auto; font-variant-numeric:tabular-nums;
  font-weight:700; font-size:12.5px; color:var(--muted) !important;
}

/* --- who is actually at their desk --------------------------------------- */
.onshift { display:flex; flex-direction:column; gap:2px; }
.shiftperson {
  display:flex; align-items:center; gap:8px;
  padding:5px 2px; font-size:12.5px; min-width:0;
}
.shiftname { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; color:var(--text); }
.shiftwhen { color:var(--dim); font-size:11.5px; font-variant-numeric:tabular-nums; }
.shiftdot {
  width:7px; height:7px; border-radius:50%; flex:none;
  background:var(--live); box-shadow:0 0 0 3px hsl(152 60% 45% / .18);
}
/* Away is amber and says so in words as well. A colour alone is no good to
   somebody who cannot tell green from amber, and this is a list people scan. */
.shiftperson.away .shiftdot {
  background:var(--signal); box-shadow:0 0 0 3px hsl(38 80% 50% / .2);
}
.shiftperson.away .shiftname { color:var(--muted); }
.shiftperson.away .shiftwhen { color:var(--signal); font-weight:600; }


/* --------------------------------------------------------------------------
   12. Recordings, and the screens that needed room
   -------------------------------------------------------------------------- */

.recsection { margin-top:28px; padding-top:22px; border-top:1px solid var(--line); }
.recsection h3 { margin:0 0 4px; font-size:14px; font-weight:600; }
.recsection .subtle { display:block; margin:0 0 14px; color:var(--dim); font-size:12px;
  line-height:1.6; max-width:62ch; }

.recrow { display:grid; gap:7px; padding:14px 2px; border-bottom:1px solid var(--line-soft); }
.recrow video {
  width:100%; max-width:520px; border-radius:var(--r-2);
  background:#0d1116; aspect-ratio:16/9;
}
.recrow .callacts { grid-column:1; margin-top:2px; }
.recrow a.linkbtn { text-decoration:none; }
/* Still being written to. The dot pulses because "recording now" beside a
   video that will not play yet otherwise reads as broken. */
.recnow {
  color:var(--warn); font-weight:600;
  display:inline-flex; align-items:center; gap:5px;
}
.recnow::before {
  content:""; width:7px; height:7px; border-radius:50%;
  background:var(--warn); animation:recpulse 1.4s ease-in-out infinite;
}
@keyframes recpulse { 50% { opacity:.25; } }

/* The record button in a call, while it is running. */
.callbtn.recording { background:var(--warn); color:#fff; }
.callbtn.recording svg circle:last-child { animation:recpulse 1.4s ease-in-out infinite; }

/* --- attendance ----------------------------------------------------------- */
/* The stat row was five equal boxes regardless of how much was in them. Hours
   worked is the number people come for; it earns more room than "0 missed". */
.calltiles, .statrow { gap:12px; }
.stat, .calcell { border-radius:var(--r-2); }
.clockcard { border-radius:var(--r-3); }
.daytable td, .calcell { transition:background-color .15s var(--ease); }
.calcell:hover { background:var(--accent-soft); }

/* --- team ----------------------------------------------------------------- */
/* The actions column was a stack of six identical blue links per person, which
   is the widest and least readable part of the screen. They sit on one line,
   quieter, and only the destructive one carries colour. */
.dirtable td { vertical-align:middle; }
.dirtable .rowacts { display:flex; flex-wrap:wrap; gap:4px 12px; justify-content:flex-end; }
.dirtable .rowacts .linkbtn { font-size:12px; color:var(--muted); white-space:nowrap; }
.dirtable .rowacts .linkbtn:hover { color:var(--claim); }
.dirtable .rowacts .linkbtn.danger { color:var(--warn); }

/* The rail's own profile button. It was a name with a role under it and no hint
   that pressing it did anything. */
.mewho .meedit { display:block; font-size:10.5px; color:var(--dim); }
.mewho:hover .meedit { color:var(--claim); }

/* The schedule preview: the time as set, then what it means everywhere else. */
.schedwhen b { display:block; font-size:13.5px; color:var(--text); font-weight:600; }
.schedwhen .elsewhere {
  display:block; margin-top:3px; font-size:11.5px; color:var(--dim); line-height:1.6;
}


/* ==========================================================================
   13. Messaging: the send, the reaction, the swipe
   --------------------------------------------------------------------------
   The portal's job is answering customers, so the composer and the bubble
   above it are the two things people touch all day. They were also the two
   least finished parts of the interface: a square text box with a rectangular
   "Send" beside it on the WhatsApp side, reactions behind a smiley you had to
   find, and no way at all to reply to a message with your thumb.

   What is here is the shape people already know from their phone. Nothing
   below invents a control: every gesture ends up calling the same reply and
   reaction code the buttons call, which is why they cannot disagree.
   ========================================================================== */

/* --- the send button ------------------------------------------------------ */
/* Quiet until there is something to send, then it arrives. The state comes
   from .has on the box, set by chatui.js as you type - the button's own
   disabled state still means what it always did: WhatsApp is not connected. */
.cbox.pill .csend {
  width:38px; height:38px;
  transform:scale(.82); opacity:.5;
  transition:transform .18s cubic-bezier(.2,1.25,.4,1), opacity .16s var(--ease), filter .14s;
}
.cbox.pill.has .csend { transform:scale(1); opacity:1; }
.cbox.pill .csend:active,
.cbox.pill.has .csend:active { transform:scale(.9); }
.cbox.pill .csend svg { transform:translateX(1px); }   /* the plane sits nose-forward */
.cbox.pill .csend:disabled { opacity:.32; }
.cbox.pill .cplus:disabled { opacity:.35; cursor:not-allowed; }

/* The WhatsApp composer is now the same object as the team chat's, so the
   old square-box rules have nothing left to style. Kept in styles.css for
   anything still using .send outside a pill. */
.composer .cbox.pill { margin:0; }
.cbox.pill textarea::placeholder { color:var(--dim); }

/* --- the reaction picker -------------------------------------------------- */
/* A floating pill of six, arriving with a spring, each one growing under the
   finger. It was a flat strip of small emoji before, which on a phone meant
   six 20px targets in a 38px bar.

   The positioning transform has to be carried through the keyframes: the
   picker is placed with translateY(-100%), so an animation ending at a bare
   scale(1) would drop it on top of the message it belongs to. */
.reactpick {
  gap:1px; padding:5px 6px; border-radius:26px;
  background:var(--panel); border-color:var(--glass-edge);
  box-shadow:0 12px 30px rgba(16,32,43,.2), 0 2px 6px rgba(16,32,43,.08);
  transform-origin:bottom right;
  animation:reactup .19s cubic-bezier(.2,1.3,.35,1) both;
}
.reactpick.below { transform-origin:top right; animation-name:reactdown; }
@keyframes reactup {
  from { opacity:0; transform:translateY(-100%) scale(.7); }
  to   { opacity:1; transform:translateY(-100%) scale(1); }
}
@keyframes reactdown {
  from { opacity:0; transform:translateY(100%) scale(.7); }
  to   { opacity:1; transform:translateY(100%) scale(1); }
}
.reactpick button {
  width:36px; height:36px; padding:0; font-size:21px; line-height:1;
  display:grid; place-items:center; border-radius:50%;
  transition:transform .13s cubic-bezier(.2,1.3,.35,1), background .12s var(--ease);
}
.reactpick button:hover { background:transparent; transform:scale(1.28) translateY(-2px); }
.reactpick button.on { background:var(--accent-soft); transform:scale(1.08); }
.reactpick button:active { transform:scale(.9); }
.reactpick .clear {
  width:30px; height:30px; font-size:13px; color:var(--dim);
  align-self:center; margin-left:2px;
}
.reactpick .clear:hover { background:var(--warn-soft,var(--raised)); color:var(--warn); transform:none; }

/* The reactions already on a message. Tappable on the team side, so they are
   sized for a thumb rather than a cursor. */
.reacts { display:flex; flex-wrap:wrap; gap:4px; margin-top:4px; }
.react {
  display:inline-flex; align-items:center; gap:3px;
  padding:2px 7px; min-height:24px; border-radius:14px;
  font-size:13px; line-height:1;
  background:var(--raised); border:1px solid var(--line-soft);
  transition:transform .12s var(--ease), background .12s;
}
button.react:hover { transform:scale(1.06); background:var(--accent-soft); }
.react.mine { background:var(--accent-soft); border-color:var(--accent-line); }

/* --- drag a message sideways to reply ------------------------------------- */
/* The arrow sits in the gap beside the bubble and is only in the document
   while a finger is on the screen.
   Which side is not a matter of taste: a bubble can be 86% of a phone's width,
   so the only place an arrow fits is the margin its own side of the thread
   leaves free - to the right of an incoming message, to the left of one of
   ours. Anywhere else and it would be cut off by the edge of the list, which
   is clipped on purpose so a sideways drag can never scroll the page. */
.msg { -webkit-touch-callout:none; }
.msg.swiping { transition:none; }
.msg.snapback { transition:transform .26s cubic-bezier(.2,.9,.3,1); }
.swipereply {
  position:absolute; top:50%; z-index:2;
  width:30px; height:30px; border-radius:50%;
  display:grid; place-items:center;
  opacity:0; transform:translateY(-50%) scale(.62);
  background:var(--accent-soft); color:var(--claim);
  pointer-events:none;
  transition:background .14s var(--ease), color .14s var(--ease);
}
.msg:not(.out) .swipereply { right:-38px; }
.msg.out .swipereply { left:-38px; }
/* Armed: let go now and it replies. */
.swipereply.armed { background:var(--claim); color:var(--on-accent); }

/* --- the phone app: three screens ----------------------------------------- */
/* WhatsApp, Team chat and Attendance, and a settings sheet. The other six
   screens still exist and still open if something links to one - a meeting in
   a chat, a call coming in - they simply have no tab in the app.
   !important because the admin and supervisor checks at sign-in unhide their
   own nav buttons, and they run long after this stylesheet. */
html[data-slim] .nav button[data-v]:not([data-v="inbox"]):not([data-v="chat"]):not([data-v="att"]) {
  display:none !important;
}
html[data-slim] .morebtn { display:flex !important; }
/* Appearance lives in the sheet on a phone; two ways to reach it in a bar this
   narrow is one too many. */
html[data-slim] #themeBtn { display:none !important; }

/* The clocks are rail furniture. In the bottom bar of a phone they were three
   more things competing for a 400px row, next to a status bar that already
   says the time. */
@media (max-width:860px) {
  .worldclocks { display:none; }
  /* Bubbles get a phone's corners, and the reaction picker a thumb's targets. */
  .msg { border-radius:13px; }
  .msg:not(.out) { border-top-left-radius:5px; }
  .msg.out { border-top-right-radius:5px; }
  .reactpick button { width:40px; height:40px; font-size:23px; }
  .reactpick { padding:6px; }
  .cbox.pill .csend { width:40px; height:40px; }
  /* Room for the arrow to appear beside a bubble that is nearly full width. */
  .msgs { overflow-x:clip; }
}


/* ==========================================================================
   14. The engineer directory
   --------------------------------------------------------------------------
   A table of a few thousand people that has to answer one question quickly:
   who do we have near this site, and can we afford them. So the name, the
   place and the rate are what the eye lands on, and everything that is merely
   present - a tag, a second number, an id at the client - is quieter.

   Rows that the import was not sure about carry a flag rather than being
   hidden away in a queue: a record nobody can see is a record nobody fixes.
   ========================================================================== */

/* SCROLLING, AND WHY IT IS NOT PER-SCREEN ANY MORE.

   .view is a grid pane inside a full-height layout and is overflow:hidden, so
   a screen taller than the window simply has its bottom cut off with nothing
   to scroll. The original answer was that each screen opts in by naming itself
   here. That answer has now failed twice: once on Engineers, where 103 people
   looked like sixteen, and again on Canned replies, where the list stopped
   dead at the ninth reply. An opt-in that somebody has to remember is a bug
   waiting for the next screen.

   So it is inverted. Every screen built the ordinary way - a .pad wrapper,
   which is what all the simple list-and-form screens use - scrolls, without
   being named. Two rules do it:

     the row is pinned to the pane's height, so the pad cannot grow the pane
     past the window and take its own content out of reach with it;
     the pad scrolls inside that.

   Screens that run their own scrolling are untouched, because they do not put
   a .pad directly inside the view: the chat panes scroll their columns, and
   the board screens scroll .opsscroll inside .opswrap. Those keep working
   exactly as they did, and now get a pane that is honestly the right height
   rather than one that happened to be.

   One more thing had to be true first. #app is height:100vh, but its grid row
   was sized to its content, so the pane could quietly grow TALLER than the
   window and spill the page instead. Every pane in here is written as though
   it is exactly window height - .opswrap says height:100%, the chat columns
   flex to fill - and none of them were getting that. Pinning the row is what
   makes the rest of this honest. */
#app { grid-template-rows:minmax(0, 1fr); }
.view.on { grid-template-rows:minmax(0, 1fr); }
.view.on > .pad { min-height:0; overflow-y:auto; overflow-x:hidden; }

/* Left in place deliberately. It is redundant now - the rule above covers it -
   but removing it is a change to a working screen for no gain. */
#v-eng { grid-template-columns:1fr; overflow-y:auto; }

.pad.wide { max-width:1180px; }

/* Tick all / Clear, on the department module list. Sixteen boxes is enough
   that doing them one at a time is a chore. Sits on the group heading rather
   than above the list, so it reads as belonging to that group and not to the
   dialog. */
/* Finding a timezone. The search box sits directly on top of the select and
   reads as one control, because it is one: the box decides what is in the
   list, it is not a second field to fill in. */
.tzfind {
  width:100%; margin-bottom:5px;
  background:var(--bg); color:var(--text);
  border:1px solid var(--line); border-radius:var(--r-2);
  padding:7px 10px; font-size:12.5px;
}
.tzfind:focus { outline:none; border-color:var(--accent-line); box-shadow:0 0 0 3px var(--accent-soft); }
.tzfind::placeholder { color:var(--dim); }
/* The one check that catches a wrong zone before the ticket is saved. */
.tznow { margin:5px 0 0; font-size:11.5px; color:var(--dim); min-height:1em; }

.deptall { display:flex; gap:6px; margin-top:6px; }
.deptall button {
  font-size:11px; font-weight:600; color:var(--claim);
  background:var(--accent-soft); border:1px solid var(--accent-line);
  border-radius:99px; padding:3px 11px; line-height:1.5;
}
.deptall button:hover { background:var(--accent-line); }

.engbar {
  display:flex; flex-wrap:wrap; gap:8px; align-items:center;
  margin:18px 0 10px;
}
.engbar input[type="text"], .engbar input:not([type]) {
  flex:1 1 260px; min-width:0;
  background:var(--bg); color:var(--text);
  border:1px solid var(--line); border-radius:var(--r-2);
  padding:9px 12px; font-size:13px;
}
.engbar input:focus, .engbar select:focus {
  outline:none; border-color:var(--accent-line); box-shadow:0 0 0 3px var(--accent-soft);
}
.engbar select {
  background:var(--bg); color:var(--text);
  border:1px solid var(--line); border-radius:var(--r-2);
  padding:9px 10px; font-size:13px; max-width:200px;
}
.engcheck {
  display:inline-flex; align-items:center; gap:7px;
  font-size:12.5px; color:var(--muted); white-space:nowrap;
  padding:8px 10px; border:1px solid var(--line); border-radius:var(--r-2);
}
.engcheck input { accent-color:var(--claim); }

.engmeta {
  display:flex; align-items:baseline; justify-content:space-between;
  gap:16px; flex-wrap:wrap;
  font-size:12px; color:var(--dim); margin:0 2px 8px;
}
.engtools { display:flex; gap:16px; }
.engtools .linkbtn { text-decoration:none; }

.engtable td { vertical-align:top; }
.engtable tbody tr { cursor:pointer; transition:background-color .12s var(--ease); }
.engtable tbody tr:hover { background:var(--raised); }
.engname { font-weight:600; }
.engsub { display:block; font-size:11px; color:var(--dim); margin-top:2px; }
.engnum { font-variant-numeric:tabular-nums; white-space:nowrap; }
.engtag {
  display:inline-block; margin:0 4px 3px 0; padding:1px 7px;
  border:1px solid var(--line); border-radius:11px;
  font-size:10.5px; color:var(--muted);
}
.engpid {
  display:inline-block; margin-left:6px; font-size:10.5px;
  color:var(--dim); font-variant-numeric:tabular-nums;
}
/* Not an error - a row that is in the directory and doing its job, with one
   detail somebody should confirm. Amber, and it says what it wants. */
.engwarn {
  display:inline-block; margin-left:7px; padding:1px 7px; border-radius:11px;
  font-size:10.5px; font-weight:600; cursor:help;
  background:var(--signal-soft); color:var(--signal);
}
.engoff {
  display:inline-block; margin-left:7px; padding:1px 7px; border-radius:11px;
  font-size:10.5px; background:var(--raised); color:var(--dim);
}
.engempty { margin:26px 2px; color:var(--dim); font-size:13px; max-width:60ch; }

.engflag {
  background:var(--signal-soft); border:1px solid transparent;
  border-left:3px solid var(--signal);
  border-radius:var(--r-2); padding:10px 12px; margin:0 0 14px;
  font-size:12.5px; color:var(--text);
}
.engflag .engsub { color:var(--muted); }

.frow.four { grid-template-columns:repeat(4, minmax(0, 1fr)); }
.enggroups { margin:16px 0 4px; padding-top:14px; border-top:1px solid var(--line-soft); }
.enggroups h4 { margin:0 0 6px; font-size:11px; font-weight:500; color:var(--dim); }
.enggroups .linkbtn { display:block; text-align:left; padding:3px 0; }

@media (max-width:860px) {
  .engbar select, .engbar input { flex:1 1 100%; max-width:none; }
  .engtable thead { display:none; }
  .engtable tbody tr { display:block; margin-bottom:10px; padding:12px 14px;
    border:1px solid var(--glass-edge); border-radius:var(--r-3); background:var(--glass-2); }
  .engtable td { display:block; border:0; padding:2px 0; }
  .frow.four { grid-template-columns:repeat(2, minmax(0, 1fr)); }
}


/* ==========================================================================
   15. Catching up
   --------------------------------------------------------------------------
   Coming on shift to sixty unread messages is the worst part of the day. The
   button that offers to read them is the only place in the portal where the
   accent is used to invite rather than to confirm - so it is warm, it only
   appears when there is enough waiting to be worth it, and it disappears
   without ceremony once you are up to date.
   ========================================================================== */

/* --- the button in a thread header ---------------------------------------- */
.catchbtn {
  display:inline-flex; align-items:center; gap:5px;
  padding:4px 11px 4px 9px; border-radius:14px;
  font-size:12px; font-weight:600; line-height:1.6;
  color:var(--claim); background:var(--accent-soft);
  border:1px solid var(--accent-line);
  transition:background .14s var(--ease), transform .12s var(--ease);
  white-space:nowrap;
}
.catchbtn:hover { background:var(--accent-line); }
.catchbtn:active { transform:scale(.96); }

/* --- the shift handover, above the conversation list ---------------------- */
/* Full width and unmissable, because the moment it is for is the moment you
   sit down and have not looked at anything yet. */
.shiftcatch {
  display:flex; flex-direction:column; align-items:flex-start; gap:2px;
  width:calc(100% - 20px); margin:10px 10px 2px; padding:10px 12px;
  border-radius:var(--r-3); text-align:left;
  color:var(--claim); background:var(--accent-soft);
  border:1px solid var(--accent-line);
  transition:background .14s var(--ease), transform .12s var(--ease);
}
.shiftcatch:hover { background:var(--accent-line); }
.shiftcatch:active { transform:scale(.99); }
.shiftcatchgo { font-size:13px; font-weight:600; }
.shiftcatchsub { font-size:11.5px; color:var(--muted); }

/* --- the summary itself --------------------------------------------------- */
.catchbody { max-height:min(56vh, 560px); overflow-y:auto; margin:4px 0 2px; }
.catchbody p { margin:0 0 9px; font-size:13.5px; line-height:1.62; color:var(--text); }
.catchhead {
  margin:16px 0 7px; font-size:11px; font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; color:var(--dim);
}
.catchhead:first-child { margin-top:4px; }
.catchitem {
  position:relative; margin:0 0 8px; padding:0 0 0 15px;
  font-size:13.5px; line-height:1.6; color:var(--text);
}
.catchitem::before {
  content:""; position:absolute; left:2px; top:8px;
  width:5px; height:5px; border-radius:50%; background:var(--accent-line);
}
.catcherr { color:var(--warn); font-size:13px; }

/* The citation. Every claim carries the message it came from, and pressing it
   goes there - a summary you cannot check is a rumour. */
.catchcite {
  display:inline-block; margin:0 1px; padding:0 5px;
  border-radius:9px; font-size:11px; font-weight:600;
  font-variant-numeric:tabular-nums;
  color:var(--claim); background:var(--accent-soft);
  transition:background .12s var(--ease);
}
.catchcite:hover { background:var(--accent-line); }

.catchfoot {
  margin:12px 0 0; padding-top:10px; border-top:1px solid var(--line-soft);
  font-size:11.5px; color:var(--dim); line-height:1.6;
}

/* --- while it reads ------------------------------------------------------- */
.catchwait {
  display:flex; align-items:center; gap:10px;
  padding:26px 2px; color:var(--dim); font-size:13px;
}
.catchdots { display:inline-flex; gap:4px; }
.catchdots i {
  width:6px; height:6px; border-radius:50%; background:var(--claim);
  animation:catchdot 1.1s ease-in-out infinite;
}
.catchdots i:nth-child(2) { animation-delay:.16s; }
.catchdots i:nth-child(3) { animation-delay:.32s; }
@keyframes catchdot { 0%, 80%, 100% { opacity:.25; transform:translateY(0); }
  40% { opacity:1; transform:translateY(-3px); } }

@media (max-width:860px) {
  .catchbtn span { display:none; }         /* the sparkle is enough at this width */
  .catchbtn { padding:5px 9px; }
  .catchbody { max-height:62vh; }
}


/* ==========================================================================
   16. Projects and rates
   --------------------------------------------------------------------------
   Six contracts, each with its own currency, minimum, uplifts and country
   table. The card has to answer one question from across the room — what does
   this project charge — so the terms are on the face of it rather than behind
   a click.
   ========================================================================== */

#v-proj { grid-template-columns:1fr; overflow-y:auto; }

.projgrid {
  display:grid; gap:12px; margin-top:18px;
  grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));
}
.projcard {
  display:flex; flex-direction:column; gap:7px;
  padding:14px 15px; text-align:left;
  border:1px solid var(--line); border-radius:var(--r-3);
  background:var(--panel);
  transition:border-color .14s var(--ease), transform .12s var(--ease), box-shadow .14s;
}
.projcard:hover {
  border-color:var(--accent-line); box-shadow:var(--sh-2); transform:translateY(-1px);
}
.projcard.retired { opacity:.55; }
.projtop { display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
.projtop b { font-size:14.5px; font-weight:600; color:var(--text); }
.projcur {
  font-size:10.5px; font-weight:600; letter-spacing:.05em;
  color:var(--claim); background:var(--accent-soft);
  padding:2px 7px; border-radius:10px;
}
.projref { font-size:10.5px; color:var(--dim); font-variant-numeric:tabular-nums; }
.projterms { display:flex; flex-wrap:wrap; gap:4px 12px; font-size:11.5px; color:var(--muted); }
.projterms b { color:var(--text); font-weight:600; }
.projuplift { font-size:11.5px; color:var(--muted); }
.projrates {
  display:flex; align-items:baseline; justify-content:space-between; gap:10px;
  margin-top:2px; padding-top:8px; border-top:1px solid var(--line-soft);
  font-size:13px; font-weight:600; color:var(--text);
}
.projplaces { font-size:11px; font-weight:400; color:var(--dim); }
.projnote { font-size:11.5px; color:var(--dim); line-height:1.5; }

/* --- the rate table inside the project --------------------------------- */
.ratehead { margin:20px 0 8px; font-size:12px; font-weight:600; color:var(--text); }
.ratehead span { font-weight:400; color:var(--dim); }
.ratewrap { max-height:38vh; overflow-y:auto; border:1px solid var(--line); border-radius:var(--r-2); }
.ratetable { width:100%; border-collapse:collapse; font-size:12.5px; }
.ratetable th {
  position:sticky; top:0; z-index:1;
  background:var(--panel); border-bottom:1px solid var(--line);
  padding:7px 8px; font-size:10.5px; text-align:left;
}
.ratetable td { padding:2px 4px; border-bottom:1px solid var(--line-soft); }
.ratetable input {
  width:100%; padding:6px 7px; font-size:12.5px;
  background:transparent; color:var(--text);
  border:1px solid transparent; border-radius:var(--r-1);
  font-variant-numeric:tabular-nums;
}
.ratetable input:hover { border-color:var(--line); }
.ratetable input:focus {
  outline:none; background:var(--bg);
  border-color:var(--accent-line); box-shadow:0 0 0 2px var(--accent-soft);
}
.ratetable .rplace { font-variant-numeric:normal; }
.flabnote { margin:5px 0 12px; font-size:11.5px; color:var(--dim); line-height:1.55; }

/* --- try it ------------------------------------------------------------- */
/* The same endpoint the ticket screen calls, so what it says here is what a
   ticket will charge. That is the point of having it. */
.tryit {
  margin:20px 0 4px; padding:14px 15px;
  border:1px solid var(--line); border-radius:var(--r-3);
  background:var(--raised);
}
.tryit h4 { margin:0 0 10px; font-size:12px; font-weight:600; }
.tryflags { display:flex; align-items:center; gap:18px; margin-top:12px; font-size:12.5px; color:var(--muted); }
.tryflags label { display:inline-flex; align-items:center; gap:6px; }
.tryflags input { accent-color:var(--claim); }
.tryout { margin-top:14px; padding-top:12px; border-top:1px solid var(--line-soft); }
.trytotal { font-size:22px; font-weight:600; color:var(--text); font-variant-numeric:tabular-nums; }
.tryup { font-size:11.5px; font-weight:500; color:var(--signal); margin-left:8px; }
.trywork { margin:10px 0 0; padding:0 0 0 16px; }
.trywork li { font-size:12px; color:var(--muted); line-height:1.7; }

@media (max-width:860px) {
  .projgrid { grid-template-columns:1fr; }
  .ratewrap { max-height:46vh; }
}


/* ==========================================================================
   17. Operations — the ticket board
   --------------------------------------------------------------------------
   This is the screen that replaces the spreadsheet, so it has to earn the
   swap. That means it reads like a sheet: dense, wide, every ticket a row,
   scannable across without opening anything. What it adds is the things the
   sheet could not do — a running total per currency, a margin column, and a
   status you can see from the far side of the room.
   ========================================================================== */

/* THE min-width:0 IS NOT COSMETIC.
   A grid or flex item defaults to min-width:auto, which means it refuses to be
   narrower than its contents. The ticket table is far wider than the window,
   so .opswrap grew to the width of the table and dragged the whole header out
   with it - the filters ran off the right of the screen and the Add button
   stretched into a green bar the width of a spreadsheet. .opsscroll is what
   should be doing the scrolling, and it cannot until both of them are told
   they are allowed to be narrow. */
#v-ops { grid-template-columns:minmax(0, 1fr); overflow:hidden; }
.opswrap { display:flex; flex-direction:column; min-height:0; min-width:0; height:100%; }

/* The title and the two actions on one line; the filters get a line of their
   own underneath. They were sharing a row, which is what made the row too wide
   to fit in the first place. */
.opshead {
  display:flex; align-items:center; justify-content:space-between;
  gap:16px; flex-wrap:wrap; min-width:0; padding:18px 22px 12px;
}
.opstitle { display:flex; align-items:center; gap:12px; min-width:0; }
.opstitle h2 { margin:0; font-size:17px; letter-spacing:-.01em; }
.opsmonth {
  background:var(--bg); color:var(--text); font-size:13px; font-weight:600;
  border:1px solid var(--line); border-radius:var(--r-2); padding:7px 10px;
}
.opsmonth:focus { outline:none; border-color:var(--accent-line); box-shadow:0 0 0 3px var(--accent-soft); }

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

/* Buttons that belong to this app rather than to the browser. flex:none is the
   part that matters - as a flex child with no say in the matter, the Add
   button was being stretched to whatever width the row happened to be. */
.opsbtn {
  display:inline-flex; align-items:center; gap:7px; flex:none;
  padding:9px 15px; border-radius:var(--r-2);
  font-size:12.5px; font-weight:600; line-height:1; white-space:nowrap;
  text-decoration:none; color:var(--text);
  background:var(--bg); border:1px solid var(--line);
  transition:border-color .14s var(--ease), background-color .14s var(--ease),
    transform .1s var(--ease), box-shadow .14s var(--ease);
}
.opsbtn:hover { border-color:var(--accent-line); background:var(--raised); }
.opsbtn:active { transform:translateY(1px); }
.opsbtn svg { flex:none; opacity:.75; }
.opsbtn.go {
  background:linear-gradient(135deg, var(--brand-mid), var(--claim));
  color:var(--on-accent); border-color:transparent; box-shadow:var(--sh-1);
}
.opsbtn.go:hover { filter:brightness(1.06); border-color:transparent; box-shadow:var(--sh-2); }
.opsbtn.go svg { opacity:1; }
.opsbtn:focus-visible { outline:none; box-shadow:0 0 0 3px var(--accent-soft); }

.opsfilters {
  display:flex; gap:8px; align-items:center; flex-wrap:wrap;
  min-width:0; padding:0 22px 12px;
}
/* The search takes the room that is going; the selects stay their own size. */
.opssearch { position:relative; display:flex; align-items:center; flex:1 1 280px; min-width:0; }
.opssearch svg { position:absolute; left:11px; color:var(--dim); pointer-events:none; }
.opssearch input { width:100%; min-width:0; padding-left:32px !important; }
.opsfilters input, .opsfilters select {
  background:var(--bg); color:var(--text); font-size:12.5px;
  border:1px solid var(--line); border-radius:var(--r-2); padding:9px 10px;
}
.opsfilters select { flex:0 0 auto; max-width:180px; }
.opsfilters input:focus, .opsfilters select:focus {
  outline:none; border-color:var(--accent-line); box-shadow:0 0 0 3px var(--accent-soft);
}

/* The running totals. Kept per currency on purpose: this business bills in
   three of them and adding pounds to dollars is the exact mistake the sheet
   made possible. */
.opstotals {
  display:flex; gap:8px 20px; flex-wrap:wrap; align-items:baseline;
  min-width:0; padding:0 22px 12px; font-size:12px; color:var(--muted);
}
.opscount b { color:var(--text); font-size:14px; }
.opspot { display:inline-flex; align-items:baseline; gap:6px; }
.opspotcur {
  font-size:10px; font-weight:600; letter-spacing:.05em;
  color:var(--claim); background:var(--accent-soft); padding:2px 6px; border-radius:9px;
}
.opspot b { color:var(--text); font-variant-numeric:tabular-nums; }
.opspot b.good { color:var(--live); }
.opspot b.bad { color:var(--warn); }

.opsscroll { flex:1; min-height:0; min-width:0; overflow:auto; padding:0 22px 26px; }
.opstable { width:100%; border-collapse:collapse; font-size:12.5px; }
.opstable th {
  position:sticky; top:0; z-index:2;
  background:var(--bg); border-bottom:1px solid var(--line);
  padding:8px 9px; font-size:10.5px; font-weight:500; color:var(--dim);
  text-align:left; white-space:nowrap;
}
.opstable th.num, .opstable td.num { text-align:right; font-variant-numeric:tabular-nums; }
.opstable td {
  padding:9px; border-bottom:1px solid var(--line-soft);
  vertical-align:top; line-height:1.45;
}
.opstable tbody tr { cursor:pointer; transition:background-color .12s var(--ease); }
.opstable tbody tr:hover { background:var(--raised); }
.opstable td.dim { color:var(--dim); }
.opstable td.good { color:var(--live); font-weight:600; }
.opstable td.bad { color:var(--warn); font-weight:600; }
.opssub { display:block; font-size:10.5px; color:var(--dim); }
.opssub.warn { color:var(--warn); }
.opsdesc { max-width:220px; }
.opshand { display:block; font-size:9.5px; color:var(--signal); font-weight:600; }
.opstable tr.ops-cancelled { opacity:.5; }
.opstable tr.ops-cancelled td { text-decoration:line-through; }

.opsbadge {
  display:inline-block; padding:2px 8px; border-radius:11px;
  font-size:10.5px; font-weight:600; white-space:nowrap;
  background:var(--raised); color:var(--muted);
}
.opsbadge.s-completed   { background:var(--live-soft);   color:var(--live); }
.opsbadge.s-in_progress { background:var(--accent-soft); color:var(--claim); }
.opsbadge.s-on_hold     { background:var(--signal-soft); color:var(--signal); }
/* The visit happened; the paperwork has not. Deliberately not the green of
   Completed — the job is not done — and not the amber of On hold either,
   because nothing is blocked, somebody just has to finish it off. */
.opsbadge.s-checked_out { background:var(--raised); color:var(--muted); box-shadow:inset 0 0 0 1px var(--line); }
.opsbadge.s-cancelled   { background:var(--warn-soft);   color:var(--warn); }

/* --- the ticket itself ---------------------------------------------------- */
.tktgrid {
  display:grid; gap:12px; margin-bottom:4px;
  grid-template-columns:repeat(4, minmax(0, 1fr));
}
.tktgrid.two { grid-template-columns:repeat(2, minmax(0, 1fr)); }

/* Something the portal worked out, shown where a field would be, so it is
   obvious it is not something to fill in. */
.tktcalc {
  padding:9px 11px; border-radius:var(--r-2);
  background:var(--raised); border:1px dashed var(--line);
  font-size:13px; font-weight:600; color:var(--text);
  font-variant-numeric:tabular-nums;
}
.tktcalcsub { display:block; font-size:10.5px; font-weight:400; color:var(--dim); }
.tktcheck {
  display:inline-flex; align-items:center; gap:8px;
  margin:10px 0 4px; font-size:12.5px; color:var(--muted);
}
.tktcheck input { accent-color:var(--claim); }

.tktmoney {
  padding:14px 15px; margin:4px 0 6px;
  border:1px solid var(--line); border-radius:var(--r-3); background:var(--raised);
}
.tktquote {
  padding:11px 13px; margin-bottom:12px; border-radius:var(--r-2);
  font-size:12.5px; color:var(--muted); background:var(--bg);
  border:1px solid var(--line);
}
.tktquote.ok { border-color:var(--accent-line); background:var(--accent-soft); color:var(--text); }
.tktquote.ok b { font-size:19px; font-variant-numeric:tabular-nums; }
.tktquote.need { border-left:3px solid var(--signal); color:var(--signal); }
.tktworking { display:block; margin-top:5px; font-size:11.5px; color:var(--muted); line-height:1.6; }

@media (max-width:860px) {
  .opshead { padding:14px 12px 8px; }
  .opsfilters { padding:0 12px 10px; }
  .opssearch { flex:1 1 100%; }
  .opsfilters select { flex:1 1 auto; max-width:none; }
  .opsactions { width:100%; }
  .opsactions .opsbtn { flex:1; justify-content:center; }
  .opstotals { padding:0 12px 10px; }
  .opsscroll { padding:0 10px 20px; }
  .opstable { font-size:12px; }
}

/* The running total under each side of the money. Two of them, never added
   together: the customer is billed in one currency and the engineer paid in
   another often enough that one combined figure would be a lie. */
.tktsum {
  margin-top:12px; padding-top:11px; border-top:1px solid var(--line);
  font-size:12.5px; color:var(--muted);
}
.tktsum b { font-size:17px; color:var(--text); font-variant-numeric:tabular-nums; margin-left:4px; }
.tktmargin { margin-left:12px; font-size:12px; font-weight:600; }
.tktmargin.good { color:var(--live); }
.tktmargin.bad { color:var(--warn); }
.tktmargin.dim { color:var(--dim); font-weight:400; }
.tktnodelete { font-size:11.5px; color:var(--dim); margin-left:auto; max-width:38ch; line-height:1.5; }

/* What the month made is the owner's business. Agents fill the rates in on the
   tickets they handle — they have to — but the margin column and the running
   totals are not for a screen in an open office. */
body.notadmin .adminonly { display:none !important; }
.opsquiet { font-size:11.5px; color:var(--dim); font-style:italic; }

/* ==========================================================================
   18. The ticket, as a page
   --------------------------------------------------------------------------
   It used to be a modal, and Usama's word for it was "congested" — which was
   fair: forty fields, two sets of money and a live quote stacked into a box
   420 pixels wide floating over the board it came from. Nothing about the
   ticket has changed here. It is the same fields, the same live quote and the
   same two sums; they have simply been given a screen, laid out in cards, two
   columns wide where there is room, so the details and the money can be read
   at the same time instead of a thousand pixels apart.

   It also writes itself into the address bar (#ticket/412), so a ticket can be
   bookmarked, or pasted to somebody, or kept open in a browser tab of its own.
   ========================================================================== */

#v-ticket { grid-template-columns:1fr; overflow:hidden; }
.tktpage { display:flex; flex-direction:column; min-height:0; height:100%; }

/* The bar stays put while the page scrolls, because on a ticket this long a
   Save button you have to go looking for is a ticket that does not get saved. */
.tkthead {
  display:flex; align-items:center; gap:16px; flex-wrap:wrap;
  padding:14px 22px; border-bottom:1px solid var(--line); background:var(--bg);
}
.tktback {
  display:inline-flex; align-items:center; gap:6px;
  font-size:12.5px; color:var(--muted);
  border:1px solid var(--line); border-radius:var(--r-2); padding:7px 12px 7px 9px;
  transition:border-color .12s var(--ease), color .12s var(--ease);
}
.tktback:hover { border-color:var(--accent-line); color:var(--claim); }
.tktwho { min-width:0; }
.tktwho h2 { margin:0; font-size:17px; letter-spacing:-.01em; }
.tktwhosub {
  display:block; margin-top:2px; font-size:11.5px; color:var(--dim);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:60ch;
}
.tktdo { margin-left:auto; display:flex; align-items:center; gap:10px; }
.tkterr { margin:12px 22px 0; }

.tktscroll { flex:1; min-height:0; overflow-y:auto; padding:18px 22px 44px; }

/* Two columns where the window allows it: the details on the left, the time on
   site and the money on the right. The money is the half that gets checked
   twice, and it should not be at the bottom of a long scroll. */
.tktcols {
  display:grid; grid-template-columns:repeat(2, minmax(0, 1fr));
  gap:18px; align-items:start; max-width:1560px;
}
.tktcol { display:flex; flex-direction:column; gap:16px; min-width:0; }

.tktcard {
  border:1px solid var(--line); border-radius:var(--r-3); background:var(--raised);
  padding:14px 18px 18px;
}
.tktcard > .ratehead:first-child { margin-top:0; }
.tktcard > .tktgrid:last-child { margin-bottom:0; }

/* Inside a card, two across. The four-across of the old modal only worked
   because the modal was one wide column; in a card half the window across it
   would give every field about 120 pixels. */
.tktcard .tktgrid { grid-template-columns:repeat(2, minmax(0, 1fr)); margin-bottom:10px; }

/* These were being styled by `.modalbox input`, which no longer applies now
   that the ticket is not in a modal. Said once, here, for every control on the
   page rather than per field. */
.tktcard input:not([type="checkbox"]):not([type="radio"]),
.tktcard select,
.tktcard textarea {
  width:100%; padding:8px 10px; font:inherit; font-size:13px;
  border:1px solid var(--line); border-radius:var(--r-2);
  background:var(--bg); color:var(--text);
  transition:border-color .12s var(--ease), box-shadow .12s var(--ease);
}
.tktcard textarea { resize:vertical; line-height:1.6; }
.tktcard input:focus, .tktcard select:focus, .tktcard textarea:focus {
  outline:none; border-color:var(--claim); box-shadow:0 0 0 3px var(--accent-soft);
}
.tktcard .flab:first-child { margin-top:0; }

.tktfoot {
  display:flex; align-items:center; gap:10px;
  margin:20px 0 0; max-width:1560px;
}

/* One column on anything narrower than a laptop, and one field per row on a
   phone — a date and a timezone side by side at 380px is two unreadable
   fields rather than one readable one. */
@media (max-width:1180px) {
  .tktcols { grid-template-columns:1fr; }
}
@media (max-width:860px) {
  .tkthead { padding:12px 14px; gap:10px; }
  .tktwhosub { max-width:none; white-space:normal; }
  .tktdo { margin-left:0; width:100%; }
  .tktdo button { flex:1; }
  .tktnodelete { display:none; }
  .tktscroll { padding:14px 12px 40px; }
  .tkterr { margin:10px 14px 0; }
  .tktcard { padding:12px 14px 16px; }
  .tktcard .tktgrid { grid-template-columns:1fr; }
}

/* `hidden` has to beat `display:grid`, or the rate row that belongs to a
   partner with no rate card shows on every ticket. */
.tktgrid[hidden] { display:none; }

/* Why a holiday balance is the number it is. The missed-shift line is the one
   that matters: a day quietly costing somebody a holiday day should be visible
   on the screen, not discovered in December. */
.balwhy { display:block; font-size:10.5px; color:var(--dim); margin-top:2px; }

/* ==========================================================================
   19. Payroll
   --------------------------------------------------------------------------
   Admin only, and the styling says so quietly rather than loudly: this is the
   same board furniture as Operations, because it is the same kind of screen —
   a month, a row per person, a running total. What is different is who can
   open it at all, and that is enforced in src/routes/api.js, not here.
   ========================================================================== */

#v-pay { grid-template-columns:minmax(0, 1fr); overflow:hidden; }
.paytable td { vertical-align:middle; }
.paywarn { color:var(--signal); font-size:11.5px; }
.paynote { max-width:78ch; margin-top:18px; }

/* One payslip, as a running sum, so the arithmetic is visible while it is
   being put together rather than only on the finished document. */
.paylines { margin:6px 0 14px; border:1px solid var(--line); border-radius:var(--r-2); overflow:hidden; }
.payline {
  display:flex; align-items:center; gap:10px;
  padding:9px 12px; font-size:12.5px; background:var(--bg);
  border-bottom:1px solid var(--line-soft);
}
.payline:last-child { border-bottom:0; }
.payline span { flex:1; min-width:0; color:var(--muted); }
.payline span i { display:block; font-style:normal; font-size:11px; color:var(--dim); }
.payline b { font-variant-numeric:tabular-nums; color:var(--text); }
.payline.base { background:var(--raised); }
.payline.plus b { color:var(--live); }
.payline.minus b { color:var(--warn); }
.payline.net { background:var(--accent-soft); border-top:1px solid var(--accent-line); }
.payline.net span { color:var(--text); font-weight:600; }
.payline.net b { font-size:16px; }
.payline .iconbtn { flex:none; }

.frow.three { grid-template-columns:repeat(3, minmax(0, 1fr)); }

@media (max-width:860px) {
  .frow.three { grid-template-columns:1fr; }
}

/* ==========================================================================
   20. Departments
   --------------------------------------------------------------------------
   A department is a set of permissions with a name on it. The card shows the
   two things somebody actually wants to know at a glance — what it can open,
   and who is in it — because a permissions screen that makes you click into
   every row to answer those is a permissions screen nobody audits.
   ========================================================================== */

#v-dept { grid-template-columns:minmax(0, 1fr); overflow:hidden; }
.deptintro { padding:0 22px 6px; max-width:88ch; }

.deptgrid {
  display:grid; gap:14px; padding-bottom:24px;
  grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));
}
.deptcard {
  border:1px solid var(--line); border-radius:var(--r-3); background:var(--raised);
  padding:14px 16px 12px; display:flex; flex-direction:column; gap:8px; min-width:0;
}
.deptcard.off { opacity:.55; }
.depthead { display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
.depthead h4 { margin:0; font-size:14px; }
.deptcount { font-size:11px; color:var(--dim); white-space:nowrap; }
.deptnote { margin:0; font-size:12px; color:var(--muted); line-height:1.55; }

.deptmods { display:flex; flex-wrap:wrap; gap:5px; }
.deptmod {
  font-size:10.5px; font-weight:600; padding:2px 8px; border-radius:10px;
  background:var(--accent-soft); color:var(--claim);
}
.deptwho { margin:0; font-size:11.5px; color:var(--dim); line-height:1.6; }

/* The pickers. Two columns where there is room, one on a phone — a list of
   fourteen checkboxes in a single column is a lot of scrolling for a dialog. */
.deptpicks {
  display:grid; gap:2px; margin:4px 0 10px;
  grid-template-columns:repeat(auto-fill, minmax(230px, 1fr));
  max-height:260px; overflow-y:auto;
  border:1px solid var(--line); border-radius:var(--r-2); padding:8px;
}
.deptpick {
  display:flex; align-items:flex-start; gap:9px;
  padding:7px 8px; border-radius:var(--r-2); cursor:pointer; font-size:12.5px;
}
.deptpick:hover { background:var(--raised); }
.deptpick input { margin-top:2px; accent-color:var(--claim); flex:none; }
.deptpick span { min-width:0; color:var(--muted); line-height:1.45; }
.deptpick span b { display:block; color:var(--text); font-weight:600; font-size:12.5px; }
/* The ones nobody can be refused: ticked, greyed, and obviously not a choice. */
.deptpick.base { opacity:.62; cursor:default; }
.deptpick.base:hover { background:none; }

/* The handover can be asked for a wider window when nine hours turns out not
   to have covered it — a quiet row under the summary rather than a control
   people have to know about in advance. */
.catchagain {
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  margin-top:10px; padding-top:10px; border-top:1px solid var(--line-soft);
  font-size:11.5px; color:var(--dim);
}

/* A tagged name is worth clicking.
   "When are you leaving for site" belongs in a one-to-one rather than in front
   of the customer, and the person you want is right there in the message. So
   the tag opens a direct chat with them — as a real button, because it looked
   exactly like this before and did nothing when tapped. Where WhatsApp has not
   shared the number it stays a plain span: better inert and obviously so than
   clickable and disappointing. */
button.mention {
  font: inherit; line-height: inherit; border: 0; cursor: pointer;
  color: var(--claim); background: var(--accent-soft);
  border-radius: 3px; padding: 0 3px; font-weight: 500;
  transition: background-color .12s var(--ease);
}
button.mention:hover { background: var(--accent-line); }
button.mention:active { transform: translateY(1px); }
button.mention:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent-soft); }
.msg.out button.mention { background: rgba(46, 99, 172, .10); }
.msg.out button.mention:hover { background: rgba(46, 99, 172, .20); }

/* ==========================================================================
   21. The dashboard
   --------------------------------------------------------------------------
   Cards of rows, not charts. What somebody needs at the start of a shift is
   "which of these has my name on it", and that is a list — a gauge would look
   busier and say less. The one piece of emphasis is reserved for work that has
   arrived and is not on the board yet, because that is the only thing here
   that is actively falling through a gap.
   ========================================================================== */

#v-dash { grid-template-columns:minmax(0, 1fr); overflow:hidden; }
.dashwhen { font-size:11.5px; color:var(--dim); }

.dashgrid {
  display:grid; gap:14px; padding-bottom:26px; align-items:start;
  grid-template-columns:repeat(auto-fill, minmax(340px, 1fr));
}
.dashcard {
  border:1px solid var(--line); border-radius:var(--r-3); background:var(--raised);
  min-width:0; overflow:hidden;
}
/* The one card that gets to raise its voice. */
.dashcard.shout { border-color:var(--signal); box-shadow:0 0 0 3px var(--signal-soft); }

.dashhead {
  display:flex; align-items:baseline; justify-content:space-between; gap:10px;
  padding:12px 15px 9px; border-bottom:1px solid var(--line);
}
.dashhead h3 { margin:0; font-size:13px; letter-spacing:-.01em; }
.dashcount {
  font-size:11px; font-weight:700; color:var(--claim);
  background:var(--accent-soft); border-radius:10px; padding:2px 8px; min-width:22px; text-align:center;
}

.dashrow {
  display:flex; align-items:flex-start; gap:10px; width:100%;
  padding:10px 15px; text-align:left; text-decoration:none; color:inherit;
  border-bottom:1px solid var(--line-soft); cursor:pointer;
  transition:background-color .12s var(--ease);
}
.dashrow:last-child { border-bottom:0; }
.dashrow:hover { background:var(--bg); }
.dashref {
  flex:none; max-width:12ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-family:"IBM Plex Mono", ui-monospace, monospace; font-size:11px;
  color:var(--claim); padding-top:1px;
}
.dashref.none { color:var(--dim); font-style:italic; font-family:inherit; }
.dashmain { flex:1; min-width:0; }
.dashdesc {
  display:block; font-size:12.5px; color:var(--text); line-height:1.4;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.dashsub { display:block; font-size:10.5px; color:var(--dim); margin-top:1px; }
/* The countdown. Tabular figures so the seconds tick in place instead of the
   whole line shuffling sideways twice a second. */
.dashdue {
  display:block; margin-top:2px; font-size:11px; font-weight:600; white-space:nowrap;
  color:var(--claim); font-variant-numeric:tabular-nums;
}
.dashdue.soon { color:var(--signal); }
.dashsub.started { color:var(--live); font-weight:600; white-space:nowrap; }

/* "Started?" and its undo. Outside the row button, because a button cannot
   live inside a button. */
.dashstart, .dashundo {
  flex:0 0 auto; align-self:center; font:inherit; font-size:11.5px; font-weight:600;
  padding:6px 11px; border-radius:99px; cursor:pointer; white-space:nowrap;
}
.dashstart {
  border:1px solid var(--live); background:var(--live-soft); color:var(--live);
}
.dashstart:hover { background:var(--live); color:#fff; }
.dashundo {
  border:1px solid var(--line); background:var(--panel); color:var(--dim);
}
.dashundo:hover { border-color:var(--signal); color:var(--signal); }
.dashstart:disabled, .dashundo:disabled { opacity:.6; cursor:default; }

/* The reason, under the description, where there is room for words.
   The right-hand column is flex:none and only ever holds short things — put a
   sentence in it and the description gets squeezed to nothing. */
.dashwhy {
  display:block; margin-top:3px; font-size:10.5px; font-weight:700;
  letter-spacing:.02em; line-height:1.4;
}
.dashwhy.late { color:var(--signal); }
.dashwhy.going { color:var(--live); }
.dashright { flex:none; display:flex; flex-direction:column; align-items:flex-end; gap:3px; }
.dashneeds { font-size:10px; color:var(--signal); white-space:nowrap; }

.dashempty, .dashnote { margin:0; padding:14px 15px; font-size:12px; color:var(--dim); line-height:1.55; }
.dashnote { color:var(--muted); border-bottom:1px solid var(--line-soft); }
.dashnote.warn { color:var(--warn); }

/* Who is about.
   A pill with a name squeezed into it read as a tag rather than a person. This
   gives each one a card with room for the face, the name and how long they
   have been on — the three things somebody actually wants off this panel — and
   lets them wrap into a grid instead of a ragged line. */
.dashwho {
  display:grid; gap:8px; padding:12px 15px;
  grid-template-columns:repeat(auto-fill, minmax(168px, 1fr));
}
.dashperson {
  display:flex; align-items:center; gap:9px; min-width:0;
  font-size:12px; color:var(--muted);
  border:1px solid var(--line); border-radius:var(--r-2, 10px);
  padding:7px 11px 7px 8px; background:var(--panel);
  transition:border-color .14s, background .14s;
}
.dashperson .dpwho { display:flex; flex-direction:column; min-width:0; line-height:1.3; }
.dashperson b {
  color:var(--text); font-weight:600; font-size:12.5px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.dashperson i { font-style:normal; font-size:10.5px; color:var(--dim); }

/* On shift gets a live edge down the side rather than a wash of colour across
   the whole card: it marks the state without shouting over the name. */
.dashperson.on {
  border-color:var(--line);
  box-shadow:inset 3px 0 0 var(--live);
  background:linear-gradient(90deg, var(--live-soft), transparent 62%);
}
.dashperson.on i { color:var(--live); font-variant-numeric:tabular-nums; }
.dashperson.off { opacity:.72; }
.dashperson.off:hover { opacity:1; }

.dashoff { padding:0 15px 4px; }
.dashoff .dashwho { padding:8px 0 10px; }
.dashofflabel {
  display:block; font-size:10px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--dim); padding-top:10px; border-top:1px solid var(--line-soft);
}

/* A recruitment site. Not a ticket, so no reference column and no click
   through to a board — just where, why, and the two things you can do about
   it. */
.dashrow.site { cursor:default; }
.dashrow.site:hover { background:transparent; }
.siteacts { display:flex; flex-direction:row; gap:12px; align-items:center; white-space:nowrap; }
.siteacts .linkbtn { font-size:11.5px; }
.siteacts .linkbtn.dim { color:var(--dim); }
.siteacts .linkbtn.dim:hover { color:var(--warn); }

/* Recruiting gets its own badge colour: it is the one status that is waiting
   on us to find a person rather than on the work itself. */
.opsbadge.s-recruiting { background:var(--signal-soft); color:var(--signal); }

/* The four things that have to be true before a ticket can be completed. */
.donelist { display:flex; flex-direction:column; gap:2px; margin-bottom:4px; }

@media (max-width:860px) {
  .dashgrid { grid-template-columns:1fr; }
}

/* ==========================================================================
   22. Field Nation, on the dashboard.
   A work order is not a second kind of ticket, so it gets a line under the
   ticket it belongs to rather than a column of its own. This is that line:
   set apart enough to read as "somewhere else says", quiet enough not to
   compete with the ticket it hangs off.
   ========================================================================== */
.dashsub.fnsub {
  color:var(--claim);
  padding-left:9px;
  border-left:2px solid var(--claim);
  margin-top:3px;
  opacity:.9;
}

/* ==========================================================================
   23. A row nobody has touched.
   The importer creates these from Freshdesk with only a reference in them, so
   the badge has to read as "your turn" rather than as a state of the work.
   Warm rather than green: it is waiting on a person, not progressing.
   ========================================================================== */
.opsbadge.s-new {
  background:var(--warn-soft);
  color:var(--warn);
  font-weight:600;
}

/* Adding a recruitment site without leaving the dashboard. */
.dashadd {
  display:flex; gap:6px; align-items:center; flex-wrap:wrap;
  padding:10px 0 2px; border-top:1px solid var(--line-soft); margin-top:8px;
}
.dashadd input {
  flex:1 1 110px; min-width:0; font-size:12px; padding:6px 9px;
  border-radius:7px; border:1px solid var(--line); background:var(--bg); color:inherit;
}
.dashadd button { flex:none; font-size:12px; padding:6px 13px; border-radius:7px; }
.dashaddopen {
  font-size:11.5px; color:var(--claim); padding:8px 0 2px;
  border-top:1px solid var(--line-soft); margin-top:8px; width:100%; text-align:left;
}
.dashaddopen:hover { text-decoration:underline; }

/* ==========================================================================
   24. Straight into Freshdesk.
   A ticket that arrived from Freshdesk keeps a way back to it, so nobody has
   to copy a reference into their search box. Quiet until you reach for it:
   the row is the thing being read, this is the thing being clicked.
   ========================================================================== */
.dashrowwrap {
  display:flex; flex-direction:column;
  border-bottom:1px solid var(--line-soft);
}
.dashrowtop { display:flex; align-items:stretch; min-width:0; }
/* A control on its own line under the row.
   The card is 340px at its narrowest and the row already spends width on the
   reference, the badges and the Freshdesk icon; a button beside all that
   leaves about seventy pixels for the description, which is not a
   description. Underneath it is also an easier target on a phone. */
.dashacts {
  display:flex; justify-content:flex-end; gap:8px; padding:0 15px 10px;
}
.dashrowwrap:last-child { border-bottom:0; }

/* LATE. A word, not a shade of grey.
   The appointment has been and gone and nobody has arrived, which is the one
   thing on this board a customer is already noticing. */
.latebadge {
  display:inline-block; padding:2px 8px; border-radius:99px;
  background:var(--signal); color:#fff;
  font-size:9.5px; font-weight:800; letter-spacing:.09em;
  line-height:1.5; margin-right:6px; vertical-align:middle;
}
/* And the whole row carries it, so it reads as late from across the office
   rather than only once your eye reaches the right-hand end of the line. */
.dashrowwrap.late, .dashrowwrap.going {
  border-bottom:0; border-radius:var(--r-2, 10px); margin:2px 0;
}
.dashrowwrap.late {
  background:var(--signal-soft); box-shadow:inset 3px 0 0 var(--signal);
}
.dashrowwrap.late .dashdesc { font-weight:600; }

/* Under way. Calm on purpose — this one is going to plan. */
.dashrowwrap.going {
  background:var(--live-soft); box-shadow:inset 3px 0 0 var(--live);
}
/* The row already drew its own line; the wrapper draws it now. */
.dashrowwrap > .dashrow { border-bottom:0; flex:1; min-width:0; }

.fdicon {
  flex:none; display:grid; place-items:center; width:34px;
  color:var(--dim); border-left:1px solid var(--line-soft);
  transition:color .13s, background .13s;
}
.fdicon:hover { color:var(--claim); background:var(--accent-soft); }
.dashrowwrap:hover .fdicon { color:var(--muted); }
.dashrowwrap:hover .fdicon:hover { color:var(--claim); }

/* On the board, beside the reference rather than in a column of its own —
   there are already fourteen columns. */
td .fdicon {
  display:inline-grid; width:auto; border-left:0; margin-left:6px;
  vertical-align:middle; opacity:.55;
}
td .fdicon:hover { opacity:1; background:none; }

/* On the ticket page, a proper button in the header beside Close and Save. */
.fdbtn {
  display:inline-flex; align-items:center; gap:6px; flex:none;
  font-size:12.5px; font-weight:600; text-decoration:none;
  padding:7px 13px; border-radius:7px; margin-left:auto;
  border:1px solid var(--line); color:var(--muted); background:var(--bg);
  transition:color .13s, border-color .13s;
}
.fdbtn:hover { color:var(--claim); border-color:var(--claim); }

/* ==========================================================================
   25. Was it a public holiday there?
   Worked out from the country and the date rather than ticked. Three states:
   nothing known yet, a plain no, and a yes that changes what the job costs —
   plus the amber middle for a regional holiday somebody has to rule on.
   ========================================================================== */
.tkholiday {
  display:flex; flex-wrap:wrap; align-items:center; gap:10px;
  padding:9px 12px; border-radius:var(--r-2, 10px); margin-top:10px;
  border:1px solid var(--line); background:var(--panel);
  font-size:12px; color:var(--muted); line-height:1.5;
}
.tkholiday .tkhline { flex:1; min-width:180px; }
.tkholiday.on {
  border-color:var(--signal); background:var(--signal-soft); color:var(--signal);
}
.tkholiday.on .tkhline { font-weight:600; }
.tkholiday.ask { border-color:var(--warn); background:var(--warn-soft); color:var(--warn); }
.tkhover { display:flex; gap:12px; flex:none; white-space:nowrap; }
.tkhover .linkbtn { font-size:11.5px; }
.tkhover .linkbtn.dim { color:var(--dim); }

/* ==========================================================================
   26. Signing.
   Documents sent to engineers to read and sign. The row that needs somebody
   is the one that has been signed and not yet countersigned — nothing is
   finished until that happens, so it is the one the eye should land on.
   ========================================================================== */
.sgdocs { display:flex; flex-wrap:wrap; gap:10px; padding:4px 0 16px; }
.sgdoc {
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:10px 14px; border:1px solid var(--line); border-radius:var(--r-2, 10px);
  background:var(--panel); font-size:13px;
}
.sgdoc.off { opacity:.5; }
.sgdoc .sgsub { color:var(--dim); font-size:11.5px; }
.sgpill {
  font-size:10px; letter-spacing:.04em; text-transform:uppercase; font-weight:600;
  padding:2px 7px; border-radius:99px; background:var(--warn-soft); color:var(--warn);
}

.sgtable { width:100%; border-collapse:collapse; font-size:13px; }
.sgtable th {
  text-align:left; font-size:10.5px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--dim); padding:8px 10px; border-bottom:1px solid var(--line);
}
.sgtable td { padding:11px 10px; border-bottom:1px solid var(--line-soft); vertical-align:top; }
.sgtable tr.sg-signed { background:var(--warn-soft); }

/* Three colours and three meanings, which is what Usama asked for on 21 Sep:
   "green and red if someone signs it or not, and if it's waiting on signature
   highlight yellow or amber."

   The old set was worse than uncoloured. All signed was AMBER and declined was
   amber-orange too, so the two states you most need to tell apart at a glance
   looked alike, and a finished document looked like a warning. Now:

     AMBER  somebody still has to do something. Sent, opened, partly signed.
     GREEN  signed. Complete is the same green, a shade firmer, because it is
            the same good news with the seal on it.
     RED    declined. Nothing else on this screen is red, so it cannot be
            mistaken for anything else. */
.opsbadge.sg-b-sent      { background:var(--signal-soft); color:var(--signal); }
.opsbadge.sg-b-opened    { background:var(--signal-soft); color:var(--signal); }
.opsbadge.sg-b-partial   { background:var(--signal-soft); color:var(--signal); font-weight:600; }
.opsbadge.sg-b-signed    { background:var(--live-soft); color:var(--live); font-weight:600; }
.opsbadge.sg-b-complete  { background:var(--live-soft); color:var(--live); font-weight:600; }
.opsbadge.sg-b-declined  { background:var(--warn-soft); color:var(--warn); font-weight:600; }

.sgfacts {
  display:grid; grid-template-columns:repeat(auto-fit, minmax(190px, 1fr)); gap:10px 18px;
  padding:14px 16px; background:var(--glass-2); border-radius:var(--r-2, 10px); margin-bottom:14px;
}
.sgfacts span {
  display:block; font-size:10px; letter-spacing:.06em; text-transform:uppercase; color:var(--dim);
}
.sgfacts b { font-size:13px; font-weight:600; }

.sgsigshow {
  margin:0 0 14px; padding:12px 14px; border:1px solid var(--line);
  border-radius:var(--r-2, 10px); background:var(--bg);
}
.sgsigshow > span {
  display:block; font-size:10px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--dim); margin-bottom:6px;
}
.sgsigshow img { max-width:260px; max-height:80px; display:block; }
.sgtyped { font-size:22px; font-style:italic; }

.sgids { margin-bottom:14px; }
.sgok  { color:var(--live); font-size:11.5px; }
.sgbad { color:var(--warn); font-size:11.5px; font-weight:600; }

/* The trail. Monospaced because it is evidence, not prose. */
.sgtrail {
  max-height:200px; overflow-y:auto; border:1px solid var(--line);
  border-radius:var(--r-2, 10px); padding:10px 12px; background:var(--bg);
}
.sgtrail div {
  display:flex; gap:10px; flex-wrap:wrap; align-items:baseline;
  padding:3px 0; font-size:11.5px; color:var(--muted);
}
.sgtrail code {
  font-family:"IBM Plex Mono", ui-monospace, monospace; font-size:10.5px; color:var(--dim);
}
.sgtrail b { color:var(--text); font-weight:600; }

/* --- the document list ------------------------------------------------------ */
.sgpill.ok   { background:var(--live-soft); color:var(--live); }
.sgpill.warn { background:var(--signal-soft); color:var(--signal); }
/* Retired. The row was already dimmed to half opacity, which reads as "loading"
   or "disabled" as easily as it reads as a decision somebody made. The word
   says which. */
/* How urgent a recruitment site is. Usama, 21 Sep: "add priority in front of
   the recruitment sites so people can understand the urgency."

   IN FRONT OF THE NAME, not after it and not only as a colour. A colour alone
   is a guess — nobody learns that amber is P2 from looking at it — and a list
   where the urgency is on the right is a list you read twice. The words are on
   the row underneath as well, because P2 means nothing until somebody has been
   told what it means once. */
.sitep {
  display:inline-block; min-width:22px; text-align:center;
  font-size:10.5px; font-weight:700; letter-spacing:.02em;
  padding:1px 5px; border-radius:5px; margin-right:7px;
  vertical-align:1px;
  background:var(--raised); color:var(--muted);
}
.sitep.p1 { background:var(--warn-soft, #fdecea); color:var(--warn, #a4301f); }
.sitep.p2 { background:var(--signal-soft); color:var(--signal); }
.sitep.p3 { background:var(--raised); color:var(--muted); }
.sitep.p4 { background:transparent; color:var(--dim); box-shadow:inset 0 0 0 1px var(--line); }
/* Only the urgent one gets an edge. If every row is marked, none of them is. */
.dashrow.site.p1 { box-shadow:inset 3px 0 0 var(--warn, #a4301f); padding-left:9px; }
.sitepsel {
  font-size:11px; padding:2px 4px; border-radius:6px;
  border:1px solid var(--line); background:var(--bg); color:var(--muted);
}
.sitepsel:focus { outline:none; border-color:var(--accent-line); box-shadow:0 0 0 3px var(--accent-soft); }
.siteacts { display:flex; align-items:center; gap:8px; }

.sgpill.retired { background:var(--raised); color:var(--muted); box-shadow:inset 0 0 0 1px var(--line); }
/* ...and half opacity was too far: a retired document still has to be readable,
   because bringing it back means reading it first. */
.sgdoc.off { opacity:.72; }
.sgdocacts { display:flex; gap:10px; margin-left:auto; }

/* Who signs, as a colour. The same colour in the editor, in the list and on
   the boxes — which box belongs to whom is the easiest thing to get wrong when
   setting a document up, and the most expensive to find out afterwards. */
.sgwho {
  display:inline-flex; align-items:center; gap:5px; margin:0 8px 2px 0; font-size:12.5px;
}
.sgwho::before {
  content:''; width:8px; height:8px; border-radius:50%; background:var(--rc, var(--claim));
}
/* The same three colours on the names themselves, so a row can be read
   without going to the badge at the end of it. The per-party colour stays on
   the dot for anyone still to act — that colour is what matches their boxes on
   the document, and losing it would cost more than it gains. */
.sgwho.done { color:var(--live); }
.sgwho.done::before { background:var(--live); box-shadow:none; }
.sgwho.no { color:var(--warn); text-decoration:line-through; }
.sgwho.no::before { background:var(--warn); }
.sgwho.now { color:var(--signal); font-weight:600; }
.sgwho.now::before { box-shadow:0 0 0 3px var(--signal-soft); }


/* --- the PDF on a document ---------------------------------------------- */
.sgpdfrow {
  display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:6px 0 4px;
  padding:12px 14px; border:1px solid var(--line); border-radius:var(--r-2, 10px);
  background:var(--glass-2); font-size:13px;
}
.sgpdfrow input[type=file] { flex:1; min-width:180px; font-size:12.5px; }
.sgtextfold { margin:10px 0 4px; }
.sgtextfold summary { cursor:pointer; font-size:12.5px; color:var(--dim); padding:4px 0; }
.sgtextfold[open] summary { color:var(--text); }

/* The boxes we fill in before it goes out. */
.sgprefills {
  display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:10px;
  margin:6px 0 4px;
}
.sgprefill { display:flex; flex-direction:column; gap:4px; }
.sgprefill span {
  font-size:10.5px; letter-spacing:.05em; text-transform:uppercase; color:var(--dim);
}
.sgprefill input { font-size:13px; padding:8px 10px; }

/* --- who signs it, when sending ------------------------------------------ */
.sgparties { display:flex; flex-direction:column; gap:8px; margin:6px 0 4px; }
.sgparty {
  display:grid; grid-template-columns:140px 1.2fr 1.6fr auto; gap:8px; align-items:center;
  padding:8px 10px 8px 14px; border:1px solid var(--line); border-radius:var(--r-2, 10px);
  background:var(--panel); position:relative;
}
.sgparty::before {
  content:''; position:absolute; left:0; top:8px; bottom:8px; width:4px;
  border-radius:0 4px 4px 0; background:var(--rc, var(--claim));
}
.sgpartyrole { font-size:11.5px; font-weight:600; color:var(--dim); }
.sgparty input { font-size:13px; padding:8px 10px; }
@media (max-width: 760px) {
  .sgparty { grid-template-columns:1fr; }
  .sgpartyrole { font-size:10.5px; letter-spacing:.05em; text-transform:uppercase; }
}

/* --- who signed it, on one request --------------------------------------- */
.sgpartylist { display:flex; flex-direction:column; gap:8px; margin-bottom:16px; }
.sgpartyrow {
  display:grid; grid-template-columns:1fr auto; gap:2px 12px; align-items:baseline;
  padding:10px 12px 10px 16px; border:1px solid var(--line); border-radius:var(--r-2, 10px);
  background:var(--panel); position:relative; font-size:13px;
}
.sgpartyrow::before {
  content:''; position:absolute; left:0; top:8px; bottom:8px; width:4px;
  border-radius:0 4px 4px 0; background:var(--rc, var(--claim));
}
.sgpartyrow.done { background:var(--live-soft); }
.sgpartyrow.done::before { background:var(--live); }
.sgpartyrow.refused { background:var(--warn-soft); }
.sgpartyrow.refused::before { background:var(--warn); }
/* Whoever the document is actually sitting with. Amber, and only ever one of
   them: the others are either finished or not yet their turn. */
.sgpartyrow.turn { background:var(--signal-soft); }
.sgpartyrow.turn::before { background:var(--signal); }
.sgpartyrow.refused .sgpartywhen { color:var(--warn); }
.sgpartymeta, .sgpartywhen { font-size:11.5px; color:var(--dim); }
.sgpartymeta { grid-column:1; }
.sgpartywhen { grid-column:1; }
.sgpartyacts { grid-column:2; grid-row:1 / span 3; display:flex; gap:10px; align-items:center; }
.sgdocbtns { display:flex; gap:14px; align-items:center; flex-wrap:wrap; margin:0 0 14px; }
.linkbtn.strong { font-weight:600; color:var(--claim); }

/* ==========================================================================
   26b. Placing the boxes.

   Palette on the left, document in the middle, the selected box's settings on
   the right. Full screen, because a contract page inside a 600px modal is
   unusable and this is the one job in the portal that wants the whole window.

   The first version put every field type in one row across the top. That is
   fine for five and unusable for twenty, which is what the list grew to.

   EVERY BOX IS POSITIONED IN PERCENTAGES, never pixels — the same fractions
   that go to the database, to the signer's phone and to pdf-lib. Three scales
   for one box, and percentages are the only thing all three agree about.
   ========================================================================== */
.modal.sgfull { padding:0; align-items:stretch; justify-content:stretch; }
.sgeditbox {
  display:flex; flex-direction:column; width:100%; height:100%;
  background:var(--bg); border-radius:0;
}
.sgedithead {
  display:flex; align-items:center; gap:14px; flex-wrap:wrap;
  padding:10px 16px; border-bottom:1px solid var(--line); background:var(--panel);
}
.sgedithead b { font-size:14px; }
.sgeditcount { font-size:11.5px; color:var(--dim); }
.sgeditstate { font-size:11.5px; font-weight:600; }
.sgeditstate.saved { color:var(--live); }
.sgeditstate.unsaved { color:var(--signal); }
.sgeditmount { flex:1; overflow:hidden; background:var(--glass-2); }

/* Three columns. The middle one scrolls; the sides do not, so the palette and
   the settings stay put while somebody works down an eight-page contract. */
.sgedit { display:grid; grid-template-columns:200px minmax(0, 1fr) 270px; height:100%; }
.sgeside, .sgeprops {
  overflow-y:auto; background:var(--panel); padding:14px 12px 30px;
}
.sgeside { border-right:1px solid var(--line); }
.sgeprops { border-left:1px solid var(--line); }
.sgemain { overflow-y:auto; }

.sgesec { margin-bottom:16px; }
.sgelabel {
  display:block; font-size:10px; letter-spacing:.07em; text-transform:uppercase;
  color:var(--dim); margin-bottom:7px;
}
.sgetools, .sgeroles { display:flex; flex-wrap:wrap; gap:5px; }
.sgetool, .sgerole {
  font:inherit; font-size:12px; cursor:pointer; padding:6px 10px;
  border:1px solid var(--line); border-radius:7px; background:var(--bg); color:var(--text);
  line-height:1.2;
}
.sgetool:hover, .sgerole:hover { border-color:var(--claim); }
.sgetool.wide { flex:1 0 100%; text-align:left; }
.sgetool.on { background:var(--claim); border-color:var(--claim); color:#fff; font-weight:600; }
.sgerole { flex:1 0 100%; text-align:left; padding-left:26px; position:relative; }
.sgerole::before {
  content:''; position:absolute; left:10px; top:50%; transform:translateY(-50%);
  width:9px; height:9px; border-radius:50%; background:var(--rc, var(--claim));
}
.sgerole.on { border-color:var(--rc); box-shadow:inset 0 0 0 1px var(--rc); font-weight:600; }
.sgehelp { font-size:11.5px; color:var(--dim); line-height:1.5; margin:10px 0 0; }

/* The settings for whichever box is selected. */
.sgenone { padding-top:6px; }
.sgenone p { font-size:12.5px; color:var(--dim); margin:0 0 8px; }
.sgecount {
  margin-top:14px; padding-top:12px; border-top:1px solid var(--line);
  font-size:11.5px; color:var(--dim);
}
.sgepropbar {
  display:flex; align-items:baseline; justify-content:space-between; gap:8px;
  padding:8px 10px; border-radius:var(--r-2, 10px); margin-bottom:12px;
  background:color-mix(in srgb, var(--rc, #2E63AC) 12%, transparent);
  border-left:3px solid var(--rc, #2E63AC);
}
.sgepropbar b { font-size:13px; }
.sgepropbar span { font-size:11px; color:var(--dim); }
.sgefield { display:block; margin-bottom:10px; }
.sgefield > span {
  display:block; font-size:11px; letter-spacing:.04em; text-transform:uppercase;
  color:var(--dim); margin-bottom:4px;
}
.sgefield input, .sgefield select, .sgefield textarea {
  width:100%; font:inherit; font-size:13px; padding:8px 10px;
}
.sgefield textarea { resize:vertical; line-height:1.5; }
.sgecheck {
  display:flex; align-items:flex-start; gap:8px; font-size:12.5px;
  margin:12px 0; cursor:pointer; line-height:1.4;
}
.sgecheck input { margin-top:2px; }
.sgenote {
  font-size:11.5px; color:var(--dim); line-height:1.5; margin:10px 0;
  padding:9px 11px; background:var(--glass-2); border-radius:8px;
}
.sgepropacts {
  display:flex; gap:14px; margin-top:16px; padding-top:12px; border-top:1px solid var(--line);
}
.linkbtn.danger { color:var(--signal); }

/* Narrow windows: the palette becomes a strip across the top and the settings
   drop underneath, rather than squeezing the document to nothing. */
@media (max-width: 1100px) {
  .sgedit { grid-template-columns:1fr; grid-template-rows:auto minmax(0, 1fr) auto; }
  .sgeside, .sgeprops { max-height:210px; border:0; border-bottom:1px solid var(--line); }
  .sgeside { display:flex; gap:18px; overflow-x:auto; padding:10px 12px; }
  .sgesec { margin-bottom:0; min-width:170px; }
  .sgetool.wide, .sgerole { flex:0 0 auto; }
  .sgeprops { border-top:1px solid var(--line); border-bottom:0; }
}

.sgepages {
  display:flex; flex-direction:column; align-items:center; gap:18px; padding:18px 12px 60px;
}
.sgepage {
  position:relative; background:#fff; border:1px solid var(--line);
  box-shadow:0 2px 10px rgba(12,20,28,.14); border-radius:3px; overflow:hidden;
  touch-action:none;                 /* dragging a box must not scroll the page */
}
.sgepage canvas { display:block; }
.sgelayer { position:absolute; inset:0; }
.sgenum {
  position:absolute; right:8px; bottom:6px; font-size:10.5px; color:#5A6B7B;
  background:rgba(255,255,255,.86); padding:1px 8px; border-radius:99px; pointer-events:none;
}

.sgebox {
  position:absolute; border:1.5px solid var(--rc, #2E63AC);
  background:color-mix(in srgb, var(--rc, #2E63AC) 14%, transparent);
  border-radius:3px; cursor:move; display:flex; align-items:center; overflow:visible;
  min-width:8px; min-height:6px;
}
.sgebox.opt { border-style:dashed; }
.sgebox.sel { box-shadow:0 0 0 2px var(--rc), 0 2px 8px rgba(12,20,28,.25); z-index:2; }
/* flex:1 with min-width:0 so the name is clipped by the box rather than
   spilling out of it — a tick box is a few millimetres wide and its label has
   to stop at the edge. The grip and the cross sit outside on purpose, which is
   why the box itself cannot simply be overflow:hidden. */
.sgeboxname {
  flex:1; min-width:0;
  font-size:9.5px; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
  color:var(--rc, #2E63AC); padding:0 3px; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; pointer-events:none;
}
.sgegrip {
  position:absolute; right:-5px; bottom:-5px; width:12px; height:12px;
  background:var(--rc, #2E63AC); border:2px solid #fff; border-radius:3px;
  cursor:nwse-resize;
}
.sgedel {
  position:absolute; right:-9px; top:-9px; width:18px; height:18px; line-height:1;
  padding:0; border-radius:50%; border:1px solid var(--line); background:#fff;
  color:#B4541F; font-size:13px; cursor:pointer; display:none;
}
.sgebox.sel .sgedel { display:block; }


/* --- Training -------------------------------------------------------------
   Video walkthroughs, recorded in the browser and watched here. Usama, 21 Sep:
   "I want something that stays within the portal, no one has to go to a
   different website to watch that." */

.tvnotice {
  margin:0 0 16px; padding:12px 14px; border-radius:9px; font-size:13.5px; line-height:1.65;
  background:var(--signal-soft); color:#6E5210; border:1px solid #F0DFAE;
}

.tvgroup { margin:0 0 26px; }
.tvgroup h3 {
  margin:0 0 10px; font-size:12px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--dim); font-weight:600;
}
/* Wide cards rather than a tight grid: the thing people scan for is the title,
   and a title that wraps to three words a line is not scannable. */
.tvcards { display:grid; gap:10px; grid-template-columns:repeat(auto-fill, minmax(320px, 1fr)); }
.tvcard {
  display:flex; gap:12px; align-items:stretch; text-align:left; width:100%;
  padding:10px; border:1px solid var(--line); border-radius:11px;
  background:var(--card, #fff); cursor:pointer; font:inherit; color:inherit;
}
.tvcard:hover { border-color:var(--brand-mid); }
.tvshot {
  flex:0 0 116px; height:68px; border-radius:7px; overflow:hidden; background:#10151b;
  display:flex; align-items:center; justify-content:center;
}
.tvshot img { width:100%; height:100%; object-fit:cover; display:block; }
.tvnoshot { color:#fff; opacity:.55; font-size:19px; }
.tvbody { display:flex; flex-direction:column; gap:3px; min-width:0; }
.tvbody b { font-size:14.5px; }
.tvnote, .tvmeta { font-size:12.5px; color:var(--muted); }
.tvmeta { color:var(--dim); }
.tvchip {
  align-self:flex-start; margin-top:3px; padding:1px 7px; border-radius:99px;
  font-size:11px; font-weight:600;
}
.tvchip.warn { background:var(--warn-soft); color:var(--warn); }
.tvchip.work { background:var(--signal-soft); color:var(--signal); }

/* The two sheets. Same shape as the signing page's, because somebody who has
   filled in a contract already knows how this behaves. */
.tvsheet { position:fixed; inset:0; z-index:60; display:none; }
.tvsheet.on { display:block; }
.tvsheet .veil { position:absolute; inset:0; background:rgba(12,20,28,.44); }
.tvsheet .panel {
  position:absolute; left:50%; top:50%; transform:translate(-50%, -50%);
  width:min(860px, calc(100vw - 32px)); max-height:88vh; overflow-y:auto;
  background:var(--card, #fff); border-radius:14px; padding:20px;
  box-shadow:0 18px 48px rgba(12,20,28,.3);
}
.tvsheet h2 { margin:0 0 4px; }
.tvsheet .hint { color:var(--muted); font-size:13.5px; margin:0 0 12px; }
.tvsheet video { width:100%; max-height:62vh; background:#000; border-radius:9px; display:block; }

.tvchapters { display:flex; flex-wrap:wrap; gap:6px; margin-top:12px; }
.tvchapters button {
  font:inherit; font-size:12.5px; padding:5px 10px; border-radius:99px;
  border:1px solid var(--line); background:transparent; color:inherit; cursor:pointer;
}
.tvchapters button:hover { border-color:var(--brand-mid); }
.tvchapters button span { color:var(--dim); margin-right:5px; font-variant-numeric:tabular-nums; }
.tvwarn { margin:12px 0 0; font-size:13px; color:var(--warn); }
.tvactions { margin-top:14px; }

.tvcaution {
  margin:14px 0; padding:11px 13px; border-radius:9px; font-size:13px; line-height:1.6;
  background:var(--soft, #F5F7FA); border:1px solid var(--line); color:var(--muted);
}
.tvclock { display:flex; align-items:baseline; gap:12px; margin:16px 0 4px; }
.tvclock b { font-size:30px; font-variant-numeric:tabular-nums; }
.tvclock span { font-size:13px; color:var(--dim); }
.tvclock span.bad { color:var(--warn); }
.tvmarks { margin-top:10px; font-size:12.5px; color:var(--dim); }
.tvmark { display:flex; align-items:center; gap:10px; margin:6px 0; }
.tvmark span { min-width:46px; color:var(--dim); font-variant-numeric:tabular-nums; }

/* The recording dot. A filled red circle that breathes — the one shape every
   recorder has used for forty years. */
.tvdot {
  display:inline-block; width:11px; height:11px; border-radius:50%;
  background:var(--warn); margin-right:7px; animation:tvpulse 1.6s ease-in-out infinite;
}
.tvdot.paused { background:var(--dim); animation:none; }
@keyframes tvpulse { 0%, 100% { opacity:1; } 50% { opacity:.3; } }
@media (prefers-reduced-motion: reduce) { .tvdot { animation:none; } }

/* The "?" beside a screen's heading. Quiet until somebody needs it. */
.helpq {
  margin-left:9px; width:21px; height:21px; padding:0; border-radius:50%;
  border:1px solid var(--line); background:transparent; color:var(--dim);
  font:inherit; font-size:12px; font-weight:600; line-height:1; cursor:pointer;
  vertical-align:middle;
}
.helpq:hover { border-color:var(--brand-mid); color:var(--brand-mid); }

/* The sound check. Usama, 21 Sep, after narrating ten minutes that turned out
   inaudible: "audio is busted, can't hear a word." A bar he can watch move is
   worth more than every setting on the page. */
.tvmeter {
  height:14px; border-radius:99px; background:var(--line-soft); overflow:hidden;
  margin:14px 0 6px;
}
.tvmeter.small { height:8px; margin:10px 0 4px; }
.tvmeter span {
  display:block; height:100%; width:0; border-radius:99px; background:var(--dim);
  transition:width .08s linear;
}
.tvmeter span.good { background:var(--live); }
.tvmeter span.hot { background:var(--warn); }
.tvverdict { margin:0 0 4px; font-size:12.5px; color:var(--muted); min-height:18px; }
.tvverdict.bad { color:var(--warn); font-weight:600; }
.tvtick { display:flex; align-items:center; gap:8px; margin:12px 0; font-size:13px; color:var(--muted); }
.tvtick input { width:auto; }
#tvGain { width:100%; }

/* Form controls inside the training sheets. Without this the microphone
   picker sits inline beside its own label and the buttons stack oddly — the
   sheets are not inside a .pad, so they miss those rules. */
.tvsheet label.fl { display:block; font-size:12.5px; color:var(--dim); margin:14px 0 5px; }
.tvsheet select, .tvsheet input[type=text] {
  width:100%; font:inherit; font-size:15px; padding:9px 11px;
  border:1px solid var(--line); border-radius:8px; background:var(--card, #fff); color:inherit;
}
.tvsheet .row { display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top:16px; }
.tvsheet .row button { width:auto; }

/* --- Recruitment ----------------------------------------------------------
   Usama, 21 Sep: two recruiters starting, a board shared with ops. The layout
   has one job — an unclaimed urgent town is the most useful thing here, so it
   is at the top and it looks like it. */

.rchead { display:flex; gap:10px; align-items:center; }
.rcbar {
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  flex-wrap:wrap; margin:0 0 18px;
}
.rccounts { display:flex; gap:18px; flex-wrap:wrap; }
.rcstat { font-size:12.5px; color:var(--dim); }
.rcstat b { font-size:19px; color:var(--text); margin-right:5px; font-variant-numeric:tabular-nums; }
.rcstat.want b { color:var(--signal); }
.rcstat.bad b { color:var(--warn); }
.rctabs { margin:0; }
.rctabs button { padding:6px 14px; font-size:13px; }

.rcgroup { margin:0 0 24px; }
.rcgroup h3 {
  display:flex; align-items:center; gap:8px; margin:0 0 10px;
  font-size:12px; letter-spacing:.05em; text-transform:uppercase; color:var(--dim); font-weight:600;
}
.rcn { color:var(--dim); font-weight:400; }
.rccards { display:grid; gap:9px; grid-template-columns:repeat(auto-fill, minmax(330px, 1fr)); }

.rccard {
  border:1px solid var(--line); border-radius:11px; padding:11px 13px;
  background:var(--card, #fff); position:relative;
}
.rccard::before {
  content:''; position:absolute; left:0; top:10px; bottom:10px; width:4px;
  border-radius:0 4px 4px 0; background:var(--line);
}
/* The four states, as colour. Nobody on it is the one that should catch an
   eye crossing the page — an unclaimed town is work nobody has started. */
.rccard.st-unclaimed::before { background:var(--signal); }
.rccard.st-working::before   { background:var(--claim, var(--brand-mid)); }
.rccard.st-part::before      { background:var(--brand-mid); }
.rccard.st-ready::before     { background:var(--live); }
.rccard.st-filled::before    { background:var(--live); }
.rccard.st-filled            { opacity:.72; }
.rccard.st-dropped::before   { background:var(--dim); }
.rccard.st-dropped           { opacity:.6; }
.rccard.late { border-color:#F0C9BE; }

.rctop { display:flex; align-items:center; gap:9px; flex-wrap:wrap; }
.rctop b { font-size:14.5px; }
.rcmeta { font-size:11.5px; color:var(--dim); margin-top:4px; }
.rcstate { color:var(--muted); font-weight:600; }
.rclate { display:block; color:var(--warn); font-weight:600; margin-top:3px; }
.rcnote {
  margin-top:7px; padding:7px 9px; border-radius:7px; font-size:12.5px;
  background:var(--soft, #F5F7FA); color:var(--muted);
}
.rcacts { display:flex; gap:14px; margin-top:8px; }

/* Coverage as pips, not a percentage. "1 of 2" is something somebody can act
   on; "50%" is something they have to translate first. */
.rccov { display:inline-flex; align-items:center; gap:3px; margin-left:auto; }
.rcpip {
  width:9px; height:9px; border-radius:50%; border:1.5px solid var(--line);
  background:transparent;
}
.rcpip.on { background:var(--live); border-color:var(--live); }
.rccovn { font-size:11px; color:var(--dim); margin-left:4px; font-variant-numeric:tabular-nums; }

/* One town */
#rcPanelBody h2 { display:flex; align-items:center; gap:9px; }
.rch3 { margin:20px 0 8px; font-size:13px; color:var(--muted); }
.rcrow2 { display:grid; grid-template-columns:1fr 1fr; gap:14px; margin-top:6px; }
.rcflat { margin:5px 0 0; font-size:14px; display:flex; gap:12px; align-items:center; }
.rceng { display:grid; gap:7px; }
.rcengrow {
  display:grid; grid-template-columns:1fr auto auto; gap:4px 12px; align-items:baseline;
  padding:9px 11px; border:1px solid var(--line); border-radius:9px; font-size:13px;
}
.rcengmeta { grid-column:1; font-size:11.5px; color:var(--dim); }
.rcrate { grid-row:1; font-size:12.5px; color:var(--live); font-weight:600; }
.rcadd { margin-top:14px; }
.rchits { display:grid; gap:5px; margin-top:7px; }
.rchit {
  display:flex; flex-direction:column; align-items:flex-start; gap:1px; text-align:left;
  padding:8px 10px; border:1px solid var(--line); border-radius:8px; background:transparent;
  font:inherit; color:inherit; cursor:pointer; width:100%;
}
.rchit:hover:not(:disabled) { border-color:var(--brand-mid); }
.rchit:disabled { opacity:.5; cursor:default; }
.rchit span { font-size:11.5px; color:var(--dim); }

.rcnotes { margin-top:12px; display:grid; gap:7px; }
.rcnoterow {
  display:grid; gap:2px; padding:8px 10px; border-left:2px solid var(--line);
  font-size:13px;
}
.rcnotewho { font-size:11px; color:var(--dim); }
.rcnotebody { white-space:pre-wrap; }
.rcbottom { margin-top:20px; }

.rccaution {
  margin:14px 0; padding:11px 13px; border-radius:9px; font-size:12.5px; line-height:1.6;
  background:var(--soft, #F5F7FA); border:1px solid var(--line); color:var(--muted);
}
.rctable { width:100%; border-collapse:collapse; font-size:13px; }
.rctable th, .rctable td { padding:8px 10px; border-bottom:1px solid var(--line-soft); text-align:left; }
.rctable th { font-size:11px; letter-spacing:.04em; text-transform:uppercase; color:var(--dim); }
.rctable .n { text-align:right; font-variant-numeric:tabular-nums; }
.rcurg { color:var(--signal); font-size:11px; font-weight:600; }
.rcsrc { display:grid; gap:5px; }
.rcsrcrow { font-size:13px; color:var(--muted); }
.rcsrcrow b { color:var(--text); font-variant-numeric:tabular-nums; margin-right:6px; }

@media (max-width: 620px) {
  .rcrow2 { grid-template-columns:1fr; }
  .rccards { grid-template-columns:1fr; }
}

/* --- The dashboard, arranged and coloured (21 Sep) -------------------------
   Usama: "make it colorful and adjustable, everyone can drag and drop card to
   adjust them as per their preference… at first glance it should explain what
   needs to be done where."

   COLOUR IS MEANING HERE, not decoration — the same rule the Signing screen
   was put right under on the same day, and the reason none of these derive
   from --acc-h. Somebody picking Rose in Appearance must not turn "running
   late" into "coming up next".

     RED     somebody must act now
     AMBER   blocked, waiting on a person
     GREEN   under way, or finished
     BLUE    ahead of us, nothing owed yet
     GREY    there to be read */

.dashcard { position:relative; transition:box-shadow .16s var(--ease), transform .16s var(--ease); }
/* A bar down the side rather than a tinted card. Tinting nine cards makes a
   screen nobody can read; one strong edge each makes them tellable apart from
   the other side of a desk. */
.dashcard::before {
  content:''; position:absolute; left:0; top:0; bottom:0; width:5px;
  background:var(--line); border-radius:var(--r-3) 0 0 var(--r-3);
}
.dashcard.t-late::before    { background:var(--warn); }
.dashcard.t-finding::before { background:var(--signal); }
.dashcard.t-now::before     { background:var(--live); }
.dashcard.t-out::before     { background:var(--brand-mid); }
.dashcard.t-next::before    { background:var(--brand-light); }
.dashcard.t-open::before    { background:var(--dim); }
.dashcard.t-sites::before   { background:var(--claim); }
.dashcard.t-done::before    { background:var(--live); opacity:.45; }
.dashcard.t-people::before  { background:var(--line); }

.dashhead { padding-left:19px; align-items:center; }
.dashhead h3 { font-size:13.5px; font-weight:700; letter-spacing:-.01em; flex:1; }
/* The count is the thing read from across a room, so it is allowed to be big
   and it is allowed to be the card's own colour. */
.dashcount {
  font-size:15px; font-weight:800; padding:1px 9px; min-width:26px;
  font-variant-numeric:tabular-nums;
}
.t-late    .dashcount { color:var(--warn);   background:var(--warn-soft); }
.t-finding .dashcount { color:var(--signal); background:var(--signal-soft); }
.t-now     .dashcount { color:var(--live);   background:var(--live-soft); }
.t-done    .dashcount { color:var(--live);   background:var(--live-soft); }
.t-open    .dashcount,
.t-people  .dashcount { color:var(--muted);  background:var(--glass-2, var(--panel)); }
/* Nought is not news. A zero in full colour makes an empty card shout as loudly
   as a full one, which is how a coloured dashboard stops meaning anything. */
.dashcard:not(.shout) .dashcount:not(:has(*)) { opacity:1; }

/* The grip and the hide, quiet until the card is under the pointer. */
.dashgrip {
  cursor:grab; color:var(--dim); font-size:15px; line-height:1; opacity:0;
  transition:opacity .14s var(--ease); user-select:none; margin-right:-2px;
}
.dashcard:hover .dashgrip { opacity:.75; }
.dashgrip:active { cursor:grabbing; }
.dashhide {
  border:0; background:none; color:var(--dim); font-size:17px; line-height:1;
  padding:0 2px; cursor:pointer; opacity:0; transition:opacity .14s var(--ease);
}
.dashcard:hover .dashhide { opacity:.6; }
.dashhide:hover { opacity:1; color:var(--warn); }
.dashcard.dragging { opacity:.5; transform:scale(.98); }
.dashcard.flash { box-shadow:0 0 0 3px var(--claim); }
.dashbacklist { padding:12px 15px 14px; display:grid; gap:6px; justify-items:start; }

/* --- the strip: what needs somebody, and where --------------------------- */
.dashstrip { display:flex; flex-wrap:wrap; gap:10px; margin:0 0 16px; }
.dashneed {
  display:flex; align-items:baseline; gap:9px; flex-wrap:wrap;
  padding:11px 15px; border-radius:var(--r-2); cursor:pointer;
  border:1px solid transparent; font:inherit; text-align:left;
}
.dashneed b { font-size:26px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums; }
.dashneedwhat { font-size:13.5px; font-weight:600; }
.dashneedwhere { font-size:12px; opacity:.85; flex-basis:100%; }
.dashneed.n-late    { background:var(--warn-soft);   color:var(--warn);   border-color:var(--warn); }
.dashneed.n-finding { background:var(--signal-soft); color:var(--signal); border-color:var(--signal); }
.dashneed.n-now     { background:var(--live-soft);   color:var(--live);   border-color:var(--live); }
.dashneed.n-out     { background:var(--accent-soft); color:var(--claim-deep); border-color:var(--accent-line); }
.dashneed:hover { filter:brightness(.97); }
.dashneed:active { transform:translateY(1px); }

/* The good outcome, and it should look like one rather than like a screen
   that failed to load. */
.dashclear {
  display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;
  padding:12px 16px; border-radius:var(--r-2);
  background:var(--live-soft); border:1px solid var(--live);
}
.dashclear b { color:var(--live); font-size:15px; font-weight:700; }
.dashclear span { color:var(--muted); font-size:13px; }

@media (max-width: 700px) {
  .dashgrip, .dashhide { opacity:.6; }     /* nothing hovers on a phone */
  .dashneed b { font-size:22px; }
}

/* --- Meet: minimising, big reactions, the sound check (21 Sep) -------------
   Usama: "if we minimise meeting its gone and you have to start a new one, so
   make it easy to locate how to bring it back to meeting", "reactions should
   be popped up on screen as a big one", "add option to users to set their own
   background", "give everyone option to test their mic and speakers." */

/* THE CALL IS STILL RUNNING. Everything is hidden except one panel, rather
   than torn down — the streams, the socket and the peers are all exactly where
   they were, and restoring is a class name coming off. */
.callroot.mini > *:not(.minibar) { display:none !important; }
.callroot.mini {
  inset:auto 18px 18px auto; width:auto; height:auto;
  background:none; box-shadow:none; backdrop-filter:none;
}
.minibar {
  display:flex; align-items:center; gap:10px;
  padding:9px 11px; border-radius:99px;
  background:#121A22; color:#E8EEF4;
  box-shadow:0 10px 34px rgba(0,0,0,.42); border:1px solid rgba(255,255,255,.12);
}
/* The way back, and it is the biggest thing in the panel on purpose. A call
   somebody cannot find their way back into is the bug this whole panel is
   here to fix. */
.miniback {
  display:flex; align-items:center; gap:8px; padding:8px 14px 8px 11px;
  border:0; border-radius:99px; cursor:pointer; font:inherit; font-size:13.5px;
  font-weight:600; background:var(--live); color:#fff;
}
.miniback:hover { filter:brightness(1.07); }
.miniwho { font-size:12.5px; color:#9FB0BF; }
.minitime { font-size:12.5px; color:#9FB0BF; font-variant-numeric:tabular-nums; }
/* A dot that keeps moving, so a minimised call never reads as a frozen one. */
.miniback::before {
  content:''; width:8px; height:8px; border-radius:50%; background:#fff;
  animation:minipulse 1.8s ease-in-out infinite;
}
@keyframes minipulse { 0%,100% { opacity:1; } 50% { opacity:.35; } }
@media (prefers-reduced-motion: reduce) { .miniback::before { animation:none; } }
.callbtn.small { width:34px; height:34px; }

/* --- a reaction, big ----------------------------------------------------- */
/* The float over a tile was too easy to miss: in a full room a tile is 180
   pixels wide, and half the time somebody is looking at a shared screen. */
.bigreact {
  position:fixed; left:50%; top:46%; z-index:130; pointer-events:none;
  display:flex; flex-direction:column; align-items:center; gap:6px;
  transform:translate(-50%, -50%);
  animation:bigreact 1.8s var(--ease) forwards;
  /* Stacked when several land at once, so three reactions read as three. */
  margin-left:calc((var(--slot, 0) - 1) * 170px);
}
.bigemoji {
  font-size:140px; line-height:1;
  filter:drop-shadow(0 12px 28px rgba(0,0,0,.45));
}
.bigwho {
  font-size:15px; font-weight:700; color:#fff;
  background:rgba(10,16,22,.72); border-radius:99px; padding:5px 16px;
}
@keyframes bigreact {
  0%   { opacity:0; transform:translate(-50%,-50%) scale(.4); }
  14%  { opacity:1; transform:translate(-50%,-50%) scale(1.12); }
  26%  { transform:translate(-50%,-50%) scale(1); }
  74%  { opacity:1; transform:translate(-50%,-58%) scale(1); }
  100% { opacity:0; transform:translate(-50%,-78%) scale(.92); }
}
@media (prefers-reduced-motion: reduce) {
  .bigreact { animation:bigreactquiet 1.8s linear forwards; }
  @keyframes bigreactquiet { 0%,85% { opacity:1; } 100% { opacity:0; } }
}
@media (max-width: 700px) {
  .bigemoji { font-size:92px; }
  .bigreact { margin-left:calc((var(--slot, 0) - 1) * 110px); }
}

/* --- the sound check ----------------------------------------------------- */
.soundcheck {
  position:absolute; bottom:calc(100% + 12px); left:50%; transform:translateX(-50%);
  width:min(360px, calc(100vw - 32px)); padding:14px 16px 16px;
  background:#121A22; color:#E8EEF4; border-radius:14px;
  border:1px solid rgba(255,255,255,.12); box-shadow:0 16px 44px rgba(0,0,0,.46);
  text-align:left;
}
.scthead { display:flex; align-items:center; justify-content:space-between; margin-bottom:10px; }
.scthead b { font-size:14px; }
.sclabel { display:block; font-size:11px; letter-spacing:.04em; text-transform:uppercase;
  color:#8DA0B1; margin:12px 0 5px; }
.soundcheck select {
  width:100%; font:inherit; font-size:13px; padding:8px 10px; border-radius:8px;
  background:#1B2632; color:#E8EEF4; border:1px solid rgba(255,255,255,.14);
}
.scmeter { height:12px; border-radius:99px; background:#1B2632; overflow:hidden; margin:10px 0 5px; }
.scmeter span {
  display:block; height:100%; width:0; border-radius:99px; background:#6E7F8F;
  transition:width .08s linear;
}
.scmeter span.good { background:var(--live); }
.scmeter span.hot { background:var(--warn); }
.scverdict { margin:0; font-size:12.5px; color:#9FB0BF; min-height:18px; }
.scverdict.good { color:var(--live); }
.scverdict.bad { color:var(--warn); }
.scplay { margin-top:10px; width:100%; }
.scnote { margin:8px 0 0; font-size:11.5px; color:#7E90A1; line-height:1.55; }

/* --- backgrounds: the new ones, and theirs ------------------------------- */
.bg-studio { background:linear-gradient(135deg,#F2F4F7,#C9D2DC); }
.bg-wood   { background:linear-gradient(135deg,#7A5233,#2B1B10); }
.bg-dusk   { background:linear-gradient(135deg,#6A4A7C,#1A1226); }
.bg-ink    { background:linear-gradient(135deg,#1F2933,#05070A); }
.bg-own    { background:#1B2632 center/cover no-repeat; }
.bgown { margin-top:10px; display:grid; gap:5px; }
.bgownnote { font-size:11px; color:#7E90A1; }

/* ==========================================================================
   20. The portal's own look
   ==========================================================================
   Usama, 22 Sep: "Style is something missing from the portal. Its missing at
   all, so work on that." Asked what he was seeing, he picked "It looks plain,
   no identity" — so this is not a rescue, it is the difference between a page
   that works and a product somebody recognises.

   Four things carry almost all of it, and the first is not decoration.

     THE RAIL is on every screen, so it IS the portal's face, and it was a flat
     column of twenty identical lines with a logo floating above it in white
     space. Twenty of anything in one list is not scannable; people learn the
     position of the four they use and never see the other sixteen. It is now
     in named sections, it scrolls in its own right rather than running off the
     bottom of a laptop screen, and it carries the brand.

     THE HEADERS opened every screen with a plain 17px word. They now have
     weight and a mark in the brand's own gradient — the same blue-to-teal as
     the logo — so a screen starts rather than just appearing.

     THE TABLES were sixteen columns of one-weight grey. Rhythm is what makes a
     wide table readable: a banded header, alternating rows, a hover that is
     actually visible, and money that lines up.

     WHAT IS DECORATION FOLLOWS THE ACCENT, so the person's own choice in
     Appearance reaches the chrome. What carries MEANING — live, warn, signal,
     night — does not, and nothing in this section touches those.
   ========================================================================== */

/* --- the rail ------------------------------------------------------------ */
/* The wash is --accent-soft rather than a hand-picked tint: every theme
   already defines one that suits it, so this follows both the theme and the
   accent without a table of five more colours to keep in step. */
.rail {
  background:linear-gradient(180deg, var(--accent-soft) 0, var(--panel) 210px);
  border-right:1px solid var(--line);
}

.brand { padding:15px 16px 12px; border-bottom:1px solid var(--line-soft); }
.brand .raillogo { width:132px; }
/* The number under the logo was rendering as a lone em dash whenever no
   WhatsApp line was linked, which is most of the time on a fresh install: a
   stray punctuation mark in the most prominent place on the page. */
.brand .railnum:empty { display:none; }

/* .nav was flex:1 with no min-height, so twenty buttons on a 768px laptop ran
   off the bottom of the rail and took the clocks and the profile block with
   them — they were simply unreachable, and had been for as long as there have
   been twenty. It scrolls now, and only as far as it has to. */
.nav {
  flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain;
  padding:6px 8px 12px; scrollbar-width:thin;
  /* The list is longer than any rail, so it has to cut off somewhere. Fading
     the last few pixels is the difference between "there is more below" and
     "this looks broken" — a half-drawn button at a hard edge reads as a
     rendering fault, which is exactly what it looked like. */
  -webkit-mask-image:linear-gradient(180deg, #000 0, #000 calc(100% - 22px), transparent 100%);
  mask-image:linear-gradient(180deg, #000 0, #000 calc(100% - 22px), transparent 100%);
}
.navgroup {
  padding:12px 10px 3px;
  font-size:9.5px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--dim);
  user-select:none;
}
.navgroup:first-child { padding-top:2px; }

.nav button[data-v], .nav .morebtn {
  border-radius:8px; padding:7px 10px; gap:10px; margin:0;
  position:relative;
  transition:background-color .13s var(--ease), color .13s var(--ease);
}
.nav button .navico { flex:none; opacity:.62; transition:opacity .13s var(--ease); }
.nav button:hover { background:var(--raised); color:var(--text); }
.nav button:hover .navico { opacity:.95; }
/* The selected tab was a faint box. It is now a tinted pill with the brand
   bar on the rail's own edge, which is why the ::before reaches back out
   through the rail's padding rather than using the old inset shadow. */
.nav button[data-v].sel {
  background:var(--accent-soft); color:var(--claim); font-weight:650;
  box-shadow:none;
}
.nav button[data-v].sel .navico { opacity:1; }
.nav button[data-v].sel::before {
  content:""; position:absolute; left:-8px; top:50%; transform:translateY(-50%);
  width:3px; height:20px; border-radius:0 3px 3px 0; background:var(--claim);
}

/* --- the headers --------------------------------------------------------- */
/* The mark is the logo's own gradient, at the size of the word it sits
   against, and it is the only place the brand gradient appears in the content
   area — which is what keeps it a signature rather than a pattern. */
.opstitle h2, .padhead h2, .colhead h2.big {
  position:relative; padding-left:14px;
  font-size:19px; font-weight:700; letter-spacing:-.022em;
}
.opstitle h2::before, .padhead h2::before, .colhead h2.big::before {
  content:""; position:absolute; left:0; top:.14em; bottom:.14em;
  width:4px; border-radius:3px;
  background:linear-gradient(180deg, var(--brand-mid), var(--claim));
}
.opshead { padding:20px 22px 12px; }

/* --- the tables ---------------------------------------------------------- */
/* color-mix keeps the stripe honest across eight themes without eight more
   tokens: a browser too old to read it simply gets no stripe, which is what
   the table looked like yesterday. */
.opstable thead th {
  background:var(--raised);
  padding:10px 9px;
  font-size:9.5px; font-weight:700; letter-spacing:.075em; text-transform:uppercase;
  color:var(--muted);
  border-bottom:1px solid var(--line);
}
.opstable tbody tr:nth-child(even) {
  background:color-mix(in srgb, var(--text) 3%, transparent);
}
.opstable tbody tr:hover,
.opstable tbody tr:nth-child(even):hover {
  background:var(--accent-soft);
}
/* Money reads down the column or it does not read at all. */
.opstable td.num, .opstable td.money { font-variant-numeric:tabular-nums; }
/* An empty cell is an absence, not a value, and sixteen columns of dashes at
   full strength is most of what made this table look like noise. */
.opstable td:empty::after { content:"—"; color:var(--dim); }

/* --- phones -------------------------------------------------------------- */
/* The rail is a bottom bar here, laid out in a row. Section headings in a row
   of buttons are five more things competing for a 400px strip, and the nav
   must not scroll in its own box when the box IS the bar. */
@media (max-width:860px) {
  .navgroup { display:none; }
  .nav { overflow:visible; padding:0; -webkit-mask-image:none; mask-image:none; }
  .nav button[data-v].sel::before { display:none; }
  .rail { background:var(--panel); }
}

/* --- the foot of the rail ------------------------------------------------ */
/* Which block gives way when the window is short. Both the nav and the clocks
   were shrinkable, so a 950px window cut a clock in half — and half a row of
   anything reads as a rendering fault, not as "scroll for more". The clocks
   hold their size and the nav, which already fades and scrolls properly,
   absorbs all of it. */
.worldclocks { flex:0 0 auto; }
.clocklist {
  max-height:200px;
  -webkit-mask-image:linear-gradient(180deg, #000 0, #000 calc(100% - 14px), transparent 100%);
  mask-image:linear-gradient(180deg, #000 0, #000 calc(100% - 14px), transparent 100%);
}

/* Four full-width bordered boxes at the foot of the rail carried as much
   visual weight as the twenty buttons above them, for settings somebody
   touches twice a year. Quiet text, and the box only on hover. */
.notifybtn {
  margin:0 8px 1px; padding:6px 10px; border-radius:8px;
  border:1px solid transparent; background:transparent;
  color:var(--dim); font-size:11.5px; text-align:left;
}
.notifybtn:hover { background:var(--raised); border-color:transparent; color:var(--text); }
/* The theme swatch is a pale gradient, so against a pale rail it was reading
   as an empty checkbox rather than as a colour. */
.themebtnsw i { border:1px solid var(--line); box-sizing:border-box; }

/* The help "?" sat on every screen heading as an unstyled question mark: its
   border is --line, which against the page is all but invisible. It is a
   button, so it should look like one. */
.helpq {
  border-color:transparent; background:var(--raised); color:var(--muted);
}
.helpq:hover { background:var(--accent-soft); border-color:transparent; color:var(--claim); }

/* --- where the table ends ------------------------------------------------ */
/* Six rows in a 900px pane left 350px of nothing and the screen looked
   unfinished. The table gets a foot, so it ends on purpose. */
.opstable { margin-bottom:0; }
.opsscroll::after {
  content:""; display:block; height:1px; margin-top:0;
  background:linear-gradient(90deg, var(--line), transparent 70%);
}

/* --- the controls the browser was drawing -------------------------------- */
/* Three screens style their own selects and the rest were left to the
   platform, so Accounting put a grey Windows dropdown next to a set of
   hand-made filter chips — which is most of what "no identity" looks like up
   close: not ugly, just obviously two different programs. An element selector,
   so any screen that already styles its own still wins on specificity.
   The chevron is inlined rather than fetched; it currentColor-matches by being
   drawn in the muted grey every theme shares closely enough. */
select {
  appearance:none; -webkit-appearance:none;
  padding:8px 30px 8px 11px;
  font:inherit; font-size:12.5px; color:var(--text);
  background-color:var(--bg);
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238A9AAB' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 10px center;
  border:1px solid var(--line); border-radius:var(--r-2);
  cursor:pointer;
}
select:hover { border-color:var(--accent-line); }
select:focus { outline:none; border-color:var(--claim); box-shadow:0 0 0 3px var(--accent-soft); }
/* A dark theme needs the list itself readable too, or the popup is a white
   sheet in the middle of a dark screen. */
select option { background:var(--panel); color:var(--text); }
/* Multi-line selects are lists, not dropdowns: the chevron and the single-row
   padding are both wrong for them. */
select[multiple], select[size]:not([size="1"]) {
  background-image:none; padding:6px; cursor:default;
}

/* --- a touch more of the rail on a laptop -------------------------------- */
/* Five headings cost about a hundred pixels, which on a 950px window is two
   or three buttons pushed below the fold. Worth most of it back. */
.nav button[data-v], .nav .morebtn { padding:6px 10px; }
.navgroup { padding:10px 10px 2px; }

/* The chevron, applied as a longhand and last. The glass themes set
   `background:` as a SHORTHAND on select, which resets background-image to
   none — so the rule above drew a rounded box with no indicator on every
   screen using the default theme, which is most of them. Longhands here, and
   the glass selectors named explicitly so this matches their specificity and
   wins on order. */
select,
[data-glass="day"] select,
[data-glass="night"] select {
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238A9AAB' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 10px center;
}
/* Lighter ink for it where the control is dark. */
[data-theme="dark"] select,
[data-theme="midnight"] select,
[data-tone="dim"] select,
[data-glass="night"] select {
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239BABBA' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
select[multiple], select[size]:not([size="1"]) { background-image:none; }

/* --- the rail wash, under glass ------------------------------------------ */
/* The wash above was dead on the DEFAULT theme. [data-glass] gives .rail a
   translucent panel of its own, at higher specificity, so the one screen
   almost everybody actually looks at was the one screen that never got the
   brand tint. Named explicitly, and layered over the glass rather than
   replacing it, or the rail stops being translucent and the whole treatment
   falls apart at the edge. */
[data-glass="day"] .rail,
[data-glass="night"] .rail {
  background-image:linear-gradient(180deg, var(--accent-soft) 0, transparent 210px);
}

/* --- the section headings ------------------------------------------------ */
/* --dim is the colour for things that do not matter, and against the tinted
   rail it came out at 2.4:1 on High contrast and 2.9 on Glass. A heading that
   cannot be read is not a heading. */
.navgroup { color:var(--muted); }

/* --- the last two dropdowns the platform still owned --------------------- */
/* Every select in the portal now shares a radius. These two were 4px and 6px
   from long before any of this, which is invisible on their own screen and
   obvious the moment the rest agree. */
.rowform select, .sitepsel select, select.sitepsel { border-radius:var(--r-1); }

/* --- the call must not be a grid item ------------------------------------ */
/* Usama, 22 Sep, with a call minimised and Useful links open: the whole screen
   drew inside the 188px rail column.
   #callmount is a direct child of #app, which is
   `display:grid; grid-template-columns:188px 1fr`. styles.css hides it with
   `#callmount:empty{display:none}`, so while there is no call it is not a grid
   item and nothing is wrong. Mount a call and it becomes one — taking row 1,
   column 2 — and EVERY VIEW DECLARED AFTER IT in app.html is pushed to row 2,
   column 1: Useful links, Dashboard, Operations, Accounting, Signing, Payroll,
   Engineers, Projects, Recruitment, Training. Most of the portal.
   It never showed before because a call covered the screen and there was
   nothing to browse; minimising is what made it reachable.
   display:contents makes the wrapper generate no box at all, so it can never
   take a cell however full it is. What is inside is position:fixed and out of
   flow, so it does not take one either. */
#callmount { display:contents; }
/* Stated rather than inherited: if this ever stopped being fixed it would
   become a grid item itself and put the same fault back. */
.callroot, .callroot.mini { position:fixed; }

/* --- the recruitment site sheet ------------------------------------------ */
/* Usama, 22 Sep: "there is no option to add priority or save the changes and
   close this popup?" The only way out was clicking the dark area outside the
   panel, which nothing on the screen said. */
.rcx {
  position:absolute; top:12px; right:14px;
  width:30px; height:30px; border-radius:50%;
  border:1px solid var(--line); background:var(--bg); color:var(--muted);
  font-size:20px; line-height:1; cursor:pointer;
}
.rcx:hover { background:var(--warn-soft); border-color:var(--warn); color:var(--warn); }
#rcPanel .panel { position:relative; }
/* The new field takes the full width under the two-up row rather than
   squeezing a third column into a panel this narrow. */
.rcwide { grid-column:1 / -1; }
.rcbottom { display:flex; align-items:center; gap:10px; }
.rcdone { margin-left:auto; }
/* Said plainly, because there is no Save button and there should not be: every
   control here writes as it changes. Leaving that unsaid is what made somebody
   look for one. */
.rcsaved { margin:8px 0 0; font-size:11.5px; color:var(--dim); }

/* --- the host's controls on a roster row --------------------------------- */
/* Usama, 22 Sep: "give me option to kick someone out of meeting as well. or
   mute their mic and video." Shown only to the host, and only on somebody
   else's row. There is deliberately no button that turns a microphone or a
   camera back ON — see renderRoster in meet.js. */
.rosteracts { display:flex; align-items:center; gap:3px; margin-left:auto; flex:none; }
.rosteract {
  display:flex; align-items:center; justify-content:center;
  width:28px; height:28px; padding:0; border-radius:7px; cursor:pointer;
  border:1px solid transparent; background:transparent;
  color:#8FA3B5; font-size:17px; line-height:1;
  opacity:.55; transition:opacity .12s var(--ease), background-color .12s var(--ease);
}
.rosterrow:hover .rosteract, .rosteract:focus-visible { opacity:1; }
.rosteract svg { width:16px; height:16px; }
.rosteract:hover { background:rgba(255,255,255,.09); color:#E8EEF4; }
/* Removing somebody is the one that cannot be undone from here, so it is the
   one that looks different. */
.rosteract.drop:hover { background:rgba(240,112,92,.18); color:#F0705C; }

/* ==========================================================================
   21. Smooth — motion, depth, and the dashboard's week
   ==========================================================================
   Usama, 23 Sep: "Improve UI, colors, themes, fonts, overall responsiveness of
   portal, icons, dashboard graphics, design. I want to see it smooth, stylish,
   good-looking and smooth." Smooth twice. Yesterday's pass gave the portal an
   identity; this one gives it motion and depth, and gives the dashboard the
   graphics it never had.

   THE RULE FOR MOTION: it explains, it never decorates. A card rises in
   because it has just arrived; a row lifts on hover because it is what you
   are about to press; a badge pulses because its number just changed. Nothing
   moves for the sake of moving, everything is under 300ms, and all of it is
   off for anybody who has asked their OS for less motion.
   ========================================================================== */

:root {
  --dur-1:.14s; --dur-2:.22s; --dur-3:.34s;
  --ease-out:cubic-bezier(.22,.8,.26,1);
  --ease-spring:cubic-bezier(.34,1.4,.44,1);
  --lift:0 10px 28px -10px rgba(20,32,44,.22), 0 2px 6px rgba(20,32,44,.06);
}
[data-theme="dark"], [data-theme="midnight"] {
  --lift:0 12px 32px -10px rgba(0,0,0,.6), 0 2px 6px rgba(0,0,0,.3);
}

/* --- cards: depth, a lift, and a way in ------------------------------------ */
.dashcard {
  border-radius:14px;
  box-shadow:var(--sh-1);
  transition:transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out),
    border-color var(--dur-2) var(--ease-out);
  /* Cards arrive rather than appear. Staggered by --i, set in renderDashboard,
     so a board of nine reads as a deal rather than a slam. */
  animation:cardin var(--dur-3) var(--ease-out) both;
  animation-delay:calc(var(--i, 0) * 32ms);
}
.dashcard:hover { transform:translateY(-2px); box-shadow:var(--lift); }
/* Not while being dragged: a lift under a drag fights the drag image. */
.dashcard.dragging, .dashcard.dragging:hover { transform:none; box-shadow:var(--sh-1); }
@keyframes cardin { from { opacity:0; transform:translateY(8px); } }

/* The tone bar was a 3px sliver on the left edge, easy to miss on a busy
   board. It is now the head of the card: a soft wash of the tone across the
   header, and the count wears the tone too. Same meaning, three times the
   presence. */
.dashcard .dashhead {
  border-radius:14px 14px 0 0;
  background:linear-gradient(90deg, var(--tone-soft, var(--raised)) 0, var(--raised) 70%);
  border-bottom:1px solid var(--line-soft);
}
.dashcard .dashcount {
  min-width:28px; height:26px; padding:0 9px; border-radius:99px;
  display:inline-flex; align-items:center; justify-content:center;
  font-weight:750; font-size:13px; letter-spacing:-.01em;
  background:var(--tone-ink, var(--claim)); color:#fff;
  transition:transform var(--dur-1) var(--ease-spring);
}
.dashcard .dashcount.bump { animation:countbump .5s var(--ease-spring); }
@keyframes countbump { 40% { transform:scale(1.28); } }

/* The tones, as tokens the card reads — the same nine the side bar already
   uses, so the head wash, the count and the bar can never disagree. Meaning
   colours for the cards that carry meaning; the accent only where a card is
   navigation rather than news. */
.dashcard.t-late    { --tone-ink:var(--warn);        --tone-soft:var(--warn-soft); }
.dashcard.t-finding { --tone-ink:var(--signal);      --tone-soft:var(--signal-soft); }
.dashcard.t-now     { --tone-ink:var(--live);        --tone-soft:var(--live-soft); }
.dashcard.t-done    { --tone-ink:var(--live);        --tone-soft:var(--live-soft); }
/* The side bar keeps its lighter blues to tell these two apart; the pill
   does not, because white on --brand-light is 2.4:1 and a count nobody can
   read is not a count. Both pills wear --claim, which clears AA everywhere. */
.dashcard.t-out     { --tone-ink:var(--claim); --tone-soft:var(--accent-soft); }
.dashcard.t-next    { --tone-ink:var(--claim); --tone-soft:var(--accent-soft); }
.dashcard.t-sites   { --tone-ink:var(--claim);       --tone-soft:var(--accent-soft); }
.dashcard.t-open    { --tone-ink:var(--dim);         --tone-soft:var(--raised); }
.dashcard.t-people  { --tone-ink:var(--muted);       --tone-soft:var(--raised); }
.dashcard.t-people .dashcount, .dashcard.t-open .dashcount {
  background:var(--raised); color:var(--muted); border:1px solid var(--line);
}

/* --- an empty card still looks designed ----------------------------------- */
/* Six of nine cards said "Nothing…" in grey on a quiet morning, and the
   screen read as broken rather than calm. A small mark and a lighter line
   make an empty card a deliberate state. */
.dashempty {
  display:flex; align-items:center; gap:10px;
  padding:14px 16px; margin:0;
  color:var(--dim); font-size:12.5px;
}
.dashempty::before {
  content:""; flex:none; width:22px; height:22px; border-radius:50%;
  background:var(--raised); border:1px solid var(--line-soft);
  /* A tick, drawn in the dim ink: nothing to do here. */
  -webkit-mask:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='m6 12.5 4 4 8-9'/></svg>") center/14px no-repeat, linear-gradient(#000,#000);
  -webkit-mask-composite:xor; mask-composite:exclude;
  background:var(--line);
}

/* --- rows lift, buttons press ---------------------------------------------- */
.dashrow, .rccard, .tvcard, .meetrow, .invrow {
  transition:transform var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out),
    box-shadow var(--dur-1) var(--ease-out);
}
.rccard:hover, .tvcard:hover { transform:translateY(-2px); box-shadow:var(--lift); }
.opstable tbody tr { transition:background-color var(--dur-1) var(--ease-out); }

/* A badge whose number just changed says so, once. */
.pill.bump { animation:countbump .5s var(--ease-spring); }

/* Views slide in a touch further and settle, rather than the old 4px fade. */
.view.on { animation:viewin var(--dur-3) var(--ease-out) both; }
@keyframes viewin { from { opacity:0; transform:translateY(6px); } }

/* --- loading looks like loading ------------------------------------------- */
/* "Loading…" in grey text is what a broken page says too. A shimmer is
   unambiguous, and it holds the shape of what is coming. */
.skeleton, .skel {
  position:relative; overflow:hidden; border-radius:8px;
  background:var(--raised); color:transparent !important; min-height:14px;
}
.skeleton::after, .skel::after {
  content:""; position:absolute; inset:0;
  background:linear-gradient(90deg, transparent 0, rgba(255,255,255,.35) 50%, transparent 100%);
  transform:translateX(-100%); animation:shimmer 1.3s ease-in-out infinite;
}
[data-theme="dark"] .skeleton::after, [data-theme="midnight"] .skeleton::after,
[data-theme="dark"] .skel::after, [data-theme="midnight"] .skel::after {
  background:linear-gradient(90deg, transparent 0, rgba(255,255,255,.07) 50%, transparent 100%);
}
@keyframes shimmer { to { transform:translateX(100%); } }

/* --- the week at a glance -------------------------------------------------- */
.dashweek {
  display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 1.5fr); gap:14px; align-items:stretch;
  margin:0 0 16px;
  animation:cardin var(--dur-3) var(--ease-out) both;
}
/* Two figures since 23 Sep — hours on site and the completion rate were taken
   off at Usama's request. Stacked, so they fill the height beside the chart. */
.wktiles { display:grid; grid-template-columns:1fr; gap:10px; align-content:stretch; }
.wktile {
  padding:14px 16px 12px; border-radius:14px;
  background:var(--panel); border:1px solid var(--line); box-shadow:var(--sh-1);
  display:flex; flex-direction:column; gap:2px; min-width:0;
  transition:transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.wktile:hover { transform:translateY(-2px); box-shadow:var(--lift); }
/* The hero number. Big, tabular, in the text ink — a figure is not a series. */
.wktile b {
  font-size:30px; font-weight:800; letter-spacing:-.035em; line-height:1;
  font-variant-numeric:tabular-nums; color:var(--text);
}
.wktile b small { font-size:16px; font-weight:700; color:var(--muted); margin-left:1px; }
.wktile span { font-size:11.5px; color:var(--muted); font-weight:500; letter-spacing:.01em; }

.wkchart {
  position:relative; padding:14px 16px 10px; border-radius:14px; min-width:0;
  background:var(--panel); border:1px solid var(--line); box-shadow:var(--sh-1);
  display:flex; flex-direction:column;
}
.wkhead {
  display:flex; align-items:baseline; justify-content:space-between; gap:10px;
  margin-bottom:8px;
  font-size:11px; font-weight:750; letter-spacing:.07em; text-transform:uppercase; color:var(--muted);
}
/* Two states of one series, not two series: booked is the pale bar, completed
   is the solid part of it. Said in words beside the chart, so the pale/solid
   difference is never colour alone. */
.wkkey { font-weight:600; letter-spacing:0; text-transform:none; font-size:11px; color:var(--dim);
  display:inline-flex; align-items:center; gap:5px; }
.wkkey i { display:inline-block; width:10px; height:10px; border-radius:3px; margin-left:8px; }
.wkkey i:first-child { margin-left:0; }
.wkkeydone { background:var(--claim); }
.wkkeybooked { background:var(--accent-line); }
/* The plot: seven columns, bars as percent heights against the busiest day.
   A fixed height, so the band is a band and not a wall. */
.wkplot {
  display:grid; grid-template-columns:repeat(7, minmax(0, 1fr)); gap:6px;
  height:118px; align-items:end;
}
.wkday {
  display:flex; flex-direction:column; align-items:stretch; height:100%;
  border-radius:8px; outline:none; cursor:default;
  transition:background-color var(--dur-1) var(--ease-out);
}
.wkday:hover, .wkday:focus-visible { background:var(--accent-soft); }
.wkbars { position:relative; flex:1; min-height:0; margin:0 4px; }
.wkbooked, .wkdone {
  position:absolute; left:0; right:0; bottom:0;
  border-radius:4px 4px 0 0;
  transform-origin:50% 100%;
  animation:wkgrow var(--dur-3) var(--ease-out) both;
  transition:height var(--dur-3) var(--ease-out);
}
/* One series, two states: booked is the pale bar, completed the solid part
   of it, drawn on top and anchored to the same baseline. Said in words in the
   key, so pale/solid is never colour alone. */
.wkbooked { background:var(--accent-line); }
.wkdone { background:var(--claim); }
.wkday:hover .wkbooked { background:var(--brand-light); }
/* A day with nothing is a hairline on the baseline — zero, not missing. */
.wkzero { position:absolute; left:0; right:0; bottom:0; height:2px; border-radius:2px; background:var(--line); }
.wkval {
  position:absolute; top:5px; left:0; right:0; text-align:center;
  font-size:11px; font-weight:750; color:#fff; pointer-events:none;
  font-variant-numeric:tabular-nums;
}
/* On a pale bar the number sits on pale, so it wears the accent ink instead. */
.wkbooked .wkval { color:var(--claim); }
.wklab {
  flex:none; padding-top:6px; text-align:center;
  font-size:11px; font-weight:600; color:var(--muted); letter-spacing:.01em;
}
.wkday.today .wklab { color:var(--claim); font-weight:750; }
@keyframes wkgrow { from { transform:scaleY(0); } }
.wktip {
  position:absolute; top:34px; transform:translateX(-50%);
  padding:7px 10px; border-radius:9px; pointer-events:none; z-index:3;
  background:var(--text); color:var(--bg); font-size:11.5px; line-height:1.35;
  box-shadow:var(--sh-2); white-space:nowrap;
  display:flex; flex-direction:column; gap:1px;
}
.wktip b { font-weight:700; }
.wktip span { opacity:.85; }

@media (max-width:1100px) {
  .dashweek { grid-template-columns:1fr; }
}
@media (max-width:600px) {
  .wktiles { grid-template-columns:1fr 1fr; }
  .wktile b { font-size:24px; }
}

/* --- the rail's icons wake up --------------------------------------------- */
.nav button .navico { transition:opacity var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-spring); }
.nav button:hover .navico { transform:translateX(1px); }
.nav button[data-v].sel .navico { transform:scale(1.08); }

/* --- and all of it stands down for anybody who asked ----------------------- */
@media (prefers-reduced-motion: reduce) {
  .dashcard, .dashweek, .view.on, .wkbooked, .wkdone { animation:none !important; }
  .dashcard, .wktile, .rccard, .tvcard, .dashrow, .nav button .navico { transition:none !important; }
  .dashcard:hover, .wktile:hover, .rccard:hover, .tvcard:hover { transform:none; }
  .dashcount.bump, .pill.bump { animation:none; }
  .skeleton::after, .skel::after { animation:none; }
}

/* --- every data table, one treatment --------------------------------------- */
/* Yesterday's banded header and zebra went on .opstable, which is three of the
   portal's eight tables. Engineers, Signing, the rates card, the attendance day
   sheet and the rest were still bare — so the portal had two kinds of table,
   which is most of what "not stylish" looks like when you look closely. One
   rule for all of them. */
.dirtable thead th, .sgtable thead th, .ratetable thead th, .daytable thead th {
  position:sticky; top:0; z-index:2;
  background:var(--raised);
  padding:10px 12px;
  font-size:9.5px; font-weight:700; letter-spacing:.075em; text-transform:uppercase;
  color:var(--muted); text-align:left; white-space:nowrap;
  border-bottom:1px solid var(--line);
}
.dirtable tbody tr, .sgtable tbody tr, .ratetable tbody tr, .daytable tbody tr {
  transition:background-color var(--dur-1) var(--ease-out);
}
.dirtable tbody tr:nth-child(even), .sgtable tbody tr:nth-child(even),
.ratetable tbody tr:nth-child(even), .daytable tbody tr:nth-child(even) {
  background:color-mix(in srgb, var(--text) 3%, transparent);
}
.dirtable tbody tr:hover, .sgtable tbody tr:hover, .ratetable tbody tr:hover, .daytable tbody tr:hover,
.dirtable tbody tr:nth-child(even):hover, .sgtable tbody tr:nth-child(even):hover,
.ratetable tbody tr:nth-child(even):hover, .daytable tbody tr:nth-child(even):hover {
  background:var(--accent-soft);
}
.dirtable td, .sgtable td, .ratetable td, .daytable td { padding:11px 12px; vertical-align:middle; }

/* --- a person is a face, not a line ---------------------------------------- */
/* The directory was a column of bold names. An initials avatar, hued per
   person the way the shift chips already are, is what makes 117 rows a list
   of people rather than a spreadsheet. */
.engwho { display:flex; align-items:center; gap:11px; min-width:0; }
.engwho .avatar { flex:none; }
.engwhotx { min-width:0; display:flex; flex-wrap:wrap; align-items:center; gap:4px 6px; }
.avatar.sm { width:30px; height:30px; font-size:11.5px; border-radius:9px; }

/* --- row actions are buttons, not stacked words ---------------------------- */
/* "Call" over "Edit" as two text links took two lines in every row and read
   as body text. A pair of icon buttons is one line, is obviously pressable,
   and appears when the row is what you are looking at. */
td.rowacts { white-space:nowrap; }
td.rowacts > * { vertical-align:middle; }
.rowacts .rowact + .rowact { margin-left:4px; }
/* Side by side whatever the column width: the old stacked pair was two lines
   in every row. */
td.rowacts { display:table-cell; }
.dirtable td.rowacts { width:84px; text-align:right; }
.rowact {
  display:inline-flex; align-items:center; justify-content:center;
  width:30px; height:30px; margin-left:4px; padding:0;
  border-radius:8px; border:1px solid transparent; background:transparent;
  color:var(--muted); cursor:pointer;
  opacity:.55; transition:opacity var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out),
    color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-spring);
}
tr:hover .rowact, .rowact:focus-visible { opacity:1; }
.rowact:hover { background:var(--accent-soft); color:var(--claim); transform:scale(1.06); }
.rowact svg { width:15px; height:15px; }
@media (prefers-reduced-motion: reduce) { .rowact { transition:none; } .rowact:hover { transform:none; } }


/* ===== 22. KPI & bonuses ==================================================
   One table, one row a person. The colour on a score is the band it landed
   in — meaning, never the accent — and a group with nothing to measure is a
   dash, not a zero in red. Bars are thin and end square at the baseline; the
   number beside each bar is what is read, the bar is what is compared. */
.kpitiles { display:flex; flex-wrap:wrap; gap:8px; margin:0 0 12px; }
.kpitile {
  display:flex; flex-direction:column; gap:2px; min-width:118px;
  padding:10px 13px; border-radius:var(--r-2); background:var(--panel);
  border:1px solid var(--line);
}
.kpitile b { font-size:18px; font-weight:650; font-variant-numeric:tabular-nums; color:var(--text); }
.kpitile span { font-size:11.5px; color:var(--muted); }
.kpitile.t-a { border-color:color-mix(in srgb, var(--live) 40%, var(--line)); }
.kpitile.t-r { border-color:color-mix(in srgb, var(--warn) 45%, var(--line)); }
.kpinote { margin:0 0 10px; font-size:12.5px; color:var(--muted); }

.kpitable td { vertical-align:middle; }
.kpitable tbody tr { cursor:pointer; }
.kpig { display:inline-flex; align-items:center; gap:7px; min-width:72px; font-variant-numeric:tabular-nums; }
.kpig i {
  display:block; width:44px; height:5px; border-radius:2px; background:var(--raised);
  position:relative; overflow:hidden;
}
.kpig i::after {
  content:''; position:absolute; left:0; top:0; bottom:0; width:var(--w, 0%);
  background:var(--tone-ink, var(--muted)); border-radius:0 2px 2px 0;
}
.kpig b { font-weight:600; font-size:12.5px; color:var(--text); }
.kpig.t-none { color:var(--dim); }

.t-a { --tone-ink:var(--live); --tone-soft:var(--live-soft); }
.t-b { --tone-ink:var(--claim); --tone-soft:var(--accent-soft); }
.t-c { --tone-ink:var(--signal); --tone-soft:var(--signal-soft); }
.t-r { --tone-ink:var(--warn); --tone-soft:var(--warn-soft); }
.t-none { --tone-ink:var(--dim); --tone-soft:var(--raised); }

.kpiscore {
  display:inline-block; min-width:38px; padding:3px 8px; border-radius:999px; text-align:center;
  font-weight:650; font-variant-numeric:tabular-nums; font-size:13px;
  background:var(--tone-soft); color:var(--tone-ink);
}
.kpiscore.big { font-size:26px; padding:6px 16px; min-width:64px; }
.kpiscore.sm { font-size:12px; min-width:32px; padding:2px 7px; }
.kpiband {
  display:inline-block; padding:2px 8px; border-radius:999px; font-size:11.5px; font-weight:600;
  background:var(--tone-soft); color:var(--tone-ink); white-space:nowrap;
}
.kpiflag {
  display:inline-block; padding:2px 7px; border-radius:6px; font-size:11px;
  background:var(--signal-soft); color:var(--signal); white-space:nowrap;
}
.kpiflag.ded { background:var(--warn-soft); color:var(--warn); }

/* the card */
.kpichead { display:flex; justify-content:space-between; align-items:flex-start; gap:16px; }
.kpibig { display:flex; flex-direction:column; align-items:flex-end; gap:6px; }
.kpiflags { margin:12px 0 4px; display:grid; gap:6px; }
.kpiflagline {
  padding:8px 11px; border-radius:8px; font-size:12.5px; line-height:1.5;
  background:var(--signal-soft); color:var(--signal);
}
.kpiflagline.ded { background:var(--warn-soft); color:var(--warn); }
.kpigroup.quiet { opacity:.6; }
.kpigw { font-weight:400; color:var(--dim); margin-left:6px; }
.kpimetrics td { vertical-align:top; }
.kpimetrics tr.none td { color:var(--dim); }
.kpimname { min-width:200px; }
.kpimname b { display:block; font-weight:600; }
.kpihow { display:block; font-size:11.5px; color:var(--dim); line-height:1.45; margin-top:2px; max-width:420px; }
.kpimnote { display:block; font-size:11.5px; color:var(--signal); margin-top:3px; }
.kpimval { white-space:nowrap; font-weight:600; }
.kpimetrics td.dim { white-space:nowrap; }
.kpiwhy { font-size:12px; color:var(--muted); min-width:160px; }

.kpiadjs { display:grid; gap:6px; margin:8px 0 10px; }
.kpiadj {
  display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px; padding:8px 11px;
  border-radius:8px; border:1px solid var(--line); font-size:12.5px;
}
.kpiadj.done { background:var(--live-soft); border-color:transparent; }
.kpiadjkind { font-size:11px; font-weight:600; padding:2px 7px; border-radius:6px; background:var(--accent-soft); color:var(--claim); }
.kpiadjkind.deduction { background:var(--warn-soft); color:var(--warn); }
.kpiadjwhy { flex:1 1 200px; }
.kpiadjdone { font-size:11.5px; color:var(--live); font-weight:600; }
.kpiadjform { display:grid; grid-template-columns:120px 120px 80px 1fr auto; gap:8px; align-items:center; }
.kpiadjform input, .kpiadjform select {
  padding:8px 10px; font-size:13px; border:1px solid var(--line); border-radius:var(--r-1);
  background:var(--bg); color:var(--text);
}
.kpisla { margin-top:16px; }
.kpisla summary { cursor:pointer; font-size:13px; color:var(--muted); }
.kpislat { margin-top:8px; font-size:12.5px; }
.kpiweights { display:flex; flex-wrap:wrap; gap:10px; }
.kpiweights .fieldlab input { width:70px; }
.kpibands input, .kpitargets input { width:74px; text-align:right; padding:6px 8px; border:1px solid var(--line); border-radius:var(--r-1); background:var(--bg); color:var(--text); }

@media (max-width: 720px) {
  .kpiadjform { grid-template-columns:1fr 1fr; }
  .kpitable th:nth-child(n+2):nth-child(-n+6), .kpitable td:nth-child(n+2):nth-child(-n+6) { display:none; }
  .kpichead { flex-direction:column; }
  .kpibig { align-items:flex-start; flex-direction:row; }
}


/* ===== 23. Handing a town out from where it is seen =======================
   The picker that used to live only inside the site sheet, on the card and
   the dashboard row. */
.rcacts { align-items:center; flex-wrap:wrap; }
.rcassign { display:inline-flex; align-items:center; gap:7px; font-size:12px; color:var(--muted); }
.rcassign select {
  font-size:12.5px; padding:4px 26px 4px 9px; border-radius:var(--r-1);
  border:1px solid var(--line); background-color:var(--bg); color:var(--text); max-width:190px;
}
.rcassign select:focus { outline:none; border-color:var(--accent-line); box-shadow:0 0 0 3px var(--accent-soft); }
.siteowner { max-width:170px; padding-right:26px; text-overflow:ellipsis; }
.dashadd #addSiteOwner { min-width:130px; }

/* On hold: its own card since 23 Sep, in the same amber as its badge. */
.dashcard.t-hold::before { background:var(--signal); opacity:.55; }
.dashcard.t-hold { --tone-ink:var(--signal); --tone-soft:var(--signal-soft); }
.t-hold .dashcount { color:var(--signal); background:var(--signal-soft); }

/* ===== 24. Where the new messages start ===================================
   A thin rule with a label, drawn once when a conversation is opened with
   something unread in it. Accent-soft, not warning: new is not wrong. */
.unreadline {
  display:flex; align-items:center; gap:10px; margin:10px 4px;
  color:var(--claim); font-size:11.5px; font-weight:600;
}
.unreadline::before, .unreadline::after { content:''; flex:1; height:1px; background:var(--accent-line, var(--line)); }
.unreadline span { padding:2px 10px; border-radius:999px; background:var(--accent-soft); }

/* The message threads jump, they do not glide. styles.css gives .msgs smooth
   scrolling, which turned every "go to the latest message" into an animation
   across thousands of pixels — overtaken by pictures loading on the way, so it
   stopped short, a few messages up (Usama, 23 Sep). Jumping to a quoted
   message still glides: that call asks for it explicitly. */
#msgs, #imsgs { scroll-behavior:auto; }

/* ===== 25. One chevron per dropdown, on every screen ======================
   Usama, 24 Sep, a screenshot of the ticket form on Midnight: every dropdown
   was covered in a wallpaper of little chevrons. The ticket card (and the
   older .rowform) style their selects with `background:` as a SHORTHAND,
   which quietly sets background-repeat back to `repeat`; the dark-theme rule
   then put the chevron image back without saying no-repeat, so it tiled.
   On the light themes the same shorthand wiped the chevron out entirely.

   Repeat and position are now pinned for every select, whatever else
   touches its background, and the two shorthand users get the chevron and
   the room for it back explicitly. */
select { background-repeat:no-repeat !important; background-position:right 10px center !important; }
.tktcard select, .rowform select { padding-right:30px; background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238A9AAB' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); }
[data-theme="dark"] .tktcard select, [data-theme="midnight"] .tktcard select,
[data-tone="dim"] .tktcard select, [data-glass="night"] .tktcard select,
[data-theme="dark"] .rowform select, [data-theme="midnight"] .rowform select,
[data-tone="dim"] .rowform select, [data-glass="night"] .rowform select { background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239BABBA' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); }
select[multiple], select[size]:not([size="1"]),
.tktcard select[multiple], .rowform select[multiple] { background-image:none; }

/* ===== 26. The home screen and one window per screen ======================
   Usama, 24 Sep: on a 12-inch laptop the rail was too crowded to read or
   scroll. In a desktop browser the portal opens on a home screen of tiles and
   each screen opens in a window of its own, with no rail — only this bar.
   Decided in app.html's <head> (data-shell), so the phone app, the Windows
   app and a phone's browser never see any of it. */
[data-shell] .rail,
[data-shell] #navOrderBtn,
[data-shell] #moreBtn { display:none !important; }
[data-shell] #app {
  grid-template-columns:minmax(0, 1fr) !important;
  grid-template-rows:auto minmax(0, 1fr) !important;
}
[data-shell] #app > .shellbar { grid-row:1; grid-column:1; }
[data-shell] #app > .view.on { grid-row:2; grid-column:1; min-height:0; }
[data-shell] .view:not(.on) { display:none !important; }
#v-home:not(.on) { display:none; }

/* --- the bar --- */
.shellbar {
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  height:52px; padding:6px 14px; min-width:0; overflow:hidden;   /* fixed: nothing in it may push the screen down */
  background:var(--panel); border-bottom:1px solid var(--line);
  box-shadow:0 1px 0 var(--line-soft, transparent);
  position:relative; z-index:20;
}
.shellbar[hidden] { display:none; }
.shleft { display:flex; align-items:center; gap:12px; flex:0 0 auto; }   /* the title never gives way */
.shright { display:flex; align-items:center; gap:8px; min-width:0; flex:1 1 auto; justify-content:flex-end; overflow:hidden; }
.shhome {
  display:inline-flex; align-items:center; gap:7px; flex:none;
  padding:7px 13px 7px 10px; border-radius:999px; cursor:pointer;
  font:inherit; font-size:13px; font-weight:650;
  color:var(--claim); background:var(--accent-soft); border:1px solid var(--accent-line, var(--line));
  transition:background-color var(--dur-1, .12s), transform var(--dur-1, .12s);
}
.shhome:hover { background:color-mix(in srgb, var(--accent-soft) 70%, var(--claim) 12%); }
.shhome:active { transform:translateY(1px); }
.shhome[hidden] { display:none; }
.shlogo { height:26px; width:auto; flex:none; }
[data-theme="dark"] .shlogo, [data-theme="midnight"] .shlogo { filter:brightness(1.6) saturate(.9); }
.shtitle {
  display:flex; align-items:center; gap:8px; min-width:0;
  font-size:15px; font-weight:750; color:var(--text); letter-spacing:-.01em;
  padding-left:12px; border-left:1px solid var(--line);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.shtitle svg { width:19px; height:19px; flex:none; color:var(--claim); }
.shtitle span { overflow:hidden; text-overflow:ellipsis; }

/* the rail's pieces, laid out along the bar */
.shellbar .wa-state { border:0; padding:4px 8px; margin:0; font-size:11.5px; white-space:nowrap; flex:none; }
.shellbar .shiftchip { margin:0; flex:none; white-space:nowrap; }
.shellbar .notifybtn {
  margin:0; flex:none; white-space:nowrap; padding:6px 10px; font-size:12px;
  border-radius:var(--r-1, 6px);
}
.shellbar .notifybtn[hidden] { display:none; }
.shellbar .worldclocks {
  margin:0; padding:0; border:0; flex:0 1 auto; min-width:0;
  flex-direction:row; align-items:center; gap:6px;
}
.shellbar .clockhead { padding:0 2px; order:2; }
.shellbar .clockhead > span { display:none; }          /* the word "Clocks" */
.shellbar .clocklist {
  display:flex; flex-direction:row; gap:6px; max-height:none;
  overflow-x:auto; overflow-y:hidden; scrollbar-width:none;
  -webkit-mask-image:none; mask-image:none;
}
.shellbar .clocklist::-webkit-scrollbar { display:none; }
.shellbar .clockrow { margin:0; flex:none; padding:4px 8px 4px 0; min-width:0; }
.shellbar .clockplace { font-size:11px; max-width:92px; }
.shellbar .clocksub { font-size:9px; max-width:92px; }
.shellbar .clocktime { font-size:14px; }
.shellbar .whoami {
  border:0; padding:0 0 0 8px; margin-left:4px; border-left:1px solid var(--line);
  flex:none; display:flex; align-items:center; gap:8px; max-width:230px;
}
.shellbar .whoami .mewho { max-width:140px; }
.shellbar .whoami .mewho b { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.shellbar .whoami button { margin-top:0; }

/* A laptop narrower than the bar wants: words give way before the clocks do. */
@media (max-width: 1280px) {
  .shellbar .whoami .mewho { display:none; }
  .shellbar .clockplace, .shellbar .clocksub { max-width:70px; }
}
@media (max-width: 1100px) {
  .shlogo { display:none; }
  .shellbar .wa-state span:not(.dot) { display:none; }
  .shellbar .clockrow:nth-child(n+3) { display:none; }
}

/* --- someone is ringing, and the phone is in another window --- */
.remotecall {
  position:fixed; top:60px; left:50%; transform:translateX(-50%); z-index:400;
  display:flex; align-items:center; gap:10px; padding:9px 12px 9px 14px;
  border-radius:999px; background:var(--panel); color:var(--text);
  border:1px solid var(--line); box-shadow:var(--sh-3, 0 10px 30px rgba(0,0,0,.2));
  font-size:13px; max-width:calc(100vw - 32px);
}
.remotecall[hidden] { display:none; }
.remotecall span { color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.rcdot { width:10px; height:10px; border-radius:50%; flex:none; background:var(--live); }
.rcdot.ring { animation:rcpulse 1s ease-in-out infinite; }
@keyframes rcpulse { 50% { transform:scale(1.5); opacity:.6; } }
.rcbtn {
  font:inherit; font-size:12.5px; font-weight:650; cursor:pointer; flex:none;
  padding:6px 13px; border-radius:999px; border:1px solid var(--line);
  background:var(--raised); color:var(--text);
}
.rcbtn.yes { background:var(--live); border-color:var(--live); color:#fff; }
.rcbtn.no { background:var(--warn); border-color:var(--warn); color:#fff; }
@media (prefers-reduced-motion: reduce) { .rcdot.ring { animation:none; } }

/* --- the home screen --- */
#v-home.on { display:block; overflow-y:auto; min-height:0; }
.homewrap { max-width:1240px; margin:0 auto; padding:24px 28px 40px; }
.homehead h1 { margin:0 0 6px; font-size:26px; font-weight:800; letter-spacing:-.02em; color:var(--text); }
.homehead p { margin:0 0 20px; color:var(--muted); font-size:14px; max-width:640px; }
/* Sections flow side by side and wrap, so a wide screen is used across its
   width and a 12-inch laptop simply gets more rows. */
.homegrid { display:flex; flex-wrap:wrap; gap:6px 34px; align-items:flex-start; }
.homesec { margin:0 0 18px; flex:0 1 auto; min-width:0; }
.homesec h2 {
  margin:0 0 10px; font-size:11px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--muted);
}
.hometiles { display:flex; flex-wrap:wrap; gap:12px; }
.hometile {
  position:relative; display:flex; flex-direction:column; align-items:flex-start; gap:12px;
  width:148px; padding:15px 15px 13px; min-height:96px; text-align:left; cursor:pointer;
  font:inherit; color:var(--text); background:var(--panel);
  border:1px solid var(--line); border-radius:16px; box-shadow:var(--sh-1, none);
  transition:transform var(--dur-2, .18s) var(--ease-out, ease), box-shadow var(--dur-2, .18s), border-color var(--dur-1, .12s);
}
.hometile:hover { transform:translateY(-2px); border-color:var(--accent-line, var(--line)); box-shadow:var(--sh-2, 0 8px 20px rgba(0,0,0,.08)); }
.hometile:focus-visible { outline:3px solid var(--accent-soft); outline-offset:2px; border-color:var(--claim); }
.hometile:active { transform:translateY(0); }
.hticon {
  width:42px; height:42px; border-radius:12px; display:flex; align-items:center; justify-content:center;
  background:var(--accent-soft); color:var(--claim);
}
.hticon svg { width:22px; height:22px; }
.htname { font-size:14px; font-weight:700; line-height:1.2; }
.htbadge {
  position:absolute; top:12px; right:12px; min-width:22px; height:22px; padding:0 7px;
  border-radius:999px; display:flex; align-items:center; justify-content:center;
  background:var(--warn); color:#fff; font-size:12px; font-weight:800; font-variant-numeric:tabular-nums;
}
.htbadge[hidden] { display:none; }
.homenote {
  margin-top:10px; padding:10px 14px; border-radius:10px; font-size:13px;
  background:var(--signal-soft); color:var(--signal);
}
.homenoaccess { max-width:520px; margin:40px auto; text-align:center; }
.homenoaccess h2 { font-size:20px; margin:0 0 8px; }
.homenoaccess p { color:var(--muted); margin:0 0 18px; }
@media (prefers-reduced-motion: reduce) { .hometile, .hometile:hover { transition:none; transform:none; } }

/* The tiles and Home are links now (so a right-click offers "Open link in new
   tab"); they must still look like the tiles and the button they were. */
a.hometile, a.hometile:hover, a.shhome, a.shhome:hover { text-decoration:none; }
a.hometile { color:var(--text); }
a.shhome { color:var(--claim); }
.shlogolink { display:flex; align-items:center; flex:none; border-radius:8px; padding:2px; }
.shlogolink:hover { background:var(--accent-soft); }
.shlogolink:focus-visible { outline:3px solid var(--accent-soft); }
@media (max-width: 1100px) { .shlogolink { display:none; } }

/* --------------------------------------------------------------------------
   27. The other chat, on whatever screen is in front (24 Sep)
   "If we are in the team chat, WhatsApp notifications are not showing; if we
   are in WhatsApp, team chat notifications are not showing." A card in the
   top right corner of the page in front, and the unread counts on the bar of
   every tab in the home-screen layout.
   -------------------------------------------------------------------------- */
.peeks {
  position:fixed; top:14px; right:14px; z-index:3900;
  display:flex; flex-direction:column; gap:8px;
  width:min(360px, calc(100vw - 24px)); pointer-events:none;
}
[data-shell] .peeks { top:62px; }
.peek {
  pointer-events:auto; cursor:pointer;
  display:flex; align-items:flex-start; gap:10px;
  padding:11px 10px 11px 12px;
  background:var(--panel); color:var(--text);
  border:1px solid var(--line); border-left:4px solid #1FA855;
  border-radius:var(--r-2, 12px);
  box-shadow:var(--sh-3, 0 10px 30px rgba(0,0,0,.2));
  animation:peek-in .22s var(--ease, ease) both;
}
.peek-team { border-left-color:var(--claim); }
.peek:hover { background:var(--raised, var(--panel)); }
.peek:focus-visible { outline:3px solid var(--accent-soft); outline-offset:2px; }
.peek.peek-going { animation:peek-out .18s ease both; }
@keyframes peek-in { from { opacity:0; transform:translateX(16px); } }
@keyframes peek-out { to { opacity:0; transform:translateX(16px); } }
.peekico { flex:none; width:30px; height:30px; border-radius:50%; display:grid; place-items:center;
  background:color-mix(in srgb, #1FA855 14%, transparent); color:#1A8F48; }
.peek-team .peekico { background:var(--accent-soft); color:var(--claim); }
.peekico svg { width:17px; height:17px; }
.peektext { min-width:0; flex:1 1 auto; display:flex; flex-direction:column; gap:2px; }
.peekhead { display:flex; align-items:center; gap:8px; font-size:11px; color:var(--muted);
  text-transform:uppercase; letter-spacing:.04em; }
.peekhead b { font-weight:700; }
.peekn { text-transform:none; letter-spacing:0; font-weight:650; color:var(--claim); }
.peektitle { font-size:13.5px; font-weight:650; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.peekbody { font-size:13px; color:var(--muted); line-height:1.4;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; overflow-wrap:anywhere; }
.peekx { flex:none; border:0; background:transparent; color:var(--muted); cursor:pointer;
  font-size:18px; line-height:1; padding:2px 6px; border-radius:6px; }
.peekx:hover { background:var(--accent-soft); color:var(--text); }
[data-theme="dark"] .peekico, [data-theme="midnight"] .peekico { color:#4ADE80; }
[data-theme="dark"] .peek-team .peekico, [data-theme="midnight"] .peek-team .peekico { color:var(--claim); }

/* the counts on the bar */
.shchip {
  display:inline-flex; align-items:center; gap:6px; flex:none; text-decoration:none;
  padding:5px 6px 5px 9px; border-radius:999px; font-size:12px; font-weight:650;
  color:var(--text); background:var(--raised, var(--panel)); border:1px solid var(--line);
}
.shchip[hidden] { display:none; }
.shchip svg { width:15px; height:15px; flex:none; }
.shchip-inbox svg { color:#1A8F48; }
.shchip-chat svg { color:var(--claim); }
.shchip b { min-width:18px; padding:1px 6px; border-radius:999px; text-align:center;
  background:var(--claim); color:var(--on-accent, #fff); font-size:11px; }
.shchip:hover { background:var(--accent-soft); }
@media (max-width: 1280px) { .shchip span { display:none; } }
[data-theme="dark"] .shchip-inbox svg, [data-theme="midnight"] .shchip-inbox svg { color:#4ADE80; }

/* --------------------------------------------------------------------------
   28. Settings live in your profile (home-screen layout, 24 Sep)
   -------------------------------------------------------------------------- */
.shellbar .clockhead { display:none; }          /* choosing clocks is in the profile */
.profset { margin:14px 0 4px; padding:12px 14px 6px; border:1px solid var(--line);
  border-radius:var(--r-2, 12px); background:var(--raised, var(--panel)); }
.profset[hidden] { display:none; }
.profset h4 { margin:0 0 4px; font-size:11.5px; font-weight:700; letter-spacing:.05em;
  text-transform:uppercase; color:var(--muted); }
.profsetrow { display:flex; align-items:center; justify-content:space-between; gap:14px;
  padding:9px 0; border-top:1px solid var(--line); }
.profsetrow:first-of-type { border-top:0; }
.profsetrow[hidden] { display:none; }
.profsetrow > div:first-child { min-width:0; display:flex; flex-direction:column; gap:2px; }
.profsetrow b { font-size:13.5px; font-weight:650; color:var(--text); }
.profsetrow span { font-size:12px; color:var(--muted); line-height:1.4; }
.profsetslot { flex:none; }
.profsetslot .notifybtn { margin:0; width:auto; white-space:nowrap; padding:7px 12px; font-size:12.5px;
  border-radius:var(--r-1, 8px); display:inline-flex; align-items:center; gap:7px; }
.profsetslot .notifybtn { border:1px solid var(--line); background:var(--panel); color:var(--text); }
.profsetslot .notifybtn:hover { border-color:var(--claim); background:var(--accent-soft); color:var(--text); }
.profsetslot .notifybtn:not([hidden]) { display:inline-flex !important; }   /* styles.css hides .notifybtn under 860px */
.profsetslot .notifybtn[hidden] { display:none; }
.profsetslot .themebtnsw { display:inline-flex; gap:3px; }

/* --------------------------------------------------------------------------
   29. Home screen or side rail — the choice (24 Sep)
   "Some users want the side rail and some want the current one — give them
   the option at the login screen, top right corner."
   -------------------------------------------------------------------------- */
.layoutpick { display:inline-flex; padding:3px; gap:2px; border-radius:999px;
  background:var(--raised, var(--panel)); border:1px solid var(--line); }
.layoutpick button { display:inline-flex; align-items:center; gap:6px; margin:0; width:auto;
  font:inherit; font-size:12.5px; font-weight:600; padding:6px 12px; border-radius:999px;
  border:0; background:transparent; color:var(--muted); cursor:pointer; box-shadow:none; }
.layoutpick button svg { width:15px; height:15px; flex:none; }
.layoutpick button:hover { color:var(--text); }
.layoutpick button[aria-pressed="true"] { background:var(--panel); color:var(--claim);
  box-shadow:0 1px 3px rgba(12,20,28,.14); }
.layoutpick button:focus-visible { outline:3px solid var(--accent-soft); }

/* on the sign-in page */
.loginlayout { position:fixed; top:16px; right:18px; z-index:20;
  display:flex; align-items:center; gap:10px; }
.loginlayout[hidden] { display:none; }
.loginlayout > span { font-size:12px; font-weight:600; color:var(--muted); }
@media (max-width: 520px) { .loginlayout > span { display:none; } }

/* --------------------------------------------------------------------------
   30. The sign-in page, in the company's own look (24 Sep)
   Usama shared a LinkedIn post of the company's — deep navy, a bright blue,
   a gold label, a heavy geometric headline, three stat cards and a contact
   band — and asked for the portal's front page in that spirit. Built from
   CSS, not a picture, so it stays sharp at any size.
   -------------------------------------------------------------------------- */
:root {
  --sn-navy:#081535; --sn-navy-2:#0C2360; --sn-navy-3:#123280;
  --sn-blue:#3A93F5; --sn-blue-2:#1F6FDB; --sn-sky:#8CC8FF;
  --sn-gold:#F6B73C; --sn-gold-ink:#1D1605;
}
body.bl { min-height:100vh; display:flex; flex-direction:column; background:var(--sn-navy); margin:0; }
.bl-stage {
  position:relative; flex:1 0 auto; isolation:isolate; overflow:hidden;
  display:grid; grid-template-columns:minmax(0, 1.15fr) minmax(0, .85fr); align-items:center;
  gap:40px; padding:44px clamp(24px, 6vw, 96px) 76px;
  color:#fff;
}
/* The background: navy depth, a suggestion of server racks, and a glow. */
.bl-bg { position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:
    radial-gradient(1200px 600px at 85% 10%, rgba(58,147,245,.22), transparent 60%),
    radial-gradient(900px 500px at 10% 100%, rgba(18,50,128,.9), transparent 70%),
    linear-gradient(160deg, #0A1B46 0%, var(--sn-navy) 55%, #06102A 100%); }
.bl-racks { position:absolute; inset:0 0 0 38%;
  background:
    repeating-linear-gradient(90deg, rgba(140,200,255,.10) 0 2px, transparent 2px 64px),
    repeating-linear-gradient(90deg, rgba(140,200,255,.05) 0 1px, transparent 1px 16px),
    repeating-linear-gradient(0deg, rgba(140,200,255,.05) 0 1px, transparent 1px 22px);
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 30%, #000 80%, transparent),
                     linear-gradient(180deg, transparent, #000 25%, #000 70%, transparent);
  -webkit-mask-composite:source-in; mask-composite:intersect;
  mask-image:linear-gradient(90deg, transparent, #000 30%, #000 80%, transparent),
             linear-gradient(180deg, transparent, #000 25%, #000 70%, transparent);
  transform:perspective(900px) rotateY(-14deg); transform-origin:right center; opacity:.9; }
.bl-glow { position:absolute; width:520px; height:520px; right:6%; top:-160px; border-radius:50%;
  background:radial-gradient(circle, rgba(58,147,245,.35), transparent 65%); filter:blur(10px); }

.bl-hero { max-width:660px; }
.bl-logo { display:block; width:clamp(190px, 18vw, 250px); height:auto; margin:0 0 22px; }
.bl-badge {
  display:inline-block; padding:7px 14px; border-radius:6px;
  background:var(--sn-gold); color:var(--sn-gold-ink);
  font-family:var(--font-display); font-weight:800; font-size:12.5px; letter-spacing:.14em; text-transform:uppercase;
  box-shadow:0 6px 18px rgba(246,183,60,.25);
}
.bl-head {
  margin:18px 0 12px; font-family:var(--font-display); font-weight:800;
  font-size:clamp(34px, 4.2vw, 58px); line-height:1.04; letter-spacing:-.01em; text-transform:uppercase;
}
.bl-head span { display:block; color:#fff; }
.bl-head .bl-blue { color:var(--sn-blue);
  background:linear-gradient(90deg, #4AA3FF, #2F86F0); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }
.bl-lead { margin:0 0 22px; font-size:clamp(15px, 1.35vw, 18px); line-height:1.55; color:#D6E3F7; max-width:40ch; }
/* What is inside, for the people who use it — not a sales pitch. Each in
   its screen's own colour (§31), the same as its tile and its rail icon. */
.bl-feats { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px; max-width:640px; }
.bl-feat {
  display:flex; align-items:center; gap:12px; padding:10px 14px; border-radius:12px;
  background:linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.03));
  border:1px solid rgba(140,200,255,.18);
}
.bl-fico { width:38px; height:38px; flex:none; border-radius:11px; display:grid; place-items:center; color:#fff;
  background:linear-gradient(135deg, var(--m1), var(--m2)); box-shadow:0 8px 18px -8px var(--m2), inset 0 1px 0 rgba(255,255,255,.3); }
.bl-fico svg { width:20px; height:20px; stroke-width:2; }
.bl-feat b { display:block; font-family:var(--font-display); font-weight:700; font-size:14px; color:#fff; }
.bl-feat small { display:block; margin-top:2px; font-size:12px; line-height:1.35; color:#C3D3EC; }

/* The card */
.bl-formside { display:flex; justify-content:center; }
.bl .formbox.bl-card {
  width:min(420px, 100%); padding:30px 30px 22px; border-radius:18px;
  background:#FFFFFF; color:#0B1B3F; border:1px solid rgba(255,255,255,.6);
  box-shadow:0 30px 80px rgba(2,8,28,.55), 0 2px 0 rgba(255,255,255,.4) inset;
  --text:#0B1B3F; --muted:#56657F; --dim:#8A98B0; --line:#DCE4F0; --bg:#FFFFFF; --panel:#F5F8FC;
}
.bl-cardtop { display:flex; align-items:center; gap:14px; margin:0 0 22px; }
.bl-cardmark { width:48px; height:48px; flex:none; border-radius:14px; display:grid; place-items:center;
  background:linear-gradient(135deg, #0C2360, #081535); box-shadow:0 8px 20px rgba(8,21,53,.3); }
.bl-cardmark img { width:22px; height:auto; }
.bl .bl-card h2 { margin:0; font-family:var(--font-display); font-weight:800; font-size:24px; letter-spacing:-.01em; color:#0B1B3F; }
.bl .bl-card .sub { margin:2px 0 0; font-size:13px; color:#56657F; }
.bl .bl-card .field label { color:#34435E; font-weight:600; font-size:12.5px; }
.bl .bl-card .field input {
  background:#F5F8FC; border:1px solid #D5DEEB; color:#0B1B3F; border-radius:10px; height:46px; font-size:15px;
}
.bl .bl-card .field input:focus { border-color:var(--sn-blue); box-shadow:0 0 0 4px rgba(58,147,245,.18); background:#fff; outline:none; }
.bl .bl-card .primary {
  height:48px; border-radius:10px; font-size:15px; font-weight:700; letter-spacing:.01em;
  background:linear-gradient(135deg, #3A93F5, #1F5FD0); color:#fff; border:0;
  box-shadow:0 10px 24px rgba(31,95,208,.35);
}
.bl .bl-card .primary:hover { filter:brightness(1.07); }
.bl .bl-card .linkbtn { color:var(--sn-blue-2); }
.bl-cardnote { display:flex; gap:9px; align-items:flex-start; margin:16px 0 0; padding-top:14px; border-top:1px solid #E6ECF4; font-size:12px; color:#6B7A93; line-height:1.5; }
.bl-cardnote svg { width:16px; height:16px; flex:none; margin-top:1px; color:#1F6FDB; }

/* The curve into the contact band */
.bl-wave { position:absolute; left:0; right:0; bottom:-1px; width:100%; height:60px; color:#EEF2F8; }

.bl-foot { background:#EEF2F8; color:#1B2A4A; padding:6px clamp(24px, 6vw, 96px) 16px; }
.bl-footrow { display:flex; flex-wrap:wrap; justify-content:space-between; gap:8px 24px; }
.bl-footrow span { display:inline-flex; align-items:center; gap:8px; font-size:14px; font-weight:600; }
.bl-footrow svg { width:17px; height:17px; flex:none; color:#1B2A4A; }
.bl-footrow.bl-small { margin-top:0; }
.bl-footrow.bl-small span { font-size:12px; font-weight:500; color:#4A5A78; }

/* The layout choice sits on the navy now */
.bl .loginlayout > span { color:#C9D8F0; }
.bl .layoutpick { background:rgba(255,255,255,.08); border-color:rgba(255,255,255,.18); }
.bl .layoutpick button { color:#C9D8F0; }
.bl .layoutpick button:hover { color:#fff; }
.bl .layoutpick button[aria-pressed="true"] { background:#fff; color:#0C2360; }

@media (max-width: 1100px) {
  .bl-stage { grid-template-columns:1fr; gap:34px; padding-top:72px; }
  .bl-hero { max-width:none; }
  .bl-formside { justify-content:flex-start; }
  /* On a phone or a narrow window the sign-in card comes straight after the
     name; the list of what is inside is for a desk. */
  .bl-feats { display:none; }
  .bl-lead { margin-bottom:0; }
}
@media (max-width: 640px) {
  .bl-stage { padding:64px 18px 86px; }
  .bl-logo { margin-bottom:20px; }
  .bl-lead { margin-bottom:20px; }
  .bl-footrow { justify-content:flex-start; }
  .bl-footrow span { font-size:13px; }
}
@media (prefers-reduced-motion: no-preference) {
  .bl-hero > * { animation:bl-rise .6s cubic-bezier(.2,.8,.2,1) both; }
  .bl-hero > :nth-child(2) { animation-delay:.05s; } .bl-hero > :nth-child(3) { animation-delay:.1s; }
  .bl-hero > :nth-child(4) { animation-delay:.15s; } .bl-hero > :nth-child(5) { animation-delay:.2s; }
  .bl-card { animation:bl-rise .6s .12s cubic-bezier(.2,.8,.2,1) both; }
}
@keyframes bl-rise { from { opacity:0; transform:translateY(14px); } }

/* --------------------------------------------------------------------------
   31. Colour and a premium finish, in every theme (24 Sep)
   "Add some colourful icons on screen, make it look like a proper stylish
   portal which gives you a premium feel." Every screen has its own colour —
   the same on its tile, in the rail, on the bar and on its page heading — so
   the portal can be read by colour as well as by word.
   -------------------------------------------------------------------------- */
.nav button[data-v="dash"], .hometile[data-open="dash"], [data-mod="dash"] { --m1:#4F8DFD; --m2:#3B5BDB; }
.nav button[data-v="inbox"], .hometile[data-open="inbox"], [data-mod="inbox"] { --m1:#34D399; --m2:#10A36A; }
.nav button[data-v="chat"], .hometile[data-open="chat"], [data-mod="chat"] { --m1:#A78BFA; --m2:#7C3AED; }
.nav button[data-v="att"], .hometile[data-open="att"], [data-mod="att"] { --m1:#FBBF24; --m2:#F08A0C; }
.nav button[data-v="ops"], .hometile[data-open="ops"], [data-mod="ops"] { --m1:#38BDF8; --m2:#0B7FC7; }
.nav button[data-v="recr"], .hometile[data-open="recr"], [data-mod="recr"] { --m1:#FB7185; --m2:#E11D48; }
.nav button[data-v="sign"], .hometile[data-open="sign"], [data-mod="sign"] { --m1:#2DD4BF; --m2:#0E9488; }
.nav button[data-v="meet"], .hometile[data-open="meet"], [data-mod="meet"] { --m1:#E879F9; --m2:#B324C9; }
.nav button[data-v="calls"], .hometile[data-open="calls"], [data-mod="calls"] { --m1:#4ADE80; --m2:#16A34A; }
.nav button[data-v="acct"], .hometile[data-open="acct"], [data-mod="acct"] { --m1:#F6B73C; --m2:#D98A0B; }
.nav button[data-v="pay"], .hometile[data-open="pay"], [data-mod="pay"] { --m1:#A3E635; --m2:#5E9E0D; }
.nav button[data-v="kpi"], .hometile[data-open="kpi"], [data-mod="kpi"] { --m1:#FDBA74; --m2:#EA580C; }
.nav button[data-v="mykpi"], .hometile[data-open="mykpi"], [data-mod="mykpi"] { --m1:#C4B5FD; --m2:#7C3AED; }
.nav button[data-v="ask"], .hometile[data-open="ask"], [data-mod="ask"] { --m1:#67E8F9; --m2:#0891B2; }
.nav button[data-v="eng"], .hometile[data-open="eng"], [data-mod="eng"] { --m1:#60A5FA; --m2:#1D4ED8; }
.nav button[data-v="dept"], .hometile[data-open="dept"], [data-mod="dept"] { --m1:#818CF8; --m2:#4F46E5; }
.nav button[data-v="proj"], .hometile[data-open="proj"], [data-mod="proj"] { --m1:#22D3EE; --m2:#0891B2; }
.nav button[data-v="canned"], .hometile[data-open="canned"], [data-mod="canned"] { --m1:#93C5FD; --m2:#3B6FD8; }
.nav button[data-v="apps"], .hometile[data-open="apps"], [data-mod="apps"] { --m1:#F472B6; --m2:#DB2777; }
.nav button[data-v="help"], .hometile[data-open="help"], [data-mod="help"] { --m1:#F87171; --m2:#DC2626; }
.nav button[data-v="team"], .hometile[data-open="team"], [data-mod="team"] { --m1:#7DA2FF; --m2:#2743B5; }
.nav button[data-v="link"], .hometile[data-open="link"], [data-mod="link"] { --m1:#5EEAD4; --m2:#0F9E8C; }
.nav button[data-v="log"], .hometile[data-open="log"], [data-mod="log"] { --m1:#A5B4CB; --m2:#5B6B86; }

/* The rail: each entry an app-style icon. On a desk only — the phone's bottom
   bar is left as it was. */
@media (min-width: 861px) {
  html:not([data-slim="1"]) .nav button[data-v] .navico {
    width:26px; height:26px; padding:5px; box-sizing:border-box; border-radius:8px;
    color:#fff; opacity:1; stroke-width:2;
    background:linear-gradient(135deg, var(--m1, var(--brand-mid)), var(--m2, var(--claim)));
    box-shadow:0 3px 8px -3px var(--m2, rgba(0,0,0,.3));
  }
  html:not([data-slim="1"]) .nav button[data-v]:not(.sel) .navico { filter:saturate(.92); }
  html:not([data-slim="1"]) .nav button[data-v] { padding-top:6px; padding-bottom:6px; }
}

/* Page headings: the screen's icon in its colour, and the display face. */
.opstitle h2, .padhead h2, .pagehead h2, .meethero h2, .homesec h2 { font-family:var(--font-display); }
.opstitle h2, .padhead h2, .pagehead h2, .meethero h2 { font-weight:800; letter-spacing:-.012em; }
@media (min-width: 861px) {
  .vhtitle { display:flex; align-items:center; gap:11px; padding-left:0 !important; }
  .vhtitle::before { display:none !important; }
  .vhico {
    width:32px; height:32px; flex:none; border-radius:10px; display:inline-grid; place-items:center; color:#fff;
    background:linear-gradient(135deg, var(--m1), var(--m2));
    box-shadow:0 8px 16px -8px var(--m2), inset 0 1px 0 rgba(255,255,255,.28);
  }
  .vhico svg { width:18px; height:18px; stroke-width:2; }
}
.vhico { display:none; }
@media (min-width: 861px) { .vhico { display:inline-grid; } }

/* The bar's title */
.shico {
  width:28px; height:28px; flex:none; border-radius:9px; display:inline-grid; place-items:center; color:#fff;
  background:linear-gradient(135deg, var(--m1), var(--m2)); box-shadow:0 6px 12px -6px var(--m2);
}
.shtitle .shico svg { width:16px; height:16px; color:#fff; stroke-width:2; }
.shtitle { font-family:var(--font-display); font-weight:800; }

/* --- the home screen: a banner in the company's own look ------------------ */
#v-home.on { background:transparent; }
.homehero {
  position:relative; isolation:isolate; overflow:hidden; color:#fff;
  padding:24px 0 62px;
  -webkit-mask:linear-gradient(#000 0 0) top / 100% calc(100% - 39px) no-repeat,
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 40' preserveAspectRatio='none'><path d='M0 0H1440V20C1080 2 360 2 0 26Z'/></svg>") bottom / 100% 40px no-repeat;
  mask:linear-gradient(#000 0 0) top / 100% calc(100% - 39px) no-repeat,
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 40' preserveAspectRatio='none'><path d='M0 0H1440V20C1080 2 360 2 0 26Z'/></svg>") bottom / 100% 40px no-repeat;
}
.hh-bg { position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:
    radial-gradient(900px 420px at 88% 0%, rgba(58,147,245,.28), transparent 62%),
    radial-gradient(700px 380px at 0% 100%, rgba(18,50,128,.85), transparent 70%),
    linear-gradient(160deg, #0B1E4C 0%, #081535 58%, #06102A 100%); }
.hh-racks { position:absolute; inset:0 0 0 45%;
  background:
    repeating-linear-gradient(90deg, rgba(140,200,255,.10) 0 2px, transparent 2px 56px),
    repeating-linear-gradient(0deg, rgba(140,200,255,.05) 0 1px, transparent 1px 18px);
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 35%, #000 85%, transparent);
  mask-image:linear-gradient(90deg, transparent, #000 35%, #000 85%, transparent); opacity:.8; }
.hh-glow { position:absolute; width:420px; height:420px; right:4%; top:-190px; border-radius:50%;
  background:radial-gradient(circle, rgba(58,147,245,.4), transparent 65%); }
.hh-wave { position:absolute; left:0; right:0; bottom:0; width:100%; height:40px; pointer-events:none; }
.hh-inner { max-width:1240px; margin:0 auto; padding:0 28px;
  display:flex; justify-content:space-between; align-items:flex-end; gap:24px 36px; flex-wrap:wrap; }
.hh-main { min-width:0; flex:1 1 380px; }
.hh-badge {
  display:inline-block; padding:6px 12px; border-radius:6px; background:var(--sn-gold); color:var(--sn-gold-ink);
  font-family:var(--font-display); font-weight:800; font-size:11.5px; letter-spacing:.14em; text-transform:uppercase;
  box-shadow:0 6px 16px rgba(246,183,60,.22);
}
.hh-head { margin:14px 0 8px; font-family:var(--font-display); font-weight:800;
  font-size:clamp(24px, 2.4vw, 32px); line-height:1.15; letter-spacing:-.015em; color:#fff; }
.hh-head span { color:#fff; }
.hh-head .hh-name { color:#4AA3FF; background:linear-gradient(90deg, #5AB0FF, #2F86F0);
  -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }
.hh-lead { margin:0; max-width:56ch; color:#D3E1F6; font-size:14.5px; line-height:1.55; }
.hh-stats { display:grid; grid-template-columns:repeat(4, minmax(128px, 150px)); gap:12px; }
.hh-stat[hidden] { display:none; }
.hh-stat {
  display:block; padding:14px 15px 13px; border-radius:12px; color:#fff; text-decoration:none;
  background:linear-gradient(180deg, rgba(255,255,255,.09), rgba(255,255,255,.035));
  border:1px solid rgba(140,200,255,.24);
  transition:transform .18s var(--ease-out, ease), border-color .15s, background .15s;
}
a.hh-stat:hover { transform:translateY(-2px); border-color:rgba(140,200,255,.5); background:rgba(255,255,255,.12); text-decoration:none; }
.hh-stat b { display:block; font-family:var(--font-display); font-weight:800; font-size:28px; line-height:1.05; color:#4AA3FF; font-variant-numeric:tabular-nums; }
.hh-stat.hh-zero b { color:#7EE2B0; }
.hh-stat span { display:block; margin-top:5px; font-size:12px; line-height:1.35; color:#D3E1F6; }
.hh-promise b { color:var(--sn-gold); }
@media (max-width: 1100px) { .hh-stats { grid-template-columns:repeat(2, minmax(128px, 1fr)); } }

/* --- the tiles ------------------------------------------------------------ */
.homewrap { padding-top:14px; }
.homesec h2 { font-size:11.5px; letter-spacing:.14em; color:var(--muted); }
.hometile {
  width:184px; min-height:124px; padding:16px 16px 14px; gap:14px; border-radius:18px;
  background:var(--panel); overflow:hidden;
}
.hometile::before { content:""; position:absolute; inset:0 0 auto 0; height:3px;
  background:linear-gradient(90deg, var(--m1), var(--m2)); opacity:0; transition:opacity .18s; }
.hometile:hover::before, .hometile:focus-visible::before { opacity:1; }
.hometile:hover {
  border-color:color-mix(in srgb, var(--m2) 40%, var(--line));
  box-shadow:0 18px 34px -16px color-mix(in srgb, var(--m2) 60%, transparent);
}
.hticon {
  width:46px; height:46px; border-radius:14px; color:#fff;
  background:linear-gradient(135deg, var(--m1), var(--m2));
  box-shadow:0 10px 20px -10px var(--m2), inset 0 1px 0 rgba(255,255,255,.3);
}
.hticon svg { width:23px; height:23px; stroke-width:2; }
.httext { display:flex; flex-direction:column; gap:3px; min-width:0; }
.htname { font-family:var(--font-display); font-size:14.5px; font-weight:700; letter-spacing:-.005em; }
.htdesc { font-size:12px; line-height:1.35; color:var(--muted); font-weight:500; }
@media (max-width: 700px) { .hometile { width:calc(50vw - 34px); } .hh-stats { grid-template-columns:1fr 1fr; } }

/* --------------------------------------------------------------------------
   32. The "Simplify" theme — the company's own colours, as an option (24 Sep)
   The light family wearing a tone (never a data-theme of its own; see 2b):
   a cool off-white page, white cards, navy ink, and the rail and the bar in
   the deep navy of the company's posts with a gold mark on the selected item.
   -------------------------------------------------------------------------- */
[data-tone="brand"] {
  --bg:#F3F6FB; --panel:#FFFFFF; --raised:#EDF2F9;
  --line:#DCE4F0; --line-soft:#E8EEF7;
  --text:#0B1B3F; --muted:#4F5F7C; --dim:#8492AC;
  --sh-1:0 1px 2px rgba(11,27,63,.06);
  --sh-2:0 8px 24px rgba(11,27,63,.08);
  --sh-3:0 24px 64px rgba(11,27,63,.20);
}
[data-tone="brand"] body { background:var(--bg); }

/* The navy chrome: the rail and the bar re-scope the tokens, so everything
   inside them — clocks, buttons, the profile — follows without being named. */
[data-tone="brand"] .rail,
[data-tone="brand"] .shellbar {
  --text:#FFFFFF; --muted:#B9C7E0; --dim:#8497BA;
  --panel:rgba(255,255,255,.06); --raised:rgba(255,255,255,.10); --bg:#0B1D4D;
  --line:rgba(255,255,255,.12); --line-soft:rgba(255,255,255,.07);
  --claim:#8CC8FF; --claim-deep:#FFFFFF; --accent-soft:rgba(255,255,255,.12); --accent-line:rgba(255,255,255,.22);
  --signal:#F7C766; --signal-soft:rgba(246,183,60,.16);
  --live:#72E6A6; --live-soft:rgba(74,222,128,.14);
  --warn:#FF8E7E; --warn-soft:rgba(255,120,100,.16);
  --night:#B8C6F2; --night-soft:rgba(140,160,230,.16);
  color:#fff;
}
[data-tone="brand"] .rail {
  background:
    radial-gradient(420px 260px at 0% 0%, rgba(58,147,245,.22), transparent 70%),
    linear-gradient(180deg, #0C2159 0%, #081535 55%, #06102A 100%);
  border-right:0; box-shadow:4px 0 24px rgba(8,21,53,.18);
}
[data-tone="brand"] .shellbar {
  background:linear-gradient(90deg, #0C2159, #081535 60%, #0A1A45);
  border-bottom:0; box-shadow:0 6px 20px rgba(8,21,53,.22);
}
[data-tone="brand"] .raillogo, [data-tone="brand"] .shlogo { content:url(/img/logo-white.png); filter:none; }
[data-tone="brand"] .rail .brand { border-color:rgba(255,255,255,.1); background:transparent; }
[data-tone="brand"] .nav button { color:#C9D6EC; }
[data-tone="brand"] .nav button:hover:not(.sel) { background:rgba(255,255,255,.07); color:#fff; }
[data-tone="brand"] .nav button.sel {
  background:linear-gradient(90deg, rgba(255,255,255,.14), rgba(255,255,255,.04));
  color:#fff; box-shadow:inset 3px 0 0 var(--sn-gold);
}
[data-tone="brand"] .nav button[data-v].sel::before { background:var(--sn-gold); }
[data-tone="brand"] .navgroup { color:#8FA6CC; }
[data-tone="brand"] .rail .pill, [data-tone="brand"] .shellbar .pill { box-shadow:0 0 0 2px #0B1D4D; }
[data-tone="brand"] .shellbar .shhome {
  background:rgba(255,255,255,.1); border-color:rgba(255,255,255,.2); color:#fff;
}
[data-tone="brand"] .shellbar .shhome:hover { background:rgba(255,255,255,.18); }
[data-tone="brand"] .shtitle { border-left-color:rgba(255,255,255,.18); color:#fff; }
[data-tone="brand"] .shellbar .shchip b { background:var(--sn-gold); color:var(--sn-gold-ink); }
[data-tone="brand"] .rail .meav, [data-tone="brand"] .shellbar .meav { box-shadow:0 0 0 2px rgba(255,255,255,.25); }
[data-tone="brand"] .rail input, [data-tone="brand"] .rail select { background:rgba(255,255,255,.08); color:#fff; }

/* The content: white cards that sit up off the page, a bright blue for the
   main action, navy for headings. */
[data-tone="brand"] .primary,
[data-tone="brand"] .linkbtns button.primary,
[data-tone="brand"] .padhead .primary {
  background:linear-gradient(135deg, #3A93F5, #1F5FD0); border-color:transparent; color:#fff;
  box-shadow:0 8px 18px -8px rgba(31,95,208,.7);
}
[data-tone="brand"] .opstitle h2, [data-tone="brand"] .padhead h2, [data-tone="brand"] .pagehead h2 { color:#0B1B3F; }
[data-tone="brand"] .opstable thead th { background:#EDF2FA; color:#3A4C70; }
[data-tone="brand"] .hometile { border-color:#E1E8F3; box-shadow:0 2px 6px rgba(11,27,63,.05); }

@media (max-width: 860px) {
  [data-tone="brand"] .rail { box-shadow:0 -6px 20px rgba(8,21,53,.2); }
}

.themeprev-simplify { background:linear-gradient(135deg, #0C2159 0 55%, #F3F6FB 55% 80%, #F6B73C 80%); }

/* --------------------------------------------------------------------------
   33. The phone system (24 Sep): the menu callers hear, where each key rings,
   transcripts on calls, and the countries the phone may call. Admins only.
   -------------------------------------------------------------------------- */
#pbxPane[hidden] { display:none; }
.pbx { display:flex; flex-direction:column; gap:10px; padding:4px 2px 18px; }
.pbxtop { display:flex; justify-content:space-between; align-items:flex-start; gap:14px;
  padding:14px 16px; border-radius:var(--r-2, 12px); background:var(--panel); border:1px solid var(--line); }
.pbxtop b, .pbxcountries > div:first-child > b { display:block; font-family:var(--font-display); font-size:15px; font-weight:800; color:var(--text); }
.pbxtop span, .pbxcountries > div:first-child > span { display:block; margin-top:2px; font-size:12.5px; color:var(--muted); }
.pbxswitch { display:inline-flex; align-items:center; gap:8px; flex:none; font-size:13px; font-weight:600; color:var(--text); cursor:pointer; }
.pbxswitch input { width:18px; height:18px; accent-color:var(--claim); margin:0; }
.pbx textarea { width:100%; resize:vertical; font:inherit; font-size:13.5px; line-height:1.5; padding:9px 11px;
  border:1px solid var(--line); border-radius:var(--r-1, 8px); background:var(--bg); color:var(--text); box-sizing:border-box; }
.pbx .flab { margin:4px 0 -4px; }
.pbxopts { display:flex; flex-direction:column; gap:6px; }
.pbxopt { display:grid; grid-template-columns:auto 54px auto minmax(90px, 1fr) auto minmax(120px, 1.1fr) auto; align-items:center; gap:7px;
  padding:7px 9px; border:1px solid var(--line); border-radius:var(--r-1, 8px); background:var(--panel); }
.pbxopt select, .pbxopt input, .pbxgrid input, .pbxgrid select, .pbxadd input {
  height:34px; padding:0 9px; font:inherit; font-size:13px; border:1px solid var(--line); border-radius:8px;
  background:var(--bg); color:var(--text); min-width:0; box-sizing:border-box; }
.pbxpress { font-size:12.5px; color:var(--muted); }
.pbxarrow { color:var(--dim); }
.pbxopt .linkbtn.danger { font-size:17px; padding:2px 6px; }
.pbxgrid { display:flex; flex-direction:column; gap:8px; margin-top:4px; }
.pbxgrid label { display:flex; flex-wrap:wrap; align-items:center; gap:7px; font-size:13px; color:var(--text); }
.pbxgrid input[type="number"] { width:64px; }
.pbxgrid select { max-width:240px; }
.pbxcheck { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text); }
.pbxcheck input { width:17px; height:17px; accent-color:var(--claim); margin:0; }
.pbxpreview { padding:12px 14px; border-radius:var(--r-2, 12px); color:#fff;
  background:linear-gradient(135deg, #0C2159, #081535); box-shadow:var(--sh-2); }
.pbxpreview span { display:block; font-size:10.5px; font-weight:800; letter-spacing:.12em; text-transform:uppercase; color:var(--sn-gold, #F6B73C); }
.pbxpreview p { margin:6px 0 0; font-size:14px; line-height:1.55; color:#E4ECF8; }
.pbxwarn { margin:0; padding:9px 12px; border-radius:8px; font-size:12.5px; background:var(--signal-soft); color:var(--signal); }
.pbxwarn[hidden] { display:none; }
.pbxsave { display:flex; justify-content:space-between; align-items:center; gap:12px; flex-wrap:wrap; }
.pbxsave span { font-size:12px; color:var(--muted); }
.pbxsave .primary.sm { width:auto; margin:0; padding:8px 20px; }
.pbxcountries { margin-top:8px; padding:14px 16px; border-radius:var(--r-2, 12px); background:var(--panel); border:1px solid var(--line);
  display:flex; flex-direction:column; gap:10px; }
.pbxchips { display:flex; flex-wrap:wrap; gap:6px; }
.pbxchip { display:inline-flex; align-items:center; gap:6px; padding:4px 6px 4px 9px; border-radius:999px; font-size:12.5px;
  background:var(--raised); border:1px solid var(--line); color:var(--text); }
.pbxchip b { display:inline; font-family:var(--font-mono); font-size:11.5px; color:var(--claim); }
.pbxchip button { border:0; background:transparent; color:var(--muted); cursor:pointer; font-size:15px; line-height:1; padding:0 3px; border-radius:6px; }
.pbxchip button:hover { color:var(--warn); background:var(--warn-soft); }
.pbxadd { display:flex; gap:8px; align-items:center; }
.pbxadd input { width:130px; text-transform:uppercase; }
@media (max-width: 700px) {
  .pbxopt { grid-template-columns:auto 54px 1fr auto; }
  .pbxopt .pbxpress:nth-of-type(2), .pbxopt .pbxarrow { display:none; }
  .pbxopt select[data-f="department_id"] { grid-column:1 / -2; }
}

/* On a call row: which department the caller chose, and what was said */
.calldept { display:inline-flex; align-items:center; padding:1px 8px; border-radius:999px; font-size:11px; font-weight:700;
  background:var(--accent-soft); color:var(--claim-deep, var(--claim)); }
.calltx { margin:4px 0 0; font-size:12.5px; }
.calltx summary { cursor:pointer; color:var(--claim); font-weight:600; font-size:12px; width:max-content; }
.calltx p { margin:6px 0 2px; padding:8px 10px; border-radius:8px; background:var(--raised); color:var(--text);
  line-height:1.55; white-space:pre-wrap; overflow-wrap:anywhere; max-height:260px; overflow:auto; }
/* Four tabs now (Recent calls, Voicemail, Recordings, Phone system): they wrap
   rather than slide under the "My calls / Everyone" picker. */
.loghead { flex-wrap:wrap; row-gap:6px; }
.logtabs { flex-wrap:wrap; flex:1 1 auto; }
.logtab { white-space:nowrap; }

/* The Phone system tab takes the full width; the dial pad is one tab away. */
.dialwrap.pbxmode { grid-template-columns:minmax(0, 1fr); }
.dialwrap.pbxmode .dialer { display:none; }
.dialwrap.pbxmode .pbx { max-width:880px; }

/* --------------------------------------------------------------------------
   34. Copying the rota between months, person by person (24 Sep)
   -------------------------------------------------------------------------- */
.rotcbox { width:min(860px, 100%); }
.rotcmonths { display:flex; align-items:flex-end; gap:12px; flex-wrap:wrap; margin:6px 0 14px; }
.rotcmonths label { display:flex; flex-direction:column; gap:4px; font-size:12px; font-weight:600; color:var(--muted); }
.rotcmonths select { min-width:180px; }
.rotcarrow { padding-bottom:9px; color:var(--dim); font-size:18px; }
.rotcwrap { max-height:min(52vh, 460px); overflow:auto; border:1px solid var(--line); border-radius:var(--r-2, 12px); }
.rotcwrap table { width:100%; border-collapse:collapse; font-size:13px; }
.rotcwrap th { position:sticky; top:0; z-index:1; background:var(--raised); text-align:left; padding:9px 10px;
  font-size:10.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase; color:var(--muted); border-bottom:1px solid var(--line); }
.rotcwrap td { padding:9px 10px; border-bottom:1px solid var(--line-soft, var(--line)); vertical-align:middle; }
.rotcwrap tr:last-child td { border-bottom:0; }
.rotcwrap .rotcck { width:34px; text-align:center; }
.rotcwrap .rotcck input { width:17px; height:17px; accent-color:var(--claim); margin:0; }
.rotcwrap .rotcact { text-align:right; white-space:nowrap; }
.rotcsame { margin-left:6px; padding:1px 7px; border-radius:999px; font-size:10.5px; font-weight:700;
  background:var(--live-soft); color:var(--live); }
.rotcfoot { align-items:center; margin-top:14px; }
.rotcfoot > .subtle { margin-right:auto; font-size:12.5px; }
.rotcfoot .primary { width:auto; margin:0; padding:9px 18px; }
.rotcfoot .primary:disabled { opacity:.5; cursor:default; }
@media (max-width: 700px) {
  .rotcwrap th:nth-child(4), .rotcwrap td:nth-child(4) { display:none; }
}

/* ===== 35. My KPI (24 Sep) ================================================
   One person's own card: the score and band first, the five groups as tiles,
   then the figures. No money anywhere on it. */
.mykpihead {
  display:flex; align-items:center; gap:18px; flex-wrap:wrap;
  padding:16px 18px; margin:0 0 12px; border-radius:var(--r-2);
  background:var(--panel); border:1px solid var(--line);
}
.mykpiscore { display:flex; flex-direction:column; align-items:center; gap:6px; }
.mykpiwhat { display:flex; flex-direction:column; gap:3px; font-size:12.5px; color:var(--muted); min-width:0; flex:1 1 260px; }
.mykpiwhat b { font-size:15px; color:var(--text); }
.mykpigroups .kpitile b { color:var(--tone-ink, var(--text)); }
.hh-stat.hh-kpi[data-tone="a"] b { color:#86EFAC; }
.hh-stat.hh-kpi[data-tone="b"] b { color:#93C5FD; }
.hh-stat.hh-kpi[data-tone="c"] b { color:#FCD34D; }
.hh-stat.hh-kpi[data-tone="r"] b { color:#FCA5A5; }
@media (max-width: 720px) {
  .kpimetrics td.kpiwhy { display:none; }
}
/* Five figures when My KPI is one of them: one row on a desktop, not four and a straggler. */
@media (min-width: 1101px) {
  .hh-stats:has(> .hh-kpi:not([hidden])) { grid-template-columns:repeat(5, minmax(112px, 136px)); gap:10px; }
}

/* ===== 36. AI search (24 Sep) =============================================
   A conversation: the question on the right, the answer on the left with the
   searches it made folded underneath, the box at the bottom. */
.askpad { display:flex; flex-direction:column; min-height:calc(100vh - 140px); }
.asklog { flex:1 1 auto; display:flex; flex-direction:column; gap:12px; padding:4px 0 16px; }
.askhello { padding:18px; border-radius:var(--r-2); border:1px dashed var(--line); }
.askchips { display:flex; flex-wrap:wrap; gap:8px; margin-top:8px; }
.askchips button {
  padding:8px 12px; border-radius:999px; border:1px solid var(--line); background:var(--panel);
  color:var(--text); font-size:13px; cursor:pointer;
}
.askchips button:hover { border-color:var(--m2, var(--claim)); }
.askmsg { max-width:min(760px, 92%); padding:11px 14px; border-radius:14px; font-size:14px; line-height:1.55; }
.askmsg p { margin:0 0 6px; } .askmsg p:last-child { margin-bottom:0; }
.askmsg ul, .askmsg ol { margin:4px 0 8px; padding-left:20px; }
.askmsg h4 { margin:8px 0 4px; font-size:14px; }
.askmsg.me { align-self:flex-end; background:var(--accent-soft); color:var(--text); border-bottom-right-radius:4px; }
.askmsg.ai { align-self:flex-start; background:var(--panel); border:1px solid var(--line); border-bottom-left-radius:4px; }
.askmsg.err { border-color:color-mix(in srgb, var(--warn) 45%, var(--line)); color:var(--warn); }
.askwait { color:var(--muted); display:flex; align-items:center; gap:4px; }
.askwait span { width:6px; height:6px; border-radius:50%; background:currentColor; animation:askdot 1s infinite ease-in-out; }
.askwait span:nth-child(2) { animation-delay:.15s; } .askwait span:nth-child(3) { animation-delay:.3s; margin-right:6px; }
@keyframes askdot { 0%, 80%, 100% { opacity:.25; } 40% { opacity:1; } }
.asksearches { margin-top:8px; font-size:12px; color:var(--muted); }
.asksearches summary { cursor:pointer; }
.asksearch { display:block; padding:2px 0; }
.askform {
  position:sticky; bottom:0; display:flex; gap:8px; align-items:flex-end; padding:10px 0 4px;
  background:linear-gradient(to top, var(--bg) 70%, transparent);
}
.askform textarea {
  flex:1; resize:none; padding:11px 13px; font:inherit; font-size:14px; border-radius:12px;
  border:1px solid var(--line); background:var(--panel); color:var(--text);
}
@media (prefers-reduced-motion: reduce) { .askwait span { animation:none; opacity:.6; } }
.askform textarea { flex:1 1 auto; min-width:0; width:auto; }
.askform button { flex:none; width:auto; min-width:92px; padding:11px 22px; }

/* AI search: photos, files, web links, report templates (24 Sep) */
.askform { flex-direction:column; align-items:stretch; }
.askrow { display:flex; gap:8px; align-items:flex-end; }
.askclip {
  flex:none; display:grid; place-items:center; width:44px; height:44px; border-radius:12px; cursor:pointer;
  border:1px solid var(--line); background:var(--panel); color:var(--muted);
}
.askclip:hover { color:var(--text); border-color:var(--m2, var(--claim)); }
.askpics { display:flex; gap:8px; flex-wrap:wrap; }
.askpic { position:relative; }
.askpic img, .askmepics img { width:72px; height:72px; object-fit:cover; border-radius:10px; border:1px solid var(--line); display:block; }
.askpic button {
  position:absolute; top:-6px; right:-6px; width:22px; height:22px; border-radius:50%; padding:0; line-height:20px;
  border:1px solid var(--line); background:var(--panel); color:var(--text); cursor:pointer;
}
.askmepics { display:flex; gap:6px; flex-wrap:wrap; margin-bottom:6px; }
.askfiles { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }
.askfile {
  display:inline-flex; align-items:center; gap:7px; padding:8px 12px; border-radius:10px; font-weight:600; font-size:13px;
  background:var(--accent-soft); color:var(--claim); text-decoration:none;
}
.askfile:hover { text-decoration:none; filter:brightness(1.05); }
.asklinks { display:flex; flex-direction:column; gap:3px; margin-top:10px; font-size:12.5px; }
.asklinks b { font-size:12px; color:var(--muted); font-weight:600; }
.asklinks a { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.askmsg a { word-break:break-word; }
.askhint { margin:10px 0 0; font-size:12.5px; }
.asktpls { display:grid; gap:8px; margin:6px 0 14px; }
.asktpl { display:flex; justify-content:space-between; align-items:center; gap:10px; flex-wrap:wrap; padding:9px 12px; border:1px solid var(--line); border-radius:10px; }
.asktpl .opssub { display:block; }
.asktplacts { display:flex; gap:10px; flex-wrap:wrap; align-items:center; }
.askcheck { display:flex; gap:8px; align-items:center; font-size:13px; margin:8px 0; }
.askalias code { font-size:11px; padding:1px 6px; border-radius:6px; background:var(--accent-soft); color:var(--claim); margin-left:4px; }
.asktpl.editing { display:block; }
.asktpl.editing .frow { margin-bottom:8px; }
.askadd { margin:4px 0 14px; }
.askadd summary { cursor:pointer; font-size:13px; font-weight:600; color:var(--claim); margin-bottom:8px; }
#askKnowText { width:100%; resize:vertical; padding:9px 11px; font:inherit; font-size:13px; border:1px solid var(--line); border-radius:var(--r-1); background:var(--bg); color:var(--text); margin-bottom:8px; }
.askfd { display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin:4px 0 6px; }
.askfd select { padding:7px 10px; border:1px solid var(--line); border-radius:var(--r-1); background:var(--bg); color:var(--text); }
.askfd .flab { margin:0; }
.askfdstate { font-size:12.5px; color:var(--muted); min-height:1.2em; margin:0 0 12px; }

/* --------------------------------------------------------------------------
   Simplify Drive (25 Sep). The company's files: spaces down the side (Shared,
   departments, My files, Bin), the folder in the middle, and uploads with
   their progress above the list.
   -------------------------------------------------------------------------- */
.nav button[data-v="drive"], .hometile[data-open="drive"], [data-mod="drive"] { --m1:#7DD3FC; --m2:#6366F1; }
.drivepad { display:flex; flex-direction:column; min-height:calc(100vh - 140px); }
.drvsearchwrap input { width:min(320px, 60vw); padding:9px 12px; border-radius:10px; border:1px solid var(--line); background:var(--panel); color:var(--text); font:inherit; font-size:13.5px; }
.drvwrap { display:grid; grid-template-columns:220px minmax(0, 1fr); gap:18px; align-items:start; align-content:start; flex:1 1 auto; }
.drvbtn {
  display:inline-flex; align-items:center; justify-content:center; gap:6px; width:auto; margin:0; white-space:nowrap;
  padding:8px 14px; border-radius:10px; border:1px solid var(--line); background:var(--panel); color:var(--text);
  font:inherit; font-size:13px; font-weight:600; cursor:pointer; transition:filter .12s, border-color .12s, background .12s;
}
.drvbtn:hover { border-color:var(--m2, var(--claim)); }
.drvbtn.pri { background:linear-gradient(135deg, var(--m1, #7DD3FC), var(--m2, #6366F1)); border-color:transparent; color:#fff; box-shadow:var(--sh-1); }
.drvbtn.pri:hover { filter:brightness(1.06); }
.drvbtn.danger { color:var(--warn); }
.drvbtn.danger:hover { border-color:var(--warn); background:var(--warn-soft); }
.drvbtn.slim { padding:4px 10px; font-size:12px; }
#v-drive, .drvmodal { --m1:#7DD3FC; --m2:#6366F1; }
.drvside { display:flex; flex-direction:column; gap:2px; position:sticky; top:12px; }
.drvspace {
  display:flex; align-items:center; gap:10px; width:100%; padding:9px 11px; border-radius:10px; border:0;
  background:transparent; color:var(--text); font:inherit; font-size:13.5px; cursor:pointer; text-align:left;
}
.drvspace svg { flex:none; color:var(--muted); }
.drvspace > span { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.drvspace small { color:var(--muted); font-size:11.5px; white-space:nowrap; }
.drvspace:hover { background:var(--raised); }
.drvspace.on { background:var(--accent-soft); color:var(--claim); font-weight:600; }
.drvspace.on svg { color:var(--claim); }
.drvspace.bin { margin-top:8px; border-top:1px solid var(--line); border-radius:0 0 10px 10px; padding-top:12px; }
.drvusage { margin:12px 11px 0; font-size:12px; color:var(--muted); }
.drvnote { margin:8px 11px 0; font-size:12px; color:var(--warn); line-height:1.5; }
.drvmain { position:relative; min-height:360px; border:1px solid var(--line); border-radius:var(--r-2, 12px); background:var(--panel); padding:6px 0 10px; }
.drvmain.dropping { outline:2px dashed var(--m2, var(--claim)); outline-offset:-6px; }
.drvbar { display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap; padding:8px 14px 10px; border-bottom:1px solid var(--line); }
.drvtools { display:flex; gap:8px; }
.drvtools[hidden] { display:none; }
.drvcrumbs { display:flex; align-items:center; gap:4px; flex-wrap:wrap; min-width:0; font-size:14px; }
.drvcrumbs b { font-size:14px; }
.drvcrumb { border:0; background:transparent; color:var(--text); font:inherit; font-weight:600; padding:4px 6px; border-radius:6px; cursor:pointer; max-width:240px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.drvcrumb:hover { background:var(--raised); }
.drvcrumb:last-child { color:var(--claim); }
.drvsep { color:var(--muted); }
.drvhint { color:var(--muted); font-size:12.5px; margin-left:8px; font-weight:400; }
p.drvhint { margin:6px 0 0; }
.drvrow {
  display:grid; grid-template-columns:minmax(0, 1fr) 150px 140px 80px 44px; gap:12px; align-items:center;
  padding:7px 14px; border-bottom:1px solid var(--line-soft, var(--line)); cursor:default; font-size:13.5px;
}
.drvlist .drvrow[data-id] { cursor:pointer; }
.drvlist .drvrow[data-id]:hover { background:var(--raised); }
.drvrow:focus-visible { outline:2px solid var(--claim); outline-offset:-2px; }
.drvrow.droptarget { background:var(--accent-soft); box-shadow:inset 0 0 0 2px var(--claim); }
.drvhead { font-size:11.5px; font-weight:600; color:var(--muted); text-transform:uppercase; letter-spacing:.04em; cursor:default; padding-top:10px; }
.drvname { display:flex; align-items:center; gap:10px; min-width:0; }
.drvname b { font-weight:500; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.drvrow.folder .drvname b { font-weight:600; }
.drvmeta { color:var(--muted); font-size:12.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.drvacts { display:flex; justify-content:flex-end; gap:8px; align-items:center; }
.drvlist .drvrow:has(.drvacts .drvbtn) { grid-template-columns:minmax(0, 1fr) 130px 180px 80px 230px; }
.drvhead:has(+ .drvrow .drvacts .drvbtn) { grid-template-columns:minmax(0, 1fr) 130px 180px 80px 230px; }
.drvmore { border:0; background:transparent; color:var(--muted); width:32px; height:32px; border-radius:8px; cursor:pointer; display:grid; place-items:center; padding:0; }
.drvmore:hover { background:var(--panel); color:var(--text); box-shadow:0 0 0 1px var(--line); }
.drvico { --kc:#7B8797; position:relative; flex:none; width:26px; height:26px; display:grid; place-items:center; color:var(--kc); }
.drvico i { position:absolute; bottom:2px; left:50%; transform:translateX(-50%); font:700 7px/1 system-ui, sans-serif; font-style:normal; color:#fff; background:var(--kc); padding:2px 3px; border-radius:3px; letter-spacing:.02em; }
.drvico.k-folder { --kc:#F2B233; }
.drvico.k-pdf { --kc:#E5484D; }
.drvico.k-doc { --kc:#2F6FE0; }
.drvico.k-sheet { --kc:#1F9D55; }
.drvico.k-slides { --kc:#E0762F; }
.drvico.k-image { --kc:#8E4EC6; }
.drvico.k-video, .drvico.k-audio { --kc:#D6409F; }
.drvempty { padding:48px 20px; text-align:center; color:var(--muted); }
.drvempty p { margin:6px 0 0; }
.drvempty svg { color:var(--m2, var(--claim)); opacity:.7; }
.drvuploads { display:flex; flex-direction:column; gap:6px; padding:10px 14px; border-bottom:1px solid var(--line); background:var(--raised); }
.drvuploads[hidden] { display:none; }
.drvup { display:grid; grid-template-columns:minmax(0, 1fr) minmax(80px, 200px) 72px auto; gap:10px; align-items:center; font-size:12.5px; }
.drvupname { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.drvupbar { height:6px; border-radius:99px; background:var(--line); overflow:hidden; }
.drvupbar i { display:block; height:100%; width:0; background:linear-gradient(90deg, var(--m1, #7DD3FC), var(--m2, #6366F1)); transition:width .25s; }
.drvupnum { color:var(--muted); text-align:right; }
.drvup.ok .drvupnum { color:var(--live, #1E8E5A); }
.drvup.bad .drvupnum { color:var(--warn); }
.drvmenu {
  position:fixed; z-index:160; width:190px; padding:6px; border-radius:12px; background:var(--bg);
  border:1px solid var(--line); box-shadow:var(--sh-3, 0 12px 40px rgba(0,0,0,.2)); display:flex; flex-direction:column;
}
.drvmenu button { border:0; background:transparent; color:var(--text); font:inherit; font-size:13.5px; text-align:left; padding:8px 10px; border-radius:8px; cursor:pointer; }
.drvmenu button:hover { background:var(--raised); }
.drvmenu button.danger { color:var(--warn); }
.linkbtn.danger { color:var(--warn); }
.drvprevbox { width:min(1000px, 100%); max-height:92vh; }
.drvprevhead { display:flex; justify-content:space-between; align-items:center; gap:12px; flex-wrap:wrap; }
.drvprevhead h3 { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.drvprevacts { display:flex; gap:8px; }
.drvprev { display:grid; place-items:center; min-height:200px; background:var(--raised); border-radius:10px; overflow:hidden; }
.drvprev img, .drvprev video { max-width:100%; max-height:72vh; display:block; }
.drvprev iframe { width:100%; height:72vh; border:0; background:#fff; }
.drvprev audio { width:90%; margin:40px 0; }
.drvnoprev { text-align:center; color:var(--muted); padding:40px; }
.drvnoprev .drvico { width:52px; height:52px; margin:0 auto; transform:scale(1.8); }
.drvshareform { display:grid; grid-template-columns:140px 1fr; gap:12px; }
.drvshareform select { width:100%; }
.drvnewlink { display:flex; gap:8px; margin-top:10px; }
.drvnewlink input { flex:1 1 auto; font-family:ui-monospace, Menlo, Consolas, monospace; font-size:12.5px !important; }
.drvlinks { display:flex; flex-direction:column; gap:6px; }
.drvlink { display:flex; justify-content:space-between; gap:10px; align-items:center; font-size:12.5px; color:var(--muted); padding:7px 10px; border:1px solid var(--line); border-radius:8px; }
.drvsharebox, .drvmovebox, .drvpickbox { width:min(560px, 100%); }
.drvbrowse { display:flex; flex-direction:column; gap:8px; min-height:260px; }
.drvchips { display:flex; gap:6px; flex-wrap:wrap; }
.drvchip { border:1px solid var(--line); background:var(--panel); color:var(--text); border-radius:99px; padding:5px 11px; font:inherit; font-size:12.5px; cursor:pointer; }
.drvchip.on { background:var(--accent-soft); color:var(--claim); border-color:var(--accent-line, var(--claim)); font-weight:600; }
.drvcrumbs.small { font-size:12.5px; }
.drvpicklist { display:flex; flex-direction:column; max-height:44vh; overflow:auto; border:1px solid var(--line); border-radius:10px; }
.drvpick { display:flex; align-items:center; gap:10px; border:0; border-bottom:1px solid var(--line-soft, var(--line)); background:transparent; color:var(--text); font:inherit; font-size:13.5px; padding:8px 12px; cursor:pointer; text-align:left; }
.drvpick:hover { background:var(--raised); }
.drvpick > span:not(.drvico) { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.drvpick small { color:var(--muted); font-size:11.5px; white-space:nowrap; max-width:45%; overflow:hidden; text-overflow:ellipsis; }
.drvpicklist .drvhint { padding:14px; margin:0; }
.drvpicksearch { width:100%; }
@media (max-width: 860px) {
  .drvwrap { grid-template-columns:minmax(0, 1fr); }
  .drvside { position:static; flex-direction:row; overflow-x:auto; gap:6px; padding-bottom:4px; }
  .drvspace { width:auto; flex:none; border:1px solid var(--line); border-radius:99px; padding:7px 12px; }
  .drvspace small, .drvusage, .drvnote { display:none; }
  .drvspace.bin { margin-top:0; border-top:1px solid var(--line); border-radius:99px; padding-top:7px; }
  .drvrow { grid-template-columns:minmax(0, 1fr) 44px; }
  .drvhead { display:none; }
  .drivepad .padhead { flex-wrap:wrap; }
  .drivepad .padhead > div:first-child { flex:1 1 100%; }
  .drvsearchwrap { flex:1 1 100%; }
  .drvlist .drvrow:has(.drvacts .drvbtn) { grid-template-columns:minmax(0, 1fr); }
  .drvlist .drvrow:has(.drvacts .drvbtn) .drvacts { justify-content:flex-start; }
  .drvrow > .drvmeta { display:none; }
  .drvsearchwrap input { width:100%; }
  .drvup { grid-template-columns:minmax(0, 1fr) 60px auto; }
  .drvup .drvupbar { display:none; }
  .drvshareform { grid-template-columns:1fr; }
}
.drvmodal .drvpick { justify-content:flex-start; text-align:left; width:100%; border-radius:0; margin:0; }
.drvmodal .drvpick > span:not(.drvico) { text-align:left; }
.drvmodal .drvpick:last-child { border-bottom:0; }
.drvup .drvbtn.slim { justify-self:end; }

/* Recruitment: the board by team member (25 Sep) */
.rcbar { flex-wrap:wrap; gap:10px; }
.rcgroupby { margin-left:auto; }
.rcgroupby + .rctabs { margin-left:0; }
.rcperson h3 { text-transform:none; letter-spacing:0; font-size:14px; color:var(--text); flex-wrap:wrap; gap:8px 10px; }
.rcperson .rcpname { font-weight:600; }
.rcav { width:26px; height:26px; border-radius:50%; display:inline-grid; place-items:center; font-size:11px; font-weight:700;
  background:var(--accent-soft); color:var(--claim); }
.rcav.none { background:var(--warn-soft, #fdecea); color:var(--warn); }
.rcperson.nobody .rcpname { color:var(--warn); }
.rcyou { font-size:11px; font-weight:600; color:var(--claim); background:var(--accent-soft); padding:1px 7px; border-radius:99px; margin-left:4px; }
.rcmix { display:inline-flex; gap:8px; align-items:center; font-size:12px; color:var(--muted); font-weight:500; }
.rcmix .sitep { margin-right:1px; }
.rcshort { font-size:12px; font-weight:500; color:var(--signal, #b4541f); }
.rclateh { font-size:12px; font-weight:600; color:var(--warn); }
.rctabs { display:inline-flex; gap:2px; padding:3px; border:1px solid var(--line); border-radius:10px; background:var(--panel); }
.rctabs button { border-radius:7px; border:0; background:transparent; color:var(--muted); }
.rctabs button.on { background:var(--accent-soft); color:var(--claim); font-weight:600; }

/* Team chat: announcements that must be acknowledged (25 Sep) */
.ctool.ackswitch { display:inline-flex; align-items:center; gap:4px; width:auto; padding:0 8px; border-radius:99px; font-size:12px; font-weight:600; }
.ctool.ackswitch span { white-space:nowrap; }
.ctool.ackswitch.on { background:#FFF1D6; color:#A15C00; box-shadow:inset 0 0 0 1px #F2C36B; }
#idraft.ackarmed { box-shadow:inset 3px 0 0 #F0A020; }
.ackbar { display:flex; flex-wrap:wrap; align-items:center; gap:6px 10px; margin:6px 0 2px; padding:6px 8px; border-radius:8px;
  background:#FFF6E3; border:1px solid #F4D493; font-size:12px; color:#7A4A00; }
.ackbar.waiting { background:#FFEFD0; border-color:#F0A020; }
.acktag { display:inline-flex; align-items:center; gap:5px; font-weight:600; }
.ackcount { border:0; background:transparent; color:#7A4A00; font:inherit; font-weight:600; text-decoration:underline; cursor:pointer; padding:0; }
.ackcount.all { color:#1E7A4B; }
.ackdo { border:0; border-radius:99px; background:#E08A00; color:#fff; font:inherit; font-weight:600; padding:4px 12px; cursor:pointer; }
.ackdo:hover { filter:brightness(1.06); }
.ackdone { color:#1E7A4B; font-weight:600; }
.ackmodal .modalbox { width:min(560px, 100%); }
.ackpop { z-index:400; }
.ackpop .modalbox { max-height:86vh; overflow:auto; border-top:4px solid #F0A020; }
.ackhead { display:flex; gap:12px; align-items:flex-start; }
.ackbell { flex:none; width:40px; height:40px; border-radius:12px; display:grid; place-items:center; background:#FFF1D6; color:#C27400; }
.ackhead h3 { margin:2px 0 2px; }
.ackitems { display:flex; flex-direction:column; gap:10px; margin-top:4px; }
.ackitem { border:1px solid var(--line); border-radius:10px; padding:10px 12px; background:var(--panel); }
.ackfrom { font-size:12px; color:var(--muted); margin-bottom:4px; }
.ackfrom b { color:var(--text); }
.ackbody { font-size:14px; line-height:1.5; white-space:normal; overflow-wrap:anywhere; max-height:240px; overflow:auto; }
.ackacts { display:flex; justify-content:flex-end; gap:8px; margin-top:10px; flex-wrap:wrap; }
.acklist { display:flex; flex-direction:column; gap:4px; max-height:50vh; overflow:auto; }
.ackrow { display:flex; justify-content:space-between; gap:10px; padding:6px 10px; border-radius:8px; background:var(--panel); border:1px solid var(--line); font-size:13px; }
.ackrow em { font-style:normal; font-size:12px; }
.ackrow.wait em { color:#B45309; font-weight:600; }
.ackrow.ok em { color:#1E7A4B; }
@media (max-width:600px) { .ctool.ackswitch span { display:none; } }

/* Pinned messages, WhatsApp and team chat (26 Sep) */
.pinbar { flex:none; border-bottom:1px solid var(--line); background:var(--panel); padding:6px 14px; position:relative; z-index:2; }
.pinhead { display:flex; align-items:center; gap:10px; min-width:0; }
.pinico { flex:none; display:grid; place-items:center; width:26px; height:26px; border-radius:8px; background:#FFF1D6; color:#B45309; }
.pintop { flex:1 1 auto; min-width:0; display:flex; gap:8px; align-items:baseline; border:0; background:transparent; color:var(--text);
  font:inherit; font-size:13px; text-align:left; padding:4px 0; cursor:pointer; }
.pintop b { flex:none; font-weight:600; }
.pintop span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--muted); }
.pintop:hover span { color:var(--text); }
.pinmore { flex:none; border:1px solid var(--line); background:var(--bg); color:var(--claim); border-radius:99px; padding:3px 10px;
  font:inherit; font-size:12px; font-weight:600; cursor:pointer; }
.pinlist { display:flex; flex-direction:column; gap:4px; margin:6px 0 4px; max-height:40vh; overflow:auto; }
.pinrow { display:flex; align-items:stretch; gap:6px; border:1px solid var(--line); border-radius:8px; background:var(--bg); }
.pingo { flex:1 1 auto; min-width:0; text-align:left; border:0; background:transparent; color:var(--text); font:inherit; font-size:13px; padding:6px 10px; cursor:pointer; }
.pingo .pintext { color:var(--text); }
.pingo small { display:block; color:var(--muted); font-size:11.5px; margin-top:2px; }
.pingo:hover { background:var(--raised); }
.pinx { flex:none; border:0; background:transparent; color:var(--muted); font-size:18px; width:34px; cursor:pointer; border-radius:0 8px 8px 0; }
.pinx:hover { color:var(--warn); background:var(--raised); }
.msg.pinnedmsg { box-shadow:inset 3px 0 0 #F0A020; }
.msg.pinnedmsg .tm::before { content:''; display:inline-block; width:11px; height:11px; margin-right:4px; vertical-align:-1px;
  background:#C27400; -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 4h6l-1 5 3 3v2H7v-2l3-3-1-5Z'/%3E%3Cpath d='M12 14v6'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 4h6l-1 5 3 3v2H7v-2l3-3-1-5Z'/%3E%3Cpath d='M12 14v6'/%3E%3C/svg%3E") center/contain no-repeat; }
.iconbtn.pinbtn.on { color:#C27400; }

/* --- Operations | Recruitment dashboards (26 Sep) -------------------------- */
.dashpick {
  height:32px; padding:0 30px 0 12px; border-radius:var(--r-1, 8px);
  border:1px solid var(--line); background-color:var(--panel); color:var(--text);
  font:inherit; font-size:13px; font-weight:600; cursor:pointer;
  appearance:none; -webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 10px center;
}
.dashpick:hover { border-color:var(--accent-line, var(--line)); }
.dashpick:focus { outline:none; border-color:var(--accent-line); box-shadow:0 0 0 3px var(--accent-soft); }
.dashrecr {
  display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:14px;
  margin:0 0 16px; animation:cardin var(--dur-3) var(--ease-out) both;
}
.dashrecr[hidden] { display:none; }
.rteamrow { display:flex; align-items:center; gap:10px; cursor:default; }
.rteamrow .dashmain { min-width:0; flex:1; }
.rteamn { font-weight:500; color:var(--muted); font-size:12px; margin-left:4px; }
.rteammix { display:flex; flex-direction:row; align-items:center; gap:4px; flex-wrap:wrap; justify-content:flex-end; max-width:150px; }
.rteamgo { display:block; margin:6px 14px 12px; padding:0; background:none; border:0; color:var(--claim); font:inherit; font-size:12.5px; font-weight:600; cursor:pointer; }
.rteamgo:hover { text-decoration:underline; }
.rteamnone {
  width:28px; height:28px; border-radius:50%; flex:none;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--raised); color:var(--muted); font-weight:700; font-size:13px;
  box-shadow:inset 0 0 0 1px var(--line);
}
@media (max-width:1100px) { .dashrecr { grid-template-columns:1fr 1fr; } }
@media (max-width:600px) {
  .opstitle { flex-wrap:wrap; }
  .dashrecr .wktile b { font-size:24px; }
}
@media (prefers-reduced-motion:reduce) { .dashrecr { animation:none !important; } }

/* --- a recruitment site given to you: acknowledge it (26 Sep) -------------- */
.sackwhere { display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:15px; }
.sackpname { font-size:12px; color:var(--muted); font-weight:500; }
.sackdue {
  display:flex; flex-direction:column; gap:2px; margin:8px 0 6px; padding:8px 10px;
  border-radius:10px; background:var(--raised); font-size:12.5px; color:var(--text);
}
.sackdue b { font-weight:700; }
.sackdue.late { background:var(--warn-soft, #fdecea); color:var(--warn, #a4301f); }
.sackdue.none { color:var(--muted); }

/* --- the rota as a month calendar (26 Sep) ---------------------------------- */
.rotaacts { display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
.segtabs.rotaview { margin:0; }
.rotacal[hidden], #rotaList[hidden] { display:none; }
.rctools { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin:4px 0 10px; }
.rcwho { display:inline-flex; align-items:center; gap:8px; font-size:12.5px; color:var(--muted); }
.rcwho select {
  height:32px; padding:0 10px; border-radius:var(--r-1, 8px); border:1px solid var(--line);
  background:var(--panel); color:var(--text); font:inherit; font-size:13px;
}
.rclegend { display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
.rcgrid {
  display:grid; grid-template-columns:repeat(7, minmax(0, 1fr));
  border:1px solid var(--line); border-radius:14px; overflow:hidden; background:var(--panel);
}
.rcdow {
  padding:8px 10px; font-size:10.5px; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--dim); background:var(--raised); border-bottom:1px solid var(--line);
}
.rcblank { background:var(--bg); border-right:1px solid var(--line-soft); border-bottom:1px solid var(--line-soft); }
.rccell {
  display:flex; flex-direction:column; gap:6px; align-items:stretch; text-align:left;
  min-height:118px; padding:8px 8px 10px; min-width:0;
  background:var(--panel); color:var(--text); font:inherit; cursor:pointer;
  border:0; border-right:1px solid var(--line-soft); border-bottom:1px solid var(--line-soft); border-radius:0;
  transition:background-color .15s var(--ease, ease);
}
.rcgrid > :nth-child(7n) { border-right:0; }
.rccell:hover { background:var(--accent-soft); }
.rccell:focus-visible { outline:2px solid var(--claim); outline-offset:-2px; }
.rccell.wkend, .rcdow.wkend { background:color-mix(in srgb, var(--raised) 60%, var(--panel)); }
.rccell.today { box-shadow:inset 0 0 0 2px var(--claim); }
.rccell.empty .rchead { opacity:.6; }
.rchead { display:flex; align-items:baseline; justify-content:space-between; gap:6px; }
.rcnum { font-size:13px; font-weight:700; color:var(--muted); font-variant-numeric:tabular-nums; }
.rccell.today .rcnum { color:var(--claim); }
.rcwd { display:none; }
.rccount { font-size:10.5px; color:var(--dim); font-weight:500; }
.rcgroup { display:flex; flex-direction:column; gap:3px; min-width:0; }
.rctime { font-family:var(--font-mono, 'IBM Plex Mono', monospace); font-size:10.5px; color:var(--muted); white-space:nowrap; }
.rcgroup.off .rctime { font-family:inherit; }
.rcpeople { display:flex; flex-wrap:wrap; gap:3px; min-width:0; }
.rcp {
  display:inline-flex; align-items:center; gap:3px; max-width:100%;
  padding:1px 6px; border-radius:99px; font-size:11px; font-weight:500; line-height:1.55;
  background:var(--raised); color:var(--text); box-shadow:inset 0 0 0 1px var(--line);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.rcp i { font-style:normal; font-size:9.5px; font-weight:700; }
.rcp em { font-style:normal; font-size:10px; color:var(--muted); }
.rcp.st-ok i, .rcp.st-now i { color:var(--live); }
.rcp.st-now { box-shadow:inset 0 0 0 1px var(--live); }
.rcp.st-late { box-shadow:inset 0 0 0 1px var(--signal); }
.rcp.st-late i { color:var(--signal); }
.rcp.st-miss { box-shadow:inset 0 0 0 1px var(--warn); color:var(--warn); }
.rcp.st-miss bdi { text-decoration:line-through; }
.rcp.st-off { background:transparent; color:var(--dim); box-shadow:inset 0 0 0 1px var(--line-soft); }
.rcp.st-extra i { color:var(--claim); }
.rcnote { font-size:11.5px; color:var(--dim); margin:8px 2px 0; }
.rcdaytable tr.st-off td { color:var(--dim); }
@media (max-width:900px) {
  .rccell { min-height:96px; padding:6px; }
  .rcp { font-size:10.5px; padding:0 5px; }
}
/* A phone: the month as a list of days. */
@media (max-width:640px) {
  .rcgrid { display:block; }
  .rcdow, .rcblank { display:none; }
  .rccell { width:100%; min-height:0; border-right:0; }
  .rccell.empty { display:none; }
  .rcwd { display:inline; }
}
/* Room for a week across: the Attendance page widens while the calendar is up. */
#v-att .pad:has(#at-team.on #rotaCal:not([hidden])) { max-width:1280px; }
