/* Cyber Academy — layout: shells, the sidebar, page header, breakpoints.
 *
 * Mobile-first: `min-width: 640px` (tablet), `832px` (the docked sidebar), `1024px` (desktop), `1440px` (wide), and a short-height class for
 * `max-height: 700px` at tablet width and above. What each shell holds is the visual contract's decision
 * (`notes/design-rulebook.md`, shells and navigation).
 *
 * SCROLL OWNERSHIP. THE DOCUMENT IS THE PAGE'S ONE VERTICAL SCROLL CONTAINER: the sidebar and the phone top bar are
 * sticky against it (fixed only for the tablet overlay and the phone drawer), and nothing here puts `overflow: auto`
 * on main, a card or a table — the document scroller is what keeps browser find, scroll restoration on Back and
 * Forward, a phone's retracting toolbar and sticky table headers working. The ONE inner scroller is the sidebar's
 * destination list (`.sidebar__scroller`): the sidebar is exactly the viewport's height, its header, the pinned
 * console action and the account row stand still, and only the list between them scrolls when it outgrows the
 * room. An open phone drawer does not lock the document (base.css: only a modal dialog does).
 */

/* == shells == */
.app { min-height: 100vh; min-height: 100dvh; background: var(--bg); color: var(--text-1); }
.app__main { min-width: 0; padding: var(--space-6) var(--gutter) var(--space-16); }
.app__content { max-width: var(--content-max); margin-inline: auto; }
.app__content--form { max-width: var(--form-max); margin-inline: 0; }
.app--sidebar { display: block; }
@media (min-width: 832px) { .app--sidebar { display: grid; grid-template-columns: auto minmax(0, 1fr); } }
/* The page column beside the sidebar: the phone top bar, then the page. It is the content card, fixed to the viewport
 * and the one scroller of a shell with a sidebar, its scrollbar inside its own edges, so the document never scrolls.
 * From a tablet up it lies flush against the top, right and bottom edges, its left corners rounded and its shadow cast
 * onto the sidebar's chrome; the docked sidebar is then transparent, so the chrome is the shell's own background and
 * nothing covers that shadow. */
.app__column { min-width: 0; }
/* A shell with the content card never scrolls its document, so the root takes no scrollbar and no gutter: the base
 * rule that reserves one on every page (base.css) yields here, and the card's own scrollbar is the only one. */
:root:has(.app--sidebar) { scrollbar-gutter: auto; }
:root:has(.app--sidebar):not([data-scroll-lock]) { overflow-y: hidden !important; }
.app--sidebar { height: 100vh; height: 100dvh; overflow: hidden; }
.app--sidebar .app__column { height: 100vh; height: 100dvh; overflow-y: auto; overscroll-behavior: contain;
  background-color: var(--bg); }
@media (min-width: 832px) {
  .app--sidebar { background: var(--chrome); }
  .app--sidebar .app__column { border-radius: var(--radius-xl) 0 0 var(--radius-xl); box-shadow: var(--elev-1); }
  /* The page title's first line (32 px) centres on the sidebar header's axis. */
  .app--sidebar .app__main { padding-top: calc(var(--header-axis) - 16px); }
}
/* A sticky table header sits under the phone's sticky top bar; from a tablet up nothing is stuck above the page and
 * the header's own default (0) holds. The one rule that offsets it. */
@media (max-width: 831px) { .app--sidebar .data-table { --sticky-offset: 56px; } }

/* Long strings never widen a page. */
.wrap-anywhere { overflow-wrap: anywhere; }

/* == layout: the product's identity in the console (client.ui.chrome/brand-lockup) == */
.brand-lockup { display: flex; align-items: center; gap: var(--space-3); flex: none; }
.brand-lockup .wordmark { display: block; height: 20px; width: auto; }
.brand-lockup .product-name { color: var(--text-1); font: var(--weight-medium) var(--text-14) / var(--lh-14) var(--font-sans); white-space: nowrap; }

/* == layout: the sidebar (client.ui.sidebar) ==
 * One sidebar for every shell: the console (dark), a customer (branded), an account (neutral). 64 px collapsed,
 * 256 px expanded; every row 48 px inside an 8 px inset. The compact mark in the toggle crossfades into the
 * `panel-left` icon: on hover or keyboard focus while collapsed, and when expanded. Labels stay mounted (they are the
 * accessible names) and are clipped, never reflowed, while the width animates. No stroke separates it from the page:
 * its surface against the page ground does, and the floating forms (tablet overlay, phone drawer) lift to level 3. */
/* On a phone the slot holds no width: the sidebar is a fixed drawer. On a tablet it is the 64 px rail (the expanded
 * panel overlays); on a desktop it takes the sidebar's own animated width, so expanding pushes the page. */
.sidebar-slot { position: relative; width: 0; flex: none; }
@media (min-width: 832px) { .sidebar-slot { width: auto; } }

/* A flex column exactly the viewport's height: header, the pinned console action and the footer keep their size, and
 * the destination list between them takes the rest and scrolls on its own. */
.sidebar {
  position: sticky;
  top: 0;
  z-index: var(--z-sidebar);
  display: flex;
  flex-direction: column;
  width: 64px;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  /* Clip, not a scroll container, and let the keyboard focus lift and glow of an edge-to-edge row (the console
   * action, a selected item) paint past the sidebar's own edge rather than be cut by it. Where
   * `overflow-clip-margin` is unsupported the value above stands and the glow is cut at the edge; the focus dash,
   * inside the row, is never cut. */
  overflow: clip;
  overflow-clip-margin: var(--space-4);
  background: var(--chrome);
  --surface-hover: var(--chrome-hover);
  /* THE RAIL INSET: the one horizontal inset of the header, the console action, the destinations and the account row.
   * Every row fills the width between the insets and its own padding puts its icon at 22 px (the account's avatar at
   * 16 px) from the sidebar's edge, so as the width animates every row and its fill grow and shrink with it while no
   * icon moves — the collapsed rail is simply the narrowest width, with nothing switched in or out. */
  --rail-inset: var(--space-2);
  /* ONE MOTION for everything the expand and collapse move: the width, and the tablet overlay's shadow with it. The
   * fades that accompany it (labels, brand, account name, the rail logo) run on --dur-base and are timed to end
   * with the width (see their delays). */
  transition: width var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out),
              box-shadow var(--dur-slow) var(--ease-out);
}
.sidebar[data-expanded="true"] { width: 256px; }
.sidebar__header, .sidebar__context, .sidebar__footer { flex: none; }
.sidebar__scroller { flex: 1 1 auto; min-height: 0; overflow-x: hidden; overflow-y: auto; }
/* Below that height the collapsed rail's list is a scroller inside 64 px: its scrollbar is thin — a 4 px gutter,
 * reserved whether or not it is needed, holding a 4 px thumb in the same colours as every other thumb (base.css), still
 * visible at rest — and the list gives the gutter back on its right, so the square rows stay centred on the rail and on
 * the header's mark above them. In both states, so nothing about the list switches while the width animates. Firefox
 * draws its own thin scrollbar here (base.css) and ignores the gutter width. */
@media (min-width: 832px) and (max-height: 439px) {
  .sidebar__scroller { scrollbar-gutter: stable; }
  .sidebar__scroller .sidebar__nav { padding-right: calc(var(--rail-inset) - 4px); }
}
@media (min-width: 832px) and (max-height: 439px) and (not (forced-colors: active)) {
  .sidebar__scroller::-webkit-scrollbar { width: 4px; }
  .sidebar__scroller::-webkit-scrollbar-thumb { background-clip: border-box; }
}
/* Docked from 832 px (the expanded sidebar, 256 px, beside the narrowest content the console lays out, 576 px), transparent over the shell's chrome; below, the push drawer that follows, at every width. */
@media (min-width: 832px) { .sidebar { background: transparent; } }
/* Phone: the employee lesson's push drawer. The sidebar, always in the expanded layout, waits beneath on the chrome,
 * pinned to both viewport edges so it follows the dynamic viewport; the page column, the floating top bar with it, is
 * the content card that slides right to uncover it — rounding, its J1 shadow unchanged — and leaves a 64 px strip of
 * itself in view; the drawer is never wider than the docked sidebar expanded (256 px). Opening runs on slow 300 ms with the decelerate curve, closing on base 200 ms with the accelerate one;
 * an interrupted motion retraces from its current frame. The backdrop is a transparent tap target over that strip. */
