/* ============================================================================
   eSuite Design System — Bootstrap 5.3 overlay  (NO BUILD STEP REQUIRED)
   VLE Support Ltd.

   HOW TO USE
   Load AFTER Bootstrap's CSS:
     <link rel="stylesheet" href="/assets/bootstrap-5.3.2-dist/css/bootstrap.min.css">
     <link rel="stylesheet" href="/assets/css/esuite-bootstrap.css">
   This file re-skins stock Bootstrap components to the eSuite brand and adds a
   few bespoke classes (avatar, status dot, metric tile, side nav, workspace
   tabs) that Bootstrap has no equivalent for. If you have a Sass build, prefer
   _esuite-variables.scss instead — this overlay is the drop-in fallback.
   ============================================================================ */

/* Fonts — swap to self-hosted @font-face for production (see CLAUDE.md) */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');

/* ----------------------------------------------------------------------------
   1. TOKENS  (the single source of truth — every rule below resolves to these)
---------------------------------------------------------------------------- */
:root {
  /* Primary blue ramp (anchor #4265AF) */
  --es-blue-900: #293F6D;  --es-blue-850: #33497D;  --es-blue-800: #4265AF;  --es-blue-700: #4B72C6;  --es-blue-600: #5481E0;
  --es-blue-tint: #E7ECF6;
  /* Aqua (secondary accent / info) */
  --es-aqua-700: #0673A1;  --es-aqua-600: #0098D4;  --es-aqua-500: #00AEEF;  --es-aqua-400: #1BB8F1;  --es-aqua-50: #DFF4FC;
  /* Status */
  --es-orange-600:#C14800; --es-orange-500:#FA650D; --es-orange-50:#FEE8D8;
  --es-red-700:#A01010;    --es-red-500:#DE1F1F;    --es-red-50:#FDDCDC;
  --es-yellow-700:#9A6200; --es-yellow-500:#FCA833; --es-yellow-50:#FEF4DC;
  --es-green-700:#09630B;  --es-green-500:#27AC2A;  --es-green-50:#D6F5D6;
  /* HR annual leave ONLY. Matches Microsoft Teams "Out of office" so the two calendars agree
     about the same person on the same day. Outside the eSuite palette on purpose, and scoped
     to leave blocks in §13 -- nothing else may reach for it. */
  --es-hr-leave-700:#8A0077; --es-hr-leave:#B4009E; --es-hr-leave-50:#F9E3F5;
  /* The one frame drawn in black: it fences Team leave's Cover row off from the people above it,
     and navy read as one more shade of the chrome it sits in. Scoped to that row and nothing else. */
  --es-hr-cover-frame:#000000;
  /* The second, and the last. A dev board card is a physical card: it carries a tint of its own,
     so a hairline in any palette grey reads as part of the tint rather than an edge -- and the
     small print under the badges has to clear contrast on twelve different hues, which grey-400
     does on none of them. Its edge and its ink are the same black. */
  --es-devcard-ink:#000000;
  /* Neutrals */
  --es-white:#FFFFFF; --es-grey-50:#F1F2F7; --es-grey-100:#E1E6EB; --es-grey-300:#C7CCD4;
  --es-grey-400:#9F9F9F; --es-grey-600:#6B6B6B; --es-grey-700:#4D4D4D;
  /* Zebra fill for table rows — grey-50 thinned toward white, not a new hue. Full-strength
     grey-50 IS the page colour, so a striped row inside a white card reads as the page
     showing through the table rather than as part of it. */
  --es-row-stripe: color-mix(in srgb, var(--es-grey-50) 50%, #fff);
  /* Semantic */
  --es-accent: var(--es-blue-800);  --es-accent-hover: var(--es-blue-700);  --es-accent-press:#34528C;
  /* muted TEXT must clear WCAG AA (4.5:1); grey-400 (#9F9F9F ~2.6:1) fails, so
     map to grey-600 (#6B6B6B ~5.3:1). grey-400 stays for icons/borders/placeholders. */
  --es-text-strong: var(--es-blue-900);  --es-text-body: var(--es-grey-700);  --es-text-muted: var(--es-grey-600);
  --es-border-subtle: rgba(10,26,47,.10);  --es-border-input: var(--es-grey-100);
  --es-focus-ring: 0 0 0 3px rgba(66,101,175,.30);
  /* Shadows */
  --es-shadow-sm: 0 1px 3px rgba(10,26,47,.08), 0 1px 2px rgba(10,26,47,.04);
  --es-shadow-md: 0 4px 12px rgba(10,26,47,.10);
  --es-shadow-lg: 0 12px 28px rgba(10,26,47,.14);
  /* Motion */
  --es-ease: cubic-bezier(.2,0,0,1);  --es-dur: .12s;
  --es-font-sans: 'Poppins','Segoe UI',system-ui,-apple-system,sans-serif;
  --es-font-mono: 'JetBrains Mono',ui-monospace,'SFMono-Regular',Menlo,monospace;

  /* ---- Re-point Bootstrap's own variables to eSuite ---- */
  --bs-body-font-family: var(--es-font-sans);
  --bs-body-font-size: .9375rem;          /* 15px */
  --bs-body-color: var(--es-text-body);
  --bs-body-bg: var(--es-grey-50);        /* light-grey page */
  --bs-emphasis-color: var(--es-text-strong);
  --bs-secondary-color: var(--es-text-muted);
  --bs-primary: var(--es-blue-800);
  --bs-primary-rgb: 66,101,175;
  --bs-link-color: var(--es-blue-800);
  --bs-link-color-rgb: 66,101,175;
  --bs-link-hover-color: var(--es-accent-press);
  --bs-border-color: var(--es-grey-100);
  --bs-border-radius: .375rem;            /* 6px  controls */
  --bs-border-radius-sm: .25rem;          /* 4px  */
  --bs-border-radius-lg: .625rem;         /* 10px cards/nav */
  --bs-border-radius-xl: .875rem;         /* 14px panels */
  --bs-border-radius-pill: 999px;
  --bs-box-shadow-sm: var(--es-shadow-sm);
  --bs-box-shadow: var(--es-shadow-md);
  --bs-box-shadow-lg: var(--es-shadow-lg);
}

body { font-family: var(--es-font-sans); }
h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6 { color: var(--es-text-strong); font-weight: 600; }
/* Pop-up (modal) headers are coloured chrome — keep their title text white (headings default to navy above). */
.modal-header.bg-navy,.modal-header.bg-orange,.modal-header.bg-danger,
.modal-header.bg-navy .modal-title,.modal-header.bg-orange .modal-title,.modal-header.bg-danger .modal-title,
.modal-header.bg-navy h1,.modal-header.bg-navy h2,.modal-header.bg-navy h3,.modal-header.bg-navy h4,.modal-header.bg-navy h5,.modal-header.bg-navy h6 { color:#fff !important; }
.h1,h1{font-size:2rem}.h2,h2{font-size:1.5rem}.h3,h3{font-size:1.1875rem}
.h4,h4{font-size:.9375rem}.display-1,.es-display{font-size:2.75rem;font-weight:600}
a { text-decoration: none; }
a:hover { text-decoration: underline; }

/* Brand wordmark — blue text, orange 2nd character */
.es-wordmark{font-weight:600;color:var(--es-blue-800);letter-spacing:-.01em}
.es-wordmark .e{font-weight:500}
.es-wordmark .o{color:var(--es-orange-500)}
.bg-navy .es-wordmark,.es-wordmark.on-navy{color:#fff}

/* Helpers */
.hide{display:none!important}
.text-navy{color:var(--es-blue-900)!important}
.text-aqua{color:var(--es-aqua-500)!important}
.text-muted-es{color:var(--es-text-muted)!important}
.bg-navy{background:var(--es-blue-800)!important;color:#fff}
.bg-blue-tint{background:var(--es-blue-tint)!important}
.font-mono{font-family:var(--es-font-mono)!important}
.es-eyebrow{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.12em;color:var(--es-aqua-700)}
/* A question mark that carries its explanation in title=. Focusable, so it is reachable without a
   mouse -- a tooltip only a hover can open is help that a keyboard user does not have. */
.es-help-mark{display:inline-flex;align-items:center;justify-content:center;width:1.15em;height:1.15em;
  margin-left:.4em;border-radius:50%;border:1px solid currentColor;font-size:.75rem;font-weight:700;
  line-height:1;cursor:help;opacity:.75;vertical-align:middle}
.es-help-mark:hover,.es-help-mark:focus{opacity:1}
.es-doc-chip{display:inline-flex;align-items:center;gap:.4em;padding:.35em .7em;border-radius:7px;background:var(--es-green-50);color:var(--es-green-700);font-size:.8125rem;font-weight:600;text-decoration:none;transition:opacity .12s cubic-bezier(.2,0,0,1)}
.es-doc-chip:hover{opacity:.86;color:var(--es-green-700)}

/* ----------------------------------------------------------------------------
   2. BUTTONS   — 6px radius, Poppins 600, hover lifts via opacity (no shadow)
---------------------------------------------------------------------------- */
.btn{--bs-btn-font-family:var(--es-font-sans);--bs-btn-font-weight:600;
  --bs-btn-border-radius:.375rem;--bs-btn-font-size:.8125rem;
  --bs-btn-padding-y:.5625rem;--bs-btn-padding-x:1.125rem;line-height:1.2;
  transition:opacity var(--es-dur) var(--es-ease),background var(--es-dur) var(--es-ease),transform var(--es-dur) var(--es-ease)}
.btn:hover{opacity:.86}
.btn:focus-visible{box-shadow:var(--es-focus-ring)}
/* Feedback on the press itself, not the release — Bootstrap's own base .btn:active only recolours
   background/border, which several variants here don't even set (.btn-ghost, .btn-navy) and which
   reads as too subtle to notice on the ones that do. A universal scale-down applies to every
   variant alike and is unmistakable regardless of colour. */
.btn:active{transform:scale(.97)}
.btn-sm{--bs-btn-padding-y:.3125rem;--bs-btn-padding-x:.75rem;--bs-btn-font-size:.6875rem}

/* Primary = brand blue */
.btn-primary{--bs-btn-bg:var(--es-blue-800);--bs-btn-border-color:var(--es-blue-800);
  --bs-btn-hover-bg:var(--es-blue-800);--bs-btn-hover-border-color:var(--es-blue-800);
  --bs-btn-active-bg:var(--es-accent-press);--bs-btn-active-border-color:var(--es-accent-press);--bs-btn-color:#fff}
/* Aqua (secondary accent / info CTA) — fill darkened aqua-500→aqua-700 so the #fff
   button label clears WCAG AA: white on aqua-500 ≈ 2.5:1 (fail), on aqua-700 ≈ 5.3:1
   (pass). Hover keeps the same fill (global .btn:hover dims via opacity, not colour). */
.btn-aqua,.btn-info{--bs-btn-bg:var(--es-aqua-700);--bs-btn-border-color:var(--es-aqua-700);
  --bs-btn-hover-bg:var(--es-aqua-700);--bs-btn-hover-border-color:var(--es-aqua-700);
  --bs-btn-active-bg:var(--es-aqua-700);--bs-btn-color:#fff;--bs-btn-hover-color:#fff}
/* Navy/deep */
.btn-navy{--bs-btn-bg:var(--es-blue-900);--bs-btn-border-color:var(--es-blue-900);
  --bs-btn-hover-bg:var(--es-blue-900);--bs-btn-color:#fff;--bs-btn-hover-color:#fff}
/* Orange (Add / create) */
.btn-orange,.btn-warning{--bs-btn-bg:var(--es-orange-500);--bs-btn-border-color:var(--es-orange-500);
  --bs-btn-hover-bg:var(--es-orange-500);--bs-btn-color:#fff;--bs-btn-hover-color:#fff;--bs-btn-active-bg:var(--es-orange-600)}
/* Danger */
.btn-danger{--bs-btn-bg:var(--es-red-500);--bs-btn-border-color:var(--es-red-500);--bs-btn-hover-bg:var(--es-red-500);--bs-btn-color:#fff;--bs-btn-hover-color:#fff;--bs-btn-active-bg:var(--es-red-700)}
/* Green (achieved / proceed) — fill darkened green-500→green-700 so the #fff
   button label clears WCAG AA (white on green-500 ≈ 2.2:1 fail, on green-700
   ≈ 6.5:1 pass), same rationale as .btn-aqua. Reserve for "successful / proceed"
   actions; blue stays the default primary CTA. */
.btn-green{--bs-btn-bg:var(--es-green-700);--bs-btn-border-color:var(--es-green-700);
  --bs-btn-hover-bg:var(--es-green-700);--bs-btn-hover-border-color:var(--es-green-700);
  --bs-btn-active-bg:var(--es-green-700);--bs-btn-color:#fff;--bs-btn-hover-color:#fff}
/* Disabled solid buttons. Bootstrap's own .btn-* variants are generated with a matching set of
   --bs-btn-disabled-* values; the five above are hand-written and set only the LIVE colours, so a
   disabled one fell through to the base .btn's transparent disabled fill and rendered as body-grey
   text on nothing — a button that looks like it has gone rather than one that cannot be pressed.
   Flat grey and faded, matching .es-pager button:disabled and the table pager above. */
.btn-navy:disabled,.btn-navy.disabled,
.btn-orange:disabled,.btn-orange.disabled,
.btn-danger:disabled,.btn-danger.disabled,
.btn-green:disabled,.btn-green.disabled,
.btn-aqua:disabled,.btn-aqua.disabled{
  --bs-btn-disabled-bg:var(--es-grey-100,#E1E6EB);
  --bs-btn-disabled-border-color:var(--es-grey-100,#E1E6EB);
  --bs-btn-disabled-color:var(--es-grey-700,#4D4D4D);
  cursor:not-allowed}
/* Outline (1.5px) + Ghost */
.btn-outline-primary{--bs-btn-color:var(--es-aqua-600);--bs-btn-border-color:var(--es-aqua-500);--bs-btn-border-width:1.5px;
  --bs-btn-hover-bg:var(--es-aqua-500);--bs-btn-hover-border-color:var(--es-aqua-500);--bs-btn-hover-color:#fff}
.btn-ghost{--bs-btn-bg:var(--es-grey-50);--bs-btn-color:var(--es-grey-700);--bs-btn-border-color:var(--es-grey-300);
  --bs-btn-border-width:1.5px;--bs-btn-hover-bg:var(--es-grey-100);--bs-btn-hover-color:var(--es-blue-900);--bs-btn-hover-border-color:var(--es-grey-400)}
/* Low-emphasis "Change a completed/verified row" action — ghost button with aqua-700 text (§6.4d) */
.btn-ghost-aqua{--bs-btn-color:var(--es-aqua-700,#0673A1);--bs-btn-hover-color:var(--es-aqua-700,#0673A1)}
/* Icon button: white icons at rest, flip to orange on hover/focus (e.g. the navy "View learner" action) */
.btn.es-btn-icon-hover [class*="fa-"]{color:#fff;transition:color .12s}
.btn.es-btn-icon-hover:hover [class*="fa-"],
.btn.es-btn-icon-hover:focus-visible [class*="fa-"]{color:var(--es-orange-500,#FA650D)}

/* ----------------------------------------------------------------------------
   3. BADGES  — soft tint background + saturated text, pill
---------------------------------------------------------------------------- */
.badge{font-family:var(--es-font-sans);font-weight:600;font-size:.6875rem;padding:.25rem .625rem;border-radius:999px}
.badge-soft-blue  {background:var(--es-blue-tint);color:var(--es-blue-800)}
.badge-soft-aqua  {background:var(--es-aqua-50);color:var(--es-aqua-600)}
.badge-soft-orange{background:var(--es-orange-50);color:var(--es-orange-600)}
.badge-soft-red   {background:var(--es-red-50);color:var(--es-red-700)}
.badge-soft-green {background:var(--es-green-50);color:var(--es-green-700)}
.badge-soft-yellow{background:var(--es-yellow-50);color:var(--es-yellow-700)}
.badge-soft-grey  {background:var(--es-grey-50);color:var(--es-grey-700)}
.badge .es-dot{width:6px;height:6px;border-radius:50%;background:currentColor;display:inline-block;margin-right:2px}

/* ----------------------------------------------------------------------------
   3b. NAV PILLS  — eSuite navy-active in-screen switcher (one source of truth)
   Bootstrap compiles the active bg as a literal #0d6efd, so re-pointing
   --bs-primary at runtime doesn't reach it. This restyles every .nav-pills bar
   (both BS5 .nav-link.active and the legacy li.active > a markup) to the eSuite
   blue-active pill with a grey-50 hover. JS hooks (.active toggling, a[data-page])
   are untouched — this is colour only.
---------------------------------------------------------------------------- */
.nav-pills{--bs-nav-pills-link-active-bg:var(--es-blue-800);gap:4px}
.nav-pills .nav-link,.nav-pills>li>a{border-radius:999px;color:var(--es-blue-800)}
.nav-pills .nav-link.active,.nav-pills>li.active>a{background-color:var(--es-blue-800)!important;color:#fff!important}
.nav-pills .nav-link:not(.active):hover,.nav-pills>li:not(.active)>a:hover{background-color:var(--es-grey-50)}
.nav-pills>li.active{cursor:default}

/* ----------------------------------------------------------------------------
   4. CARDS  — white box, 10px radius, hairline border, coloured header bar
---------------------------------------------------------------------------- */
.card{--bs-card-border-radius:.625rem;--bs-card-border-color:var(--es-border-subtle);
  --bs-card-bg:var(--es-white);--bs-card-inner-border-radius:.625rem;border-width:.5px;overflow:hidden}
.card-header{font-weight:600;font-size:.8125rem;color:#fff;border-bottom:none;
  display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.625rem 1rem}
.card-header.bg-navy  {background:var(--es-blue-800)}
.card-header.bg-aqua  {background:var(--es-aqua-500)}
.card-header.bg-orange{background:var(--es-orange-500)}
/* Linked headings / accordion toggles inside a coloured card header must read as the
   header text, not as a blue hyperlink (which blends into navy). Excludes .btn so
   styled buttons placed in a header keep their own colours. */
.card-header.bg-navy a:not(.btn),.card-header.bg-aqua a:not(.btn),.card-header.bg-orange a:not(.btn),
.card-header.bg-navy a:not(.btn):hover,.card-header.bg-aqua a:not(.btn):hover,.card-header.bg-orange a:not(.btn):hover{color:#fff}
.card-body{color:var(--es-text-body);font-size:.8125rem}
/* A card that opens. The whole header bar is the control, following .es-alert-group (6b). */
details.card > summary.card-header{cursor:pointer;list-style:none;user-select:none}
details.card > summary.card-header::-webkit-details-marker{display:none}
details.card > summary.card-header:focus-visible{outline:none;box-shadow:inset 0 0 0 3px var(--es-focus-ring)}
.es-card-caret{font-size:.6875rem;transition:transform .14s cubic-bezier(.2,0,0,1)}
details.card[open] > summary.card-header .es-card-caret{transform:rotate(90deg)}
/* Organisation search — the JS builds an absolutely-positioned results dropdown
   (position:absolute; top:100%) and inserts it into the search column. Without a
   positioned wrapper the dropdown anchors to the nearest positioned ancestor —
   the .card, which sets overflow:hidden above — so it renders at the bottom edge
   of the whole card and is clipped out of view (visible in the Network tab but
   not on screen). Give the container its own positioning context so the dropdown
   anchors to the search row and escapes the card's clip. Fixes every
   organisation search (employmentStatus, learningAim, claims, referrals copies). */
.organisation_search_container{position:relative}

/* Content list-group (record-detail rows) — white rows, hairline dividers, one 10px rounded shell.
   :not(.lhs):not(.lhs-sub) keeps the navy VerticalNav (§10) on its own theme. Without this,
   stock BS5 derives --bs-list-group-bg from --bs-body-bg (re-pointed to grey-50) → grey full-bleed rows. */
.list-group:not(.lhs):not(.lhs-sub){
  --bs-list-group-bg: var(--es-white);
  --bs-list-group-border-color: var(--es-border-subtle);
  --bs-list-group-border-radius: .625rem;     /* 10px shell, not the 6px control radius */
  border-radius:.625rem; overflow:hidden;
}
.list-group:not(.lhs):not(.lhs-sub) > .list-group-item{
  background: var(--es-white);
  border:0; border-bottom:.5px solid var(--es-grey-100);
  padding:.75rem .9375rem; color:var(--es-text-body);
}
.list-group:not(.lhs):not(.lhs-sub) > .list-group-item:last-child{ border-bottom:0; }
.list-group:not(.lhs):not(.lhs-sub) > .list-group-item:hover{ background:var(--es-grey-50); }

/* ── Everything else BS5 paints with the page colour ──────────────────────────────────
   Stock Bootstrap derives these from --bs-body-bg, which §2 re-points to the light-grey
   PAGE (its own default is white, so upstream never had this problem). Every one of them
   is a box that sits ON the page, so left alone each paints itself the colour of the
   ground behind it: a plain .table dissolves into the screen inside its own white card —
   which is what the enrolment form-builder screens showed — and so do dropdown menus,
   modals, accordions, pagers, and the box of an unticked checkbox.

   CONTENT IS WHITE; ONLY THE PAGE IS GREY. .card and .list-group above are the same fix,
   made earlier and one component at a time; this is the rest of the set. Row striping
   uses the thinned tint rather than grey-50, for the reason given at the token. */
.table       { --bs-table-bg: var(--es-white); --bs-table-striped-bg: var(--es-row-stripe); }
.dropdown-menu{ --bs-dropdown-bg: var(--es-white); }
.modal       { --bs-modal-bg: var(--es-white); }
.accordion   { --bs-accordion-bg: var(--es-white); }
.pagination  { --bs-pagination-bg: var(--es-white); }
.popover     { --bs-popover-bg: var(--es-white); }
.offcanvas   { --bs-offcanvas-bg: var(--es-white); }
.nav-tabs    { --bs-nav-tabs-link-active-bg: var(--es-white); }
.form-check-input{ --bs-form-check-bg: var(--es-white); }

/* ----------------------------------------------------------------------------
   5. FORMS  — 1.5px "blue white" border, blue focus ring
---------------------------------------------------------------------------- */
.form-label{font-size:.6875rem;font-weight:600;color:var(--es-text-body);margin-bottom:.3125rem}
.form-control,.form-select{font-family:var(--es-font-sans);font-size:.8125rem;color:var(--es-text-body);
  border:1.5px solid var(--es-border-input);border-radius:.375rem;padding:.5625rem .75rem;background:#fff}
.form-control:focus,.form-select:focus{border-color:var(--es-blue-800);box-shadow:var(--es-focus-ring)}
.form-control.width-auto,.form-select.width-auto{display:inline-block;width:auto!important}
/* An .es-ribbon divider orphaned at the start/end of a wrapped 2nd tier is hidden by init.js (esRibbonCollapseDividers). */
.es-ribbon-divider--wrapped{display:none!important}
/* Legacy active-item marker: init.js injects <div class="ribbonItemSelected"> into the active ribbon item
   for backwards-compat; eSuite ribbons show the active item via the .active class instead. vlesupport-theme.css
   gives .ribbonItemSelected an aqua-tint background, so left visible it paints a stripe over the active tab's
   label. Hide it globally for every eSuite ribbon (covers ribbons that don't declare their own local rule,
   e.g. providerManager). */
.es-ribbon .ribbonItemSelected,.es-ribbon-tab .ribbonItemSelected{display:none!important}
.form-control.is-invalid{border-color:var(--es-red-500)}
.form-text{font-size:.625rem;color:var(--es-text-muted)}
.form-check-input{width:1rem;height:1rem;border:1.5px solid var(--es-grey-300);accent-color:var(--es-accent)}
.form-check-input:checked{background-color:var(--es-accent);border-color:var(--es-accent)}
.form-check-input:focus{box-shadow:var(--es-focus-ring)}
.form-check-label{font-size:.8125rem;color:var(--es-text-body)}

/* ----------------------------------------------------------------------------
   6. ALERTS  — 4px left rule + tint + leading icon (NOT a full border)
---------------------------------------------------------------------------- */
.alert{font-size:.8125rem;line-height:1.3;border:none;border-left:4px solid;border-radius:.5rem;
  padding:.75rem 1rem;display:flex;gap:.625rem;align-items:flex-start;overflow-wrap:break-word;word-break:break-word}
/* Flex children must be allowed to shrink below their content width, or long text/tokens
   overflow the alert in a narrow column (e.g. a col-lg-2 sidebar). Icon keeps flex-shrink:0. */
.alert>*{min-width:0}
.alert>.es-alert-icon{font-size:1rem;flex-shrink:0;margin-top:2px}
.alert-info   {background:var(--es-aqua-50);border-left-color:var(--es-aqua-500);color:#006A8E}
.alert-warning{background:var(--es-yellow-50);border-left-color:var(--es-yellow-500);color:var(--es-yellow-700)}
.alert-danger {background:var(--es-red-50);border-left-color:var(--es-red-500);color:var(--es-red-700)}
.alert-success{background:var(--es-green-50);border-left-color:var(--es-green-500);color:var(--es-green-700)}

/* --- 6b. ALERT GROUP — one collapsible "Alerts" segment above a screen's content ------------ */
.es-alert-group{background:var(--es-white);border:1px solid var(--es-grey-100);border-radius:.625rem;
  margin-bottom:1rem;overflow:hidden}
.es-alert-group-head{display:flex;align-items:center;gap:.5rem;padding:.625rem .875rem;cursor:pointer;
  list-style:none;user-select:none;font-size:.8125rem;color:var(--es-blue-900);
  transition:background .14s cubic-bezier(.2,0,0,1)}
.es-alert-group-head::-webkit-details-marker{display:none}   /* the native triangle; we draw our own */
.es-alert-group-head:hover{background:var(--es-grey-50)}
.es-alert-group-head:focus-visible{outline:none;box-shadow:inset 0 0 0 3px var(--es-focus-ring)}
.es-alert-group-title{font-weight:600}
.es-alert-group-count{color:var(--es-grey-400);font-size:.6875rem}
.es-alert-group-dot{width:8px;height:8px;border-radius:999px;flex-shrink:0;background:var(--es-aqua-500)}
.es-alert-group-dot.tone-danger {background:var(--es-red-500)}
.es-alert-group-dot.tone-warning{background:var(--es-yellow-500)}
.es-alert-group-dot.tone-success{background:var(--es-green-500)}
.es-alert-group-dot.tone-info   {background:var(--es-aqua-500)}
/* The chevron sits hard right and turns to point at the content it has opened. */
.es-alert-group-chevron{margin-left:auto;color:var(--es-grey-400);font-size:.75rem;
  transition:transform .14s cubic-bezier(.2,0,0,1)}
.es-alert-group[open] .es-alert-group-chevron{transform:rotate(180deg)}
.es-alert-group-body{padding:0 .875rem .875rem;display:flex;flex-direction:column;gap:.5rem}
.es-alert-group-body .alert{margin-bottom:0}

/* --- 6c. PRODUCT TIMELINE — contract bars on a shared time axis (account overview) ----------
   Hand-built inline SVG, computed server-side; this file only styles the frame. */
.es-timeline-scroll{overflow-x:auto;padding:4px 0}
.es-product-timeline{min-width:560px;display:block}
.es-timeline-key{display:flex;flex-wrap:wrap;gap:14px;margin-top:8px;font-size:.6875rem;
  color:var(--es-grey-700)}
.es-timeline-key span{display:inline-flex;align-items:center;gap:5px}
.es-timeline-key i{width:10px;height:10px;border-radius:999px;display:inline-block;opacity:.85}

/* ----------------------------------------------------------------------------
   7. PROGRESS  — 10px pill track, brand fill, label row above
---------------------------------------------------------------------------- */
.es-progress-label{display:flex;justify-content:space-between;font-size:.6875rem;color:var(--es-text-body);margin-bottom:4px}
.es-progress-label .val{font-weight:600}
.progress{height:10px;background:var(--es-grey-100);border-radius:999px}
.progress-bar{border-radius:999px;transition:width .3s var(--es-ease)}
.progress-bar.tone-aqua{background:var(--es-aqua-500)}.es-progress-label.tone-aqua .val{color:var(--es-aqua-500)}
.progress-bar.tone-green{background:var(--es-green-500)}.es-progress-label.tone-green .val{color:var(--es-green-500)}
.progress-bar.tone-orange{background:var(--es-orange-500)}.es-progress-label.tone-orange .val{color:var(--es-orange-500)}
.progress-bar.tone-red{background:var(--es-red-500)}.es-progress-label.tone-red .val{color:var(--es-red-500)}
.progress-bar.tone-blue{background:var(--es-blue-800)}.es-progress-label.tone-blue .val{color:var(--es-blue-800)}
.progress-bar.tone-yellow{background:var(--es-yellow-500)}.es-progress-label.tone-yellow .val{color:var(--es-yellow-700)}

/* ----------------------------------------------------------------------------
   8. TABLES (DataTable)  — navy header, zebra rows, blue-tint hover
      Canonical markup emitted by Table::render (see public/assets/lib/Table.php):
        <div class="es-table-wrap">
          <div class="es-table-scroll"><table class="es-table">
            <thead><tr><th><span class="es-th">
              <button class="es-th-label" …>Label <i class="fa-solid fa-sort"></i></button>
              <button class="es-th-funnel" data-filter …><i class="fa-solid fa-filter"></i></button>
            </span></th>…</tr></thead>
            <tbody><tr data-href…><td class="mono">…</td>…</tr></tbody>
            <tfoot><tr><td colspan="…"><div class="es-table-foot">…pager…</div></td></tr></tfoot>
          </table></div>
        </div>
      NOTE the foot is INSIDE the table, not a sibling of .es-table-scroll — this comment said
      otherwise until it was checked against the renderer. It matters: being inside the scroller is
      why a wide table needs the pager pinned (see "Pin the pager" below).
      Funnel popover + client-side filter behaviour lives in /assets/js/esuite-table.js.
      Add .is-active to a .es-th-funnel when its column is filtered (turns aqua).
---------------------------------------------------------------------------- */
.es-table-wrap{background:#fff;border:.5px solid var(--es-border-subtle);border-radius:10px;overflow:hidden;
  font-family:var(--es-font-sans)}
.es-table-scroll{overflow-x:auto}

.es-table{width:100%;border-collapse:collapse;font-size:13.5px;color:var(--es-grey-700);margin:0}

/* Header — navy bar, white text */
.es-table>thead>tr>th{background:var(--es-blue-900,#293F6D);color:#fff;font-weight:600;font-size:13px;
  text-align:left;padding:9px 14px;white-space:nowrap;vertical-align:middle;position:sticky;top:0;z-index:1}
.es-table.head-orange>thead>tr>th{background:var(--es-orange-500)}
.es-th{display:inline-flex;align-items:center;gap:4px}
.es-th-label{display:inline-flex;align-items:center;gap:7px;background:none;border:0;padding:0;margin:0;
  cursor:pointer;color:#fff;font:600 13px var(--es-font-sans)}
.es-th-label>i{font-size:10px;color:rgba(255,255,255,.55);transition:color .12s}
.es-th-label:hover>i{color:#fff}
/* Funnel */
.es-th-funnel{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:4px;
  border:0;padding:0;margin-left:2px;cursor:pointer;font-size:10px;background:transparent;
  color:rgba(255,255,255,.62);transition:background .12s,color .12s}
.es-th-funnel:hover{color:#fff}
.es-th-funnel.is-active{color:var(--es-aqua-400,#1BB8F1);background:rgba(255,255,255,.2)}
.es-th-funnel:focus-visible{outline:2px solid #fff;outline-offset:1px}

/* Body — striped + aqua hover */
.es-table>tbody>tr{transition:background .12s;cursor:pointer}
.es-table>tbody>tr:nth-child(even){background:var(--es-row-stripe)}
.es-table>tbody>tr:hover{background:var(--es-aqua-50,#DFF4FC)}
.es-table>tbody>tr>td{padding:9px 14px;border-bottom:.5px solid var(--es-border-subtle);white-space:nowrap}
.es-table td.mono,.es-table .mono{font-family:var(--es-font-mono);font-size:12.5px}
.es-table td.strong,.es-table .strong{font-weight:600;color:var(--es-blue-900,#293F6D)}
.es-table a{color:var(--es-aqua-700,#0673A1);text-decoration:none}
.es-table a:hover{text-decoration:underline}
/* Buttons embedded in a cell are not links — never underline them (the gap between icons was showing as a stray "_") */
.es-table a.btn,.es-table a.btn:hover,.es-table a.btn:focus{text-decoration:none}
/* ...and not aqua-text either. .es-table a above sets colour for a plain row link, but wins over
   a .btn's own colour by specificity — a coloured button rendered as an <a> was showing its fill
   instead of the white every other .btn of that colour gets. Read the button's own variable back. */
.es-table a.btn{color:var(--bs-btn-color)}

/* Auto-fit — equal-width columns that truncate (ellipsis) so the table fits the viewport (opt-in via Table "autoFit") */
.es-table.es-table-fit{table-layout:fixed}
.es-table.es-table-fit>thead>tr>th,.es-table.es-table-fit>tbody>tr>td{overflow:hidden;text-overflow:ellipsis}
.es-table.es-table-fit>thead>tr>th .es-th{max-width:100%}
.es-table.es-table-fit>thead>tr>th .es-th-label{min-width:0;overflow:hidden;text-overflow:ellipsis}

/* Column sizing — drag a heading's right-hand edge; esuite-table.js pins the widths it measures.
   The grip sits inside the th, so the th has to be the positioning context. */
.es-table>thead>tr>th{position:relative}
.es-col-grip{position:absolute;top:0;right:0;width:9px;height:100%;cursor:col-resize;touch-action:none;
  -webkit-user-select:none;user-select:none}
.es-col-grip::after{content:"";position:absolute;top:22%;right:4px;width:1px;height:56%;
  background:rgba(255,255,255,.28);transition:background .12s,width .12s}
.es-col-grip:hover::after{background:var(--es-aqua-400,#1BB8F1);width:2px}
.es-col-grip:focus-visible{outline:2px solid #fff;outline-offset:-2px;border-radius:2px}
.es-table.es-resizing{-webkit-user-select:none;user-select:none;cursor:col-resize}
.es-table.es-resizing .es-col-grip::after{background:var(--es-aqua-400,#1BB8F1);width:2px}
/* Pinned widths only hold under a fixed layout, so anything too long is clipped rather than pushing out. */
.es-table.es-table-sized{table-layout:fixed}
.es-table.es-table-sized>thead>tr>th,.es-table.es-table-sized>tbody>tr>td{overflow:hidden;text-overflow:ellipsis}
.es-table.es-table-sized>thead>tr>th .es-th{max-width:100%}
.es-table.es-table-sized>thead>tr>th .es-th-label{min-width:0;overflow:hidden;text-overflow:ellipsis}

/* Dev board — the stages side by side, one column each, cards inside. */
.es-kanban{display:flex;align-items:flex-start;gap:12px;overflow-x:auto;padding-bottom:6px}
.es-kanban-col{flex:1 1 0;min-width:232px;display:flex;flex-direction:column;
  background:var(--es-grey-50,#F1F2F7);border:.5px solid var(--es-border-subtle);border-radius:10px}
.es-kanban-head{display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:10px 12px;border-bottom:.5px solid var(--es-border-subtle)}
.es-kanban-name{font:600 11px var(--es-font-sans);letter-spacing:.12em;text-transform:uppercase;
  color:var(--es-blue-900,#293F6D)}
.es-kanban-body{display:flex;flex-direction:column;gap:8px;padding:12px}
.es-kanban-card{display:block;padding:10px 12px;background:var(--es-white);border-radius:8px;
  border:1px solid var(--es-devcard-ink);box-shadow:var(--es-shadow-sm);text-decoration:none;
  transition:box-shadow .12s,transform .12s}
.es-kanban-card:hover,.es-kanban-card:focus-visible{box-shadow:var(--es-shadow-md);
  transform:translateY(-1px);text-decoration:none}
.es-kanban-title{display:block;font-weight:600;color:var(--es-blue-900,#293F6D);margin-bottom:6px}
.es-kanban-tags{display:flex;flex-wrap:wrap;gap:4px;margin-bottom:6px}
.es-kanban-meta{display:block;font-size:12px;color:var(--es-devcard-ink)}
/* .text-muted carries its own grey, which is the colour that failed here. */
.es-kanban-meta .text-muted{color:var(--es-devcard-ink)}
.es-kanban-empty{font-size:12px;color:var(--es-grey-400,#9F9F9F)}
/* Item tints — the soft hues a development item can be painted in, on a card or a row.
   Six are the palette's own -50 steps; the rest fill the ramp out so there is a usable range.

   Each class names a tint rather than painting one, so one rule paints whatever wears it and a
   row can beat the zebra stripe without a rule per colour. A tint is a background only: the
   text on it stays body colour, so contrast never moves. */
:root{
  --es-tint-slate:#EEF1F6; --es-tint-blue:#E6ECF8; --es-tint-aqua:#DFF4FC;
  --es-tint-teal:#DDF3EF; --es-tint-mint:#D6F5D6; --es-tint-lime:#EEF6DC;
  --es-tint-sand:#FBF1DA; --es-tint-gold:#FEF4DC; --es-tint-peach:#FEE8D8;
  --es-tint-blush:#FDDCDC; --es-tint-rose:#FAE3EE; --es-tint-lilac:#EFE7F8;
}
.es-tint-slate{--es-tint:var(--es-tint-slate)}
.es-tint-blue{--es-tint:var(--es-tint-blue)}
.es-tint-aqua{--es-tint:var(--es-tint-aqua)}
.es-tint-teal{--es-tint:var(--es-tint-teal)}
.es-tint-mint{--es-tint:var(--es-tint-mint)}
.es-tint-lime{--es-tint:var(--es-tint-lime)}
.es-tint-sand{--es-tint:var(--es-tint-sand)}
.es-tint-gold{--es-tint:var(--es-tint-gold)}
.es-tint-peach{--es-tint:var(--es-tint-peach)}
.es-tint-blush{--es-tint:var(--es-tint-blush)}
.es-tint-rose{--es-tint:var(--es-tint-rose)}
.es-tint-lilac{--es-tint:var(--es-tint-lilac)}
[class*="es-tint-"]{background:var(--es-tint)}
/* A tinted row has to outrank the stripe, which is a more specific selector than a class. */
.es-table>tbody>tr[class*="es-tint-"],
.es-table>tbody>tr[class*="es-tint-"]:nth-child(even){background:var(--es-tint)}
.es-table>tbody>tr[class*="es-tint-"]>td{background:transparent}
/* Hover darkens the tint rather than replacing it, so the colour survives the pointer. */
.es-table>tbody>tr[class*="es-tint-"]:hover{background:var(--es-tint)}
.es-table>tbody>tr[class*="es-tint-"]:hover>td{background:rgba(0,0,0,.045)}
/* Colour picker — the choice is made by looking, so the control is the colours themselves. */
.es-colourpick{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:2px}
.es-colourpick-option{position:relative;display:inline-flex;margin:0;cursor:pointer}
.es-colourpick-option input{position:absolute;width:1px;height:1px;opacity:0;margin:0}
.es-colourpick-swatch{display:block;width:26px;height:26px;border-radius:6px;
  border:1px solid var(--es-devcard-ink);transition:transform .12s,box-shadow .12s}
.es-colourpick-option:hover .es-colourpick-swatch{transform:translateY(-1px)}
.es-colourpick-option input:checked+.es-colourpick-swatch{
  box-shadow:0 0 0 2px var(--es-white),0 0 0 4px var(--es-blue-800)}
.es-colourpick-option input:focus-visible+.es-colourpick-swatch{box-shadow:var(--es-focus-ring)}
/* No colour at all, said in the same shape as the twelve that are. */
.es-colourpick-none{background:var(--es-white);background-image:linear-gradient(45deg,
  transparent 44%,var(--es-grey-400) 44%,var(--es-grey-400) 56%,transparent 56%)}
/* A swatch beside each colour in a picker, so the name is not the only clue. */
.es-swatch{display:inline-block;width:14px;height:14px;border-radius:4px;vertical-align:-2px;
  margin-right:6px;border:.5px solid var(--es-border-subtle)}

/* Dragging a card: only the stages it may legally reach light up. */
.es-kanban-card{cursor:grab}
/* A board whose cards are opened rather than dragged. Without this the grab cursor promises a
   drag that does nothing, and a task moves with a note attached -- which a drag cannot collect. */
.es-kanban-static .es-kanban-card{cursor:pointer}
.es-kanban-card.is-dragging{opacity:.5;cursor:grabbing}
.es-kanban-col.is-drop-target{border-color:var(--es-orange-500,#FA650D);border-style:dashed}
.es-kanban-col.is-drop-over{background:var(--es-orange-50,#FEE8D8)}
/* Board / list switch, on a card's own header bar. Bootstrap's own switch, on a translucent
   pill so it reads in both states against navy. */
.es-viewtoggle{display:inline-flex;align-items:center;gap:9px;padding:3px 12px;border-radius:999px;
  background:rgba(255,255,255,.16)}
.es-viewtoggle-label{font:600 12px var(--es-font-sans);color:rgba(255,255,255,.6);transition:color .12s}
.es-viewtoggle-label.is-on{color:#fff}
.es-viewtoggle .form-check{min-height:0;padding-left:2.2em}
.es-viewtoggle .form-check-input{margin:0 0 0 -2.2em;cursor:pointer}
.es-viewtoggle .form-check-input:focus-visible{outline:2px solid #fff;outline-offset:2px}

/* Active-filter chip toolbar */
.es-table-filters{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:9px 14px;
  background:var(--es-grey-50,#F1F2F7);border-bottom:.5px solid var(--es-border-subtle)}
.es-table-filters .es-filters-label{font:600 11px var(--es-font-sans);letter-spacing:.04em;text-transform:uppercase;
  color:var(--es-grey-400,#9F9F9F)}
.es-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 6px 4px 11px;border-radius:999px;
  background:var(--es-aqua-50,#DFF4FC);color:var(--es-aqua-700,#0673A1);font:600 12px var(--es-font-sans)}
.es-chip button{border:0;background:transparent;color:var(--es-aqua-700,#0673A1);cursor:pointer;font-size:12px;line-height:1;padding:2px}
.es-clear-all{margin-left:auto;border:1.5px solid var(--es-grey-100,#E1E6EB);border-radius:6px;padding:5px 12px;
  font:600 12px var(--es-font-sans);color:var(--es-grey-700,#4D4D4D);background:#fff;cursor:pointer;white-space:nowrap}
.es-clear-all:hover{background:var(--es-grey-50,#F1F2F7)}

/* The chip toolbar lives in a row of its own inside <thead>, below the header row.

   Inside the table because the AJAX sort/page round-trip replaces the table's children and keeps
   the table, so that is the only place the active-filter state survives a page click. BELOW the
   header row because esuite-table.js reads tHead.rows[0].cells to find the columns -- above it,
   this row would become "the headers" for every filtered table in the estate. */
.es-filter-row>td{padding:0;border:0}
.es-filter-row>td>.es-table-filters{border-top:.5px solid var(--es-border-subtle)}
.es-filter-empty>td{padding:18px 14px;text-align:center;color:var(--es-grey-400,#9F9F9F)}

/* ---- Funnel filter popover (Excel-style: search checklist + condition row) ---- */
.es-filter-menu{position:fixed;z-index:1100;width:264px;background:#fff;border:.5px solid var(--es-border-subtle);
  border-radius:12px;box-shadow:0 12px 32px rgba(10,26,47,.22);overflow:hidden;font-family:var(--es-font-sans);
  color:var(--es-grey-700,#4D4D4D);animation:esMenuIn .12s var(--es-ease)}
@keyframes esMenuIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
.es-fm-scope{margin:6px 0 2px;font:600 11px var(--es-font-sans);letter-spacing:.04em;
  text-transform:uppercase;color:var(--es-grey-400,#9F9F9F)}
.es-fm-head{display:flex;align-items:center;gap:8px;padding:9px 12px;background:var(--es-grey-50,#F1F2F7);
  border-bottom:.5px solid var(--es-border-subtle)}
.es-fm-head>i{font-size:11px;color:var(--es-blue-900,#293F6D)}
.es-fm-head strong{font:600 11px var(--es-font-sans);letter-spacing:.05em;text-transform:uppercase;color:var(--es-blue-900,#293F6D);flex:1}
.es-fm-body{padding:10px 12px}
.es-fm-search,.es-fm-val,.es-fm-op{width:100%;box-sizing:border-box;padding:6px 9px;border-radius:6px;
  border:1.5px solid var(--es-grey-100,#E1E6EB);font:400 13px var(--es-font-sans);color:var(--es-grey-700,#4D4D4D);background:#fff;outline:none}
.es-fm-search:focus,.es-fm-val:focus,.es-fm-op:focus{border-color:var(--es-aqua-500,#00AEEF);box-shadow:0 0 0 3px rgba(0,174,239,.25)}
.es-fm-list{max-height:168px;overflow-y:auto;margin:8px 0 0;border:1px solid var(--es-grey-100,#E1E6EB);border-radius:6px}
.es-fm-opt{display:flex;align-items:center;gap:8px;padding:6px 9px;font-size:13px;cursor:pointer}
.es-fm-opt:hover{background:var(--es-grey-50,#F1F2F7)}
.es-fm-opt input{accent-color:var(--es-aqua-500,#00AEEF);width:15px;height:15px;flex:none}
.es-fm-opt span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.es-fm-opt.all{border-bottom:1px solid var(--es-grey-100,#E1E6EB);font-weight:600;position:sticky;top:0;background:#fff}
.es-fm-cond{display:flex;gap:6px;margin-top:10px}
.es-fm-op{flex:0 0 96px}
.es-fm-divider{margin:10px 0 0;font:600 10px var(--es-font-sans);letter-spacing:.06em;text-transform:uppercase;color:var(--es-grey-400,#9F9F9F)}
.es-fm-foot{display:flex;gap:8px;padding:10px 12px;border-top:.5px solid var(--es-border-subtle);background:var(--es-grey-50,#F1F2F7)}
.es-fm-foot button{flex:1;padding:7px 10px;border-radius:6px;font:600 12px var(--es-font-sans);cursor:pointer}
.es-fm-clear{border:1.5px solid var(--es-grey-100,#E1E6EB);background:#fff;color:var(--es-grey-700,#4D4D4D)}
.es-fm-clear:hover{background:#fff;border-color:var(--es-grey-300,#C7CCD4)}
.es-fm-apply{border:none;background:var(--es-blue-900,#293F6D);color:#fff}
.es-fm-apply:hover{opacity:.9}

/* Footer / pagination */
.es-table-foot{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;
  padding:10px 16px;border-top:.5px solid var(--es-border-subtle);background:#fff}
.es-table-foot .es-foot-count{font:400 12px var(--es-font-sans);color:var(--es-grey-400,#9F9F9F)}
/* Rows-per-page selector */
.es-foot-size{display:inline-flex;align-items:center;gap:8px;margin-left:auto}
.es-foot-size label{margin:0;font:600 11px var(--es-font-sans);letter-spacing:.04em;text-transform:uppercase;color:var(--es-grey-400,#9F9F9F)}
.es-foot-select{height:34px;padding:0 30px 0 11px;border:1px solid var(--es-grey-100,#E1E6EB);border-radius:6px;background-color:#fff;
  color:var(--es-grey-700,#4D4D4D);font:600 13px var(--es-font-sans);cursor:pointer;appearance:none;-webkit-appearance:none;-moz-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%234D4D4D' stroke-width='2'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 9px center;background-size:14px}
.es-foot-select:focus{outline:none;border-color:var(--es-aqua-500,#00AEEF);box-shadow:0 0 0 3px rgba(0,174,239,.25)}
/* Legacy server-side pager (Bootstrap .pagination emitted by Table::render) sits in the foot — proper bordered buttons */
.es-table-foot .pagination{margin:0;display:inline-flex;flex-wrap:wrap;gap:4px;padding-left:0;list-style:none}
.es-table-foot .pagination>li{display:inline-flex}
.es-table-foot .pagination>li>a{display:inline-flex;align-items:center;justify-content:center;min-width:34px;height:34px;padding:0 10px;
  border:1px solid var(--es-grey-100,#E1E6EB);border-radius:6px;background:#fff;color:var(--es-grey-700,#4D4D4D);
  font:600 13px var(--es-font-sans);line-height:1;text-decoration:none;cursor:pointer;transition:background .12s,border-color .12s,color .12s}
.es-table-foot .pagination>li>a:hover{background:var(--es-grey-50,#F1F2F7);border-color:var(--es-grey-300,#C7CCD4);color:var(--es-blue-900,#293F6D)}
.es-table-foot .pagination>li.active>a{border:1.5px solid var(--es-blue-800,#4265AF);background:var(--es-blue-800,#4265AF);color:#fff;cursor:default}
.es-table-foot .pagination>li.disabled>a{opacity:.45;background:var(--es-grey-50,#F1F2F7);cursor:not-allowed;pointer-events:none}

/* ── Pin the pager when the table is wider than the screen ─────────────────────
   The foot is a <tfoot> row INSIDE .es-table-scroll -- Table::render puts it there on purpose, so
   the AJAX page/sort round-trip replaces the pager together with the rows it belongs to. The cost
   is that on a wide table it scrolls away to the right with the last column: the question bank has
   nine columns, so reaching page 2 meant scrolling sideways to find the control first.

   So the ROW keeps the full-width rule and fill, and the controls inside it shrink to their own
   width and stick to the left edge of the scroll port. `.es-scrolls` is set by esuite-table.js only
   when the table actually overflows -- CSS cannot ask -- so a table that fits is untouched and keeps
   its count-left / size-right spread.                                                             */
.es-table-scroll.es-scrolls>.es-table>tfoot>tr>td{padding:0;background:#fff;
  border-top:.5px solid var(--es-border-subtle)}
.es-table-scroll.es-scrolls .es-table-foot{position:sticky;left:0;width:max-content;
  border-top:0;justify-content:flex-start}
.es-table-scroll.es-scrolls .es-foot-size{margin-left:0}

/* Navy dropdown menu — matches the system nav chrome; opt-in via .es-dropdown-navy on a Bootstrap .dropdown-menu */
.dropdown-menu.es-dropdown-navy{background:var(--es-blue-900,#293F6D);border:1px solid rgba(255,255,255,.12);border-radius:10px;
  box-shadow:var(--es-shadow-md,0 8px 24px rgba(10,26,47,.22));padding:6px;min-width:240px}
.dropdown-menu.es-dropdown-navy .dropdown-item{display:flex;align-items:center;gap:9px;color:rgba(255,255,255,.9);
  border-radius:6px;padding:8px 12px;font-size:13px;white-space:nowrap}
.dropdown-menu.es-dropdown-navy .dropdown-item:hover,.dropdown-menu.es-dropdown-navy .dropdown-item:focus{background:rgba(255,255,255,.12);color:#fff}
.dropdown-menu.es-dropdown-navy .dropdown-item .text-danger{color:#F36B6B!important}
.dropdown-menu.es-dropdown-navy .dropdown-divider{border-top-color:rgba(255,255,255,.14)}
.es-pager{display:inline-flex;gap:4px}
.es-pager button{min-width:30px;height:30px;border-radius:6px;cursor:pointer;font:600 12px var(--es-font-sans);
  border:1px solid var(--es-grey-100,#E1E6EB);background:#fff;color:var(--es-grey-700,#4D4D4D)}
.es-pager button[aria-current="page"]{border:1.5px solid var(--es-aqua-500,#00AEEF);background:var(--es-aqua-50,#DFF4FC);color:var(--es-aqua-700,#0673A1)}
.es-pager button:disabled{opacity:.4;cursor:not-allowed}

/* ----------------------------------------------------------------------------
   9. BESPOKE — no Bootstrap equivalent
---------------------------------------------------------------------------- */
/* Avatar — initials chip */
.es-avatar{width:40px;height:40px;border-radius:50%;background:var(--es-aqua-500);color:#fff;
  display:inline-flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;flex-shrink:0}

/* Status dot */
.es-status{display:inline-flex;align-items:center;gap:6px;font-size:.8125rem;color:var(--es-text-body)}
.es-status::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--es-grey-400)}
.es-status.tone-green::before{background:var(--es-green-500)}
.es-status.tone-orange::before{background:var(--es-orange-500)}
.es-status.tone-red::before{background:var(--es-red-500)}
.es-status.tone-aqua::before{background:var(--es-aqua-500)}

/* Metric tile. White, not grey-50: a tile sits directly on the page, so the body-background token would leave the figure people read first with no contrast. */
.es-metric{background:var(--es-white);border:.5px solid var(--es-border-subtle);border-radius:.5rem;
  padding:.875rem 1rem;text-align:center}
/* Linked tiles: the whole tile is the target. Hover follows the button rule -- opacity. */
a.es-metric{display:block;text-decoration:none;transition:opacity var(--es-dur) var(--es-ease),
  box-shadow var(--es-dur) var(--es-ease)}
a.es-metric:hover{opacity:.86;box-shadow:var(--es-shadow-sm)}
a.es-metric:focus-visible{outline:3px solid var(--es-blue-800);outline-offset:2px}
.es-metric .val{font-size:1.625rem;font-weight:600;line-height:1;color:var(--es-blue-800)}
.es-metric .val.tone-green{color:var(--es-green-500)}.es-metric .val.tone-orange{color:var(--es-orange-500)}
.es-metric .val.tone-red{color:var(--es-red-500)}.es-metric .val.tone-aqua{color:var(--es-aqua-500)}
/* Callers were already asking for these two and silently getting the base blue, so "Awaiting
   approval" drew identically to "Entitlement" and a nought read as a figure worth having. */
.es-metric .val.tone-yellow{color:var(--es-yellow-500)}
.es-metric .val.tone-grey{color:var(--es-grey-400)}
.es-metric .lbl{font-size:.625rem;color:var(--es-text-muted);margin-top:4px;text-transform:uppercase;letter-spacing:.08em}

/* Task rows. .es-task-row is a whole-row link because a title is a small hit area; .es-task:target tints the row a deep link landed on. */
.es-task-row{color:inherit;text-decoration:none;
  transition:background var(--es-dur) var(--es-ease)}
.es-task-row:hover{background:var(--es-aqua-50);color:inherit}
.es-task-row:focus-visible{outline:3px solid var(--es-blue-800);outline-offset:-3px}
.es-task:target{background:var(--es-aqua-50);border-radius:.375rem}

/* Vertical (sidebar) nav — variant 1i: navy panel, white current-page fill + 4px orange rail
   (matches the ul.lhs VerticalNav in §10). */
.es-vnav{background:var(--es-blue-900);border-radius:.625rem;padding:8px 0;min-width:232px;overflow:hidden}
.es-vnav a{display:flex;align-items:center;gap:10px;padding:10px 16px;font-size:.8125rem;
  color:rgba(255,255,255,.82);border-left:4px solid transparent;text-decoration:none;
  transition:background var(--es-dur) var(--es-ease),color var(--es-dur) var(--es-ease)}
.es-vnav a i{width:18px;text-align:center;font-size:15px;color:rgba(255,255,255,.6)}
.es-vnav a:hover{background:rgba(255,255,255,.06);color:#fff}
.es-vnav a:hover i{color:rgba(255,255,255,.85)}
.es-vnav a:focus-visible{outline:3px solid #fff;outline-offset:-3px}
/* Current page — white fill + navy text + 4px orange rail + navy icon */
.es-vnav a.active{background:#fff;color:var(--es-blue-900,#293F6D);font-weight:600;
  border-left-color:var(--es-orange-500,#FA650D)}
.es-vnav a.active i{color:var(--es-blue-800,#4265AF)}
.es-vnav a.active .badge{background:var(--es-blue-tint,#E7ECF6)!important;color:var(--es-blue-800,#4265AF)!important}
/* Default/home item (e.g. Course Detail) — persistent slightly-lighter navy so the landing
   page stands out when a different sub-page is active. The active state wins (:not(.active))
   so the home item still gets the orange current-page rail when it IS the open page. */
.es-vnav a.nav-home:not(.active){background:var(--es-blue-850,#33497D);color:#fff}
.es-vnav a.nav-home:not(.active) i{color:rgba(255,255,255,.85)}

/* In-screen underline tabs (DS "Tabs") */
.es-tabs{display:flex;border-bottom:2px solid var(--es-grey-100)}
.es-tabs button{appearance:none;background:none;border:none;cursor:pointer;padding:9px 16px;margin-bottom:-2px;
  font:600 .8125rem var(--es-font-sans);color:var(--es-text-muted);border-bottom:2px solid transparent}
.es-tabs button.active{color:var(--es-accent-press);border-bottom-color:var(--es-blue-800)}

/* Workspace tabs (folder tabs below chrome) — server-rendered, JS wires interactions */
.es-wtabs{display:flex;align-items:flex-end;gap:3px;background:var(--es-grey-100);padding:7px 14px 0;min-height:43px;overflow-x:auto;scrollbar-width:thin}
.es-wtabs::-webkit-scrollbar{height:6px}.es-wtabs::-webkit-scrollbar-thumb{background:var(--es-grey-300);border-radius:3px}
.es-wtab{position:relative;display:flex;align-items:center;gap:9px;height:36px;padding:0 11px 0 14px;
  flex:0 1 auto;min-width:48px;max-width:220px;border-radius:9px 9px 0 0;cursor:pointer;user-select:none;
  white-space:nowrap;font:500 .8125rem/1.2 var(--es-font-sans);color:var(--es-grey-700);
  transition:background var(--es-dur) var(--es-ease),color var(--es-dur) var(--es-ease)}
.es-wtab:not(.active):hover{background:rgba(255,255,255,.55);color:var(--es-blue-800)}
.es-wtab.active{background:#fff;color:var(--es-blue-800);font-weight:600;z-index:3;flex-shrink:.35;min-width:96px}
.es-wtab.active::before,.es-wtab.active::after{content:"";position:absolute;bottom:0;width:10px;height:10px;background:#fff}
.es-wtab.active::before{left:-10px;-webkit-mask:radial-gradient(circle at top left,#0000 10px,#000 10.5px);mask:radial-gradient(circle at top left,#0000 10px,#000 10.5px)}
.es-wtab.active::after{right:-10px;-webkit-mask:radial-gradient(circle at top right,#0000 10px,#000 10.5px);mask:radial-gradient(circle at top right,#0000 10px,#000 10.5px)}
.es-wtab .lead{font-size:12px;width:13px;text-align:center;flex-shrink:0;color:var(--es-grey-400)}
.es-wtab.active .lead{color:var(--es-accent)}
.es-wtab .title{overflow:hidden;text-overflow:ellipsis;min-width:0;flex:0 1 auto}
.es-wtab .close{width:19px;height:19px;border:none;background:transparent;border-radius:5px;color:var(--es-grey-400);
  cursor:pointer;font-size:11px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center}
.es-wtab .close:hover{background:rgba(159,159,159,.26);color:var(--es-blue-900)}
.es-wtab .pin{font-size:10px;color:var(--es-accent);opacity:.7;margin-left:1px;transform:rotate(40deg);flex-shrink:0}
.es-wtab.dragging{opacity:.4}.es-wtab.drop-target{box-shadow:inset 2.5px 0 0 0 var(--es-accent)}

/* ----------------------------------------------------------------------------
   9b. MODULE RIBBON  (.es-ribbon)  — variant 1c "bordered chips + orange active"
   The per-module action strip below the workspace tabs. Shared, authoritative
   styling: a module ribbon.php should carry markup only (container .es-ribbon,
   items .es-ribbon-tab or <a>) and NOT ship its own inline <style> — let this
   block govern so every ribbon looks identical and wraps (never scrolls).

   Two active mechanisms are both supported:
     · .es-ribbon-tab.active            — set server-side (e.g. home/ribbon.php)
     · .es-ribbon-tab:has(.ribbonItemSelected)
                                        — init.js injects <div.ribbonItemSelected>
                                          into the item matching `var activeRibbon`
   The injected marker itself stays hidden (rule in §5); the parent chip shows the
   active state. Accessibility (WCAG 2.2 AA): ≥44px targets, non-colour active cue
   (orange frame + leading dot + bold), 3px focus ring, full labels + wrap.
---------------------------------------------------------------------------- */
.es-ribbon{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:9px 16px 12px;
  background:var(--es-grey-100,#E1E6EB);                                   /* fallback ≈ slightly darker step */
  background:color-mix(in srgb, var(--es-grey-50,#F1F2F7) 95%, #000);      /* 5% darker — a shade lighter than the 10%-darker workspace-tabs strip */
  border-bottom:2px solid var(--es-white,#FFFFFF)}
.es-ribbon .hidden{display:none!important}
.es-ribbon a,.es-ribbon .es-ribbon-tab{
  display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:10px 14px;
  border:1.5px solid var(--es-grey-300,#C7CCD4);border-radius:8px;background:#fff;
  color:var(--es-grey-700,#4D4D4D);font-size:13px;font-weight:500;line-height:1.2;
  text-decoration:none;white-space:nowrap;cursor:pointer;position:static;top:auto;
  transition:background .12s var(--es-ease),color .12s var(--es-ease),border-color .12s var(--es-ease)}
.es-ribbon a>i,.es-ribbon .es-ribbon-tab>i{color:var(--es-grey-400,#9F9F9F);font-size:15px}
.es-ribbon a:hover,.es-ribbon .es-ribbon-tab:hover{
  border-color:var(--es-blue-600,#5481E0);background:#fff;color:var(--es-blue-800,#4265AF)!important}
/* active — orange frame + tint + leading dot + semibold navy label */
.es-ribbon a.active,.es-ribbon .es-ribbon-tab.active,
.es-ribbon .es-ribbon-tab:has(.ribbonItemSelected){
  border-color:var(--es-orange-500,#FA650D);background:var(--es-orange-50,#FEE8D8);
  color:var(--es-blue-900,#293F6D)!important;font-weight:600}
.es-ribbon a.active>i,.es-ribbon .es-ribbon-tab.active>i,
.es-ribbon .es-ribbon-tab:has(.ribbonItemSelected)>i{color:var(--es-orange-600,#C14800)}
.es-ribbon a.active::before,.es-ribbon .es-ribbon-tab.active::before,
.es-ribbon .es-ribbon-tab:has(.ribbonItemSelected)::before{
  content:"";width:8px;height:8px;border-radius:999px;background:var(--es-orange-500,#FA650D);flex:none}
/* keyboard focus */
.es-ribbon a:focus-visible,.es-ribbon .es-ribbon-tab:focus-visible{
  outline:3px solid var(--es-blue-800,#4265AF);outline-offset:2px;border-radius:8px}
/* counter badge — outlined pill (not colour-only) */
.es-ribbon .es-tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;
  height:18px;padding:0 6px;border-radius:999px;font-family:var(--es-font-mono);font-size:10px;font-weight:700;line-height:1}
.es-ribbon .es-tab-badge-red   {background:var(--es-red-50,#FDDCDC);color:var(--es-red-700,#A01010);border:1px solid var(--es-red-500,#DE1F1F)}
.es-ribbon .es-tab-badge-aqua  {background:var(--es-aqua-50,#DFF4FC);color:var(--es-aqua-700,#0673A1);border:1px solid var(--es-aqua-500,#00AEEF)}
.es-ribbon .es-tab-badge-orange{background:var(--es-orange-50,#FEE8D8);color:var(--es-orange-600,#C14800);border:1px solid var(--es-orange-500,#FA650D)}
.es-ribbon .es-tab-badge-navy  {background:var(--es-blue-tint,#E7ECF6);color:var(--es-blue-800,#4265AF);border:1px solid var(--es-blue-600,#5481E0)}
.es-ribbon .es-tab-badge-yellow{background:var(--es-yellow-50,#FEF1DC);color:var(--es-yellow-700,#B4760E);border:1px solid var(--es-yellow-500,#FCA833)}
.es-ribbon .es-tab-badge-green {background:var(--es-green-50,#E1F4E1);color:var(--es-green-700,#1F7A22);border:1px solid var(--es-green-500,#27AC2A)}
.es-ribbon .es-tab-badge-grey  {background:var(--es-grey-50,#F1F2F7);color:var(--es-grey-700,#4D4D4D);border:1px solid var(--es-grey-400,#9F9F9F)}
/* Legacy solid-fill counter circles (.label-as-badge) used by some ribbons
   (woodson, claims, jobsList, opsManager, continuousimprovement)
   ship a saturated background but no outline, so they read flatter than the
   bordered .es-tab-badge-* set above. Give every solid-fill counter circle a
   matching darker edge — a translucent-dark border reads as "the same colour,
   one shade darker/bolder" over any fill (fixed or DB-driven HighlightColour),
   so all ribbon number circles are outlined consistently. */
.es-ribbon .label-as-badge{border:1px solid rgba(0,0,0,.3)}
/* chips are self-delimiting — retire inline separators */
.es-ribbon .es-ribbon-divider{display:none}

/* ----------------------------------------------------------------------------
   9c. Priority-overflow (⋯ More) — shared by the ribbon + workspace tabs
   A strip managed by /assets/js/es-chrome-overflow.js is given .es-ovf-on: it
   stops wrapping, clips the overflow, and folds the items that don't fit into a
   trailing ⋯ dropdown. The base strips keep their flex-wrap fallback for the
   no-JS / in-overlay (unmanaged) case, so this never breaks a modal ribbon.
---------------------------------------------------------------------------- */
.es-ovf-on{flex-wrap:nowrap!important;overflow:hidden!important}
.es-ovf-hidden{display:none!important}
.es-ovf-more[hidden],.es-ovf-menu[hidden]{display:none!important}
.es-ovf-more{display:inline-flex;align-items:center;gap:6px;flex:none;cursor:pointer;
  border:1.5px solid var(--es-grey-300,#C7CCD4);border-radius:8px;background:#fff;
  color:var(--es-grey-700,#4D4D4D);font-family:'Poppins',system-ui,sans-serif;
  transition:background .12s var(--es-ease),color .12s var(--es-ease),border-color .12s var(--es-ease)}
.es-ovf-more:hover{border-color:var(--es-blue-600,#5481E0);color:var(--es-blue-800,#4265AF)}
.es-ovf-more[aria-expanded="true"]{border-color:var(--es-blue-600,#5481E0);
  color:var(--es-blue-800,#4265AF);background:var(--es-blue-tint,#E7ECF6)}
.es-ovf-more:focus-visible{outline:3px solid var(--es-blue-800,#4265AF);outline-offset:2px}
.es-ovf-more>i{font-size:15px}
.es-ovf-count{font-family:var(--es-font-mono);font-size:11px;font-weight:700;line-height:1}
.es-ovf-count:empty{display:none}
/* ⋯ sized to sit in each strip */
.es-ribbon .es-ovf-more{min-height:44px;padding:10px 14px}
#esWorkspaceTabs .es-ovf-more{min-height:42px;padding:10px 12px;align-self:flex-end;font-size:13px;font-weight:500}

/* the dropdown panel (body-mounted) */
.es-ovf-menu{position:fixed;z-index:1080;min-width:220px;max-width:340px;max-height:70vh;overflow-y:auto;
  background:#fff;border:.5px solid rgba(10,26,47,.10);border-radius:10px;padding:6px;
  box-shadow:0 12px 28px rgba(10,26,47,.14);font-family:'Poppins',system-ui,sans-serif}
.es-ovf-item{display:flex;align-items:center;gap:2px;border-radius:6px}
a.es-ovf-item,.es-ovf-link{display:flex;align-items:center;gap:11px;flex:1 1 auto;min-width:0;
  padding:9px 11px;border-radius:6px;text-decoration:none;font-size:13px;font-weight:500;
  color:var(--es-grey-700,#4D4D4D)}
a.es-ovf-item:hover,.es-ovf-link:hover,.es-ovf-item:hover{background:var(--es-aqua-50,#DFF4FC);color:var(--es-blue-900,#293F6D)}
a.es-ovf-item>i,.es-ovf-link>i{width:16px;text-align:center;font-size:13px;color:var(--es-grey-400,#9F9F9F);flex:none}
a.es-ovf-item:hover>i,.es-ovf-item:hover .es-ovf-link>i{color:var(--es-aqua-500,#00AEEF)}
.es-ovf-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
a.es-ovf-item.is-current,.es-ovf-item.is-current{background:var(--es-orange-50,#FEE8D8)}
a.es-ovf-item.is-current .es-ovf-label,.es-ovf-item.is-current .es-ovf-label{color:var(--es-blue-900,#293F6D);font-weight:600}
.es-ovf-item .es-tab-badge,.es-ovf-item .label,a.es-ovf-item .es-tab-badge,a.es-ovf-item .label{margin-left:auto}
.es-ovf-x{flex:none;width:26px;height:26px;border:none;background:transparent;border-radius:6px;cursor:pointer;
  color:var(--es-text-muted,#6B6B6B);display:inline-flex;align-items:center;justify-content:center;font-size:12px}
.es-ovf-x:hover{background:var(--es-red-50,#FDDCDC);color:var(--es-red-700,#A01010)}

/* ----------------------------------------------------------------------------
   9e. MODULE TOP BAR  (#crmNav)
   The chrome for a module that has its own front door -- currently only the CRM,
   on its way out into a repository of its own.

   IT IS URBI'S BAR, MINUS WHAT DOES NOT BELONG. Same 56px navy, same recessed
   pill track, same white chip on the current area, same red sign-out. What is
   absent is absent on purpose: search, shortcuts, the account link, the contract
   switcher and the workspace tabs are all URBI's.

   SCOPED TO #crmNav RATHER THAN #esNav. URBI's bar carries its rules in an inline
   <style> block; a stylesheet rule sharing that id could beat it on a page this
   module never touches. A different id makes that impossible rather than unlikely.
   The values below are copied from urbi/navigation.php deliberately -- the two
   bars are meant to look like one product.
---------------------------------------------------------------------------- */
#crmNav.navbar{background-color:var(--es-blue-800,#4265AF);border:none;border-radius:0;
  box-shadow:none;min-height:56px;margin-bottom:0;padding-top:0;padding-bottom:0;
  font-family:'Poppins',system-ui,sans-serif}
#crmNav > .container-fluid{min-width:0}
#crmNav .navbar-collapse{border-color:rgba(255,255,255,.10);box-shadow:none;min-width:0}

/* -- brand chip: white, so the blue-800 wordmark stays legible on the blue bar -- */
#crmNav #crmNavBar > li.es-brand-pill{display:flex;align-items:center;margin-right:4px}
#crmNav #crmNavBar > li.es-brand-pill > a.es-brand-link{height:36px;display:flex;align-items:center;
  padding:0 15px;border-radius:999px;text-decoration:none;border-bottom:none;box-shadow:none;
  background-color:color-mix(in srgb,var(--es-grey-50,#F1F2F7) 55%,var(--es-grey-100,#E1E6EB) 45%);
  color:var(--es-blue-800,#4265AF);
  transition:opacity .14s ease,background-color .14s ease}
#crmNav #crmNavBar > li.es-brand-pill.active > a.es-brand-link{background-color:var(--es-white,#fff)}
#crmNav #crmNavBar > li.es-brand-pill > a.es-brand-link:hover,
#crmNav #crmNavBar > li.es-brand-pill > a.es-brand-link:focus{opacity:.86}
#crmNav .es-wordmark{color:var(--es-blue-800,#4265AF);font-weight:600;font-size:20px;
  letter-spacing:-.01em;vertical-align:middle}
#crmNav .es-wordmark .es-orange{color:var(--es-orange-500,#FA650D)}

/* -- primary links -- */
#crmNav .navbar-nav{margin:0;align-items:stretch}
#crmNav .navbar-nav > li > a,#crmNav .navbar-nav > li > .nav-link{display:flex;align-items:center;
  height:56px;padding:0 11px;font-size:14px;font-weight:500;line-height:18px;white-space:nowrap;
  color:rgba(255,255,255,.90);border-bottom:3px solid transparent;
  transition:color .14s ease,background-color .14s ease}
#crmNav .navbar-nav > li > a:hover,#crmNav .navbar-nav > li > a:focus,
#crmNav .navbar-nav .show > a,#crmNav .navbar-nav .show > a:hover,
#crmNav .navbar-nav .show > a:focus{color:#fff;background-color:rgba(255,255,255,.12)}
#crmNav .navbar-nav > .active > a,#crmNav .navbar-nav > .active > a:hover,
#crmNav .navbar-nav > .active > a:focus{color:#fff;font-weight:600;background-color:transparent;
  border-bottom:3px solid var(--es-aqua-500,#00AEEF)}
#crmNav .navbar-nav > li > a.dropdown-toggle::after{display:none}
#crmNav .es-caret{font-size:9px;opacity:.7;margin-left:3px}

/* -- dropdowns: navy surface, aqua-700 on the current page -- */
#crmNav .dropdown-menu{background-color:var(--es-blue-900,#293F6D);border:1px solid rgba(255,255,255,.10);
  border-radius:10px;box-shadow:var(--es-shadow-md,0 8px 24px rgba(10,26,47,.22));margin-top:0;
  padding:6px;min-width:200px;max-height:80vh;overflow-y:auto}
/* `li .dropdown-item`, not `li > .dropdown-item`: the bell's "Dismiss all" is a submit button
   inside a <form> inside the <li>, and a direct-child selector left it on Bootstrap's own
   near-black link colour -- unreadable on this navy surface. A menu item is a menu item however
   it is wrapped. `background:none` and `border:0` because a <button> brings both of its own. */
#crmNav .dropdown-menu li .dropdown-item{color:rgba(255,255,255,.85);font-size:13px;
  padding:8px 12px;border-radius:6px;white-space:nowrap;background:none;border:0;width:100%;
  text-align:left}
#crmNav .dropdown-menu li .dropdown-item:hover,
#crmNav .dropdown-menu li .dropdown-item:focus{background-color:rgba(255,255,255,.12);color:#fff}
#crmNav .dropdown-menu li form{margin:0}
#crmNav .dropdown-menu .dropdown-item.active{background-color:var(--es-aqua-700,#0673A1);color:#fff}
#crmNav .dropdown-menu .dropdown-item.disabled{color:rgba(255,255,255,.38)}
#crmNav .dropdown-menu .dropdown-divider{background-color:rgba(255,255,255,.10);border:none;
  height:1px;margin:6px 4px}

/* -- right-hand utilities -- */
#crmNav #crmNavRight{margin:0;align-items:center;flex-shrink:0}
#crmNav #crmNavRight > li > a,#crmNav #crmNavRight > li > .nav-link{display:flex;align-items:center;
  gap:7px;height:auto;padding:9px 10px;font-size:14px;font-weight:500;line-height:18px;
  white-space:nowrap;border-radius:8px;border-bottom:none;color:rgba(255,255,255,.90)}
#crmNav #crmNavRight > li > a:hover,#crmNav #crmNavRight > li > a:focus{color:#fff;
  background-color:rgba(255,255,255,.12)}
#crmNav #crmNavRight .es-icon{font-size:15px;vertical-align:middle}

/* -- bell: orange, because unread is attention rather than alarm -- */
#crmNav #crmNavRight > li > a .es-bell-count{position:absolute;top:1px;right:1px;min-width:16px;
  height:16px;padding:0 4px;border-radius:999px;background:var(--es-orange-500,#FA650D);color:#fff;
  font:700 10px/16px var(--es-font-mono);text-align:center}
#crmNav .es-bell-menu{min-width:320px;max-width:380px}
#crmNav .es-bell-menu li .dropdown-item{display:flex;align-items:flex-start;gap:11px;
  white-space:normal;padding:9px 12px}
#crmNav .es-bell-menu li .dropdown-item > i{margin-top:2px;width:16px;text-align:center;
  color:rgba(255,255,255,.55);flex:none}
#crmNav .es-bell-menu li .dropdown-item.es-bell-unread{background-color:rgba(255,255,255,.10)}
#crmNav .es-bell-subject{display:block;color:#fff}
#crmNav .es-bell-when{display:block;font-size:11px;color:rgba(255,255,255,.60)}

/* -- sign out -- */
#crmNav #crmNavLogout{background-color:var(--es-red-500,#DE1F1F);border-radius:6px;border:none;
  padding:0;margin:0 4px 0 6px;align-self:center}
#crmNav #crmNavLogout a{display:flex;align-items:center;gap:5px;color:#fff;font-size:12px;
  font-weight:600;padding:7px 12px;line-height:16px;white-space:nowrap;text-decoration:none}
#crmNav #crmNavLogout:hover{background-color:var(--es-red-700,#A01010)}

#crmNav .navbar-toggler{border:none;padding:8px;align-self:center}
#crmNav .navbar-toggler .navbar-toggler-icon{filter:brightness(0) invert(1)}
#crmNav .navbar-toggler:hover,#crmNav .navbar-toggler:focus{background-color:rgba(255,255,255,.15);
  box-shadow:none}

@media (min-width:992px){
  /* the recessed pill track, 44px centred in the 56px bar */
  #crmNav #crmNavBar{align-self:center;align-items:center;margin-left:8px;padding:4px;
    background:rgba(0,0,0,.22);border-radius:999px;box-shadow:inset 0 1px 3px rgba(0,0,0,.25)}
  #crmNav #crmNavBar > li > a{height:36px;padding:0 15px;border-radius:999px;border-bottom:none}
  #crmNav #crmNavBar > li > a:hover,#crmNav #crmNavBar > li > a:focus,
  #crmNav #crmNavBar .show > a{color:#fff;background-color:rgba(255,255,255,.12)}
  /* the area you are in = solid white chip with navy-900 text */
  #crmNav #crmNavBar > .active > a,#crmNav #crmNavBar > .active > a:hover,
  #crmNav #crmNavBar > .active > a:focus{color:var(--es-blue-900,#293F6D);font-weight:600;
    background-color:#fff;border-bottom:none;box-shadow:0 2px 6px rgba(10,26,47,.28)}
  #crmNav #crmNavBar > .active > a .es-caret{opacity:.55}
  #crmNav #crmNavRight > li > a.es-iconbtn{position:relative;width:34px;height:34px;padding:0;
    justify-content:center;border-radius:8px}
}
/* Top app bar (chrome) */
.es-appbar{background:var(--es-blue-800);height:60px;display:flex;align-items:center;padding:0 18px;color:#fff}
.es-appbar .es-wordmark{font-size:20px}

/* ----------------------------------------------------------------------------
   9d. VIEW AS  (.es-viewas / .es-viewas-banner)
   A Caseload supervisor looking at another tutor's caseload. Two presentations
   of one session-wide mode, both rendered from includes/caseloadViewAs.php:

     .es-viewas         the picker, on the three Caseload tabs that follow the
                        switch. Sits directly under the ribbon and reads as a
                        continuation of the chrome, not as page content.
     .es-viewas-banner  the mode indicator everywhere else in the product. It
                        wraps a plain .alert, so it inherits the alert's 4px
                        left rule — the one construction in this area entitled
                        to one, because it IS a status alert.

   The picker deliberately does NOT carry the orange active treatment used by
   the ribbon and side nav: orange means "this is where you are", and this
   control is about WHOSE data you are looking at. Aqua tint for the active
   state instead, which is the palette's information colour.
---------------------------------------------------------------------------- */
.es-viewas{padding:10px 24px;background:var(--es-grey-50,#F1F2F7);
  border-bottom:1px solid var(--es-grey-100,#E1E6EB)}
.es-viewas-form{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:0}
.es-viewas-state{display:inline-flex;align-items:center;gap:7px;font-size:12px;
  color:var(--es-grey-700,#4D4D4D)}
.es-viewas-state>i{color:var(--es-grey-400,#9F9F9F);font-size:13px}
.es-viewas-state.viewing{color:var(--es-aqua-700,#0673A1);font-weight:600}
.es-viewas-state.viewing>i{color:var(--es-aqua-500,#00AEEF)}
.es-viewas-note{font-weight:400;color:var(--es-grey-400,#9F9F9F)}
.es-viewas-label{font-size:12px;color:var(--es-grey-400,#9F9F9F);margin:0 0 0 auto}
.es-viewas-select{width:auto;min-width:240px;max-width:100%}
.es-viewas-flash{display:inline-flex;align-items:center;gap:6px;font-size:12px;
  color:var(--es-red-700,#A01010)}
.es-viewas-flash>i{color:var(--es-red-500,#DE1F1F)}
/* viewing state — tint the whole strip so it cannot be mistaken for the resting
   one at a glance, without borrowing the ribbon's orange */
.es-viewas:has(.es-viewas-state.viewing){background:var(--es-aqua-50,#DFF4FC);
  border-bottom-color:var(--es-aqua-500,#00AEEF)}

/* the same notice inside the register, which the overlay loads with ?exclusive=1
   and therefore without navigation.php's banner */
.es-register-readonly{display:flex;align-items:center;gap:10px;
  max-width:1100px;margin:0 auto 12px}

.es-viewas-banner{padding:12px 24px 0}
.es-viewas-banner .alert{display:flex;align-items:center;gap:10px;margin:0}
.es-viewas-banner-form{margin:0 0 0 auto}

@media (max-width:575.98px){
  .es-viewas{padding:10px 16px}
  .es-viewas-label{margin-left:0}
  .es-viewas-select{min-width:0;flex:1 1 100%}
  .es-viewas-banner{padding:12px 16px 0}
  .es-viewas-banner .alert{flex-wrap:wrap}
  .es-viewas-banner-form{margin-left:0}
}

/* ----------------------------------------------------------------------------
   9f. GLOW RING  (.es-glow-ring)  — rotating rainbow border
   A conic-gradient swept by animating a registered angle: the box stays still
   and the colours travel round it. @property is what makes the angle
   animatable — an unregistered custom property cannot interpolate, so the
   sweep would jump rather than turn.

   Two forms, one gradient and one animation:
   - .es-glow-ring WRAPS its content: a sharp ring (::before) plus a blurred
     halo (::after), both painted behind it. The content must have an opaque
     background and the same radius — it is what covers the gradient's centre,
     leaving only the overhang visible.
   - .es-glow-ring-surface IS the ring, for when the target cannot be wrapped.
     Position it behind the target, larger by the ring width on every side.

   The glow cannot go on the element that carries the opaque background. That
   element becomes a stacking context, which paints its own background first and
   its negative-z-index children second — so the gradient fills the box instead
   of ringing it. Hence a wrapper, or a separate earlier sibling.

   Tunable per use: --es-glow-duration (one full turn), --es-glow-radius (match
   the content), --es-glow-width (visible ring thickness, wrapper form only).
---------------------------------------------------------------------------- */
@property --es-glow-angle{syntax:"<angle>";inherits:false;initial-value:0deg}
@keyframes esGlowSpin{to{--es-glow-angle:360deg}}

.es-glow-ring{position:relative;z-index:0;border-radius:var(--es-glow-radius,10px)}
.es-glow-ring::before,
.es-glow-ring::after{content:"";position:absolute;inset:calc(var(--es-glow-width,3px) * -1);z-index:-1;border-radius:inherit}
.es-glow-ring::after{inset:-8px;z-index:-2;filter:blur(14px);opacity:.85}
.es-glow-ring-surface{border-radius:var(--es-glow-radius,10px)}

.es-glow-ring::before,
.es-glow-ring::after,
.es-glow-ring-surface{
  background:conic-gradient(from var(--es-glow-angle),#ff7a00,#ffd166,#06d6a0,#118ab2,#ff7a00);
  animation:esGlowSpin var(--es-glow-duration,8s) linear infinite;
}

/* Held still rather than removed: the ring still marks what it marks. */
@media (prefers-reduced-motion:reduce){
  .es-glow-ring::before,.es-glow-ring::after,.es-glow-ring-surface{animation:none}
}

/* ----------------------------------------------------------------------------
   10. VERTICAL NAV  (.lhs side menu)  — navy panel, aqua active rail, accordion
   Re-skins the legacy global .lhs/.lhs-sub list-group into the eSuite VerticalNav.
   Markup: <ul class="list-group lhs">…</ul>; sub-groups are a <li> that contains
   <ul class="list-group lhs-sub">. JS hooks preserved: data-href, .active (header),
   .selected (current), .open (expanded group), .greyArrow (chevron), .search,
   .badge. Pair with /assets/js/esuite-nav.js for click-to-expand.
   NOTE: critical rules are scoped under `ul.lhs` and forced !important ON PURPOSE
   — the app's legacy main.css still ships the old white-flyout .lhs theme and
   loads after this file; without these guards the sub-menu renders as a white
   popover floating over (and dimming) the items below. Do not relax them.
---------------------------------------------------------------------------- */
ul.lhs.list-group{background:var(--es-blue-900,#293F6D)!important;border:0!important;border-radius:10px!important;
  overflow:hidden;padding:6px 0;margin-bottom:16px;box-shadow:var(--es-shadow-sm,0 1px 3px rgba(10,26,47,.18));
  list-style:none;user-select:none}
ul.lhs>.list-group-item,
ul.lhs .lhs-sub>.list-group-item{border:0!important;border-left:4px solid transparent!important;border-radius:0!important;
  box-shadow:none!important;float:none!important;position:relative;cursor:pointer;font-size:13px;line-height:1.3;
  padding:9px 16px;background-color:transparent!important;color:rgba(255,255,255,.82)!important;
  transition:background-color .12s var(--es-ease),color .12s var(--es-ease)}
ul.lhs .list-group-item.disabled{pointer-events:none}
ul.lhs>.list-group-item:focus-visible,
ul.lhs .lhs-sub>.list-group-item:focus-visible{outline:3px solid #fff!important;outline-offset:-3px}

/* Header row (.active) — "Learners" / learner name */
ul.lhs>.list-group-item.active{background-color:transparent!important;color:#fff!important;font-weight:600;font-size:14px;
  cursor:default;padding:12px 16px 11px;border-left:0!important;border-bottom:1px solid rgba(255,255,255,.10)!important;margin-bottom:4px}
ul.lhs>.list-group-item.active .font-mono{color:rgba(255,255,255,.6)!important}

/* Section eyebrow — additive, non-interactive group label (variant 1i grouping).
   Matched at any depth so an eyebrow inside a conditional block/sub-list still styles. */
ul.lhs .lhs-section{border-left:0!important;background:transparent!important;cursor:default;list-style:none;
  padding:15px 16px 6px!important;font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  color:rgba(255,255,255,.85)!important}
ul.lhs .lhs-section:hover{background:transparent!important}

/* Idle leading icons */
ul.lhs>.list-group-item:not(.active)>span.fal,
ul.lhs>.list-group-item:not(.active)>span.fa{width:17px;text-align:center;color:rgba(255,255,255,.6)!important}
/* Hover */
ul.lhs>.list-group-item:not(.active):not(.search):not(.selected):not(.lhs-section):hover{background-color:rgba(255,255,255,.06)!important;color:#fff!important}
ul.lhs>.list-group-item:not(.active):not(.search):not(.selected):not(.lhs-section):hover>span.fal,
ul.lhs>.list-group-item:not(.active):not(.search):not(.selected):not(.lhs-section):hover>span.fa{color:rgba(255,255,255,.85)!important}

/* Current page (.selected) — variant 1i: white fill + 4px orange rail + navy text/icon */
ul.lhs>.list-group-item.selected{background-color:#fff!important;color:var(--es-blue-900,#293F6D)!important;font-weight:600;
  border-left:4px solid var(--es-orange-500,#FA650D)!important}
ul.lhs>.list-group-item.selected>span.fal,
ul.lhs>.list-group-item.selected>span.fa{color:var(--es-blue-800,#4265AF)!important}
/* badge must stay legible on the white selected row */
ul.lhs>.list-group-item.selected .badge{background:var(--es-blue-tint,#E7ECF6)!important;color:var(--es-blue-800,#4265AF)!important}

/* Search row */
ul.lhs>.list-group-item.search{cursor:default;padding:6px 12px 8px}
ul.lhs>.list-group-item.search .input-group{background:rgba(255,255,255,.10);border-radius:6px;overflow:hidden}
ul.lhs>.list-group-item.search .form-control{border:0;background:transparent;color:#fff;font-size:12.5px;box-shadow:none}
ul.lhs>.list-group-item.search .form-control::placeholder{color:rgba(255,255,255,.55)}
ul.lhs>.list-group-item.search .btn{border:0;background:transparent;color:rgba(255,255,255,.7)}

/* Expand chevron */
ul.lhs .greyArrow{color:rgba(255,255,255,.55)!important;transition:transform .15s var(--es-ease)}
ul.lhs>.list-group-item.open>.greyArrow{transform:rotate(180deg)}

/* Collapsible sub-menu — inset panel, in-flow (never a white flyout).
   Solid navy one step lighter than the #293F6D rail (NOT a translucent black):
   a semi-transparent fill would composite over the parent's white .selected row
   and render light grey, washing out the white child labels. */
ul.lhs .lhs-sub{list-style:none;margin:4px -16px -9px!important;padding:0!important;overflow:hidden;max-height:0;
  width:auto!important;min-width:0!important;position:static!important;float:none!important;inset:auto!important;
  transform:none!important;z-index:auto!important;box-shadow:none!important;border:0!important;border-radius:0!important;
  display:block!important;background:var(--es-blue-850,#33497D)!important;transition:max-height .2s var(--es-ease)}
ul.lhs>.list-group-item.open>.lhs-sub{max-height:1000px}
ul.lhs .lhs-sub>.list-group-item{font-size:12.5px;padding:8px 16px 8px 34px;color:rgba(255,255,255,.85)!important}
ul.lhs .lhs-sub>.list-group-item:not(.selected):hover{background-color:rgba(255,255,255,.05)!important;color:#fff!important}
ul.lhs .lhs-sub>.list-group-item.selected{background-color:#fff!important;color:var(--es-blue-900,#293F6D)!important;font-weight:600;
  border-left:4px solid var(--es-orange-500,#FA650D)!important}
ul.lhs .lhs-sub>.list-group-item span.fal,
ul.lhs .lhs-sub>.list-group-item span.fa{width:16px;text-align:center;color:rgba(255,255,255,.6)!important}
ul.lhs .lhs-sub>.list-group-item.selected span.fal,
ul.lhs .lhs-sub>.list-group-item.selected span.fa{color:var(--es-blue-800,#4265AF)!important}
ul.lhs .lhs-sub>.list-group-item.selected .badge{background:var(--es-blue-tint,#E7ECF6)!important;color:var(--es-blue-800,#4265AF)!important}

/* Count badges — translucent white pill, monospace */
ul.lhs .badge{background:rgba(255,255,255,.14)!important;color:#fff!important;font-weight:500;font-family:var(--es-font-mono)}
ul.lhs .lhs-sub .badge{margin-left:auto}

@media (prefers-reduced-motion:reduce){
  ul.lhs .lhs-sub,ul.lhs .greyArrow,ul.lhs .list-group-item{transition:none!important}
}

/* ----------------------------------------------------------------------------
   11. RAG MATRIX  (.es-matrix)  — attendance/KSB/risk cross-tab
   Markup: <table class="es-matrix"> with a sticky first column (row header) and
   sticky header row. Tint intersection cells with a tone class and ALWAYS keep
   the figure/letter in the cell — never colour alone (WCAG 1.4.1).
   Tones: rag-green / rag-yellow / rag-orange / rag-red / rag-info / rag-muted.
---------------------------------------------------------------------------- */
.es-matrix{border-collapse:collapse;font-family:var(--es-font-sans);font-size:13px}
.es-matrix th,.es-matrix td{border:.5px solid var(--es-grey-100,#E1E6EB);padding:8px 12px;text-align:center;white-space:nowrap}
.es-matrix thead th{background:var(--es-blue-900,#293F6D);color:#fff;font-weight:600;position:sticky;top:0;z-index:1}
.es-matrix thead th:first-child,.es-matrix tbody th{position:sticky;left:0;z-index:2;background:var(--es-blue-900,#293F6D);
  color:#fff;text-align:left;font-weight:600}
.es-matrix tbody th{background:#fff;color:var(--es-blue-900,#293F6D);border-right:.5px solid var(--es-grey-100,#E1E6EB)}
.es-matrix tbody td{font-family:var(--es-font-mono)}
.es-matrix .rag-green{background:var(--es-green-50,#D6F5D6);color:var(--es-green-700,#09630B)}
.es-matrix .rag-yellow{background:var(--es-yellow-50,#FEF4DC);color:var(--es-yellow-700,#9A6200)}
.es-matrix .rag-orange{background:var(--es-orange-50,#FEE8D8);color:var(--es-orange-600,#C14800)}
.es-matrix .rag-red{background:var(--es-red-50,#FDDCDC);color:var(--es-red-700,#A01010)}
.es-matrix .rag-info{background:var(--es-aqua-50,#DFF4FC);color:var(--es-aqua-700,#0673A1)}
.es-matrix .rag-muted{background:var(--es-grey-50,#F1F2F7);color:var(--es-grey-400,#9F9F9F)}

/* ----------------------------------------------------------------------------
   12. REGISTER TAKE-VIEW  (.es-register)  — tutor attendance-marking screen
   One navy-headed card: header (back · title/meta + keyboard hint · classroom
   actions · mark all present) → summary-chip well → learner register rows.
   Two new shared patterns live here: the summary chips (.es-rchip — NB the bare
   .es-chip name is already taken by the table filter chip, §9) and the
   variable-length attendance code toggle group (.es-codes / .es-code-btn).
   Provider colours (CourseAttendanceCodes.HighlightColour) arrive only via the
   inline `style` set in JS — the one sanctioned exception to tokens-only.
---------------------------------------------------------------------------- */
/* Header — 3-part flex on bg-navy */
.es-register-head{display:flex;align-items:flex-start;gap:16px;justify-content:space-between}
.es-register-head .title-block{flex:1 1 auto;min-width:0}
.es-register-head .title{font-weight:600;color:#fff;font-size:.9375rem;line-height:1.3}
.es-register-head .meta{color:rgba(255,255,255,.72);font-size:.8125rem;margin-top:2px}
.es-register-head .kbd-hint{margin-top:6px;color:rgba(255,255,255,.62);font-size:.8125rem;font-weight:400}
.es-register-head .lead-actions{flex:0 0 auto}
.es-register-head .trail-actions{display:flex;gap:8px;align-items:center;flex:0 0 auto;flex-wrap:wrap;justify-content:flex-end}
.btn.es-on-navy{color:#fff;border-color:rgba(255,255,255,.35);background:transparent}
.btn.es-on-navy:hover{background:rgba(255,255,255,.12);color:#fff}
/* Solid white chip for an action on a navy header. .bg-navy is --es-blue-800, so .btn-primary on it is 1:1 and .btn-aqua 1.7:1; white fill with blue-800 text is 5.6:1 for both the label and the chip. */
.btn.es-on-navy-solid{background:#fff;color:var(--es-blue-800);border-color:#fff;--bs-btn-border-width:1.5px}
.btn.es-on-navy-solid:hover,
.btn.es-on-navy-solid:focus-visible{background:#fff;color:var(--es-blue-800);border-color:#fff;opacity:.86}

/* Summary chips (the .es-chip name is taken — register uses .es-rchip) */
.es-chip-row{display:flex;flex-wrap:wrap;gap:12px;padding:10px 16px;
  background:var(--es-grey-50,#F1F2F7);border-bottom:.5px solid var(--es-border-subtle)}
.es-rchip{display:inline-flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--es-text-body)}
.es-rchip-dot{width:10px;height:10px;border-radius:999px;background:var(--es-grey-400,#9F9F9F);flex:0 0 auto}
.es-rchip-dot.tone-green{background:var(--es-green-500)} .es-rchip-dot.tone-yellow{background:var(--es-yellow-500)}
.es-rchip-dot.tone-red{background:var(--es-red-500)}     .es-rchip-dot.tone-aqua{background:var(--es-aqua-500)}
.es-rchip-dot.tone-blue{background:var(--es-blue-800)}   .es-rchip-dot.tone-grey{background:var(--es-grey-400,#9F9F9F)}
.es-rchip-n{font-family:var(--es-font-mono);font-variant-numeric:tabular-nums;font-weight:600}

/* Learner rows */
.es-register-rows{display:flex;flex-direction:column}
.es-register-row{display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;
  gap:16px;padding:10px 16px;min-height:56px;
  border-bottom:.5px solid var(--es-border-subtle);border-left:3px solid transparent;outline:none}
.es-register-row:last-child{border-bottom:0}
.es-register-row:hover{background:var(--es-blue-tint,#E7ECF6)}
.es-register-row:focus,.es-register-row.is-cursor{        /* the keyboard cursor */
  background:var(--es-blue-tint,#E7ECF6);border-left-color:var(--es-accent,#4265AF)}
.es-register-row .identity{min-width:0}
.es-register-row .name{color:var(--es-text-strong)}
.es-register-row .sub{font-size:.8125rem;color:var(--es-text-muted);margin-top:1px}
.es-register-row .sub .font-mono{font-family:var(--es-font-mono)}
.es-register-row .send-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:5px}
.es-send-chip{cursor:help}
.es-register-row .level-display{cursor:pointer;color:var(--es-blue-800);text-decoration:underline}

/* Code toggle group */
.es-codes{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}
.es-code-btn{min-width:44px;min-height:44px;padding:0 10px;border-radius:999px;
  border:1.5px solid var(--es-border-input,#E1E6EB);background:transparent;
  color:var(--es-text-muted);font-weight:600;font-size:.8125rem;line-height:1;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  transition:opacity .14s var(--es-ease),background .14s var(--es-ease),border-color .14s var(--es-ease)}
.es-code-btn:hover{opacity:.86}
.es-code-btn:focus-visible{box-shadow:var(--es-focus-ring);border-color:var(--es-accent)}
.es-code-btn.active{border-color:transparent}   /* active fill + letter colour set inline from HighlightColour */

/* Notes button */
.es-note-btn{min-width:44px;min-height:44px;position:relative;color:var(--es-text-muted)}
.es-note-btn.has-note{color:var(--es-aqua-600,#0098D4)}
.es-note-btn.has-note::after{content:"";width:6px;height:6px;border-radius:999px;
  background:var(--es-aqua-500);position:absolute;top:9px;right:9px}

/* Empty state */
.es-register-empty{text-align:center;padding:40px 24px;color:var(--es-text-muted)}
.es-register-empty .fa-users{font-size:2rem;color:var(--es-grey-300,#C7CCD4);margin-bottom:12px}

/* Compact density (40+ learners) */
.es-register.compact .es-register-row{min-height:0;padding:6px 16px}
.es-register.compact .es-avatar{width:32px!important;height:32px!important;font-size:12px!important}
.es-register.compact .send-chips .es-send-chip .lbl{display:none}   /* icon-only chips */
.es-register.compact .es-register-head,
.es-register.compact .es-chip-row{position:sticky;top:0;z-index:5}

/* Narrow screens: stack codes under identity */
@media (max-width:768px){
  .es-register-row{grid-template-columns:auto 1fr auto}
  .es-codes{grid-column:1 / -1;justify-content:flex-start;margin-top:6px}
}

/* ----------------------------------------------------------------------------
   13. RECORD-DETAIL ROW ACTIONS  (decision.php staff-action / profile rows)
   Purpose-named buttons for the trailing action in a .list-group row, so the
   markup is one class per the §6.4d taxonomy. Use INSTEAD of `.btn .btn-*` for
   these row actions. Icon rule: leading icon for labelled actions; trailing
   chevron for "fix / next". Auto-width + nowrap — never stretch with w-100.
---------------------------------------------------------------------------- */
.es-rowbtn{
  display:inline-flex; align-items:center; gap:.4em; white-space:nowrap;
  font:600 .75rem/1 var(--es-font-sans); border-radius:.375rem;
  padding:.4375rem .8125rem; border:1.5px solid transparent; cursor:pointer;
  text-decoration:none; transition:opacity var(--es-dur) var(--es-ease),
    background var(--es-dur) var(--es-ease), border-color var(--es-dur) var(--es-ease);
}
.es-rowbtn:hover{ opacity:.86; text-decoration:none; }
.es-rowbtn:focus-visible{ box-shadow:var(--es-focus-ring); outline:none; }
.es-rowbtn>i{ font-size:.6875rem; }

/* CHANGE — low-emphasis aqua text button (verified / completed rows; also Re-invite, Add another course) */
.es-rowbtn--change{ background:transparent; border-color:transparent;
  color:var(--es-aqua-700,#0673A1); padding-inline:.5rem; }
.es-rowbtn--change:hover{ color:var(--es-aqua-700,#0673A1); text-decoration:underline; opacity:1; }

/* FIX THIS ISSUE / SET DATES — solid red, compact, trailing chevron (outstanding action) */
.es-rowbtn--fix{ background:var(--es-red-500); border-color:var(--es-red-500); color:#fff; }
.es-rowbtn--fix:hover{ color:#fff; }
.es-rowbtn--fix:active{ background:var(--es-red-700); border-color:var(--es-red-700); }

/* MARK AS COMPLETED — solid navy / primary (NOT red) */
.es-rowbtn--complete{ background:var(--es-blue-800); border-color:var(--es-blue-800); color:#fff;
  padding:.5625rem 1.125rem; font-size:.8125rem; }
.es-rowbtn--complete:hover{ color:#fff; }
.es-rowbtn--complete:active{ background:var(--es-accent-press); border-color:var(--es-accent-press); }

/* Enrolment decision workspace — green status check icons 50% larger (scoped, user request).
   scale() gives a true +50% of the rendered size whether the icon is base or fa-lg. */
.es-decision-workspace .fa-circle-check{ display:inline-block; transform:scale(1.5); transform-origin:center; }


/* ----------------------------------------------------------------------------
   14. LEGACY RECONCILIATION  — rules folded in from the deleted main.css
   ----------------------------------------------------------------------------
   main.css (and its siblings) were removed in the ui-uplift cleanup, but the
   <link> lingered so pages kept working off a stale server copy. These are the
   rules still referenced by live markup/JS, re-homed here (the single canonical
   overlay) and re-expressed with eSuite tokens instead of the old raw hex.
   Dead main.css rules (#modal, .flashit, .badge-danger/success/…, .ribbon big-
   icon, .table-dropdown-menu, .button-bar-*) were intentionally NOT migrated.
---------------------------------------------------------------------------- */

/* Loading overlay — full-screen scrim + centred spinner box (cover.js; 469 pages).
   cover.js builds the overlay with #cCover / #cCover_container IDs (not a .cover
   class), so these rules target those IDs. The old cover.css was folded in here;
   its dead <link> injection was removed from cover.js. The old
   `animation: colorchange` referenced a keyframe from a deleted file and is
   dropped (no behavioural loss).

   THE CUSTOMISATION POINT. Clients shipped a byte-identical
   assets/css/cover.css and injected it at runtime from cover.js; this file folds
   it in and drops the injection. What was missing
   was any way for a future client to restyle the overlay WITHOUT forking cover.js
   or re-introducing a second stylesheet — the exact fork that produced the
   divergence in the first place.

   The five --es-cover-* properties below are that surface. They default to the
   eSuite values, so nothing changes for existing clients; a client overrides them
   in one place and touches no shared file:

       :root{ --es-cover-backdrop: rgba(0,0,0,.5); --es-cover-size: 160px; }

   Note --es-cover-backdrop is expressed through the token's RGB channel triple
   rather than a raw rgba(), so a brand change to --es-blue-900 carries into the
   scrim instead of leaving a hard-coded 41,63,109 behind. That hex was the last
   raw colour value in this block and the reason this row was worth doing at all. */
:root{
  --es-blue-900-rgb: 41,63,109;
  --es-cover-backdrop: rgba(var(--es-blue-900-rgb), .35);
  --es-cover-backdrop-inline: rgba(255,255,255,.75);
  --es-cover-panel-bg: var(--es-grey-50, #F1F2F7);
  --es-cover-panel-width: 240px;
  --es-cover-radius: 10px;
  --es-cover-shadow: var(--es-shadow-md);
  --es-cover-anim: 500ms;
}

/* Scrim. Flex-centred rather than absolutely positioned, so the panel stays centred
   at any size -- the old fixed 140px box could not, which is why the panel width is
   now a token and the geometry is not. */
#cCover{ inset:0; width:100%; height:100%; z-index:1000;
  display:flex; align-items:center; justify-content:center; }
#cCover:not(.cover-small){ position:fixed; background-color:var(--es-cover-backdrop); }

/* Inline variant. cover.js adds .cover-small in show(element) when it covers one
   element instead of the page; without these rules that call rendered a full-page
   scrim over a single panel. */
#cCover.cover-small{ position:absolute; background-color:var(--es-cover-backdrop-inline); }

#cCover:not(.cover-small) #cCover_container{
  background-color:var(--es-cover-panel-bg); border-radius:var(--es-cover-radius);
  box-shadow:var(--es-cover-shadow); padding:20px; width:var(--es-cover-panel-width);
  display:flex; flex-direction:column; align-items:center; justify-content:center; }

#cCover:not(.cover-small) #cCover_icon{ display:inline-block; margin-bottom:.25em;
  width:70px; height:70px; text-align:center; }
#cCover.cover-small #cCover_icon{ font-size:3em; color:var(--es-grey-400,#9F9F9F); }

#cCover:not(.cover-small) #cCover_text{ display:block; text-align:center; }
#cCover.cover-small #cCover_text{ display:none; }

#cCover:not(.cover-small) #cCover_progressContainer{ margin:.5em 0 0; width:100%;
  overflow:visible; }

/* ---- STATE ANIMATIONS. NOT DECORATION -- LOAD-BEARING. ----------------------
   cover.js does NOT remove the overlay directly. It swaps .cover-show for
   .cover-hide and then removes the element inside an `animationend` handler:

       cover.cover_container.removeClass('cover-show').addClass('cover-hide');
       cover.cover_container.one('...animationend', function(){ cover.cover.remove(); });

   With no animation on .cover-hide the event never fires, `remove()` never runs,
   and THE LOADING OVERLAY NEVER CLOSES -- on every page that calls done(),
   updated(), error() or hidden(). Restored here after the fold dropped them.

   Vendor prefixes from the original are not carried over: -webkit/-moz/-ms/-o
   animation prefixes are only consulted by browsers that predate this codebase's
   own use of `inset` and `calc()`, so keeping them would protect nothing. */
#cCover .cover-show{ animation-name:cover-show; animation-duration:var(--es-cover-anim); }
#cCover .cover-hide{ animation-name:cover-hide; animation-duration:var(--es-cover-anim);
  animation-fill-mode:forwards; }

@keyframes cover-show{ 0%{ opacity:0; } 100%{ opacity:1; } }
@keyframes cover-hide{ 0%{ opacity:1; } 100%{ opacity:0; } }

/* Respect a reduced-motion preference without breaking the removal contract: the
   animation still runs (so animationend still fires and the overlay is removed),
   it is merely too fast to read as motion. Setting `animation:none` here would
   reintroduce the stuck-overlay bug for those users only -- the worst kind. */
@media (prefers-reduced-motion: reduce){
  :root{ --es-cover-anim: 1ms; }
}

/* jQuery-validation states (validation.js toggles .error / label.error at runtime). */
.error:not(label){ background-color:var(--es-red-50,#FDDCDC);
  border:1px solid var(--es-red-500,#DE1F1F) !important; border-left-width:2px !important; font-weight:600; }
label.error{ color:var(--es-red-700,#A01010); margin-left:0; }
.error::placeholder{ color:var(--es-red-700,#A01010); }
.required{ border-left:2px solid var(--es-red-500,#DE1F1F) !important; font-weight:600; }
.required::placeholder{ color:var(--es-red-700,#A01010); }
.requireOneOf{ border-left:2px dashed var(--es-red-500,#DE1F1F) !important; font-weight:600; }

/* Stacked "input block" — joins a column of inputs into one bordered group. */
.input-block > input[type='text']:not(:first-of-type):not(:last-of-type),
.input-block > span:not(:first-of-type):not(:last-of-type),
.input-block > .input-group:not(:first-of-type):not(:last-of-type) > input[type='text'],
.input-block > .input-group:not(:first-of-type):not(:last-of-type) > .input-group-addon{
  border-top:0 !important; border-bottom:0 !important; border-radius:0 !important; }
.input-block > input[type='text']:first-of-type,
.input-block > span:first-of-type,
.input-block > .input-group:first-of-type > input[type='text'],
.input-block > .input-group:first-of-type > .input-group-addon{
  border-bottom:0 !important; border-bottom-left-radius:0 !important; border-bottom-right-radius:0 !important; }
.input-block > input[type='text']:last-of-type,
.input-block > span:last-of-type,
.input-block > .input-group:last-of-type > input[type='text'],
.input-block > .input-group:last-of-type > .input-group-addon{
  border-top:0 !important; border-top-left-radius:0 !important; border-top-right-radius:0 !important; }
.input-block > .input-group{ width:100%; }
.input-block > .input-group > .input-group-addon{ width:40px; text-align:center; }

/* Textarea word/character counters. */
.textarea-counters{ color:var(--es-grey-400,#9F9F9F); font-size:.8em; }
.textarea-counters > .word-count{ float:left; }
.textarea-counters > .character-count{ float:right; }

/* Plain Bootstrap .table (legacy pages — NOT the .es-table DataTable) — restore a
   readable header band. The old rule painted an off-brand blue (#0580cb); re-homed
   as the eSuite grey-tint + navy header used elsewhere, so headers stay legible
   even where the stale main.css is gone (this was the learner-dashboard symptom). */
.table > tbody > tr > th{ background-color:var(--es-grey-100,#E1E6EB) !important;
  color:var(--es-blue-900,#293F6D); font-weight:600; }
.table > tbody > tr > th > a{ color:var(--es-blue-900,#293F6D); font-weight:400; }
.table:not(.table-condensed) > tbody > tr > td{ height:41px; line-height:20px; padding:10px 15px; }
.table:not(.table-condensed) > tbody > tr > th{ height:42px; line-height:20px; padding:10px 15px; }
.table.table-condensed > tbody > tr > td,
.table.table-condensed > tbody > tr > th{ padding:5px 15px; }
.table-hover > tbody > tr:hover > td{ background-color:var(--es-aqua-50,#DFF4FC) !important; cursor:pointer !important; }
.table-j-fix{ margin:0; border-bottom:0 !important; }
.table-j-fix.table-no-results{ padding:15px; margin-bottom:0 !important; border-top:1px solid var(--es-grey-100,#E1E6EB); }
.table-j-fix th a{ color:var(--es-blue-900,#293F6D); }

/* Wrap-free layout table (timeline.php / search look.php helper). */
.timeline-table{ width:100%; }
.timeline-table > tbody > tr > td{ vertical-align:top; }
.timeline-table > tbody > tr > th:not(:last-child),
.timeline-table > tbody > tr > td:not(:last-child){ white-space:nowrap; }
.timeline-table > tbody > tr > th:last-child,
.timeline-table > tbody > tr > td:last-child{ width:100%; }

/* File-upload chip list. */
.file-upload-list{ padding:0; margin:5px -5px; list-style:none; display:flex; flex-wrap:wrap; align-items:flex-start; }
.file-upload-list li{ margin:5px; padding:5px; display:flex; align-items:flex-start;
  background:var(--es-grey-50,#F1F2F7); border:1px solid var(--es-grey-100,#E1E6EB); border-radius:5px; }
.file-upload-list li .remove{ margin-right:10px; }

/* Vertical (stacked) dropdown-button group. */
.dd-btn-j .dropdown-menu{ top:0; min-width:auto; margin:0; padding:0; border:none; background:none;
  box-shadow:none; text-align:right; flex-direction:column; }
.dd-btn-j .open > .dropdown-menu{ display:flex; }
.dd-btn-j .dropdown-menu > button{ width:100%; margin:0 !important; padding:.5em .5em .5em .25em;
  border-radius:0; border-bottom-width:0; text-align:left; display:flex; align-items:center; }
.dd-btn-j .dropdown-menu > button:first-child{ border-top-left-radius:4px; border-top-right-radius:4px; }
.dd-btn-j .dropdown-menu > button:last-child{ border-bottom-left-radius:4px; border-bottom-right-radius:4px; }

/* ---------------------------------------------------------------------------
   Row-action ⋮ menu (STANDARDISED) — the "edit" dropdown used in list-table
   action columns: <div class="btn-group float-end"> ⋮ + .dropdown-menu of action
   buttons. Several tables (appConfig contractOutcomeType(s)/Groups, learners
   learningAims/claim_new/destinations, organisations profile_contacts) each
   shipped their own near-identical copy of this CSS; the copies fought Bootstrap's
   Popper placement and let the menu render off-screen.

   This one global rule standardises them: the menu OVERLAYS the table (absolute →
   never widens it) and drops DOWN from the button, right-aligned — its right edge
   pinned to the button's right edge so it grows leftward over the table (which is
   always to its left) and never off the right of the screen. It escapes the card /
   .table-responsive clip via the overflow-release rule below. The !important
   overrides are deliberate: they beat the inline styles Popper writes on the menu.

   (Superseded the earlier LEFT side-flyout — inset:0 100% — which top-aligned the
   menu beside the button, overlapping the adjacent column and pushing multi-item
   menus below the row's bottom edge where the lower items read as missing.) */
.btn-group.float-end > .dropdown-menu{
  position:absolute !important;
  inset:100% 0 auto auto !important;    /* drop below the button; pin the menu's right edge to the button's right edge */
  transform:none !important;
  margin:.35rem 0 0 0 !important;       /* small gap below the ⋮ button */
  min-width:12rem;                      /* right edge is pinned, so it grows leftward — never toward the screen edge */
  padding:.35rem;
  flex-direction:column; gap:.35rem;
  border:1px solid var(--es-grey-100,#E1E6EB);
  border-radius:10px;
  background:#fff;
  box-shadow:var(--es-shadow-md,0 4px 12px rgba(41,63,109,.12));
  text-align:left;
  z-index:1030;
}
.btn-group.float-end > .dropdown-menu.show{ display:flex; }
.btn-group.float-end > .dropdown-menu > .btn{
  width:100%; margin:0; border-radius:6px;
  display:flex; align-items:center; justify-content:flex-start;
  text-align:left; white-space:nowrap;
}
.btn-group.float-end > .dropdown-menu > .btn > span:first-child{
  width:1.5em; flex:0 0 auto; text-align:center; margin-right:.4em;
}
/* Clicks land on the action button, never its icon/label span (so delegated
   handlers reading the button's data-* always fire). */
.btn-group.float-end > .dropdown-menu > .btn > *{ pointer-events:none; }

/* The flyout is position:absolute, so it is still clipped by every ancestor with a
   non-visible overflow. For a Table::render list that is FOUR nested clippers:
   .es-table-scroll (overflow-x:auto → computed overflow-y:auto) and .es-table-wrap
   (overflow:hidden) that the renderer wraps every table in, then the page's own
   .table-responsive and the .card (overflow:hidden). The innermost pair is what
   crops the menu at the row's bottom edge (a short scrollbar hugging the header).
   Release the clip on all of them — but ONLY while a row-action menu is actually
   open — so the menu overlays everything; each container goes back to
   clipping/scrolling the instant it closes. :has() is already used here (§9b). */
.es-table-scroll:has(.btn-group.float-end > .dropdown-menu.show),
.es-table-wrap:has(.btn-group.float-end > .dropdown-menu.show),
.table-responsive:has(.btn-group.float-end > .dropdown-menu.show),
.card:has(.btn-group.float-end > .dropdown-menu.show){
  overflow: visible;
}

/* ── Go to top — global floating button (markup in assets/includes/footer.php) ──
   Fixed bottom-right; fades in once a long page is scrolled past the threshold.
   z-index sits below Bootstrap modals (1050+) so it never floats over a dialog. */
.es-gototop{
  position:fixed; left:50%; bottom:1.25rem; z-index:1030;
  width:2.75rem; height:2.75rem;
  display:inline-flex; align-items:center; justify-content:center;
  border:1px solid #fff; border-radius:999px;
  background:var(--es-blue-800,#4265AF); color:#fff; font-size:1rem; line-height:1;
  box-shadow:var(--es-shadow-md,0 4px 12px rgba(10,26,47,.18));
  cursor:pointer;
  opacity:0; visibility:hidden; transform:translateX(-50%) translateY(8px); pointer-events:none;
  transition:opacity .16s cubic-bezier(.2,0,0,1), transform .16s cubic-bezier(.2,0,0,1), visibility .16s;
}
.es-gototop.is-visible{ opacity:1; visibility:visible; transform:translateX(-50%); pointer-events:auto; }
.es-gototop:hover{ opacity:.86; }
.es-gototop:focus-visible{ outline:none; box-shadow:var(--es-focus-ring); }
@media (prefers-reduced-motion: reduce){ .es-gototop{ transition:none; } }
@media print{ .es-gototop{ display:none !important; } }

/* ── Standardised form pages (checkbox/toggle-heavy settings forms) ─────────────
   The card-grouped pattern from handoff_php_bootstrap/standardise-form-pages.md:
   flat field lists become concept .cards; single yes/no flags become toggle
   switches (label left, switch right); multi-select groups stay checkboxes in a
   responsive 2-column grid; short inputs/selects are width-capped. Presentational
   only — a page using these never changes a field name/id/submitted value.
   Checked/focus states already inherit eSuite blue via .form-check-input above. */

/* Small muted group sub-label inside a card — a sub-label, NOT a heading. */
.es-form-grouphd{
  font-size:.8125rem; font-weight:500; color:var(--es-text-muted);
  margin-bottom:.75rem;
}

/* Single yes/no flag row: switch on the left, concept label to its right
   (Bootstrap's native .form-switch order). Hairline separators between rows. */
.es-toggle-row{
  padding:.5rem 0; border-top:1px solid var(--es-grey-100);
}
.es-toggle-row:first-of-type{ border-top:0; }
.es-toggle-row .form-check{ margin:0; min-height:auto; }
.es-toggle-row .form-check-label{ color:var(--es-text-body); }

/* Multi-select checkbox group (4+ items) → responsive 2-column grid. */
.es-check-grid{ display:grid; grid-template-columns:1fr; gap:.5rem .75rem; }
@media (min-width:768px){ .es-check-grid{ grid-template-columns:1fr 1fr; } }
.es-check-grid .form-check{ margin:0; }

/* Width-cap short inputs/selects so they size to content, not the container.
   NB: only for genuinely short values (codes, hours, grades). Descriptive-option
   dropdowns are NOT short inputs — leave them full width (see §7 / .es-combo). */
.es-input-short{ max-width:220px; }
.es-input-xshort{ max-width:120px; }

/* ── Combo-select (descriptive-option dropdown) — standardise-form-pages.md §7 ──
   PREFER a native <select> when the option set is short and static: it is
   accessible for free, its chrome already matches text inputs, and its panel is
   browser-drawn so it can never overflow a neighbouring column. Reach for
   .es-combo ONLY when a searchable/custom combobox is genuinely needed for long
   descriptive options; wire open/close, selection and ARIA (role="combobox" on
   the trigger, role="listbox" on the panel, role="option" on each row,
   aria-expanded/aria-controls/aria-selected) in the page's own JS.
   The trigger reuses .form-select so the closed state is identical to §5 inputs. */
.es-combo{ position:relative; }
/* Open panel — width-LOCKED to its own trigger (never wider than its column),
   overlays as an absolute layer so it never reflows or covers an adjacent field. */
.es-combo-panel{
  position:absolute; top:calc(100% + 2px); left:0;
  width:100%; max-width:100%; max-height:16rem; overflow-y:auto;
  z-index:1035; background:#fff;
  border:1px solid var(--es-grey-100); border-radius:8px;
  box-shadow:var(--es-shadow-md,0 4px 12px rgba(10,26,47,.12));
  padding:.25rem;
  /* No blank header/decorative row — options start immediately. Put a real
     <input class="es-combo-search"> here if the list is long enough to search. */
}
.es-combo-panel[hidden]{ display:none; }
.es-combo-search{
  width:100%; margin:.25rem 0 .35rem; padding:.375rem .6rem;
  font-size:.8125rem; border:1.5px solid var(--es-border-input); border-radius:6px;
}
/* Options — full-width rows, ≥40px touch target, long text wraps rather than
   widening the panel; hover/focus/active use the accent tint, not a grey block. */
.es-combo-option{
  display:flex; align-items:flex-start; gap:.5rem;
  min-height:2.5rem; padding:.5rem .75rem;
  font-size:.8125rem; color:var(--es-text-body); line-height:1.3;
  white-space:normal; border-radius:6px; cursor:pointer;
}
.es-combo-option:hover, .es-combo-option:focus, .es-combo-option.is-active{
  background:var(--es-aqua-50); outline:none;
}
/* Selected marked by weight (+ optional leading <i class="fa-solid fa-check">),
   never colour alone. */
.es-combo-option[aria-selected="true"]{ font-weight:600; }
.es-combo-option .es-combo-check{ flex:0 0 1em; color:var(--es-blue-800); }

/* ── Segmented radio group (single-select pill control) — §8 ────────────────────
   For a factual, mutually-exclusive question with 2–4+ options (Yes/No, Care
   status, EHC/DLA/PIP, …). NOT a switch (that implies an independent flag) and
   NOT a checkbox group. Restyles a horizontal native-radio group — emit it with
   Form::radioButtons(.., 'segmented'), which wraps label.radio-inline > radio
   options in <div class="es-segmented" role="radiogroup"> — into equal-height
   pills: one active at a time, active = blue fill + white, inactive = border-only.
   Native radios keep their name/value + keyboard/AT behaviour; :has() reflects the
   checked child onto its label. Pills wrap to a second row rather than overflow. */
.es-segmented{ display:flex; flex-wrap:wrap; gap:.375rem; align-items:center; }
.es-segmented label,
.es-segmented .radio-inline{
  position:relative; margin:0;
  display:inline-flex; align-items:center; justify-content:center;
  min-height:2.25rem; padding:.375rem .85rem;
  font-size:.8125rem; font-weight:500; color:var(--es-text-body);
  background:#fff; border:1.5px solid var(--es-border-input); border-radius:999px;
  cursor:pointer; white-space:nowrap;
  transition:background .12s cubic-bezier(.2,0,0,1), color .12s cubic-bezier(.2,0,0,1), border-color .12s cubic-bezier(.2,0,0,1);
}
/* Native radio hidden but present + focusable (keyboard/screen-reader intact). */
.es-segmented input[type="radio"]{
  position:absolute; width:1px; height:1px; margin:0; padding:0;
  opacity:0; pointer-events:none;
}
.es-segmented label:hover,
.es-segmented .radio-inline:hover{ border-color:var(--es-blue-600); }
/* Active pill = the label whose radio is checked. */
.es-segmented label:has(> input:checked),
.es-segmented .radio-inline:has(> input:checked){
  background:var(--es-blue-800); border-color:var(--es-blue-800); color:#fff;
}
/* Keyboard focus ring on the focused pill. */
.es-segmented label:has(> input:focus-visible),
.es-segmented .radio-inline:has(> input:focus-visible){
  box-shadow:var(--es-focus-ring);
}
/* Variant B: hand-written .form-check.form-check-inline radios (input is a sibling
   BEFORE the label, not a child). The base pill rule above already styles the
   .form-check-label and hides the input; here we reset the wrapper and drive the
   active/focus state via the adjacent-sibling selector (no :has() needed). */
.es-segmented .form-check{ padding:0; margin:0; min-height:auto; display:inline-flex; }
.es-segmented .form-check-input[type="radio"]:checked + .form-check-label{
  background:var(--es-blue-800); border-color:var(--es-blue-800); color:#fff;
}
.es-segmented .form-check-input[type="radio"]:focus-visible + .form-check-label{
  box-shadow:var(--es-focus-ring);
}

/* ── ILR file viewer (urbi/ilr/ilrViewer.php) ──────────────────────────────────
   Deliberately SMALL. The viewer is a sheet-tab strip over a big data grid, and
   the design system already owns both of those: the strip is the DS Tabs
   component (.es-tabs, §9) and the grid is .es-table-wrap / .es-table-scroll /
   .es-table with the .es-table-foot pager (§9). What is left below is only the
   handful of things the viewer needs that nothing else in the product does.

   The version of this screen we were handed shipped its own stylesheet that
   redeclared the palette (including an --ilr-navy of #0D1F35, which is not an
   eSuite colour at all) and rebuilt tabs, table, sticky header and pager from
   scratch. That file was deleted rather than imported. If you find yourself
   about to add a colour here, use a token. */

/* Sheet tabs as LINKS. .es-tabs styles <button>, because in-screen tabs normally
   switch a view client-side; here each sheet is its own server-rendered page, so
   they have to be anchors to keep middle-click / open-in-new-tab / copy-link
   working. Same visual contract as .es-tabs button, kept adjacent to it on
   purpose so the two cannot drift apart unnoticed. */
.es-tabs a{
  appearance:none; background:none; border:none; cursor:pointer;
  padding:9px 16px; margin-bottom:-2px; text-decoration:none;
  display:inline-flex; align-items:center; gap:7px; white-space:nowrap;
  font:600 .8125rem var(--es-font-sans); color:var(--es-text-muted);
  border-bottom:2px solid transparent;
}
.es-tabs a:hover{color:var(--es-blue-800); text-decoration:none}
.es-tabs a.active{color:var(--es-accent-press); border-bottom-color:var(--es-blue-800)}
/* A file can produce a dozen sheets, which is more than the row can hold. */
.es-tabs.es-tabs-wrap{flex-wrap:wrap}

/* Row count riding on each sheet tab. */
.es-tab-count{
  font:500 .6875rem var(--es-font-mono); padding:1px 7px; border-radius:999px;
  background:var(--es-grey-100); color:var(--es-grey-700);
}
.es-tabs a.active .es-tab-count{background:var(--es-aqua-50); color:var(--es-aqua-700,#0673A1)}

/* A count that reports a problem rather than a size, framed to match the .es-tab-badge-red pill
   the module ribbon carries (9b) -- it is the same number, so it cannot look like two things.
   After the active-state rule on purpose, so an error keeps its colour on the tab you are already
   standing on. */
.es-tab-count-red,
.es-tabs a.active .es-tab-count-red{
  display:inline-flex; align-items:center; justify-content:center;
  min-width:18px; height:18px; padding:0 6px; border-radius:999px;
  font:700 10px/1 var(--es-font-mono);
  background:var(--es-red-50,#FDDCDC); color:var(--es-red-700,#A01010);
  border:1px solid var(--es-red-500,#DE1F1F);
}

/* Tall scroll box with the header row pinned. An ILR sheet is both wide and long,
   and losing the column names after twenty rows makes the grid unreadable. The
   base .es-table-scroll only scrolls sideways, so this is a viewer-only modifier
   rather than a change to shared behaviour. */
.ilrv-scroll{max-height:68vh; overflow:auto}
.ilrv-scroll>.es-table>thead>tr>th{position:sticky; top:0; z-index:2}

/* Code meaning as a subtitle under the raw value, so the value itself stays
   scannable down the column. Nothing equivalent exists in the DS. */
.ilrv-code{
  display:block; font:400 .6875rem/1.2 var(--es-font-sans);
  color:var(--es-grey-400); margin-top:1px;
}

/* Row-number gutter — an ordinal, not data, so it is muted and monospaced. */
.ilrv-rownum{
  width:3.5rem; text-align:right;
  font-family:var(--es-font-mono); font-size:12.5px; color:var(--es-grey-400);
}

/* An absent value is a real ILR fact (most fields are optional), so it is drawn
   as a dash rather than left blank — but lighter than a value, so a column of
   them reads as empty at a glance. */
.ilrv-blank{color:var(--es-grey-300,#C7CCD4)}

.ilrv-nothing{
  padding:2rem 1rem; text-align:center; white-space:normal;
  color:var(--es-grey-400);
}

@media print{
  .ilrv-noprint{display:none}
  .ilrv-scroll{max-height:none; overflow:visible}
  .ilrv-scroll>.es-table>thead>tr>th{position:static}
}


/* ----------------------------------------------------------------------------
   15. VALUE PICKER  (.value-picker / .vp-modal)  — long code lists
   ----------------------------------------------------------------------------
   A rule whose InputQuery returns hundreds of ILR codes used to render as a flat
   four-column checkbox grid: no way to find a value, no way to audit what was
   set. The picker splits that into a read-only POOL on the page (what is
   selected, grouped by code type) and a MODAL that does the searching, filtering
   and ticking. Markup comes from /assets/includes/value-picker.php, behaviour
   from /assets/js/value-picker.js.

   Action colours follow the client's rule rather than each element's own
   instinct: orange opens the editor, green commits, red removes or deselects,
   grey is a neutral dismiss. That is why Close is a ghost button and Save
   selection is green while the PAGE's Save stays blue — blue is still the
   primary CTA, and only one of those two buttons actually saves the contract.
---------------------------------------------------------------------------- */

.value-picker .vp-head{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:8px;
}
.value-picker .vp-title{
  font-weight:600; font-size:.9375rem; color:var(--es-blue-900);
}
.value-picker .vp-pool{
  background:var(--es-grey-50); border:1px solid var(--es-grey-100);
  border-radius:8px; padding:16px 18px;
}
.value-picker .vp-pool-head{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
}
.value-picker .vp-pool-title{
  font-weight:600; font-size:.8125rem; color:var(--es-blue-900);
}
.value-picker .vp-pool-count{
  background:var(--es-blue-tint); color:var(--es-blue-800);
  border-radius:999px; padding:2px 10px; font-size:.6875rem; font-weight:600;
}
.value-picker .vp-pool-meta{
  font-size:.8125rem; color:var(--es-text-muted);
}
/* Orange-600, not the .btn-orange orange-500: white on orange-500 is ~3.0:1 and fails AA at this size. Scoped here rather than changed globally. */
.value-picker .vp-edit{
  margin-left:auto;
  --bs-btn-bg:var(--es-orange-600); --bs-btn-border-color:var(--es-orange-600);
  --bs-btn-hover-bg:var(--es-orange-600); --bs-btn-hover-border-color:var(--es-orange-600);
  --bs-btn-active-bg:var(--es-orange-600); --bs-btn-color:#fff; --bs-btn-hover-color:#fff;
  min-height:34px;
}
.value-picker .vp-pool-body:not(:empty){ margin-top:14px; }
.value-picker .vp-pool-empty{
  margin:14px 0 0; font-size:.8125rem; color:var(--es-text-muted);
}
.value-picker .vp-pool-group + .vp-pool-group{ margin-top:12px; }
.value-picker .vp-pool-group-head{
  display:flex; align-items:center; gap:8px; margin-bottom:6px;
  font:600 .6875rem/1 var(--es-font-sans); letter-spacing:.12em; text-transform:uppercase;
  color:var(--es-text-muted);
}
.value-picker .vp-pool-group-head .font-mono{
  letter-spacing:0; color:var(--es-blue-800); font-size:.6875rem;
}
.vp-pool-chips{ display:flex; flex-wrap:wrap; gap:6px; }

.vp-chip{
  display:inline-flex; align-items:center; gap:8px; max-width:340px;
  background:var(--es-white); border:1px solid var(--es-grey-100);
  border-radius:999px; padding:6px 12px; font-size:.8125rem; color:var(--es-text-body);
}
.vp-chip-code{ font-size:.6875rem; font-weight:600; color:var(--es-blue-900); flex:0 0 auto; }
.vp-chip-label{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.vp-chip-remove{
  flex:0 0 auto; width:20px; height:20px; padding:0; border:0; border-radius:50%;
  background:var(--es-red-500); color:#fff; font-size:.625rem; line-height:1;
  display:inline-flex; align-items:center; justify-content:center;
  transition:opacity var(--es-dur) var(--es-ease);
}
.vp-chip-remove:hover{ opacity:.86; }

.vp-modal .modal-dialog{ max-width:1120px; }
.vp-modal .modal-content{ height:88vh; }
.vp-modal .modal-header{ align-items:flex-start; gap:16px; }
.vp-modal .vp-modal-heading{ flex:1 1 auto; min-width:0; }
.vp-modal .vp-subtitle{
  margin:2px 0 0; font-size:.8125rem; color:rgba(255,255,255,.78);
}
.vp-modal .vp-modal-state{
  display:flex; flex-direction:column; align-items:flex-end; gap:2px;
  font-size:.75rem; color:#fff; white-space:nowrap;
}
.vp-modal .vp-state-drift{ color:rgba(255,255,255,.78); }

/* Tier 1 of the scroll model: the modal body scrolls without a visible bar, so the only scrollbar on screen is the values grid's. The wheel is routed here by value-picker.js. */
.vp-modal .modal-body{
  display:flex; flex-direction:column; gap:14px;
  overflow-y:auto; overscroll-behavior:contain;
  scrollbar-width:none; -ms-overflow-style:none;
}
.vp-modal .modal-body::-webkit-scrollbar{ width:0; height:0; }

.vp-modal .vp-controls{ display:flex; align-items:center; gap:10px; }
.vp-modal .vp-search-wrap{ position:relative; flex:1 1 auto; }
.vp-modal .vp-search-wrap>[class*="fa-"]{
  position:absolute; left:12px; top:50%; transform:translateY(-50%);
  color:var(--es-grey-400); pointer-events:none;
}
.vp-modal .vp-search{ padding-left:36px; }

.vp-modal .vp-cats{ display:flex; flex-wrap:wrap; gap:6px; }
.vp-modal .vp-cat{
  display:inline-flex; align-items:center; gap:6px; white-space:nowrap;
  background:var(--es-white); border:1.5px solid var(--es-grey-100);
  border-radius:999px; padding:6px 14px; min-height:32px;
  font:600 .75rem/1 var(--es-font-sans); color:var(--es-blue-800);
  transition:background-color var(--es-dur) var(--es-ease), border-color var(--es-dur) var(--es-ease);
}
.vp-modal .vp-cat:hover{ background:var(--es-grey-50); }
.vp-modal .vp-cat.active{
  background:var(--es-blue-800); border-color:var(--es-blue-800); color:#fff;
}
.vp-modal .vp-cat .font-mono{ font-size:.6875rem; opacity:.8; }

.vp-modal .vp-meta{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  font-size:.8125rem; color:var(--es-text-muted);
}
.vp-modal .vp-bulk{ display:flex; gap:6px; margin-left:auto; }
.vp-modal .vp-select-all{
  background:var(--es-white); border:1.5px solid var(--es-aqua-600); color:var(--es-aqua-700);
  font-weight:600;
}
.vp-modal .vp-deselect-all,
.vp-modal .vp-clear-all{
  background:var(--es-white); border:1.5px solid var(--es-red-500); color:var(--es-red-700);
  font-weight:600;
}

/* Tier 2: the values grid keeps its scrollbar. overflow-y is `scroll`, not `auto`, so the list does not jump sideways as a search narrows it. */
.vp-modal .vp-list{
  display:grid; grid-template-columns:repeat(2, minmax(0,1fr));
  height:46vh; min-height:200px; overflow-y:scroll; overscroll-behavior:contain;
  border:1px solid var(--es-grey-100); border-radius:8px; scrollbar-width:thin;
  /* The modal body is a flex column; without this the grid is squeezed instead of the body scrolling. */
  flex:0 0 auto;
}
.vp-modal .vp-row{
  display:flex; align-items:center; gap:11px; margin:0; padding:10px 14px;
  border-bottom:1px solid var(--es-grey-100); cursor:pointer;
  transition:background-color var(--es-dur) var(--es-ease);
}
.vp-modal .vp-row:hover{ background:var(--es-grey-50); }
/* Selection is never colour alone -- the tint is there so a ticked row survives peripheral vision. */
.vp-modal .vp-row--on{ background:var(--es-blue-tint); }
.vp-modal .vp-row input[type="checkbox"]{ flex:0 0 auto; margin:0; accent-color:var(--es-blue-800); }
.vp-modal .vp-code{
  flex:0 0 auto; min-width:66px; font-size:.75rem; font-weight:600; color:var(--es-blue-900);
}
.vp-modal .vp-label{ min-width:0; font-size:.8125rem; }
.vp-modal .vp-empty{
  padding:1rem; text-align:center; font-size:.8125rem; color:var(--es-text-muted);
}

.vp-modal .vp-draft{
  background:var(--es-grey-50); border:1px solid var(--es-grey-100);
  border-radius:8px; padding:12px 14px; flex:0 0 auto;
}
.vp-modal .vp-draft-head{
  display:flex; align-items:center; gap:10px; margin-bottom:8px;
  font:600 .8125rem/1 var(--es-font-sans); color:var(--es-blue-900);
}
.vp-modal .vp-clear-all{ margin-left:auto; }
.vp-modal .vp-draft-chips{ display:flex; flex-wrap:wrap; gap:6px; max-height:96px; overflow-y:auto; }
.vp-modal .vp-draft-empty{ font-size:.8125rem; color:var(--es-text-muted); }

.vp-modal .modal-footer{ gap:10px; }
.vp-modal .vp-footer-note{
  flex:1 1 auto; min-width:0; margin-right:auto;
  font-size:.75rem; color:var(--es-text-muted);
}
/* In a flex-end footer next to a note, buttons squeeze below their label width and the text wraps. */
.vp-modal .modal-footer .btn{ white-space:nowrap; flex-shrink:0; }

/* Folded into the existing card footer rather than a second floating bar: the footer is the AJAX insertion anchor, and .card is overflow:hidden, which stops position:sticky on a child. */
#pageForm .vp-total{
  margin-right:auto; font-size:.8125rem; color:var(--es-text-muted);
}

@media (max-width: 1199.98px){
  .vp-modal .vp-list{ grid-template-columns:minmax(0,1fr); }
}
@media (max-width: 767.98px){
  .value-picker .vp-edit{ margin-left:0; width:100%; }
  .vp-modal .modal-content{ height:92vh; }
  .vp-modal .vp-controls{ flex-wrap:wrap; }
}


/* ----------------------------------------------------------------------------
   16. FUNDING SOURCE EDIT  (.es-contract-edit)  — vertical rhythm
   ----------------------------------------------------------------------------
   contractEdit.php is one long stack of label/control pairs and a run of
   seventeen checkbox statements, and it carried no vertical spacing at all: the
   rows are separated by <div class="clearfix">, which has no height, and neither
   a bare <label> nor .form-control takes a margin from the DS. So each field sat
   directly on the one above it and the checkbox run was Bootstrap's default
   .125rem apart — hard to scan, and easy to tick the wrong line.

   Both are loosened by roughly a quarter of their own pitch:

     field row   ~57px (label 22.5 + control 35)  ->  ~71px   (+24%)
     check row   ~26px (min-height 24 + 2)        ->  ~32px   (+23%)

   row-gap rather than --bs-gutter-y: the gutter variable pairs a margin-top on
   the children with a compensating NEGATIVE margin-top on the row, so it adds
   nothing between one row and the next and would eat the margin-bottom below.
   row-gap spaces wrapped columns without that compensation, so the two rules
   stay independent of each other.
---------------------------------------------------------------------------- */
.es-contract-edit .card-body > .row{ margin-bottom:.875rem; row-gap:.875rem; }
.es-contract-edit .card-body > .row:last-child{ margin-bottom:0; }
.es-contract-edit .form-check{ margin-bottom:.5rem; }
.es-contract-edit .form-check:last-child{ margin-bottom:0; }


/* ----------------------------------------------------------------------------
   HR — leave calendars  (public/urbi/crm/hr)

   Three grids: a month on Staff home, a twelve-month year grid on My annual
   leave, and a 21-day team strip. All three are rendered server-side by
   hr/includes/hrCalendars.php; nothing here depends on JavaScript.

   COLOUR IS NEVER THE ONLY ENCODING (WCAG 1.4.1). Every cell carries title text,
   the team strip carries a letter, and the legend keeps a word beside every
   swatch. These rules only colour what the markup has already named.

   Every colour is a token. Nothing below invents a hex.
   ---------------------------------------------------------------------------- */

/* The base fill is AT WORK, NOTHING BOOKED -- scheduled that day and available. Standard blue,
   so the grids read: blue at work, a status colour booked, grey not working that day. */
.hr-cell{ display:inline-block; width:100%; height:100%; min-width:9px; min-height:9px;
          border-radius:2px; background:var(--es-blue-800); color:#fff; }
.hr-cell-approved      { background:var(--es-green-500); }
.hr-cell-pending       { background:var(--es-yellow-500); }
.hr-cell-cancelpending,
.hr-cell-cancel_pending{ background:var(--es-orange-500); }
.hr-cell-rejected      { background:var(--es-red-50); border:1px solid var(--es-red-500); }
.hr-cell-cancelled     { background:var(--es-grey-100); }
.hr-cell-holiday       { background:repeating-linear-gradient(45deg,
                             var(--es-aqua-50) 0 3px, var(--es-grey-50) 3px 6px); }
/* Not working that day at all -- no pattern row. Grey, because nothing is expected of it. */
.hr-cell-unscheduled   { background:var(--es-grey-100); }

.hr-legend{ display:flex; flex-wrap:wrap; gap:16px; margin-top:12px; font-size:.8125rem;
            color:var(--es-grey-700); }
.hr-legend-item{ display:inline-flex; align-items:center; gap:6px; }
.hr-legend-item .hr-cell{ width:12px; height:12px; min-width:12px; flex:0 0 12px; }

.hr-month{ display:grid; grid-template-columns:repeat(7, 1fr); gap:4px; }
.hr-month-head{ font-size:.6875rem; letter-spacing:.12em; text-transform:uppercase;
                color:var(--es-grey-400); padding:4px 2px; text-align:center; }
.hr-month-cell{ min-height:84px; border:.5px solid var(--es-grey-100); border-radius:6px;
                padding:4px; background:#fff; }
.hr-month-blank{ border:none; background:transparent; }
.hr-month-cell.is-today{ border:1.5px solid var(--es-blue-800); }
.hr-month-cell.is-holiday{ background:var(--es-aqua-50); }
.hr-month-num{ display:block; font-size:.75rem; color:var(--es-grey-400); margin-bottom:2px; }
.hr-chip{ display:block; font-size:.6875rem; line-height:1.3; padding:1px 4px; border-radius:999px;
          margin-bottom:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.hr-chip-leave  { background:var(--es-green-50, #E4F5E4); color:var(--es-green-700, #1B7A1D); }
.hr-chip-event  { background:var(--es-aqua-50);  color:var(--es-aqua-700, #0673A1); }
.hr-chip-crm    { background:var(--es-blue-50, #E7EDF8); color:var(--es-blue-900); }
.hr-chip-holiday{ background:var(--es-grey-100); color:var(--es-grey-700); }

.hr-year{ display:grid; grid-template-columns:repeat(4, 1fr); gap:16px; }
.hr-year-title{ font-size:.75rem; font-weight:600; color:var(--es-blue-900); margin-bottom:4px; }
.hr-year-days{ display:grid; grid-template-columns:repeat(7, 1fr); gap:2px; }
/* One 26px cell per day, split AM | PM — the ancestor page's shape, kept deliberately. */
.hr-year-head{ font-size:.5625rem; text-align:center; color:var(--es-grey-400);
               text-transform:uppercase; letter-spacing:.04em; }
.hr-blank{ visibility:hidden; }
.hr-day{ display:flex; flex-direction:column; gap:1px; min-height:26px; }
.hr-num{ font-size:.5625rem; line-height:1.1; text-align:center; color:var(--es-grey-700);
         font-variant-numeric:tabular-nums; }
.hr-day.is-quiet .hr-num{ color:var(--es-grey-400); }
.hr-slots{ display:flex; flex-direction:column; gap:1px; flex:1 1 auto; min-height:12px; }
.hr-slots .hr-cell{ flex:1 1 50%; }
/* An adjusted day is marked, not recoloured: the leave status underneath still has to read. */
.hr-day.is-override{ outline:1.5px solid var(--es-orange-500); outline-offset:1px; border-radius:3px; }

@media (max-width: 991.98px){ .hr-year{ grid-template-columns:repeat(2, 1fr); } }
@media (max-width: 575.98px){ .hr-year{ grid-template-columns:1fr; } }

.hr-strip-wrap{ overflow-x:auto; }
.hr-strip{ border-collapse:separate; border-spacing:2px; font-size:.75rem; }
.hr-strip th{ font-weight:500; color:var(--es-grey-700); padding:2px 6px; white-space:nowrap; }
.hr-strip thead th{ text-align:center; color:var(--es-grey-400); font-size:.6875rem; }
.hr-strip-dow{ display:block; }
.hr-strip-num{ display:block; font-weight:600; color:var(--es-grey-700); }
/* display:table-cell is load-bearing: hr_cell_class() puts .hr-cell on these <td>s, and that
   shared class is unscoped and sets display:inline-block -- which takes them out of the table
   and collapses all 21 into one anonymous cell. Only the fill was ever wanted from it. */
.hr-strip tbody td{ display:table-cell; width:22px; height:22px; text-align:center;
                    vertical-align:middle; border-radius:3px; font-weight:600;
                    font-size:.6875rem; color:var(--es-blue-900); }
/* These set the fill only; the display above is what keeps them behaving as cells.

   ORDER IS LOAD-BEARING. Every cell carries .hr-cell AND its variant, and both selectors weigh
   the same, so the LAST one wins. The base fill therefore has to come first -- below it, or the
   at-work blue paints over approved, pending and not-scheduled alike. */
.hr-strip tbody td.hr-cell               { background:var(--es-blue-800); color:#fff; }
.hr-strip tbody td.hr-cell-approved      { background:var(--es-green-500); color:#fff; }
.hr-strip tbody td.hr-cell-pending       { background:var(--es-yellow-500);
                                           color:var(--es-blue-900); }
.hr-strip tbody td.hr-cell-cancel_pending{ background:var(--es-orange-500); color:#fff; }
.hr-strip tbody td.hr-cell-unscheduled   { background:var(--es-grey-100);
                                           color:var(--es-text-muted); }
.hr-strip tfoot .hr-cover{ text-align:center; font-weight:600; font-size:.6875rem;
                           color:var(--es-grey-700); background:var(--es-grey-50); border-radius:3px; }
/* Short cover is red AND carries the count as text — never colour alone. */
.hr-strip tfoot .hr-cover.is-short{ background:var(--es-red-50, #FDDCDC);
                                    color:var(--es-red-700, #A01010); }

.hr-chip-toggle{ display:inline-flex; align-items:center; gap:6px; padding:4px 10px;
                 border:1.5px solid var(--es-grey-100); border-radius:999px; font-size:.8125rem;
                 cursor:pointer; }
.hr-chip-toggle:has(input:checked){ border-color:var(--es-orange-500);
                                    background:var(--es-orange-50, #FFEDE0); }

/* --- the staff list: a row is a record, and selecting it opens the record ---
   The name is a real <button>, so the record is reachable by keyboard and named
   to a screen reader; the row-wide pointer is the mouse affordance over the top
   of it, never the only way in. */
#hrStaffTable tbody tr{ cursor:pointer; }
.hr-staff-open{ font:inherit; color:var(--es-blue-800); text-decoration:none;
                border:0; background:none; text-align:left; }
.hr-staff-open:hover,
.hr-staff-open:focus{ color:var(--es-blue-900); text-decoration:underline; }

.hr-next{ display:grid; grid-template-columns:1fr auto; gap:2px 8px; padding:8px 10px;
          border:.5px solid var(--es-grey-100); border-radius:8px; text-decoration:none;
          color:var(--es-grey-700); }
.hr-next:hover,
.hr-next:focus{ background:var(--es-aqua-50); color:var(--es-blue-900); }
.hr-next-title { font-weight:600; color:var(--es-blue-900); }
.hr-next-when  { grid-column:1; font-size:.8125rem; color:var(--es-grey-400); }
.hr-next-status{ grid-row:1 / span 2; grid-column:2; align-self:center; }

/* The linked row marks itself, so arriving from Staff home lands somewhere obvious. */
.es-table tr:has(.hr-request:target){ background:var(--es-aqua-50); }

/* --- who is working when: one day, by the hour ---
   The bar is positioned by an inline left/width percentage, the way es_progress
   sets its fill: geometry from data, never a colour. Every bar carries its own
   times as text, so the hours read without the bar. */
.hr-timeline-scroll{ overflow-x:auto; }
.hr-timeline{ min-width:720px; }
.hr-timeline-row{ display:grid; grid-template-columns:200px 1fr 64px 96px; gap:12px;
                  align-items:center; padding:4px 0; }
.hr-timeline-head{ padding-bottom:0; }
.hr-timeline-name{ display:block; font-weight:600; font-size:.8125rem;
                   color:var(--es-blue-900); }
.hr-timeline-meta{ display:block; font-size:.6875rem; color:var(--es-aqua-700, #0673A1); }
.hr-timeline-track{ position:relative; height:30px; border-radius:6px;
                    background:var(--es-grey-50); }
.hr-timeline-track.is-ruler{ height:18px; background:transparent; }
.hr-timeline-tick{ position:absolute; top:0; transform:translateX(-50%); font-size:.625rem;
                   color:var(--es-grey-400); white-space:nowrap; }
.hr-timeline-bar{ position:absolute; top:3px; bottom:3px; display:flex; align-items:center;
                  justify-content:center; border:1.5px solid var(--es-aqua-500);
                  background:var(--es-aqua-50); border-radius:6px; font-size:.75rem;
                  font-weight:600; color:var(--es-blue-900); overflow:hidden;
                  white-space:nowrap; }
.hr-timeline-bar.is-off{ border-color:var(--es-grey-100); background:var(--es-grey-100);
                         color:var(--es-grey-700); font-weight:400; }
.hr-timeline-none{ position:absolute; left:10px; top:50%; transform:translateY(-50%);
                   font-size:.75rem; color:var(--es-grey-400); }
.hr-timeline-hours{ text-align:right; font-size:.75rem; color:var(--es-grey-700); }
.hr-timeline-state{ text-align:right; }

@media (max-width: 767.98px){
    .hr-timeline-row{ grid-template-columns:140px 1fr 56px 84px; gap:8px; }
}

.hr-working{ font-size:.8125rem; margin-bottom:0; }
.hr-working th,
.hr-working td{ vertical-align:middle; white-space:nowrap; }
.hr-working-dow{ display:block; font-size:.6875rem; letter-spacing:.12em;
                 text-transform:uppercase; color:var(--es-grey-400); }
.hr-working-num{ display:block; font-weight:600; color:var(--es-blue-900); }
.hr-working-cell{ text-align:center; font-variant-numeric:tabular-nums; }
.hr-working-cell.is-working    { color:var(--es-grey-700); }
.hr-working-cell.is-off        { background:var(--es-aqua-50); color:var(--es-aqua-700, #0673A1); }
.hr-working-cell.is-unscheduled{ background:var(--es-grey-100); color:var(--es-text-muted); }

.hr-pattern{ font-size:.8125rem; margin-bottom:0; }
.hr-pattern th,
.hr-pattern td{ vertical-align:middle; white-space:nowrap; padding:6px 8px; }
.hr-pattern input[type="time"]{ min-width:112px; }
.hr-pattern input[type="number"]{ max-width:88px; }
.hr-pattern select{ min-width:132px; }

/* ============================================================ §12 helpdesk conversation ===== */

.es-msg{
    border:.5px solid var(--es-grey-100);
    border-radius:8px;
    padding:12px 14px;
    margin-bottom:12px;
    background:#fff;
}
.es-msg:last-child{ margin-bottom:0; }

/* An agent reply reads as "ours": tinted well, so the thread alternates without colour-coding. */
.es-msg.is-agent{ background:var(--es-grey-50); }

/* The one exception to "no coloured left borders outside Alerts" would be wrong here, so an
   internal note is marked by its badge and a dashed edge instead of a rule. */
.es-msg.is-private{ border-style:dashed; border-color:var(--es-orange-500); }

.es-msg-head{
    display:flex;
    align-items:center;
    gap:8px;
    flex-wrap:wrap;
    margin-bottom:8px;
}
.es-msg-who{ font-weight:600; color:var(--es-blue-900); }
.es-msg-when{ margin-left:auto; font-size:.8125rem; color:var(--es-grey-400); }
.es-msg-body{
    font-size:.9375rem;
    color:var(--es-grey-700);
    white-space:normal;
    overflow-wrap:anywhere;
}

/* ============================================================ §13 disclosure + leave ========= */

/* A shut-by-default panel for an occasional act on a screen that is mostly for reading.
   The summary is the control, so it carries the focus ring and the hit area, not the marker. */
.es-disclosure                { border:.5px solid var(--es-border-subtle); border-radius:10px;
                                background:var(--es-white); }
.es-disclosure > summary      { list-style:none; cursor:pointer; padding:12px 16px;
                                font-weight:600; font-size:.9375rem; color:var(--es-blue-900);
                                display:flex; align-items:center; gap:10px; border-radius:10px; }
.es-disclosure > summary::-webkit-details-marker { display:none; }
/* A real <i>, not a ::after content code: a mistyped font-family renders a tofu box. */
.es-disclosure > summary .es-chevron { margin-left:auto; font-size:.75rem;
                                       color:var(--es-grey-400);
                                       transition:transform .14s cubic-bezier(.2,0,0,1); }
.es-disclosure[open] > summary .es-chevron { transform:rotate(180deg); }
.es-disclosure > summary:hover{ background:var(--es-grey-50); }
.es-disclosure > summary:focus-visible{ outline:none; box-shadow:var(--es-focus-ring); }
.es-disclosure > div          { padding:0 16px 16px; }
/* The card inside a disclosure is already framed by the disclosure itself. */
.es-disclosure > div > .card  { border:0; box-shadow:none; }

/* --- annual leave, in the Teams "Out of office" colour ---
   Leave REPLACES the working bar rather than hiding the row: scheduled but away is a different
   fact from not scheduled at all, and the two must not read the same. */
.hr-timeline-bar.is-leave     { border-color:var(--es-hr-leave);
                                background:var(--es-hr-leave-50);
                                color:var(--es-hr-leave-700); }
.hr-working-cell.is-leave     { background:var(--es-hr-leave-50); color:var(--es-hr-leave-700); }
.es-status.tone-leave > .es-dot,
.hr-legend-swatch.is-leave    { background:var(--es-hr-leave); }

/* A day nobody is scheduled on. Read from the patterns, never from the weekday -- a Saturday
   somebody works is an ordinary column, and the weekend is only ever the usual case of this. */
.hr-strip th.is-closed        { background:var(--es-grey-50); color:var(--es-text-muted); }
.hr-strip th.is-closed .hr-strip-num { color:var(--es-text-muted); }
/* Empty on purpose: there is nothing to be available for, so no number to print. */
.hr-strip-closed              { background:var(--es-grey-50); border-radius:3px; }

/* Pinned on all three rows: width on a td is only a preference, so an empty cell let its column
   collapse and handed the slack to its neighbours. First column is the name, so exempt. */
.hr-strip thead th:not(:first-child),
.hr-strip tbody td,
.hr-strip tfoot td            { width:24px; min-width:24px; max-width:24px; padding:2px 0; }

/* Today, so a month-long strip says where you are in it without counting columns. */
.hr-strip th.is-today         { background:var(--es-aqua-50); color:var(--es-blue-900);
                                box-shadow:inset 0 -2px 0 var(--es-aqua-500); }
.hr-strip th.is-today .hr-strip-num { color:var(--es-blue-900); }

/* --- a day whose halves disagree ---
   Leave is booked AM/PM, so a cell splits when its morning and afternoon differ. Days that agree
   stay whole and keep their letter: the split itself is the signal, and splitting every day would
   shrink the letter on the whole-day bookings that are most of them. */
.hr-strip tbody td.hr-strip-split { padding:0; background:transparent; line-height:0; }
.hr-strip-split > .hr-cell    { display:block; width:100%; height:10px; margin:0 0 2px;
                                border-radius:2px; font-size:.5625rem; font-weight:600;
                                line-height:10px; text-align:center; color:#fff; }
.hr-strip-split > .hr-cell:last-child { margin-bottom:0; }
/* Yellow needs dark text; the whole-cell rules above make the same call. */
.hr-strip-split > .hr-cell-pending     { color:var(--es-blue-900); }
.hr-strip-split > .hr-cell-unscheduled { color:var(--es-text-muted); }

.hr-strip tfoot .hr-cover.is-split { padding:0; background:transparent; }
.hr-cover-half                { display:block; height:10px; line-height:10px; margin:0 0 2px;
                                border-radius:2px; font-size:.5625rem;
                                background:var(--es-grey-50); color:var(--es-grey-700); }
.hr-cover-half:last-child     { margin-bottom:0; }
.hr-cover-half.is-short       { background:var(--es-red-50, #FDDCDC);
                                color:var(--es-red-700, #A01010); }

/* --- the cover row is fenced, not merely tinted ---
   It sat directly under the last name and read as one more member of the team. The top and bottom
   rules are single cells spanning the table, because 2px border-spacing turns a border-per-cell
   into a dashed line; the sides are borders on the two end cells. */
.hr-strip tfoot .hr-cover-rule td { width:auto; min-width:0; max-width:none; height:2px;
                                    padding:0; background:var(--es-hr-cover-frame); }
.hr-strip tfoot .hr-cover-row > th,
.hr-strip tfoot .hr-cover-row > td { background:var(--es-grey-50); }
.hr-strip tfoot .hr-cover-row > th:first-child { font-weight:600; color:var(--es-blue-900);
                                    border-left:2px solid var(--es-hr-cover-frame); }
.hr-strip tfoot .hr-cover-row > td:last-child  {
                                    border-right:2px solid var(--es-hr-cover-frame); }

/* Where one month ends and the next begins, for a strip read a whole year wide. */
.hr-strip th.is-month-start   { box-shadow:inset 2px 0 0 var(--es-grey-100); }
.hr-strip th.is-today.is-month-start { box-shadow:inset 2px 0 0 var(--es-grey-100),
                                                   inset 0 -2px 0 var(--es-aqua-500); }

/* The month a run of columns belongs to, above them. A year-wide strip is otherwise 365 numbered
   columns with nothing saying where March is. */
.hr-strip thead .hr-strip-months th { width:auto; min-width:0; max-width:none; padding:0 0 3px;
                                text-align:left; font-size:.6875rem; font-weight:600;
                                color:var(--es-blue-900); white-space:nowrap; overflow:hidden;
                                box-shadow:inset 2px 0 0 var(--es-grey-100); }
.hr-strip thead .hr-strip-months th:first-child { box-shadow:none; }

/* --- the Person column stays put ---
   A year-wide strip is nine thousand pixels across, and a row of coloured cells with the name
   scrolled off the left says nothing at all. The cells are already opaque; the header and the name
   column need their own background or the columns slide underneath them. */
.hr-strip th:first-child      { position:sticky; left:0; z-index:2;
                                background:var(--es-white); }
.hr-strip thead th:first-child,
.hr-strip tfoot .hr-cover-row > th:first-child { z-index:3; }
.hr-strip tbody th:first-child{ box-shadow:1px 0 0 var(--es-grey-100); }

/* Who leads the team, on the row the ordering already puts first. */
.hr-strip-lead                { display:inline-block; margin-left:4px; padding:0 5px;
                                border-radius:999px; background:var(--es-aqua-50);
                                color:var(--es-blue-900); font-size:.5625rem; font-weight:600;
                                letter-spacing:.06em; text-transform:uppercase; }

/* ============================================================ §14 verification progress ====== */
/* One segment per verification still standing between an enrolment and being moved on. Segments
   rather than one filled bar: a percentage would say how many are done while hiding which. */
.es-vprogress                 { display:flex; gap:8px; width:100%; align-items:flex-end; }
.es-vprogress-item            { flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:5px; }
.es-vprogress-label           { font-size:.625rem; font-weight:600; line-height:1.25; text-align:center;
                                letter-spacing:.03em; color:var(--es-grey-400, #9F9F9F);
                                overflow-wrap:anywhere; text-wrap:balance; }
.es-vprogress-seg             { display:block; height:8px; border-radius:999px;
                                background:var(--es-grey-100, #E1E6EB); }
.es-vprogress-seg--met        { background:var(--es-green-500, #27AC2A); }
.es-vprogress-seg--due        { background:var(--es-red-500, #DE1F1F); }

/* The overlay host, for two-column sub-tasks: lg is 800px and even xl only 1140. Capped so it does
   not stretch to nonsense on an ultrawide. */
.modal-dialog.modal-wide       { max-width:min(1600px, calc(100vw - 3rem)); }

/* Turned rather than swapped for another glyph, so open and shut are one element. */
.es-collapse-caret            { transition:transform .15s cubic-bezier(.2,0,0,1); }
[aria-expanded="true"] > .es-collapse-caret { transform:rotate(90deg); }

/* The answer a "Change" link landed on. Cells as well as the row: .table-striped paints
   `tbody tr > *`, which would cover a row background on every other one. */
.es-focus-answer,
.es-focus-answer > td         { animation:esFocusAnswer 2.4s cubic-bezier(.2,0,0,1) forwards; }
@keyframes esFocusAnswer      { 0%, 45% { background-color:var(--es-aqua-50, #DFF4FC); }
                                100%    { background-color:transparent; } }
@media (prefers-reduced-motion: reduce) {
    .es-focus-answer,
    .es-focus-answer > td     { animation:none; background-color:var(--es-aqua-50, #DFF4FC); }
}

/* ============================================================ §15 list filter bar ============ */
/* A list screen's own filters, in a well directly above the grid: quick-find, then one dropdown
   per facet with a live record count against each value. Replaces the left-hand filter rail, so
   the grid takes the full page width. The per-column funnel inside the table (§9) is a different
   control and is untouched — the funnel narrows a column, this bar narrows the screen.
   Facets are <details>, so opening, selecting and applying all work with JavaScript off;
   es-filter-bar.js adds only one-open-at-a-time, outside-click close, and the quick-find. */
.es-fbar                      { display:flex; align-items:center; flex-wrap:wrap; gap:8px;
                                padding:9px 14px; background:var(--es-grey-50, #F1F2F7);
                                border-bottom:.5px solid var(--es-border-subtle); }

/* A bar with no card to sit inside — the CRM support queue and dev board render their grids bare
   on purpose, and a grey bar on the grey page would have no edges. So it brings its own shell. */
.es-fbar--bare                { background:#fff; border:.5px solid var(--es-border-subtle);
                                border-radius:10px; margin-bottom:12px; }

/* Quick-find */
.es-fbar-find                 { position:relative; flex:0 1 300px; min-width:170px; }
.es-fbar-find > i             { position:absolute; left:11px; top:50%; transform:translateY(-50%);
                                font-size:12px; color:var(--es-grey-400, #9F9F9F); pointer-events:none; }
.es-fbar-find > input         { width:100%; height:34px; box-sizing:border-box; padding:0 11px 0 30px;
                                border:1.5px solid var(--es-border-input, #E1E6EB); border-radius:6px;
                                background:#fff; font:400 13px var(--es-font-sans);
                                color:var(--es-grey-700, #4D4D4D); outline:none; }
.es-fbar-find > input:focus   { border-color:var(--es-accent); box-shadow:var(--es-focus-ring); }
/* Both boxes are type=search for the native clear button; Safari would otherwise round them. */
.es-fbar-find > input,
.es-fbar-search               { -webkit-appearance:none; appearance:none; }

/* Submit beside the find box, on a bar whose search runs on the server (a paged table, where a
   browser-side row filter would only ever search the page you are on). Deliberately the form's
   FIRST submit, so Enter in the box searches rather than firing a facet's Clear. */
.es-fbar-go                   { height:34px; padding:0 13px; flex:none; border-radius:6px;
                                border:1.5px solid var(--es-accent); background:var(--es-accent);
                                color:#fff; font:600 13px var(--es-font-sans); cursor:pointer;
                                white-space:nowrap; transition:opacity .12s var(--es-ease); }
.es-fbar-go:hover             { opacity:.86; }
.es-fbar-go:focus-visible     { box-shadow:var(--es-focus-ring); }

/* Facet trigger. Aqua once it carries a selection — the same language as the active-filter chips
   in §9; orange stays reserved for nav chrome. */
.es-fbar-facet                { position:relative; }
.es-fbar-facet > summary      { display:inline-flex; align-items:center; gap:7px; height:34px;
                                padding:0 11px; border:1.5px solid var(--es-border-input, #E1E6EB);
                                border-radius:6px; background:#fff; cursor:pointer; list-style:none;
                                white-space:nowrap; font:600 13px var(--es-font-sans);
                                color:var(--es-grey-700, #4D4D4D);
                                transition:background .12s var(--es-ease), border-color .12s var(--es-ease); }
.es-fbar-facet > summary::-webkit-details-marker { display:none; }
.es-fbar-facet > summary::marker { content:""; }
.es-fbar-facet > summary:hover{ background:var(--es-grey-50, #F1F2F7); border-color:var(--es-grey-300, #C7CCD4); }
.es-fbar-facet > summary:focus-visible { box-shadow:var(--es-focus-ring); border-color:var(--es-accent); }
.es-fbar-facet.is-on > summary{ border-color:var(--es-aqua-500, #00AEEF);
                                background:var(--es-aqua-50, #DFF4FC); color:var(--es-aqua-700, #0673A1); }
.es-fbar-caret                { font-size:9px; color:var(--es-grey-400, #9F9F9F);
                                transition:transform .14s var(--es-ease); }
.es-fbar-facet.is-on > summary .es-fbar-caret { color:var(--es-aqua-700, #0673A1); }
.es-fbar-facet[open] > summary .es-fbar-caret { transform:rotate(180deg); }
/* The count of what is selected, and the count of what a value would return. A count is a value
   the reader is comparing, not chrome, so it takes the strong ink everywhere — including on the
   aqua active chip, where the label stays aqua-700 and only the number goes dark. */
.es-fbar-n                    { font-family:var(--es-font-mono); font-variant-numeric:tabular-nums;
                                font-size:11px; font-weight:600; color:var(--es-text-strong); }
/* A single-select facet names its choice instead of counting it — one of two says which one. */
.es-fbar-pick                 { font-weight:600; color:var(--es-aqua-700, #0673A1); }

/* Panel. Chrome matches the funnel popover (§9) and reuses its .es-fm-* internals, but anchors to
   its own summary rather than being placed by script. */
.es-fbar-menu                 { position:absolute; z-index:1100; top:calc(100% + 6px); left:0;
                                width:320px; background:#fff; border-radius:12px;
                                border:.5px solid var(--es-border-subtle);
                                box-shadow:0 12px 32px rgba(10,26,47,.22); overflow:hidden;
                                font-family:var(--es-font-sans); color:var(--es-grey-700, #4D4D4D);
                                animation:esMenuIn .12s var(--es-ease); }
.es-fbar-menu.is-end          { left:auto; right:0; }
.es-fbar-menu .es-fm-list     { max-height:252px; }
.es-fbar-menu .es-fm-opt      { gap:9px; }
.es-fbar-menu .es-fm-opt .lbl { flex:1 1 auto; min-width:0; }
.es-fbar-menu .es-fm-opt .es-fbar-n { flex:0 0 auto; }
.es-fbar-none                 { padding:14px 9px; text-align:center; font-size:13px;
                                color:var(--es-text-muted); }

/* Row count — feedback on what the bar has done, so the quick-find is not silent. */
.es-fbar-count                { margin-left:auto; font:400 12px var(--es-font-sans);
                                color:var(--es-text-body); }
.es-fbar-count b              { font-family:var(--es-font-mono); font-weight:600;
                                color:var(--es-text-strong); }
/* .es-clear-all carries margin-left:auto for the in-table chip toolbar; here the count claims it. */
.es-fbar > .es-clear-all      { margin-left:0; }

/* The panel is position:absolute, so .card's overflow:hidden crops it at the card's bottom edge
   (same trap as the row-action flyout above). Release the clip only while a facet is open. */
.card:has(.es-fbar-facet[open]) { overflow:visible; }

@media (max-width:576px){
  .es-fbar-find               { flex:1 1 100%; }
  .es-fbar-menu               { width:min(320px, calc(100vw - 32px)); }
  .es-fbar-count              { margin-left:0; }
}