@media (max-width: 831px) {
  .app--sidebar { --drawer-w: min(100vw - 64px, 256px); overflow-x: clip; background: var(--chrome); }
  .sidebar {
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    height: auto;
    z-index: 0;
    width: var(--drawer-w);
  }
  .sidebar[data-expanded="true"], .sidebar[data-expanded="false"] { width: var(--drawer-w); }
  /* Closed, the drawer is out of the tab order and the accessibility tree, as `inert` and `aria-hidden` would put it:
   * hidden once the card has covered it, and shown the moment it starts opening. */
  .sidebar[data-open="false"] { visibility: hidden; transition: visibility 0s linear var(--dur-base); }
  .sidebar[data-open="true"] { visibility: visible; transition: visibility 0s; }
  .app--sidebar .app__column { position: relative; z-index: 1;
    transform: none; border-radius: 0; box-shadow: none;
    transition: transform var(--dur-base) var(--ease-in), border-radius var(--dur-base) var(--ease-in), box-shadow var(--dur-base) var(--ease-in); }
  .app--sidebar:has(.sidebar[data-open="true"]) .app__column { transform: translateX(var(--drawer-w)); border-radius: var(--radius-xl) 0 0 var(--radius-xl);
    box-shadow: var(--elev-1); transition-duration: var(--dur-slow); transition-timing-function: var(--ease-out); }
  .drawer-backdrop.drawer-backdrop--phone { left: var(--drawer-w); background: transparent; }

  /* One toggle: the floating menu button riding on the strip opens and closes the drawer, as in the lesson. */
  .sidebar .sidebar__toggle { display: none; }
}

/* One header for both states — 64 px (56 in a short window) with 8 px above its content — so opening or closing the
 * sidebar moves nothing up or down. Collapsed, the rail is one uniform stack: the logo tile is exactly a rail button
 * (48 px, 40 px in a short window), with its radius and its centring, and stands 12 px below the top — the same 12 px
 * that parts it from the first item (the header's 4 px below it and the nav's 8 px). */
/* The toggle stays at the right inset in both states: in the collapsed rail that is where centring put it, and it rides
 * the right edge through the transition rather than snapping to the centre when the collapse begins. */
.sidebar__header { position: relative; display: flex; align-items: center; justify-content: flex-end; height: 64px; padding: var(--space-2) var(--rail-inset) 0; }
/* The rail logo keeps one size: it is only ever seen in the collapsed rail, and a size tied to the state would snap
 * while it fades. */
.sidebar__toggle-logo .logo-tile { --logo-size: 48px; }
.sidebar__toggle {
  position: relative;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-standard);
}
/* The toggle shows its hover fill in both states, as the lesson's does: collapsed, the icon that replaces the logo
 * stands on it. */
@media (hover: hover) { .sidebar__toggle:hover { background: var(--surface-hover); } }
.sidebar__toggle-layer {
  grid-area: 1 / 1;
  display: grid;
  place-items: center;
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
  /* Kept on its own layer at rest too: WebKit otherwise drops the layer when the scale settles and repaints the glyph
   * snapped to a whole pixel, a visible jump after the entrance. */
  will-change: opacity, transform;
}
.sidebar[data-expanded="false"] .sidebar__toggle-logo { opacity: 1; transform: scale(1); transition-delay: var(--delay-rail-logo); }
.sidebar[data-expanded="false"] .sidebar__toggle-icon { opacity: 0; transform: scale(var(--scale-icon)); transition-delay: var(--delay-rail-logo); }
.sidebar[data-expanded="true"] .sidebar__toggle-logo { opacity: 0; transform: scale(var(--scale-icon)); transition-delay: 0ms; }
.sidebar[data-expanded="true"] .sidebar__toggle-icon { opacity: 1; transform: scale(1); transition-delay: 0ms; }
@media (hover: hover) {
  .sidebar[data-expanded="false"] .sidebar__toggle:hover:not(.is-settling) .sidebar__toggle-logo { opacity: 0; transform: scale(var(--scale-icon)); transition-delay: 0ms; }
  .sidebar[data-expanded="false"] .sidebar__toggle:hover:not(.is-settling) .sidebar__toggle-icon { opacity: 1; transform: scale(1); transition-delay: 0ms; }
}
.sidebar[data-expanded="false"] .sidebar__toggle:focus-visible .sidebar__toggle-logo { opacity: 0; transform: scale(var(--scale-icon)); transition-delay: 0ms; }
.sidebar[data-expanded="false"] .sidebar__toggle:focus-visible .sidebar__toggle-icon { opacity: 1; transform: scale(1); transition-delay: 0ms; }
/* The expanded header's identity, standing where the collapsed toggle's mark stood: centred on the header's height,
 * so a one-line lockup and a two-line one (a customer's wide logo or name over the product caption) sit alike. */
.sidebar__brand {
  position: absolute;
  left: 16px;
  top: var(--space-2);
  bottom: 0;
  right: 64px;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  white-space: nowrap;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
}
.sidebar[data-expanded="true"] .sidebar__brand { opacity: 1; transition-delay: var(--delay-label-in); }
.sidebar[data-expanded="false"] .sidebar__brand { opacity: 0; transition-delay: 0ms; }
.sidebar__lockup { display: flex; flex-direction: column; justify-content: center; min-width: 0; }
/* The wide logo placement: at most 28 px tall and 144 px wide, never cropped. */
.sidebar__wide-logo { display: block; max-width: 144px; max-height: 28px; width: auto; height: auto; object-fit: contain; object-position: left center; }
/* A wide logo and the product caption share one line, centred on the header: the caption keeps its width and the logo
 * gives way — at most 24 px tall, shrinking to the room left, contained and never cropped. */
.sidebar__lockup--inline { flex-direction: row; align-items: center; gap: var(--space-2); }
.sidebar__lockup--inline .sidebar__wide-logo { flex: 0 1 auto; min-width: 0; height: 24px; max-height: 24px; }
.sidebar__lockup--inline .sidebar__caption { flex: none; }
.sidebar__customer-name { overflow: hidden; text-overflow: ellipsis; color: var(--text-1); font: var(--weight-semibold) var(--text-14) / var(--lh-14) var(--font-sans); }
.sidebar__caption { color: var(--text-3); font: var(--weight-medium) var(--text-12) / var(--lh-12) var(--font-sans); }
/* The drawer is always drawn expanded, whatever the rail was left at; these outrank the collapsed rules. */
@media (max-width: 831px) {
  .sidebar[data-expanded] .sidebar__brand { opacity: 1; }
  .sidebar[data-expanded] .sidebar__toggle-logo { opacity: 0; }
  .sidebar[data-expanded] .sidebar__toggle-icon { opacity: 1; transform: scale(1); }
}
/* A customer's mark: its logo on white, or its monogram on the brand secondary. Organisations are rounded squares. */
.logo-tile {
  display: grid;
  place-items: center;
  flex: none;
  width: var(--logo-size, 32px);
  height: var(--logo-size, 32px);
  border-radius: var(--radius-md);
  background: #FFFFFF;
  overflow: hidden;
}
.logo-tile > img { width: 100%; height: 100%; padding: 3px; object-fit: contain; }
/* The square icon is made for the tile: it fills it edge to edge and the radius clips its corners. A full logo standing
 * in for a missing icon keeps its inset and is contained, since cropping would cut the wordmark. */
.logo-tile--icon > img { padding: 0; object-fit: cover; }
.logo-tile--monogram { background: var(--brand-secondary, var(--accent)); color: var(--monogram-ink, var(--accent-ink)); font: var(--weight-medium) var(--text-14) / 1 var(--font-sans); }
.logo-tile--placeholder { background: var(--surface-2); }
.logo-tile--24 { --logo-size: 24px; }
.logo-tile--28 { --logo-size: 28px; }
.logo-tile--32 { --logo-size: 32px; }
/* The product's own mark in the console and an account shell: the academy glyph on the mode's accent tile. */
.product-glyph { display: grid; place-items: center; flex: none; border-radius: var(--radius-md); background: var(--accent); color: var(--accent-ink); }
.product-glyph--28 { width: 28px; height: 28px; }
.product-glyph--32 { width: 32px; height: 32px; }

/* The destinations. The list's own inset keeps a row's hover and selected fill clear of the scroller's edges. */
.sidebar__nav { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-2) var(--rail-inset); margin: 0; list-style: none; }
/* Each row fills the rail: the item (and its selected fill) is as wide as the row, whatever its label. */
.sidebar__nav > li { display: flex; }
.sidebar__nav > li .sidebar__item { flex: 1; min-width: 0; width: 100%; }
.sidebar__item {
  position: relative;
  display: flex;
  align-items: center;
  height: 48px;
  padding-left: calc(22px - var(--rail-inset));
  border-radius: var(--radius-md);
  color: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  transition: background-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
@media (hover: hover) { .sidebar__item:hover { background-color: var(--surface-hover); color: var(--text-1); } }
.sidebar__item:active { background-color: var(--surface-pressed); transition-duration: var(--dur-instant); }
.sidebar__item > svg { flex: none; }
.sidebar__label {
  margin-left: var(--space-3);
  font: var(--weight-medium) var(--text-14) / var(--lh-14) var(--font-sans);
  white-space: nowrap;
  overflow: hidden;
  transition: opacity var(--dur-base) var(--ease-out);
}
/* Labels fade in once the width has made room, as the brand does, and fade out at once as the rail closes. */
.sidebar[data-expanded="true"] .sidebar__label { transition-delay: var(--delay-label-in); }
.sidebar[data-expanded="false"] .sidebar__label { opacity: 0; }
@media (max-width: 831px) { .sidebar[data-expanded] .sidebar__label { opacity: 1; } }
/* Selected: a filled pill in the accent tint, the 600 label and the icon in the accent — never a bar, never the tint
 * alone. The selected fill outranks hover and keyboard focus. Focused, the pill also lifts a step (level 1) besides
 * its dash, so focus on the selected item is a change of more than one mark. The dash, in the focus colour on the
 * tint, measures 11.94:1 (lime on the dark tint), 7.67:1 (default purple on the light tint) and 16.02:1 (neutral ink);
 * a brand primary at the 4.5:1 floor keeps about 4.0:1 on its own 10 % tint, above the 3:1 a focus cue needs. */
.sidebar__item[aria-current="page"],
.sidebar__item[aria-current="page"]:hover,
.sidebar__item[aria-current="page"]:focus-visible { background-color: var(--accent-tint); color: var(--text-1); }
.sidebar__item[aria-current="page"]:focus-visible { box-shadow: var(--elev-1); }
.sidebar__item[aria-current="page"] > svg { color: var(--accent); }
.sidebar__item[aria-current="page"] .sidebar__label { font-weight: var(--weight-semibold); }

/* The superadmin's pinned way back to the console, inside a customer: the console's own dark surface and lime icon on
 * the customer's light sidebar, so it reads as the operator's action (`notes/design-rulebook.md` §4). Its colours are
 * the console's named primitives (tokens.css, `--console-*`), read directly because this row lives inside the
 * customer mode and a nested `data-mode` would repaint the whole document's ground. It lifts on the ladder and takes
 * the filled control's keyboard focus (fill step, lift, glow, a dash in its own ink). The space under it keeps the
 * dark square apart from the selected item below, so it never reads as the current one. */
.sidebar__context { display: flex; padding: var(--space-2) var(--rail-inset); }
.sidebar__console-link {
  --fill-hover: var(--console-surface-hover);
  --fill-shadow-2: var(--elev-2);
  position: relative;
  display: flex;
  flex: 1;
  align-items: center;
  min-width: 0;
  height: 48px;
  padding-left: calc(22px - var(--rail-inset));
  border-radius: var(--radius-md);
  background-color: var(--console-surface);
  color: var(--console-ink);
  box-shadow: var(--elev-1);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  transition: background-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.sidebar__console-link > svg { flex: none; color: var(--console-accent); }
@media (hover: hover) { .sidebar__console-link:hover { background-color: var(--console-surface-hover); box-shadow: var(--elev-2); } }
.sidebar__console-link:active { background-color: var(--console-surface); box-shadow: var(--elev-1); transition-duration: var(--dur-instant); }

.sidebar__footer { padding: var(--space-2) var(--rail-inset); }
/* The account row stays within the rail; its menu renders in the shell overlay root. */
.sidebar__account {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  height: 48px;
  padding: 0 calc(16px - var(--rail-inset));
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-1);
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  transition: background-color var(--dur-fast) var(--ease-standard);
}
@media (hover: hover) { .sidebar__account:hover { background-color: var(--surface-hover); } }
/* On the profile the row opens, the row is the current page: the selected pill of a destination, lifting on focus. */
.sidebar__account[aria-current="page"],
.sidebar__account[aria-current="page"]:hover,
.sidebar__account[aria-current="page"]:focus-visible { background-color: var(--accent-tint); }
.sidebar__account[aria-current="page"] .sidebar__account-name { font-weight: var(--weight-semibold); }
.sidebar__account[aria-current="page"]:focus-visible { box-shadow: var(--elev-1); }
.sidebar__account-name { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: var(--weight-medium) var(--text-13) / var(--lh-13) var(--font-sans); transition: opacity var(--dur-base) var(--ease-out); }
.sidebar[data-expanded="true"] .sidebar__account-name { transition-delay: var(--delay-label-in); }
.sidebar[data-expanded="false"] .sidebar__account-name { opacity: 0; }
@media (max-width: 831px) { .sidebar[data-expanded] .sidebar__account-name { opacity: 1; } }

.drawer-backdrop { position: fixed; inset: 0; z-index: var(--z-backdrop); border: 0; padding: 0; background: var(--backdrop); cursor: default; }
@media (min-width: 832px) { .drawer-backdrop--phone { display: none; } }
/* Behind the expanded panel only where it overlays the page: a tablet. The sidebar sits above it. */

/* The phone's top bar, for every shell with a sidebar, as the employee lesson composes it: no bar of its own, but a
 * round floating menu button and, 8 px beside it, the identity as a floating pill of the same height, both on the
 * surface at level 2, 12 px from the edges. The row keeps the 56 px the sticky table headers are offset by. */
.mobile-topbar {
  --float-size: 40px;
  position: sticky;
  top: 0;
  z-index: var(--z-topbar);
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  height: 56px;
  padding: calc(var(--header-axis) - var(--float-size) / 2) var(--space-3) 0;
}
@media (pointer: coarse) { .mobile-topbar { --float-size: 44px; } }
.mobile-topbar > .icon-btn { flex: none; width: var(--float-size); height: var(--float-size); border-radius: var(--radius-full);
  background: var(--surface); box-shadow: var(--elev-2); }
.mobile-topbar__identity { display: flex; align-items: center; gap: var(--space-3); min-width: 0; height: var(--float-size);
  padding: 0 var(--space-4); border-radius: var(--radius-full); background: var(--surface); box-shadow: var(--elev-2); }
/* The pill fades out as the drawer opens (the drawer's own header names the shell then) and back as it closes, on
 * the card's own timing; the menu button stays. */
.mobile-topbar__identity { transition: opacity var(--dur-base) var(--ease-in); }
.app--sidebar:has(.sidebar[data-open="true"]) .mobile-topbar__identity { opacity: 0; transition-duration: var(--dur-slow); transition-timing-function: var(--ease-out); }
.mobile-topbar .wordmark { display: block; height: 18px; width: auto; }
/* A logo alone: inline padding equal to the space above and below the mark, so it sits centred in a compact pill. */
.mobile-topbar__identity--logo { padding: 0 var(--space-3); }
.mobile-topbar__logo { display: block; height: 24px; max-width: 160px; width: auto; object-fit: contain; }
.mobile-topbar__title { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
@media (min-width: 832px) { .mobile-topbar { display: none; } }

/* == layout: page header (client.ui.page/page-header) == */
.page-header { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-4); align-items: start; margin-bottom: var(--space-6); }
.page-header__main { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.page-header__title { color: var(--text-1); overflow-wrap: anywhere; }
.page-header__title:focus { outline: none; }
.page-header__count { color: var(--text-3); font-weight: var(--weight-regular); margin-left: var(--space-2); font-variant-numeric: tabular-nums; }
.page-header__meta { color: var(--text-2); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.page-header__actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; justify-content: flex-end; min-height: var(--lh-24); }
@media (max-width: 639px) {
  .page-header { grid-template-columns: minmax(0, 1fr); }
  .page-header__actions { justify-content: flex-start; }
  .page-header__actions > .btn:only-child { width: 100%; }
}
.section-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-3); }
.section-header__title { color: var(--text-1); }
.section-header__count { color: var(--text-3); font-weight: var(--weight-regular); margin-left: var(--space-2); }
.section + .section { margin-top: var(--space-10); }
.toolbar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-3); }
.toolbar__end { margin-left: auto; display: flex; gap: var(--space-2); flex-wrap: wrap; }
.charts-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
@media (min-width: 1200px) { .charts-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* == layout: neutral shell (pre-authentication, unassigned-administrator landing) == */
.preauth { min-height: 100vh; min-height: 100dvh; padding: 12vh var(--space-4) var(--space-8); background: var(--bg); }
.preauth__card { --holder: var(--surface); --holder-below: var(--bg); width: min(400px, 100%); margin: 0 auto; padding: var(--space-8); background-color: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--elev-1); }
.product-mark { display: flex; align-items: center; gap: 6px; margin: 0 0 var(--space-6); color: var(--text-1); font: var(--weight-semibold) var(--text-14) / var(--lh-14) var(--font-sans); }
.product-mark__glyph { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; flex: none; clip-path: polygon(4px 0, calc(100% - 4px) 0, 100% 4px, 100% calc(100% - 4px), calc(100% - 4px) 100%, 4px 100%, 0 calc(100% - 4px), 0 4px); background: var(--console-accent); color: var(--console-surface); }
.preauth__outcome { display: flex; flex-direction: column; gap: var(--space-6); }
.preauth__card > .preauth__outcome > p { margin: 0; color: var(--text-1); }
.preauth__card > .preauth__outcome > .btn { margin-top: 0; }
.preauth__card > .btn--ghost { margin: 0 0 var(--space-2) -10px; }
.preauth__title { margin: 0 0 var(--space-2); color: var(--text-1); font: var(--weight-semibold) 20px / 28px var(--font-sans); }
.preauth__title:focus { outline: none; }
.product-mark:focus { outline: none; }
.preauth__subtitle { margin: 0 0 var(--space-6); color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); overflow-wrap: anywhere; }
.preauth__error { margin-bottom: var(--space-4); }
.preauth__card form { display: flex; flex-direction: column; }
.preauth__card form > .notice { margin-bottom: var(--space-4); }
.preauth__card .input { height: var(--control-h-lg); }
.preauth__card form > .btn, .preauth__card > div > .btn, .preauth__card > .btn { width: 100%; height: var(--control-h-lg); margin-top: var(--space-2); }
.preauth__card form > .btn--ghost { width: auto; align-self: flex-start; }
.preauth__actions { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--space-2); margin-top: var(--space-2); }
.preauth__actions > .btn { width: 100%; height: var(--control-h-lg); }
/* The line under the mark sits midway between the mark and what follows: 16 px above and below it. */
.preauth__card > .product-mark:has(+ .preauth__subtitle) { margin-bottom: var(--space-4); }
.preauth__card > .product-mark + .preauth__subtitle { margin-bottom: var(--space-4); }
.preauth__card > .product-mark + .preauth__subtitle + .btn { margin-top: 0; }
.preauth__card > div > p { color: var(--text-2); }
.preauth__aside { margin: var(--space-4) 0 0; text-align: right; font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); }
.identity-qr svg { display: block; width: min(100%, 192px); height: auto; margin: var(--space-4) auto; background: #FFFFFF; }
/* The authenticator step: the instruction, the QR, the manual-key fallback and the code, evenly spaced. The key is two
 * lines of four groups (`client.ui.qr/grouped-key`), centred, with its copy button centred under it. */
.identity-provisioning { display: flex; flex-direction: column; gap: var(--space-3); }
.identity-provisioning > p { margin: 0; }
.identity-provisioning .identity-qr svg { margin: 0 auto; }
/* "nebo" between the QR code and the key: centred, muted, and held equally far from both by the column's gap. */
.identity-provisioning > .identity-or { text-align: center; color: var(--text-2); }
.identity-key-row { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
.identity-key-row > [role="group"] > .identity-key { margin: 0; }
.identity-key-row > [role="group"] { display: flex; flex-direction: column; gap: 6px; align-self: stretch; }
.identity-key { display: block; text-align: center; white-space: pre-line; font-family: var(--font-mono); letter-spacing: 0.08em; line-height: 1.6; }
@media (max-width: 639px) {
  .preauth { padding: 0; background: var(--surface); }
  .preauth__card { border-radius: 0; box-shadow: none; padding: 48px 16px; }
}

/* == short height (desktop width) == */
@media (max-height: 700px) and (min-width: 832px) {
  .sidebar__header { height: 56px; }
  /* The rows are 40 px tall, and a 12 px rail inset makes them 40 px wide in the 64 px collapsed rail — square, as the
   * logo tile is — while their padding still puts every icon where the 48 px rows do. Nothing switches with the state,
   * so the fills shrink and grow with the width. */
  .sidebar { --rail-inset: 12px; }
  .sidebar__toggle { width: 40px; height: 40px; }
  .sidebar__toggle-logo .logo-tile { --logo-size: 40px; }
  .sidebar__item, .sidebar__account, .sidebar__console-link { height: 40px; }
  .app__main { padding-top: var(--space-4); }
}

/* == layout: the customer frame and its destinations == */
.customer-page > .refresh-failure { margin-bottom: var(--space-6); }
.employees-switch { margin-bottom: var(--space-4); }
/* The stat tiles of the Overview and of Reports: 4 across on a desktop, 2 on a tablet, and 2 on a phone while a
 * tile keeps 160 px, else 1 (`notes/design-rulebook.md`, stat tiles). */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--space-4); margin-bottom: var(--space-6); }
@media (min-width: 640px) { .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .stat-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } }
.overview-empty { margin-top: var(--space-6); }
.report-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-4) var(--space-6); margin-bottom: var(--space-6); }
.report-filters > .form-field { min-width: 240px; max-width: 360px; flex: 0 1 320px; margin: 0; }
.report-filters__period { display: flex; flex-direction: column; gap: var(--space-1); }
.report-filters__label { color: var(--text-2); font: var(--weight-medium) var(--text-13) / var(--lh-13) var(--font-sans); }
.report-filters__actions { display: flex; margin-inline-start: auto; }
@media (max-width: 639px) {
  .report-filters { flex-direction: column; align-items: stretch; }
  .report-filters > .form-field { max-width: none; flex: none; min-width: 0; }
  .report-filters .segmented { width: 100%; }
  .report-filters .segmented__item { flex: 1; }
  .report-filters__actions { margin-inline-start: 0; }
  .report-filters__actions > .btn { flex: 1 1 auto; }
}
.charts-grid + .report-activity, .report-loading + .report-activity { margin-top: var(--space-6); }
.report-activity { --holder: var(--surface); --holder-below: var(--bg); display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-5); background-color: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--elev-1); min-width: 0; }
@media (max-width: 639px) { .report-activity { padding: var(--space-4); } }
.report-activity .data-table th { position: static; }
.report-activity .data-table tbody tr:last-child th, .chart-card__table .data-table tbody tr:last-child th { border-bottom: 0; }
.report-activity .data-table th[scope="row"], .chart-card__table .data-table th[scope="row"] { height: var(--row-h); color: var(--text-1); font: var(--weight-regular) var(--text-14) / var(--lh-14) var(--font-sans); letter-spacing: 0; white-space: normal; overflow-wrap: anywhere; }

/* == layout: customer settings (client.views.customer.settings) ==
 * One surface holds the three configuration phases; management sits apart at its foot. */
.settings { container: settings / inline-size; display: flex; flex-direction: column; gap: var(--space-8); }
.settings-form { display: flex; flex-direction: column; gap: var(--space-6); }
.settings-section { min-width: 0; }
.settings-section .section-header { margin-bottom: var(--space-4); }
.settings-section--identity .form-field { max-width: var(--form-max); }
.settings-lifecycle { display: flex; flex-direction: column; gap: var(--space-3); width: fit-content; min-width: min(100%, 32rem); max-width: 100%; }
.settings-lifecycle .section-header { margin-bottom: var(--space-1); }
.logo-assets { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
@container settings (min-width: 640px) { .logo-assets { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.logo-asset { display: flex; flex-direction: column; min-width: 0; }
.logo-asset .import-drop { --holder: var(--surface); --holder-below: var(--bg); height: 160px; flex-direction: column; justify-content: center; text-align: center; padding: var(--space-4); }
.logo-asset .import-drop__icon, .logo-asset__image { display: grid; place-items: center; height: 64px; width: 100%; min-width: 0; }
.logo-asset--icon .logo-asset__image { width: 64px; }
.logo-asset__image img { display: block; max-width: 100%; max-height: 64px; object-fit: contain; }
.logo-asset .import-drop__text { align-items: center; }
.logo-asset__footer { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); min-height: var(--lh-12); margin-top: var(--space-2); }
.logo-asset__status { min-width: 0; }
.settings-colour-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-5); align-items: start; }
@container settings (min-width: 760px) { .settings-colour-layout { grid-template-columns: minmax(320px, 360px) minmax(0, 1fr); } }
.settings-colour-controls { min-width: 0; }
.colour-roles { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.colour-role { min-width: 0; }
/* The form's actions are set apart by 24 px of space, not a rule. */
.form-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-6); }
.save-confirmation { margin: 0 0 var(--space-4); color: var(--status-on-track-fg); }
/* Management is an operator's section, as wide as its content but never narrower than a comfortable 32 rem (or the
 * room there is): one grid for both rows, the headings in the first column taking the spare width and the actions in
 * the second, so the two actions share a common right edge. The rows stack only in a genuinely narrow container. */
.lifecycle__rows { display: grid; grid-template-columns: minmax(max-content, 1fr) max-content; align-items: center; column-gap: var(--space-8); row-gap: var(--space-3); }
.lifecycle__state, .lifecycle__row { display: contents; }
.lifecycle__title { margin: 0; }
.lifecycle__rows .btn { justify-self: end; }
@container settings (max-width: 419px) {
  .lifecycle__rows { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-2); }
  .lifecycle__rows .btn { justify-self: start; margin-bottom: var(--space-4); }
}
/* The one operator mark of a section only an operator sees: the shield beside its heading. */
.operator-heading { display: inline-flex; align-items: center; gap: var(--space-2); }
.operator-heading__mark { display: inline-flex; padding: 2px; color: var(--text-3); }
/* The live preview: the real components under the brand being chosen, in a recess of the page's own ground — sunk into
 * the panel by the well's inset shadow, never lifted by a drop shadow (decided by the operator 2026-09-28). Its sidebar
 * panels are the white surface the real sidebar is, so the brand is previewed on the surfaces it will sit on. */
.brand-preview { --holder: var(--bg); display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-5); border-radius: var(--radius-lg); background: var(--bg); box-shadow: var(--inset-rest); color: var(--text-1); }
.brand-preview__caption { margin: 0; color: var(--text-3); font: var(--weight-medium) var(--text-12) / var(--lh-12) var(--font-sans); letter-spacing: 0.01em; }
.brand-preview__panels { display: flex; gap: var(--space-3); }
.brand-preview__rail { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-1); border-radius: var(--radius-md); background: var(--surface); }
.brand-preview__rail-item { justify-content: center; width: 40px; height: 40px; padding: 0; }
.brand-preview__sidebar { display: flex; flex: 1; flex-direction: column; gap: var(--space-1); min-width: 0; padding: var(--space-2); border-radius: var(--radius-md); background: var(--surface); }
.brand-preview__brand { display: flex; align-items: center; gap: var(--space-2); min-width: 0; height: 32px; }
.brand-preview__wide { display: block; max-width: 176px; max-height: 32px; object-fit: contain; object-position: left center; }
.brand-preview__name { overflow: hidden; font: var(--weight-semibold) var(--text-14) / var(--lh-14) var(--font-sans); white-space: nowrap; text-overflow: ellipsis; }
.brand-preview__product { margin-bottom: var(--space-1); color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); }
.brand-preview__item { height: 40px; }
.brand-preview__buttons { display: flex; gap: var(--space-2); }
.brand-preview__buttons .btn { height: var(--control-h-sm); padding: 0 var(--space-2); font-size: var(--text-13); }
.brand-preview__button { display: inline-flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); }
.brand-preview__hover { background-color: var(--accent-hover); }
.brand-preview__pressed { background-color: var(--accent-pressed); box-shadow: var(--elev-1-fill); }
.brand-preview__note { color: var(--text-3); font: var(--weight-regular) var(--text-12) / var(--lh-12) var(--font-sans); }
.brand-preview__text { margin: 0; }
.brand-preview__link { color: var(--link); text-decoration: underline; text-underline-offset: 2px; }
.brand-preview__marks { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.brand-preview__check { display: inline-flex; align-items: center; gap: var(--space-2); }
.brand-preview__checked { display: inline-grid; place-items: center; width: 16px; height: 16px; border-radius: var(--radius-xs); background-color: var(--accent); color: var(--accent-ink); }
.brand-preview__stepper { margin: 0; }
.brand-preview__print { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-md); background: #FFFFFF; box-shadow: var(--elev-1); }
.brand-preview__print-logo { max-width: 220px; max-height: 48px; object-fit: contain; }
.brand-preview__print-icon { width: 48px; height: 48px; object-fit: contain; }
.brand-preview__print-title { color: var(--text-1); font: var(--weight-semibold) var(--text-14) / var(--lh-14) var(--font-sans); }
.brand-preview__monogram { display: flex; align-items: center; gap: var(--space-2); }
.brand-preview [data-brand-role] { transition: opacity var(--dur-fast) var(--ease-standard); }
.brand-preview[data-role="primary"] [data-brand-role="secondary"],
.brand-preview[data-role="secondary"] [data-brand-role="primary"] { opacity: 0.28; }
/* An operator-only control: the shield before its label (design synthesis §6.2). */
.operator-only { display: inline-flex; align-items: center; gap: 6px; }
.operator-only__mark { display: inline-flex; color: var(--text-3); }

/* == layout: people lists (Administration, a customer's administrators) == */
.status-slot:empty { display: none; }
.status-slot { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-4); }
.status-slot > .status-line:focus { outline: none; }
.person { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; flex-wrap: wrap; }
.person__name { color: var(--text-1); font-weight: var(--weight-medium); overflow-wrap: anywhere; }
.person__tags { display: inline-flex; gap: var(--space-1); flex-wrap: wrap; }
.person__tags:empty { display: none; }
.person-line { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); min-width: 0; }
.page-notices { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-6); }
.page-notices:empty { display: none; }
.row-actions { display: flex; justify-content: flex-end; }
/* A list whose rows open an actions menu lets the menu out of its card rather than clipping it; the card's rounded
 * corners are then drawn by the first and last things inside it instead of by the clip. */
.table-card:has(.row-actions) { overflow: visible; }
.table-card:has(.row-actions) .inline-create__trigger,
.table-card:has(.row-actions) .inline-create__form { border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0; }
.table-card:has(.row-actions) .data-table tbody tr:last-child td:first-child { border-bottom-left-radius: calc(var(--radius-lg) - 1px); }
.table-card:has(.row-actions) .data-table tbody tr:last-child td:last-child { border-bottom-right-radius: calc(var(--radius-lg) - 1px); }
.table-card:has(.row-actions) .list-item:last-child { border-radius: 0 0 calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px); }
/* The row trigger sits at the right edge: its tooltip opens leftwards so it never widens the page, and a long
 * unbroken address in its name (people who share a name are told apart by it) wraps rather than overflowing. */

.tag-row { display: flex; flex-wrap: wrap; gap: var(--space-1); align-items: center; }
.tag-help > [tabindex] { display: inline-flex; border-radius: var(--radius-xs); }
.toolbar .search-field { width: 320px; max-width: 100%; }
@media (max-width: 639px) {
  .toolbar .search-field { width: 100%; }
  .toolbar__end { margin-left: 0; width: 100%; }
}

/* == layout: the profile page == */
.profile { max-width: var(--form-max); }
.profile-section { padding-top: var(--space-2); }
.profile-section + .profile-section { margin-top: var(--space-10); }
@media (max-width: 639px) {
  .profile-section { padding-top: 0; }
  .profile-section + .profile-section { margin-top: var(--space-8); }
  .profile-section > h2 { margin-bottom: var(--space-3); }
}
.profile-section > h2 { margin: 0 0 var(--space-4); color: var(--text-1); }
.profile-section .toolbar { margin: var(--space-4) 0 0; }
.profile-form { display: flex; flex-direction: column; gap: var(--space-4); }
.profile-form .toolbar { margin: 0; }
.profile-form__row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
@media (min-width: 640px) { .profile-form__row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.profile-photo { display: flex; gap: var(--space-4); align-items: flex-start; flex-wrap: wrap; }
.profile-photo__controls { display: flex; flex-direction: column; gap: var(--space-2); flex: 1 1 240px; min-width: 0; }
.profile-photo__controls .toolbar { margin: 0; }
.profile-email { margin: 0; color: var(--text-1); overflow-wrap: anywhere; }
.profile-factor { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin: 0; color: var(--text-2); }

/* == layout: the landing of an administrator assigned to no customer == */
.landing { display: flex; flex-direction: column; gap: var(--space-4); }
.landing > * { margin: 0; }
.landing__icon { color: var(--text-3); display: inline-flex; }
.landing > h1:focus { outline: none; }
.landing .toolbar { margin: var(--space-2) 0 0; }

/* == reduced motion: the sidebar's state changes land at once ==
 * The duration tokens already fall to 0 (base.css); the header's crossfade also waits by a delay, which would still
 * hold the brand and the logo back — so the delays go too. The logo↔icon swap remains, as a state change. */
@media (prefers-reduced-motion: reduce) {
  .sidebar, .sidebar * { transition-delay: 0ms !important; }
}
/* The authenticator's replacement reuses the pre-authentication fields; on this page they keep the form width of
 * the fields around them. */
.profile .field { max-width: var(--field-max); }
.profile-section .toolbar + .form-field__help { margin-top: var(--space-2); }
.preauth__title + form { margin-top: var(--space-4); }

/* == layout: panel sections and the danger zone (client.views.course, client.views.customer.settings) == */
.panel { --holder: var(--surface); --holder-below: var(--bg); padding: var(--space-6); background-color: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--elev-1); }
.section-gap { margin-top: var(--space-8); }
/* The danger zone is set apart by 32 px of space and its own section heading, not a rule. */
.danger-zone { margin-top: var(--space-10); }
.danger-zone .field-note { margin-bottom: var(--space-4); }

/* == layout: the console's Customers and Courses lists (client.views.customers, client.views.courses) ==
 * The creation slot is the card's first row. It needs no rule under it: the table header below is a half-step fill,
 * and the slot's own surface and 44 px rhythm set it apart. */
.operator-list__create { min-height: 44px; }
.operator-list__create .inline-create { min-height: 44px; }
.operator-list__customer { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.operator-list tr.is-new td { background: var(--accent-tint); }

/* == layout: the course screen (client.views.course) == */
.course-heading { margin-bottom: var(--space-6); }
.course-heading .page-header__meta { display: block; -webkit-line-clamp: unset; overflow: visible; }
.course-heading__meta { display: inline-flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.course-heading__unavailable, .course-delete-unavailable { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
.course-heading .disabled-reason, .course-delete-unavailable .disabled-reason { margin: 0; }
.course-modules { padding-left: var(--space-6); }
/* The entitlement rows are set apart by the list's gap and their own padding, never by a rule. */
.entitlement-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) 0; }
.entitlement-identity { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.entitlement-name { color: var(--text-1); text-decoration: none; }
/* The customer's monogram tile; the view paints it in the customer's primary colour. */
.entitlement-glyph {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--text-1);
  font: var(--weight-semibold) var(--text-16) / 1 var(--font-sans);
}
@media (max-width: 639px) {
  .entitlement-row { flex-wrap: wrap; }
  .entitlement-row > .btn { margin-left: auto; }
}
/* == layout: the Employees view (client.views.customer.employees) == */
/* Scrolled to after a page change; the margin keeps its top clear of the phone's sticky top bar. */
.employees { scroll-margin-top: 72px; }
.employees > .status-slot:empty { display: none; }
/* The toolbar: the search and the two filters, then — on the same row wherever the width allows — the selection
 * status and the two bulk acts, pushed to the right; they wrap under the filters where they do not fit. */
.employees-toolbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3) var(--space-4); }
.employees-toolbar__filters { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-3); flex: 1 1 520px; min-width: 0; }
.employees-toolbar__filters > .search-field-group { flex: 1 1 240px; max-width: 320px; }
.employees-toolbar__filters > .form-field { flex: 0 1 200px; min-width: 160px; }
.employees-toolbar__filters .search-field { width: auto; }
.employees-toolbar__actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); margin-inline-start: auto; }
/* The selection status: how many are selected, or how to select; its line is the enrolment button's disabled reason. */
.selection-status { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); min-height: 32px; }
/* With nothing visible to say (the count and the acts are the docked bar's), the line keeps its place for focus and
 * assistive technology but takes no room in the toolbar. */
.selection-status--quiet { position: absolute; min-height: 0; }
.selection-status__line { margin: 0; max-width: 420px; color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); font-variant-numeric: tabular-nums; }
/* The phone list's select-all stands above the items, as the table header's does above the rows. */
.employees-select-page { padding: var(--space-2) var(--space-4) 0; }
.employees .inline-create__row { flex-wrap: wrap; }
.employees .inline-create__row .form-field { flex: 1 1 180px; }
.employees .data-table td:last-child { width: 50%; }
.employee-email { display: block; max-width: 260px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--text-2); }
.employee-email-inline { display: none; }
@media (min-width: 640px) and (max-width: 1023px) { .employee-email-inline { display: block; } }
.employee-no-course { color: var(--text-3); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); }
.data-table .data-table__name:focus-visible { outline-offset: 2px; border-radius: var(--radius-xs); }
.course-labels--wide { padding-top: 7px; }
.course-labels__more-toggle {
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-2);
  font: var(--weight-medium) var(--text-12) / var(--lh-12) var(--font-sans);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
}
@media (hover: hover) { .course-labels__more-toggle:hover { background: var(--surface-3); color: var(--text-1); } }
@media (max-width: 639px) {
  .employees-toolbar__filters > .search-field-group { flex-basis: 100%; max-width: none; }
  .employees-toolbar__filters > .form-field { flex: 1 1 0; min-width: 0; }
  .employees-toolbar__actions { width: 100%; justify-content: stretch; }
  .employees-toolbar__actions > .selection-status:not(.selection-status--quiet) { flex-basis: 100%; }
  .employees-toolbar__actions > .btn { flex: 1 1 0; }
  .employee-email { max-width: none; }
}
@media (min-width: 640px) { .employees-select-page { display: none; } }

/* == layout: the Employees page's two bulk-act popovers (client.views.enrolment, client.views.employee-import) ==
 * Each is an overlay card anchored to its toolbar button; the card is the overlay's own surface and holds nothing
 * framed — sections are told apart by space and type, the drop zone and the totals by a fill. A card taller than the
 * room the overlay layer gives it scrolls inside itself, the one inner scroller here, so the page behind the modal
 * popover never has to. */
.overlay--popover:has(> .overlay__body .bulk-card) { display: flex; flex-direction: column; }
.overlay--popover:has(> .overlay__body .bulk-card) .overlay__dialog { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
.bulk-card { display: flex; flex-direction: column; gap: var(--space-4); min-height: 0; }
/* The import card never scrolls as a whole: its title stays, and each stage scrolls inside itself — the preview's body
 * alone, between the title and its pinned footer, so the scrollbar belongs to the content it moves. */
.bulk-card--import { overflow: hidden; }
.bulk-card--import > .bulk-card__section { overflow-y: auto; overscroll-behavior: contain; min-height: 0; }
.import-step { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
.import-step__body { display: flex; flex-direction: column; gap: var(--space-5); min-height: 0; flex: 1 1 auto; padding-right: var(--space-2); margin-right: calc(-1 * var(--space-2)); }
.import-step__group { display: flex; flex-direction: column; gap: var(--space-3); }
.import-step__head { display: flex; align-items: center; gap: var(--space-1); min-width: 0; }
.import-step__head > .bulk-card__meta { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.import-step__edit { margin-left: auto; padding: 0 0 1px; border: 0; background-color: transparent; cursor: pointer; white-space: nowrap; font: var(--weight-medium) var(--text-13) / var(--lh-13) var(--font-sans); }
.import-step__table { display: flex; flex-direction: column; gap: var(--space-2); }
.import-step__filter { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.import-step__footer { display: flex; flex-direction: column; gap: var(--space-3); flex: none; padding-top: var(--space-4); }
.import-step__footer .bulk-card__summary p { color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); }
.import-step__footer .bulk-card__actions { justify-content: space-between; }
.bulk-card__title { margin: 0; color: var(--text-1); font: var(--weight-semibold) var(--text-18) / var(--lh-18) var(--font-sans); }
.bulk-card__subtitle { margin: 0; color: var(--text-1); font: var(--weight-semibold) var(--text-16) / var(--lh-16) var(--font-sans); }
.bulk-card__section { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.bulk-card__line { display: flex; align-items: center; gap: var(--space-2); margin: 0; color: var(--text-1); overflow-wrap: anywhere; }
.bulk-card__meta { margin: 0; color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); overflow-wrap: anywhere; }
.bulk-card__summary { display: flex; flex-direction: column; gap: var(--space-1); }
.bulk-card__summary p { margin: 0; color: var(--text-1); overflow-wrap: anywhere; }
.bulk-card__course { font-weight: var(--weight-medium); }
.bulk-card__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); }
.enrol-course { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }
.enrol-course > .form-field { width: 100%; }
.enrol-tempo .form-field__help { margin: var(--space-1) 0 0; }
/* The drop zone: a filled region on the card, never a dashed frame. */
/* The drop zone is ONE control — a label for the hidden file input, marked as the input well it is
 * (`data-focus="well"`), so its rest, hover, pressed and invalid states are the recessed input's own. It also sinks
 * as a focused input does while its hidden input has keyboard focus, and further, toward the accent, while a file is
 * dragged over it — never with a ring or a line. A chosen file's name takes the instruction's place. */
.import-drop {
  --holder: var(--surface-overlay);
  --holder-below: var(--overlay-below);
  display: flex; align-items: center; gap: var(--space-3);
  min-height: 72px; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.import-drop[data-focus="well"]:focus-within { background-color: var(--field-focus); box-shadow: var(--inset-focus); }
.import-drop[data-focus="well"][data-dragging] { background-color: color-mix(in srgb, var(--field-focus) 88%, var(--accent)); box-shadow: var(--inset-focus); }
.import-drop__icon { display: inline-flex; flex: none; color: var(--text-2); transition: color var(--dur-fast) var(--ease-standard); }
.import-drop:is(:hover, :focus-within, [data-dragging]) .import-drop__icon { color: var(--accent); }
.import-drop[data-file] .import-drop__icon { color: var(--status-on-track-fg); }
.import-drop[data-invalid="true"] .import-drop__icon { color: var(--destructive-fg); }
.import-drop__text { display: flex; flex-direction: column; min-width: 0; }
.import-drop__lead { color: var(--text-1); font: var(--weight-medium) var(--text-14) / var(--lh-14) var(--font-sans); }
.import-drop__file { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.import-drop__meta { color: var(--text-2); font: var(--weight-regular) var(--text-12) / var(--lh-12) var(--font-sans); }
.import-card__delimiter { display: flex; flex-direction: column; gap: var(--space-3); }
.import-card__spinner { display: inline-flex; }
@media (prefers-reduced-motion: no-preference) { .import-card__spinner svg { animation: btn-spin var(--dur-spin) var(--ease-linear) infinite; } }
/* The file's headers as the server read them, in the technical face: mojibake and a wrong delimiter show here. */
.import-card__headers { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); margin: 0; padding: 0; list-style: none; }
.import-card__headers li { padding: 2px var(--space-2); border-radius: var(--radius-xs); background-color: var(--surface-overlay-hover); color: var(--text-1); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-mono); overflow-wrap: anywhere; }
.import-mapping { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--space-4); }
.import-mapping__column > .form-field { max-width: none; }
/* The totals as one row of compact figure chips: the figure first, its word after, on the overlay's hover fill. */
.import-totals { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; }
.import-totals__item { display: flex; flex-direction: row-reverse; align-items: baseline; gap: var(--space-1); padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); background-color: var(--surface-overlay-hover); }
.import-totals__item dt { color: var(--text-2); font: var(--weight-regular) var(--text-12) / var(--lh-12) var(--font-sans); }
.import-totals__item dd { margin: 0; color: var(--text-1); font: var(--weight-semibold) var(--text-13) / var(--lh-13) var(--font-sans); font-variant-numeric: tabular-nums; }
.import-preview .data-table { width: 100%; }
.import-preview__name, .import-preview__email { display: block; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.import-preview .data-table th, .import-preview .data-table td { --holder: var(--surface-overlay); }
.import-pager { padding: 0; }
/* The enrolment: the checkbox with its help tip, then — once chosen — the course and the tempo side by side, the tempo's
 * radios in one row. */
.import-enrol { display: flex; flex-direction: column; gap: var(--space-3); }
.import-enrol__toggle { display: flex; align-items: center; gap: var(--space-1); }
.import-enrol__fields { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-3) var(--space-4); align-items: start; padding-left: var(--space-6); }
.import-enrol__fields > .notice { grid-column: 1 / -1; }
.import-enrol .radio-group { flex-direction: row; flex-wrap: wrap; gap: var(--space-1) var(--space-4); }
.import-card__confirm { display: flex; flex-direction: column; gap: var(--space-3); }
.import-card__enrolment { display: flex; flex-direction: column; gap: var(--space-3); }
@media (max-width: 639px) {
  .bulk-card__actions { justify-content: stretch; }
  .bulk-card__actions > .btn { flex: 1 1 auto; }
  .import-enrol__fields { padding-left: 0; grid-template-columns: minmax(0, 1fr); }
}

/* == layout: employee detail (client.views.employee) == */
.person-header { display: grid; grid-template-columns: 48px minmax(0, 1fr); gap: var(--space-4); align-items: start; }
.person-header .page-header__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); -webkit-line-clamp: unset; overflow: visible; }
.contact-line { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-4); min-width: 0; }
.contact-link { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; color: var(--link); text-decoration: none; }
.contact-link > svg { flex: none; }
/* A long address stays one line and ellipsises (the whole of it is in the link's name and its mailto:), so the two
 * contact actions keep their place on a phone instead of wrapping over several lines. */
.contact-link > span { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
@media (pointer: coarse) { .contact-link { min-height: 44px; } }
.contact-fact { color: var(--text-2); }
.employee-edit__fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: var(--space-4); }
.employee-edit .autosave { margin-top: var(--space-3); }
/* One raised card of the person's runs; runs are told apart by their rhythm, never by a rule. */
.employee-runs { list-style: none; margin: 0; padding: var(--space-1) 0; background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--elev-1); }
.employee-run { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3) var(--space-4); }
.employee-run__facts { display: flex; flex-wrap: wrap; align-items: center; margin: 0; color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); font-variant-numeric: tabular-nums; }
.employee-run__sep { color: var(--text-3); padding: 0 6px; }
.employee-run__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* From 1024 px the page is two columns — the runs at the left, the history at the right — so neither is a thin strip
 * in a wide page. The trail and the header span both; a person with no history keeps the runs full width. */
@media (min-width: 1024px) {
  .employee-detail { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); column-gap: var(--space-8); align-items: start; }
  .employee-detail > :not(.section) { grid-column: 1 / -1; }
  .employee-detail > .employee-edit { grid-column: 1 / -1; }
  .employee-detail > .section:first-of-type:last-child { grid-column: 1 / -1; }
  .employee-detail > .section + .section { margin-top: 0; }
}
@media (max-width: 639px) {
  .contact-line { display: flex; width: 100%; }
  .person-header { grid-template-columns: 40px minmax(0, 1fr); gap: var(--space-3); }
  .person-header .avatar--48 { width: 40px; height: 40px; }
}

/* The timeline: round markers on one line, the dates in a right-aligned column. The rendering of its entries is
 * the timeline's own; only its frame is drawn here. */
.run-timeline { position: relative; list-style: none; margin: 0; padding: var(--space-2) var(--space-4); background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--elev-1); }
.run-timeline::before { content: ""; position: absolute; left: 163.5px; top: 22px; bottom: 22px; width: 1px; background: var(--border); }
.run-timeline li { position: relative; display: grid; grid-template-columns: 136px 24px minmax(0, 1fr); align-items: center; padding: 6px 0; color: var(--text-1); }
.run-timeline li::before { content: ""; grid-column: 2; grid-row: 1; justify-self: center; width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--border-strong); box-shadow: 0 0 0 3px var(--surface); }
.run-timeline li > span { grid-column: 3; grid-row: 1; min-width: 0; overflow-wrap: anywhere; }
.run-timeline time { grid-column: 1; grid-row: 1; text-align: right; color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); font-variant-numeric: tabular-nums; }
.run-timeline__module { color: var(--text-2); }
.run-timeline__icon { display: inline-flex; vertical-align: -2px; margin-right: 6px; }
.run-timeline li.is-automated > span { color: var(--text-3); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); }
.run-timeline li.is-automated .run-timeline__module { color: var(--text-3); }
@media (max-width: 639px) {
  .run-timeline::before { left: 111.5px; }
  .run-timeline li { grid-template-columns: 84px 24px minmax(0, 1fr); align-items: start; }
  .run-timeline li::before { margin-top: 5px; }
}

/* == layout: runs (client.views.runs) == */
/* The Employees toolbar's arrangement: the search, then the state filter right beside it, bottoms aligned. */
.runs-toolbar { align-items: flex-end; gap: var(--space-3); }
.runs-toolbar > .search-field-group { flex: 1 1 240px; max-width: 320px; }
.runs-toolbar .search-field { width: auto; }
.runs-toolbar__actions { display: flex; margin-inline-start: auto; }
@media (max-width: 639px) {
  .runs-toolbar > .search-field-group, .runs-toolbar > .runs-filter, .runs-toolbar__actions { flex-basis: 100%; max-width: none; }
  .runs-toolbar__actions > .btn { flex: 1 1 0; }
}
.runs-filter__select { display: none; }
@media (max-width: 1023px) {
  .runs-filter__segmented { display: none; }
  .runs-filter__select { display: block; }
  .runs-filter__select .form-field { min-width: 220px; }
}
.runs-attention { display: block; margin-top: var(--space-1); color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); }
.runs-date { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--text-2); }
.runs .data-table .course-labels { padding-top: 7px; }
.tempo-mark { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.tempo-mark__icon { display: inline-flex; color: var(--text-2); }
.runs .list-item__extra { display: flex; flex-direction: column; gap: var(--space-1); }
.runs .list-item__primary > .check { min-height: 0; }
/* A run no transition applies to has no checkbox; its gutter keeps its name aligned with the others. */
.check-gutter { display: inline-block; width: 16px; flex: none; }
/* The person column is capped so the course column takes the width: long names and addresses ellipsise. */
.runs .runs-person { width: 240px; max-width: 240px; }
.runs .runs-person > .data-table__name,
.runs .runs-person > .data-table__secondary { display: block; max-width: 216px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.runs .runs-course { width: 100%; }

/* Choice lists (client.ui.choice, and the course screen's entitlement rows): one choice is a raised card, selected by
 * its fill; rows are told apart by their rhythm. */
.choice-list { display: flex; flex-direction: column; gap: var(--space-2); }
.choice {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 56px;
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-md);
  box-shadow: var(--elev-1);
  color: var(--text-1);
  font: var(--weight-medium) var(--text-14) / var(--lh-14) var(--font-sans);
  text-align: left;
  cursor: pointer;
}
.choice > .muted { color: var(--text-2); font-weight: var(--weight-regular); font-size: var(--text-13); white-space: nowrap; }
@media (hover: hover) { .choice:hover { background: var(--surface-2); } }
.choice.is-selected { background: var(--accent-tint); }
.choice-row { display: flex; align-items: center; gap: var(--space-3); min-height: 44px; padding: var(--space-2) var(--space-4); cursor: pointer; }
.choice-row .muted { margin-left: auto; font-size: var(--text-13); color: var(--text-2); overflow-wrap: anywhere; text-align: right; }
@media (hover: hover) { .choice-row:hover { background: var(--surface-2); } }

.overview-section { margin-top: var(--space-8); }
.overview-reason { color: var(--text-2); }

/* == layout: a customer destination still loading == */
.customer-loading > .skeleton--title { display: block; height: 32px; width: 200px; margin-bottom: var(--space-6); }
/* Chart cards are as tall as their content: a short chart beside a tall one does not stretch. */
.charts-grid { align-items: stretch; }
.overview-section__note { margin: calc(-1 * var(--space-1)) 0 var(--space-3); color: var(--text-2); font: var(--weight-regular) var(--text-13) / var(--lh-13) var(--font-sans); }
/* The phone's link to the whole attention list, set apart from the items above by space alone. */
.overview-section__more { padding: var(--space-3) var(--space-4) var(--space-2); }
.report-activity__quiet { color: var(--text-2); }
.report-activity__quiet > summary { --dash-bottom: 2px; width: fit-content; cursor: pointer; padding: var(--space-2) 0; color: var(--text-1); font: var(--weight-medium) var(--text-14) / var(--lh-14) var(--font-sans); }
.report-activity__quiet-list { margin: 0; padding: 0 0 0 var(--space-4); display: flex; flex-direction: column; gap: var(--space-1); }
.report-activity__course { color: var(--text-1); }
/* A row's module stands on the card's surface like any cell — no header fill down the column — with its course under
 * it; the three counts are the figures, tabular. */
.report-activity .data-table th[scope="row"] { background-color: transparent; }
.report-activity__module { display: block; color: var(--text-1); }
.report-activity .data-table td { font-variant-numeric: tabular-nums; }
.responsive-table + .report-activity__quiet { margin-top: var(--space-2); }
/* The import's format hint: the recognised headers and a sample, in the technical face (rulebook §7). */
/* What a file should look like, quiet beneath the drop zone: the headers as one row of chips (the required one marked),
 * the limits behind the title's help, and the sample small and low in emphasis. */
.import-format { display: flex; flex-direction: column; gap: var(--space-2); max-width: 640px; }
.import-format p { margin: 0; }
.import-format__title { color: var(--text-2); font: var(--weight-medium) var(--text-12) / var(--lh-12) var(--font-sans); }
.import-format__columns { display: flex; flex-wrap: wrap; gap: var(--space-1); margin: 0; padding: 0; list-style: none; }
.import-format__column { padding: 2px var(--space-2); border-radius: var(--radius-xs); background-color: var(--surface-overlay-hover); color: var(--text-1); font: var(--weight-regular) var(--text-12) / var(--lh-12) var(--font-mono); }
.import-format__required { margin-left: 2px; color: var(--destructive-fg); }
.import-format__sample { margin: 0; padding: var(--space-2); border-radius: var(--radius-sm); background: transparent; color: var(--text-3); font: var(--weight-regular) var(--text-12) / var(--lh-12) var(--font-mono); white-space: pre; overflow-x: auto; }

/* == layout: the operator console's Overview (client.views.overview) ==
 * Its tiles are the shared stat grid; its list of customers with stalled runs names each by its mark and name. */
.overview-customer { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }

/* Printing restores the document flow the fixed content card replaces on screen, so a long page prints whole. */
@media print {
  .app--sidebar, .app--sidebar .app__column { height: auto; overflow: visible; transform: none; box-shadow: none; }
}
