/*
 * Calm indigo.
 *
 * Specified in "ELD Trex Support — UI implementation brief", 2026-10-06. It replaces the warm
 * paper-and-teal palette that was here: a cool lavender-grey ground, one indigo accent, and amber
 * reserved for anything pressing.
 *
 * Every value is a token and no component holds a raw colour. Both themes are built from the same
 * token names, so a rule written once is correct in both — a component that needs a different
 * colour in dark mode is a sign the token is wrong, not the rule.
 *
 * **The contrast ratios in the brief were checked against WCAG 2.2 AA. Do not change a value
 * without re-checking the pair it appears in** — the tightest are muted-on-sunken (4.86 light,
 * 6.37 dark) and control-on-surface for input borders (3.48 / 3.52), which have very little room.
 */
:root {
  color-scheme: light;

  /*
   * The neutral ramp: cool, and deliberately so.
   *
   * The palette this replaced warmed every neutral towards paper. This one turns the same
   * lightnesses towards lavender instead, which is what makes the indigo accent read as part of
   * the page rather than dropped onto it.
   */
  --ground: #f6f6fa;
  --surface: #ffffff;
  --sunken: #eeedf4;
  --sidebar: #f0eff6;

  /*
   * Two greys that are not interchangeable.
   *
   * `line` is for hairline dividers and nothing else. `control` is the border of anything a person
   * can type in or click — it is darker because a control border has to clear 3:1 against its own
   * background, and `line` does not. Using `line` on an input is the specific mistake this split
   * exists to prevent, and the brief calls it out twice.
   */
  --line: #e4e3ee;
  --control: #8a879c;

  --ink: #1f1b2d;
  --ink-2: #47435a;
  --muted: #676579;

  /* One accent. Primary buttons, links, selected navigation, focus rings. */
  --accent: #4f46c8;
  --accent-hover: #4038a8;
  --accent-soft: #eeedfb;
  --on-accent: #ffffff;

  /*
   * Amber means pressing, and only pressing.
   *
   * High and urgent priority, Not ready, wrap-up. It is not a second accent and not a decoration:
   * if amber appears on a screen, something on that screen wants attention.
   */
  --high: #a3540d;
  --high-soft: #fcf0e3;
  --on-high: #ffffff;

  /* Internal notes sit on their own ground, so a staff-only note is never mistaken for a reply. */
  --note: #fdf6ea;

  /* Errors, Hang up, Abandoned. Distinct from amber: amber is urgent, this is wrong or ended badly. */
  --danger: #b0302a;
  --danger-soft: #fbeaea;
  --on-danger: #ffffff;

  /* Closed and neutral. The brief gives statuses no hue of their own beyond New, which is accent. */
  --closed: #66637a;
  --closed-soft: #ecebf2;

  /*
   * The incoming-call banner keeps its own four tokens, identical in both themes.
   *
   * It is a dark capsule over whatever is behind it, and it has to look the same whichever theme
   * the page is in — a ringing phone is not a surface that should change colour. These are the only
   * tokens that do not vary.
   */
  --island: #1f1b2d;
  --island-ink: #eeedf5;
  --island-muted: #b3b0c4;
  --island-accent: #a49dff;
  --island-danger: #f2958c;

  --scrim: rgba(31, 27, 45, 0.4);

  /* ---------------------------------------------------------------- type --------------------- */

  /*
   * Three families, each with a job.
   *
   * Figtree carries the interface. IBM Plex Mono is for strings a person reads character by
   * character and may have to repeat down a phone — VINs, ELD serials, usernames, temporary
   * passwords, transcript timestamps — where a proportional font makes 1, l and I the same shape.
   * Noto Sans Gurmukhi is not a preference: without it, Punjabi falls back to whatever the device
   * has and the line height collapses.
   */
  --sans: 'Figtree', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
  --gurmukhi: 'Noto Sans Gurmukhi', 'Nirmala UI', 'Raavi', var(--sans);

  /* Composite font shorthands, so a style is applied in one declaration and cannot drift apart. */
  --type-display: 700 34px/40px var(--sans);
  --type-page: 700 28px/34px var(--sans);
  --type-title: 700 23px/29px var(--sans);
  --type-section: 700 18px/24px var(--sans);
  --type-body: 400 15px/22px var(--sans);
  --type-callout: 600 14px/20px var(--sans);
  --type-caption: 400 13px/18px var(--sans);
  --type-label: 600 12px/16px var(--sans);
  --type-mono: 400 13px/18px var(--mono);
  --type-gurmukhi: 400 16px/26px var(--gurmukhi);

  /* ---------------------------------------------------------------- space ------------------- */

  /* A 4-point scale. Named by value, because "--space-12" is unambiguous and "--space-3" is not. */
  --space-4: 4px;
  --space-8: 8px;
  --space-12: 12px;
  --space-16: 16px;
  --space-20: 20px;
  --space-24: 24px;
  --space-32: 32px;
  --space-40: 40px;
  --space-48: 48px;
  --space-64: 64px;

  --page-pad-y: 26px;
  --page-pad-x: 36px;
  --card-pad: 20px;
  --rail-width: 248px;
  --rail-width-collapsed: 72px;

  /* ---------------------------------------------------------------- radius ------------------ */

  --r-s: 8px;
  --r-control: 11px;
  --r-row: 14px;
  --r-card: 18px;
  --r-dialog: 24px;
  --r-pill: 999px;

  /* ---------------------------------------------------------------- shadow ------------------ */

  /*
   * Three depths, by how far off the page the thing is meant to sit.
   *
   * A card is barely lifted; a composer or popover is clearly above the page; the softphone, the
   * call banner and dialogs float over all of it. Tinted with the ink hue rather than pure black,
   * which is what stops a lifted card reading as grey against a lavender ground.
   */
  --shadow-card: 0 1px 2px rgba(31, 27, 45, 0.06);
  --shadow-mid: 0 6px 18px rgba(31, 27, 45, 0.1);
  --shadow-lg: 0 24px 56px rgba(31, 27, 45, 0.18);

  /* ------------------------------------------------- legacy names, being retired ------------- */

  /*
   * Aliases for the token names the rest of this stylesheet still uses.
   *
   * The palette changed name-for-name as well as value-for-value, and there are about 1,200 lines
   * of rules below written against the old names. Aliasing them means the new palette applies
   * everywhere at once instead of screen by screen, and the two sets cannot disagree because these
   * are not copies of the values — they point at them.
   *
   * Every one of these should disappear as its screen is rebuilt. Anything still here when the last
   * screen is done is a rule nobody rewrote.
   *
   * Note what the status aliases do: the brief gives Open, Pending and Solved **no hue of their
   * own** — only New is coloured, in accent, and everything else is the neutral pill. So the four
   * status hues that were here collapse onto the neutral pair. That is a deliberate loss of colour
   * on the queue, and the brief's reason is that status already carries a word and an icon.
   */
  --surface-2: var(--sunken);
  --rule: var(--line);
  --rule-strong: var(--control);
  --accent-ink: var(--on-accent);
  --radius: var(--r-card);
  --radius-s: var(--r-s);
  --lift: var(--shadow-card);
  --lift-strong: var(--shadow-mid);

  /* The rail is a light surface now, not a dark one. */
  --rail: var(--sidebar);
  --rail-ink: var(--ink-2);
  --rail-ink-bright: var(--ink);
  --rail-hover: rgba(31, 27, 45, 0.05);
  --rail-active: var(--accent-soft);
  --rail-rule: var(--line);

  --urgent: var(--high);
  --urgent-soft: var(--high-soft);
  --normal: var(--closed);
  --normal-soft: var(--closed-soft);
  --low: var(--muted);
  --open: var(--closed);
  --open-soft: var(--closed-soft);
  --pending: var(--closed);
  --pending-soft: var(--closed-soft);
  --resolved: var(--closed);
  --resolved-soft: var(--closed-soft);
}

/*
 * Dark, for the two states that mean "dark": the OS says so and the user has not overridden it, or
 * the user chose it explicitly.
 *
 * Only tokens are redefined here. No component rule belongs in this block — a colour whose only
 * definition sits inside a media query is a colour that does not exist in the un-stamped state,
 * which is the default most people are in.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    color-scheme: dark;

    --ground: #131219;
    --surface: #1c1b24;
    --sunken: #17161e;
    --sidebar: #16151d;

    --line: #2c2a38;
    --control: #726f86;

    --ink: #eeedf5;
    --ink-2: #d0cee0;
    --muted: #9a98ab;

    --accent: #a49dff;
    --accent-hover: #b8b2ff;
    --accent-soft: #221f3d;
    --on-accent: #131219;

    --high: #eda55f;
    --high-soft: #2c2013;
    --on-high: #131219;

    --note: #251f15;

    --danger: #f2958c;
    --danger-soft: #33191a;
    --on-danger: #131219;

    --closed: #a6a3b8;
    --closed-soft: #26242f;

    /* The island does not change. See the note on it above. */
    --island: #26242f;

    --scrim: rgba(0, 0, 0, 0.6);

    /* Deeper, and black rather than ink-tinted: a shadow over a dark ground only reads at all if
     * it is darker than the ground, and a tinted one turns grey. */
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-mid: 0 6px 18px rgba(0, 0, 0, 0.35);
    --shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.5);

    --rail-hover: rgba(255, 255, 255, 0.05);
  }
}

/* And again for an explicit choice, so the user's own setting wins over the OS in both directions. */
:root[data-theme='dark'] {
  color-scheme: dark;

  --ground: #131219;
  --surface: #1c1b24;
  --sunken: #17161e;
  --sidebar: #16151d;

  --line: #2c2a38;
  --control: #726f86;

  --ink: #eeedf5;
  --ink-2: #d0cee0;
  --muted: #9a98ab;

  --accent: #a49dff;
  --accent-hover: #b8b2ff;
  --accent-soft: #221f3d;
  --on-accent: #131219;

  --high: #eda55f;
  --high-soft: #2c2013;
  --on-high: #131219;

  --note: #251f15;

  --danger: #f2958c;
  --danger-soft: #33191a;
  --on-danger: #131219;

  --closed: #a6a3b8;
  --closed-soft: #26242f;

  --island: #26242f;

  --scrim: rgba(0, 0, 0, 0.6);

  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-mid: 0 6px 18px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.5);

  --rail-hover: rgba(255, 255, 255, 0.05);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: var(--type-body);
  -webkit-font-smoothing: antialiased;
}

/*
 * Punjabi, wherever it appears.
 *
 * Attached to the language rather than to a class, so any element marked `lang="pa"` gets the
 * right face and the 16/26 the brief specifies — Gurmukhi needs the extra line height for its
 * ascenders and descenders, and at the interface's 15/22 the diacritics collide.
 *
 * Never truncate it: an ellipsis can cut a vowel sign off its consonant and change the word.
 */
[lang='pa'] {
  font: var(--type-gurmukhi);
  text-overflow: clip;
}

a {
  color: var(--accent);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ------------------------------------------------------------------- 2. Shell -- */

/*
 * One shell for every signed-in screen: a 248px rail and a fluid content column.
 *
 * The rail is a light surface now rather than the dark slab it was. That is the brief's call and it
 * changes what the rail *is* — it used to be chrome you looked past, and now it is part of the same
 * page as the content, separated by a hairline and a shade rather than by a hard edge.
 */
.app {
  display: grid;
  grid-template-columns: var(--rail-width) minmax(0, 1fr);
  min-height: 100vh;
}

/* Text only a screen reader needs: present in the accessibility tree, absent from the page. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * One focus style for everything, defined once.
 *
 * `:focus-visible` rather than `:focus`, so a mouse click does not leave a ring behind while a
 * keyboard tab does. 2px at 2px offset is the brief's figure and is visible on every surface in
 * both themes, which a 1px inset ring is not on `sunken`.
 */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-s);
}

/* ------------------------------------------------------------------- the rail -- */

.rail {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  background: var(--sidebar);
  border-right: 1px solid var(--line);
  padding: var(--space-16) var(--space-12);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  padding: var(--space-4) var(--space-8) var(--space-16);
  color: var(--ink);
  text-decoration: none;
}

/* The 34px accent square. The one place on a signed-in page where the accent is a surface. */
.brand-mark {
  display: grid;
  place-items: center;
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--accent);
  color: var(--on-accent);
}

.brand-words {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.15;
}

.brand-words strong {
  font: var(--type-callout);
  font-size: 15px;
  letter-spacing: -0.01em;
}

.brand-words small {
  font: var(--type-label);
  font-weight: 500;
  color: var(--muted);
}

.rail-nav {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.rail-item {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  height: 40px;
  padding: 0 var(--space-12);
  border-radius: var(--r-control);
  color: var(--ink-2);
  text-decoration: none;
  font: var(--type-callout);
}

/*
 * Icons inherit the item's colour, which is what makes the selected state one declaration.
 *
 * `flex-shrink: 0` matters more than it looks: without it the icon is the thing that squashes when
 * a long label and a count compete for 248px, and a squashed icon reads as a rendering fault.
 */
.rail-item .icon {
  flex: 0 0 auto;
  color: inherit;
}

.rail-item:hover {
  background: var(--rail-hover);
  color: var(--ink);
}

.rail-item.is-active {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

.rail-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tabular figures so a count changing from 9 to 10 does not shift the label beside it. */
.rail-count {
  flex: 0 0 auto;
  min-width: 22px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--sunken);
  color: var(--muted);
  font: var(--type-label);
  font-variant-numeric: tabular-nums;
  text-align: center;
  line-height: 20px;
}

.rail-item.is-active .rail-count {
  background: var(--accent);
  color: var(--on-accent);
}

/* The flexible space the brief asks for: availability and the user menu sit at the bottom. */
.rail-foot {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding-top: var(--space-12);
}

/* ------------------------------------------------------- availability control -- */

.availability {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.availability-label {
  margin: 0 0 0 var(--space-4);
  font: var(--type-label);
  color: var(--muted);
}

/*
 * A two-option segmented control, not a toggle.
 *
 * A switch would say "Ready / not Ready" and leave the off state unnamed, which is the wrong shape
 * for something a person is accountable for: "Not ready" is a deliberate position, and the control
 * should show it as one. Both halves are real buttons, so either can be pressed at any time and
 * pressing the one you are already on is harmless.
 */
.segmented {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px;
  padding: 2px;
  background: var(--sunken);
  border-radius: var(--r-control);
}

.segment {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 32px;
  padding: 0 var(--space-4);
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--muted);
  font: var(--type-label);
  cursor: pointer;
}

.segment .dot {
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
  color: var(--control);
}

.segment:hover {
  color: var(--ink-2);
}

.segment.is-on {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-card);
}

/* Ready is the only green-adjacent thing in the palette, so the accent carries it. */
.segment.is-on:first-child .dot {
  color: var(--accent);
}

.segment.is-on:last-child .dot {
  color: var(--control);
}

/*
 * On a call, or wrapping up: a status, not a control.
 *
 * Set by the softphone rather than by the person, so there is nothing to press. Amber for wrap-up
 * because it is a state with a timer running out; accent for on-call because it is simply true.
 */
.availability-status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  min-height: 36px;
  padding: 0 var(--space-12);
  border-radius: var(--r-control);
  font: var(--type-callout);
}

.availability-status .dot {
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
}

.availability-status.is-oncall {
  background: var(--accent-soft);
  color: var(--accent);
}

.availability-status.is-wrap {
  background: var(--high-soft);
  color: var(--high);
}

/* ------------------------------------------------------------- the user menu -- */

/*
 * A `details` element, so it opens, closes and takes focus with no JavaScript.
 *
 * The trade is that it does not close on an outside click. For a menu whose items are all links and
 * form submissions — every one of which navigates — that costs nothing, and it buys a menu that
 * still signs you out when a script fails to load.
 */
.user-menu {
  position: relative;
}

.user-menu > summary {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-8);
  border-radius: var(--r-control);
  cursor: pointer;
  list-style: none;
  color: var(--ink);
}

/* Both, because WebKit uses its own pseudo-element for the marker. */
.user-menu > summary::-webkit-details-marker {
  display: none;
}

.user-menu > summary::marker {
  content: '';
}

.user-menu > summary:hover {
  background: var(--rail-hover);
}

.user-who {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  line-height: 1.2;
}

.user-who strong {
  font: var(--type-callout);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.user-who small {
  font: var(--type-caption);
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.user-menu > summary .icon {
  flex: 0 0 auto;
  color: var(--muted);
}

.user-menu[open] > summary .icon {
  transform: rotate(180deg);
}

/* Opens upward: the control sits at the bottom of a full-height rail. */
.menu {
  position: absolute;
  bottom: calc(100% + var(--space-8));
  left: 0;
  right: 0;
  z-index: 40;
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-mid);
}

.menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  width: 100%;
  min-height: 36px;
  padding: 0 var(--space-8);
  border: 0;
  border-radius: var(--r-s);
  background: transparent;
  color: var(--ink-2);
  font: var(--type-callout);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu-item span {
  flex: 1;
}

.menu-item:hover {
  background: var(--sunken);
  color: var(--ink);
}

.menu-item.is-selected {
  color: var(--accent);
}

.menu-item .icon {
  flex: 0 0 auto;
  color: inherit;
}

.menu-sep {
  height: 1px;
  margin: var(--space-4) 0;
  background: var(--line);
}

.menu-heading {
  margin: var(--space-8) var(--space-8) var(--space-4);
  font: var(--type-label);
  color: var(--muted);
}

.menu-themes {
  display: contents;
}

/* ----------------------------------------------------------------- avatars -- */

.avatar {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  color: var(--accent);
  font: var(--type-label);
  letter-spacing: 0.02em;
}

.avatar-24 {
  width: 24px;
  height: 24px;
  font-size: 10px;
}

.avatar-32 {
  width: 32px;
  height: 32px;
}

.avatar-44 {
  width: 44px;
  height: 44px;
  font-size: 15px;
}

.avatar-64 {
  width: 64px;
  height: 64px;
  font-size: 22px;
}

/* The voice agent's own avatar, so every action it took is attributable at a glance. */
.avatar-ai {
  background: var(--accent);
  color: var(--on-accent);
}

/* ------------------------------------------------------------------ top bar -- */

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

.topbar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-16);
  padding: var(--page-pad-y) var(--page-pad-x) 0;
}

.topbar-titles {
  min-width: 0;
}

.topbar-sub {
  margin: 0 0 2px;
  font: var(--type-caption);
  color: var(--muted);
}

.topbar-title {
  margin: 0;
  font: var(--type-page);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.topbar-tools {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  flex-shrink: 0;
}

.search {
  position: relative;
  display: flex;
  align-items: center;
  width: 300px;
}

.search .icon {
  position: absolute;
  left: 10px;
  color: var(--muted);
  pointer-events: none;
}

.search input {
  width: 100%;
  height: 40px;
  padding: 0 58px 0 34px;
  border: 1px solid transparent;
  border-radius: var(--r-control);
  background: var(--sunken);
  color: var(--ink);
  font: var(--type-body);
}

.search input::placeholder {
  color: var(--muted);
}

.search input:focus {
  border-color: var(--accent);
  background: var(--surface);
}

/* Hidden once the field has focus: the shortcut is how you get here, not what you do next. */
.search kbd {
  position: absolute;
  right: 8px;
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  color: var(--muted);
  font: var(--type-mono);
  font-size: 11px;
}

.search input:focus ~ kbd {
  display: none;
}

.icon-button {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border: 1px solid var(--control);
  border-radius: var(--r-control);
  background: var(--surface);
  color: var(--ink-2);
  cursor: pointer;
  text-decoration: none;
}

.icon-button:hover {
  background: var(--sunken);
  color: var(--ink);
}

.page {
  min-width: 0;
  padding: var(--space-24) var(--page-pad-x) 72px;
}

/*
 * The heading a screen draws for itself.
 *
 * Kept, and restyled onto the new type scale, because eleven screens still use it. Each one loses
 * it as the brief rebuilds it and starts passing a heading to the shell instead; when the last one
 * has, this block and `page-sub` and `page-actions` go with it.
 */
.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-16);
  margin-bottom: var(--space-20);
}

.page-head h1 {
  margin: 0;
  font: var(--type-page);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.page-sub {
  margin: var(--space-4) 0 0;
  color: var(--muted);
  font: var(--type-caption);
}

.page-actions {
  display: flex;
  gap: var(--space-8);
  align-items: center;
  flex-shrink: 0;
}

/* The sign-in page has no shell. */
.solo {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--space-24);
}

.solo .card {
  width: 100%;
  max-width: 400px;
  padding: 28px 28px 24px;
  box-shadow: var(--lift-strong);
}

.solo-lead {
  margin-bottom: 16px;
}

.solo-brand {
  font-weight: 650;
  font-size: 21px;
  letter-spacing: -0.02em;
  margin: 0 0 4px;
}

/* -------------------------------------------------------------- 3. Primitives -- */

/*
 * The shared components, on the new tokens.
 *
 * Every class name that was here before is still here: eleven screens use them and the brief
 * rebuilds those screens one at a time, so the old names keep working while the new components are
 * added alongside. What changed underneath is the palette, the type scale and the radii.
 */

/* ------------------------------------------------------------------- cards -- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--card-pad);
  box-shadow: var(--shadow-card);
}

.card + .card {
  margin-top: var(--space-16);
}

.card h2 {
  margin: 0 0 var(--space-16);
  font: var(--type-section);
}

/*
 * A card heading that is a label rather than a title.
 *
 * Used where the card's contents are self-describing and the heading is only there to name the
 * group — "Recent calls", "Team right now". Distinguished by a class rather than by nesting depth
 * so a card can hold either kind.
 */
.card h2.label {
  font: var(--type-label);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

.card-flush {
  padding: 0;
  overflow: hidden;
}

.card-flush h2 {
  padding: var(--card-pad) var(--card-pad) 0;
  margin-bottom: var(--space-12);
}

.card-foot {
  border-top: 1px solid var(--line);
  padding: var(--space-12) var(--card-pad);
  background: var(--sunken);
  font: var(--type-caption);
}

/* ----------------------------------------------------------------- buttons -- */

/*
 * Five variants, two sizes, and one primary per view.
 *
 * 44px is the default because it is the brief's medium and also the minimum comfortable target —
 * so the easy thing to reach for is the accessible one, and the 32px variant has to be asked for
 * by name. Tinted is reserved for the one-click Call buttons that appear all over the customer
 * screens: they are frequent and safe, which is exactly what a primary button is not.
 */
button,
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  min-height: 44px;
  padding: 0 var(--space-16);
  border: 1px solid transparent;
  border-radius: var(--r-control);
  background: var(--accent);
  color: var(--on-accent);
  font: var(--type-callout);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}

button:hover,
.button:hover {
  background: var(--accent-hover);
}

button .icon,
.button .icon {
  flex: 0 0 auto;
  color: inherit;
}

/* Small. For a control inside a row or a card header, where 44px would dominate. */
button.s,
.button.s {
  min-height: 32px;
  padding: 0 var(--space-12);
  border-radius: var(--r-s);
  font: var(--type-label);
}

button.secondary,
.button.secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--control);
}

button.secondary:hover,
.button.secondary:hover {
  background: var(--sunken);
  border-color: var(--accent);
  color: var(--accent);
}

/* Tinted: the accent at low weight. Frequent, safe actions — the Call buttons. */
button.tinted,
.button.tinted {
  background: var(--accent-soft);
  color: var(--accent);
}

button.tinted:hover,
.button.tinted:hover {
  background: color-mix(in srgb, var(--accent-soft) 80%, var(--accent));
}

/* Ghost: no weight at all until hovered. For a tertiary action beside something louder. */
button.ghost,
.button.ghost {
  background: transparent;
  color: var(--ink-2);
}

button.ghost:hover,
.button.ghost:hover {
  background: var(--sunken);
  color: var(--ink);
}

/* Danger. Hang up, and destructive actions. Nothing else. */
button.danger,
.button.danger {
  background: var(--danger);
  color: var(--on-danger);
}

button.danger:hover,
.button.danger:hover {
  background: color-mix(in srgb, var(--danger) 85%, #000);
}

button:disabled,
.button[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
}

.link-button {
  display: inline;
  min-height: 0;
  padding: 0;
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
  white-space: normal;
}

.link-button:hover {
  background: none;
  color: var(--accent);
}

/* ------------------------------------------------------------------ inputs -- */

select,
input[type='text'],
input[type='email'],
input[type='password'],
input[type='search'],
input[type='tel'],
input[type='number'],
/* Added with the coaching plan, the first screen to ask for a date. Without it the browser
 * rendered its own date control at the default size and ignored every token here. */
input[type='date'],
textarea {
  font: var(--type-body);
  width: 100%;
  min-height: 44px;
  padding: 0 var(--space-12);
  /* `control`, never `line`. A divider does not have to clear 3:1 against its background and a
   * control border does — using the hairline token here is the mistake the brief names twice. */
  border: 1px solid var(--control);
  border-radius: var(--r-control);
  background: var(--surface);
  color: var(--ink);
}

textarea {
  min-height: 96px;
  padding: var(--space-12);
  resize: vertical;
  line-height: 1.55;
}

select:focus,
input:focus,
textarea:focus {
  border-color: var(--accent);
  /* Doubled with a shadow so the 2px focus weight does not shift layout by 1px. */
  box-shadow: 0 0 0 1px var(--accent);
  outline: none;
}

/* Filled: an input on a sunken ground, for a field inside a card that is already white. */
input.filled,
select.filled,
textarea.filled {
  background: var(--sunken);
  border-color: transparent;
}

input[aria-invalid='true'],
select[aria-invalid='true'],
textarea[aria-invalid='true'] {
  border-color: var(--danger);
}

input:disabled,
select:disabled,
textarea:disabled {
  background: var(--sunken);
  color: var(--muted);
  cursor: not-allowed;
}

label {
  font: var(--type-callout);
  color: var(--ink-2);
}

/* The message under a field. Says how to fix it, never only that it is wrong. */
.field-error {
  margin: var(--space-4) 0 0;
  font: var(--type-caption);
  color: var(--danger);
}

.field-hint {
  margin: var(--space-4) 0 0;
  font: var(--type-caption);
  color: var(--muted);
}

/* ------------------------------------------------------------------- pills -- */

/*
 * One shape for every pill, and the family decides the colour.
 *
 * **Never colour alone.** Every pill carries its word, and most carry a dot or an icon as well, so
 * the information survives a monochrome screen, a photocopy, and the eight percent of men who
 * cannot separate the red from the green. That is a requirement in the brief and it is the reason
 * these are generated by `pills.ts` rather than hand-written per screen.
 */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: var(--closed-soft);
  color: var(--closed);
  font: var(--type-label);
  white-space: nowrap;
}

.badge .dot {
  width: 6px;
  height: 6px;
  flex: 0 0 auto;
}

.badge .icon {
  flex: 0 0 auto;
  color: inherit;
}

/* --- status. Only New is coloured; the rest are neutral and carry their word. --- */

.badge-s-new {
  background: var(--accent);
  color: var(--on-accent);
}

.badge-s-open,
.badge-s-pending,
.badge-s-hold,
.badge-s-resolved,
.badge-s-closed {
  background: var(--closed-soft);
  color: var(--closed);
}

/* --- priority. Urgent is the only solid one, which is what makes it mean something. --- */

.badge-urgent {
  background: var(--high);
  color: var(--on-high);
}

.badge-high {
  background: var(--high-soft);
  color: var(--high);
}

.badge-normal {
  background: var(--closed-soft);
  color: var(--closed);
}

.badge-low {
  background: transparent;
  border-color: var(--control);
  color: var(--muted);
}

/* --- outcome, on calls and recordings. --- */

.badge-o-transferred,
.badge-o-ticket {
  background: var(--accent-soft);
  color: var(--accent);
}

.badge-o-abandoned {
  background: var(--danger-soft);
  color: var(--danger);
}

/* --- the device in the truck. --- */

/*
 * Its own three variants rather than the priority ones it used to borrow.
 *
 * A malfunctioning ELD is a legal problem for the carrier, not a busy afternoon on the desk, and
 * rendering it in the same amber as a high-priority ticket made a fleet table read as a worklist.
 * Malfunction is the only one that earns a colour; the other two are deliberately quiet, because
 * most of a real fleet is in one of them and a table where every row is coloured says nothing.
 */
.badge-e-connected {
  background: var(--sunken);
  color: var(--ink-2);
}

.badge-e-malfunction {
  background: var(--danger-soft);
  color: var(--danger);
}

.badge-e-unknown {
  background: transparent;
  color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--line);
}
/* --- the plan, and whether they are paying. --- */

/*
 * Premium, in accent and not in amber.
 *
 * Amber in this interface means something wants attention. A premium account is not a problem,
 * it is a commitment — so it takes the one accent the product has, which is also the colour of
 * everything else that means "this one, first".
 */
.badge-plan-premium {
  background: var(--accent);
  color: var(--on-accent);
}

/* Overdue wants a conversation with accounts. */
.badge-pay-overdue {
  background: var(--high-soft);
  color: var(--high);
}

/*
 * On hold is the only customer-level state in the product drawn in red.
 *
 * It is the state in which somebody must not be promised work, and the failure it guards against
 * is a desk cheerfully booking a call back for an account that is not to be serviced. A quiet
 * label would be read past.
 */
.badge-pay-on_hold {
  background: var(--danger);
  color: var(--on-danger);
}
/* --- presence. --- */

.badge-p-ready {
  background: var(--accent-soft);
  color: var(--accent);
}

.badge-p-on_call {
  background: var(--accent);
  color: var(--on-accent);
}

.badge-p-wrap_up,
.badge-p-not_ready {
  background: var(--high-soft);
  color: var(--high);
}

.badge-p-offline {
  background: var(--closed-soft);
  color: var(--muted);
}

/* --- language, and the neutral tag. --- */

.badge-lang,
.badge-source,
.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--muted);
  font: var(--type-label);
  white-space: nowrap;
}

/* --------------------------------------------------------------- stat tiles -- */

/*
 * A number worth looking at, with the one line that says what to do about it.
 *
 * The note is not optional in the markup even when it is empty, because a tile whose note
 * sometimes exists and sometimes does not makes a row of tiles different heights.
 */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-16);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
}

.stat-icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--r-s);
  background: var(--accent-soft);
  color: var(--accent);
}

.stat-label {
  margin: 0;
  font: var(--type-label);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.stat-value {
  margin: 0;
  font: var(--type-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

.stat-note {
  margin: 0;
  min-height: 18px;
  font: var(--type-caption);
  color: var(--muted);
}

/* Pressing: something here is waiting on a person. Amber, and only ever earned. */
.stat.is-pressing .stat-icon {
  background: var(--high-soft);
  color: var(--high);
}

.stat.is-pressing .stat-value {
  color: var(--high);
}

/* ---------------------------------------------------------------- layout -- */

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.stack.compact {
  gap: var(--space-4);
  margin-top: var(--space-12);
}

.row {
  display: flex;
  gap: var(--space-8);
  align-items: center;
  flex-wrap: wrap;
}

.fields {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-4) var(--space-12);
  margin: 0;
  font: var(--type-caption);
}

.fields dt {
  color: var(--muted);
  white-space: nowrap;
  /* Contact labels are whatever the person entering them typed: dispatch, Billing, ops. The
     first letter is capitalised here rather than on the way into the database, so the label
     still reads back exactly as it was entered. */
  text-transform: capitalize;
}

.fields dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.mono {
  font-family: var(--mono);
  font-size: 13px;
}

.quiet {
  margin: 0;
  color: var(--muted);
  font: var(--type-caption);
}

.warn {
  margin: var(--space-4) 0 0;
  color: var(--high);
  font: var(--type-caption);
}

.when {
  color: var(--muted);
  font: var(--type-caption);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.sep {
  color: var(--control);
}

.back {
  display: inline-block;
  margin-bottom: var(--space-12);
  font: var(--type-caption);
  text-decoration: none;
}

.back:hover {
  text-decoration: underline;
}

/* --------------------------------------------- empty, loading and error -- */

/*
 * The three states every list needs, and the brief is specific about all three.
 *
 * An empty list says why it is empty and what to do next, because "No results" leaves somebody
 * wondering whether the filter is wrong or the work is done. An error says what failed, says that
 * nothing was lost, and offers the error text — support staff relay that text down a phone, and
 * "something went wrong" is unrelayable.
 */
.state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-48) var(--space-24);
  text-align: center;
}

.state-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-pill);
  background: var(--sunken);
  color: var(--muted);
}

.state h3 {
  margin: 0;
  font: var(--type-section);
}

.state p {
  margin: 0;
  max-width: 46ch;
  color: var(--muted);
  font: var(--type-body);
}

.state .row {
  margin-top: var(--space-8);
  justify-content: center;
}

/* Kept: the plain empty line used inside cards that are too small for a full state. */
.empty {
  color: var(--muted);
  padding: var(--space-24) var(--space-4);
  text-align: center;
  font: var(--type-caption);
}

.state-error .state-icon {
  background: var(--danger-soft);
  color: var(--danger);
}

/* The error text itself, so it can be read out or copied rather than described. */
.state-error pre {
  max-width: 100%;
  margin: 0;
  padding: var(--space-8) var(--space-12);
  overflow-x: auto;
  background: var(--sunken);
  border-radius: var(--r-s);
  font: var(--type-mono);
  color: var(--ink-2);
  text-align: left;
}

.error {
  margin: 0 0 var(--space-16);
  padding: var(--space-12);
  background: var(--danger-soft);
  color: var(--danger);
  border: 1px solid currentColor;
  border-radius: var(--r-control);
  font: var(--type-body);
}

/*
 * Skeleton rows, shown after 300ms rather than immediately.
 *
 * A skeleton that appears for 80ms is a flash of grey that reads as a fault. The delay means a
 * fast response shows no loading state at all, which is the correct amount.
 */
.skeleton {
  background: var(--sunken);
  border-radius: var(--r-s);
  height: 1em;
  animation: skeleton-pulse 1.4s ease-in-out infinite;
}

.skeleton-row {
  height: 44px;
  border-radius: var(--r-row);
}

@keyframes skeleton-pulse {
  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.55;
  }
}

/* ------------------------------------------------------------------ toasts -- */

/*
 * Bottom centre, five seconds, paused on hover — and errors that stay until dismissed.
 *
 * `aria-live="polite"` on the region rather than `assertive`: a confirmation should not interrupt
 * a screen reader mid-sentence. An error toast is the exception the markup makes by setting
 * `role="alert"` on itself.
 */
.toasts {
  position: fixed;
  left: 50%;
  bottom: var(--space-24);
  z-index: 80;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  transform: translateX(-50%);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  max-width: min(92vw, 420px);
  padding: var(--space-12) var(--space-16);
  background: var(--island);
  color: var(--island-ink);
  border-radius: var(--r-control);
  box-shadow: var(--shadow-lg);
  font: var(--type-callout);
  pointer-events: auto;
}

.toast .icon {
  flex: 0 0 auto;
}

.toast-success .icon {
  color: var(--island-accent);
}

.toast-error .icon {
  color: var(--island-danger);
}

.toast button {
  min-height: 0;
  padding: 0;
  margin-left: var(--space-8);
  background: none;
  color: var(--island-muted);
}

.toast button:hover {
  background: none;
  color: var(--island-ink);
}

/* ------------------------------------------------------------------ tables -- */

/* A table that scrolls sideways rather than pushing the page sideways. */
.scroller {
  overflow-x: auto;
}

table {
  width: 100%;
  border-collapse: collapse;
  font: var(--type-body);
}

th {
  text-align: left;
  padding: var(--space-8) var(--space-16);
  background: var(--sunken);
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font: var(--type-label);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

td {
  padding: var(--space-12) var(--space-16);
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr:hover {
  background: var(--sunken);
}

/* The selected row, and the row the user currently holds. */
tbody tr[aria-selected='true'],
tbody tr.is-selected {
  background: var(--accent-soft);
}

td a {
  text-decoration: none;
  font-weight: 500;
}

td a:hover {
  text-decoration: underline;
}

.num {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ----------------------------------------------------------------- 4. Screens -- */

/* Dashboard ------------------------------------------------------------------- */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}

.tile {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 15px 17px;
  text-decoration: none;
  color: inherit;
  display: block;
  box-shadow: var(--lift);
  transition:
    box-shadow 140ms ease,
    transform 140ms ease;
}

.tile:hover {
  border-color: var(--rule-strong);
  box-shadow: var(--lift-strong);
  transform: translateY(-1px);
}

/* Somebody who has asked not to see movement does not get any. */
@media (prefers-reduced-motion: reduce) {
  .tile {
    transition: none;
  }

  .tile:hover {
    transform: none;
  }
}

.tile-value {
  font-size: 27px;
  font-weight: 650;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.tile-label {
  font-size: 12px;
  color: var(--muted);
  margin-top: 3px;
}

/* A count that means somebody should do something, rather than one that is merely true. */
.tile-alert {
  border-color: var(--urgent);
}

.tile-alert .tile-value {
  color: var(--urgent);
}

.columns {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: 14px;
  align-items: start;
}

.column {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.column .card + .card {
  margin-top: 0;
}

.stat-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 7px 12px;
  font-size: 13px;
  margin: 0;
}

.stat-list dt {
  color: var(--muted);
}

.stat-list dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.card-link {
  margin-top: 10px;
}

.timeline {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.timeline li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 3px 10px;
  font-size: 13px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--rule);
}

.timeline li:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.timeline .what {
  color: var(--ink-2);
}

.timeline .subject {
  grid-column: 1 / -1;
  color: var(--muted);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.timeline .subject a {
  color: inherit;
  text-decoration: none;
}

.timeline .subject a:hover {
  text-decoration: underline;
}

/* Queue ----------------------------------------------------------------------- */

.filters {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.chip {
  padding: 5px 11px;
  border-radius: 20px;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink-2);
  text-decoration: none;
  font-size: 13px;
  font-weight: 500;
}

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

.chip-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.chip-count {
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
  margin-left: 4px;
}

.tickets {
  list-style: none;
  margin: 0;
  padding: 0;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--lift);
}

.ticket + .ticket {
  border-top: 1px solid var(--rule);
}

.ticket a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 16px;
  text-decoration: none;
  color: inherit;
}

.ticket a:hover {
  background: var(--accent-soft);
}

.ticket-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.ticket-subject {
  font-weight: 550;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ticket-context {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 12px;
  color: var(--muted);
  flex-wrap: wrap;
}

.ticket-context .customer {
  color: var(--ink-2);
  font-weight: 500;
}

.ticket .meta {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-shrink: 0;
  margin: 0;
}

.more {
  display: inline-block;
  margin-top: 12px;
  font-size: 13px;
}

/* Ticket ---------------------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 290px;
  gap: 14px;
  align-items: start;
}

.main {
  min-width: 0;
}

.side {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.side .card + .card {
  margin-top: 0;
}

.panel-title {
  font-weight: 600;
  margin: 0 0 8px;
}

.ticket-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}

.ticket-head h1 {
  font-size: 18px;
  margin: 0;
  letter-spacing: -0.01em;
}

.ref {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
}

.card .meta {
  display: flex;
  gap: 7px;
  align-items: center;
  flex-wrap: wrap;
  margin: 0 0 12px;
}

.body {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: 0;
  color: var(--ink-2);
}

.history {
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.event {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 3px 10px;
  padding-left: 12px;
  border-left: 2px solid var(--rule-strong);
  font-size: 13px;
}

.event .what {
  font-weight: 500;
}

.event .body {
  grid-column: 1 / -1;
  font-size: 13px;
}

.event-commented {
  border-left-color: var(--accent);
}

.event-commented .body {
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-s);
  padding: 8px 10px;
  margin-top: 4px;
}

.note-form {
  border-top: 1px solid var(--rule);
  padding-top: 14px;
}

.locked {
  border-top: 1px solid var(--rule);
  padding-top: 14px;
  color: var(--muted);
  font-size: 13px;
  margin: 0;
}

/* Calls ----------------------------------------------------------------------- */

.transcript {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.turn {
  display: grid;
  grid-template-columns: 68px minmax(0, 1fr);
  gap: 12px;
  font-size: 13px;
}

.turn-who {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  padding-top: 8px;
}

.turn-text {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  padding: 8px 11px;
  border-radius: var(--radius-s);
  background: var(--surface-2);
  border: 1px solid var(--rule);
}

.turn-agent .turn-text {
  background: var(--accent-soft);
  border-color: var(--accent);
}

.turn-tool .turn-text,
.turn-system .turn-text {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
}

/* Calls on a ticket (Slice 010) ----------------------------------------------- */

/*
 * The call bar. Zendesk floats a live console over the page; this one sits in the flow, because
 * there is no live call to follow — only a number that was dialled and an outcome to report.
 */
.callbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 11px 14px;
  margin-bottom: 14px;
}

.callbar-main {
  display: flex;
  align-items: baseline;
  gap: 9px;
  flex-wrap: wrap;
  min-width: 0;
}

.callbar-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
}

.callbar-number {
  font-weight: 600;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* A number with a dial button beside it. */
.dialable {
  display: flex;
  align-items: center;
  gap: 7px;
  justify-content: space-between;
}

.dialable form {
  margin: 0;
}

button.dial {
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--rule-strong);
  padding: 1px 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: 20px;
}

button.dial:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  filter: none;
}

/* A call inside the ticket conversation. */
.event-call {
  border-left-color: var(--accent);
}

.callcard {
  grid-column: 1 / -1;
  margin-top: 6px;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius-s);
  padding: 10px 12px;
}

.callcard-meta {
  display: flex;
  gap: 7px;
  align-items: center;
  flex-wrap: wrap;
  margin: 0 0 8px;
}

.player {
  width: 100%;
  max-width: 420px;
  height: 34px;
  margin: 2px 0 6px;
}

/* The preview is a few turns, not a transcript: tighter, and it does not want its own frame. */
.transcript-inline {
  gap: 6px;
  margin-top: 8px;
}

.transcript-inline .turn {
  grid-template-columns: 58px minmax(0, 1fr);
  font-size: 12px;
}

.transcript-inline .turn-text {
  padding: 6px 9px;
  background: var(--surface);
}

/* Onboarding (Slice 012) ------------------------------------------------------- */

.notice {
  border-color: var(--resolved);
}

.notice h2 {
  color: var(--resolved);
}

.notice-line {
  background: var(--resolved-soft);
  color: var(--resolved);
  border: 1px solid currentColor;
  border-radius: var(--radius-s);
  padding: 9px 12px;
  margin: 0 0 14px;
  font-size: 13px;
}

/* A credential shown on screen exactly once. Monospaced and large enough to read aloud. */
.credential {
  font-family: var(--mono);
  font-size: 18px;
  letter-spacing: 0.04em;
  background: var(--surface-2);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-s);
  padding: 10px 12px;
  margin: 8px 0 0;
  overflow-wrap: anywhere;
}

/* Reports (Slice 013) ---------------------------------------------------------- */

/* The warning that has to be read before the numbers are used. Marked, not decorated. */
.caution {
  border-left: 3px solid var(--high);
}

.caution h2 {
  color: var(--high);
}

.caution p {
  margin: 0 0 8px;
  font-size: 13px;
}

.caution p:last-child {
  margin-bottom: 0;
}

tfoot td {
  border-top: 1px solid var(--rule-strong);
  border-bottom: none;
  color: var(--muted);
  font-size: 12px;
}

/* Movement against the same person last period — never against a colleague. */
.trend {
  font-size: 11px;
  font-weight: 600;
  margin-left: 5px;
}

.trend-up {
  color: var(--resolved);
}

.trend-down {
  color: var(--muted);
}

.points {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.points li {
  padding-left: 11px;
  border-left: 2px solid var(--accent);
  font-size: 13px;
}

.points p {
  margin: 3px 0 0;
}

/* ------------------------------------------------------ Ticket detail (step 4) -- */

/*
 * The screen a support employee spends the day on.
 *
 * A header across the full width, then a fluid thread and a 320px context column. The thread is
 * where the work is and gets the room; the context column is reference and gets a fixed width, so
 * the thread grows with the window rather than the cards beside it.
 */
.ticket-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
  padding-bottom: var(--space-16);
  margin-bottom: var(--space-20);
  border-bottom: 1px solid var(--line);
}

.crumbs {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  font: var(--type-caption);
}

.crumbs .back {
  margin: 0;
}

.crumb-id {
  color: var(--muted);
}

.crumb-nav {
  display: flex;
  gap: var(--space-4);
  margin-left: auto;
}

.icon-button.s {
  width: 32px;
  height: 32px;
  border-radius: var(--r-s);
  font-size: 16px;
}

/*
 * An arrow with nowhere to go.
 *
 * Rendered as a span rather than a disabled link, and deliberately muted rather than hidden: a
 * control that disappears at the end of a list makes the two buttons swap places as you move
 * through it, and people click the wrong one.
 */
.icon-button.is-off {
  color: var(--line);
  border-color: var(--line);
  cursor: default;
}

.ticket-title-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-16);
}

.ticket-subject {
  margin: 0;
  font: var(--type-title);
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.ticket-actions {
  flex-shrink: 0;
  gap: var(--space-8);
}

.ticket-meta {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  flex-wrap: wrap;
}

.meta-assignee {
  display: flex;
  align-items: center;
  gap: 6px;
  font: var(--type-caption);
  color: var(--ink-2);
}

/*
 * Attribution for the voice agent.
 *
 * Accent-coloured and carrying the sparkle, because the single most useful thing a colleague can
 * know when picking up a ticket is whether a person or the agent wrote what they are reading.
 */
.by-agent {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: var(--type-caption);
  color: var(--accent);
}

.follows-note {
  margin: 0 0 var(--space-16);
  padding: var(--space-8) var(--space-12);
  background: var(--sunken);
  border-radius: var(--r-control);
  font: var(--type-caption);
  color: var(--ink-2);
}

.ticket-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 330px;
  gap: var(--space-20);
  align-items: start;
}

.thread-column,
.context-column {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
  min-width: 0;
}

/* --------------------------------------------------------------- the thread -- */

/*
 * The agent's own account of the call, labelled as such.
 *
 * Accent-soft, because it is the one block on the page that came from a machine rather than a
 * person, and "Check before acting" is attached to it permanently. It is usually right. It is never
 * evidence — the recording below it is.
 */
.ai-summary {
  background: var(--accent-soft);
  border: 1px solid transparent;
  border-radius: var(--r-card);
  padding: var(--card-pad);
}

.ai-summary-label {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  margin: 0 0 var(--space-8);
  font: var(--type-callout);
  color: var(--accent);
}

.ai-summary-label .tag {
  border-color: var(--accent);
  color: var(--accent);
}

.ai-summary .body {
  margin: 0;
  white-space: pre-line;
}

.thread {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
}

/*
 * A system event is one line.
 *
 * "Status changed to pending" is worth recording and is not worth a card. Reducing it to a muted
 * line is what lets the notes and the calls either side of it read as the content they are.
 */
.thread-event {
  display: flex;
  align-items: baseline;
  gap: var(--space-8);
  padding: 0 var(--space-4);
  font: var(--type-caption);
  color: var(--muted);
}

.thread-event .what {
  flex: 1;
  min-width: 0;
}

/* An internal note, on its own ground so it can never be mistaken for a message to a customer. */
.note-card {
  background: var(--note);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--card-pad);
}

.note-head {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  margin: 0 0 var(--space-8);
  font: var(--type-callout);
  flex-wrap: wrap;
}

.note-head .when {
  margin-left: auto;
}

.note-card .body {
  margin: 0;
  white-space: pre-line;
}

.call-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-8);
  margin: 0 0 var(--space-8);
}

.call-what {
  font: var(--type-callout);
}

.call-card-meta {
  margin: 0 0 var(--space-12);
}

.call-card .player {
  width: 100%;
  margin: var(--space-8) 0;
}

/* -------------------------------------------------------------- the composer -- */

.composer {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-mid);
  overflow: hidden;
}

/*
 * Two tabs, with no JavaScript behind them.
 *
 * The radios are visually hidden but focusable, their labels are the tabs, and the sibling
 * selectors below show the panel whose radio is checked. A composer that needs a script to change
 * tabs is a composer that cannot take a note when the script fails to load — and taking the note is
 * the entire purpose of this screen.
 */
.composer-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border-bottom: 1px solid var(--line);
  background: var(--sunken);
}

.composer-tabs > input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.composer-tabs > label {
  padding: var(--space-12) var(--space-16);
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font: var(--type-callout);
  cursor: pointer;
}

.composer-tabs > label:hover {
  color: var(--ink);
}

.composer-tabs > input:checked + label {
  background: var(--surface);
  border-bottom-color: var(--accent);
  color: var(--accent);
}

/* The focus ring has to land on the label, because the radio itself is invisible. */
.composer-tabs > input:focus-visible + label {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.composer-panel {
  display: none;
  width: 100%;
  padding: var(--card-pad);
  background: var(--surface);
}

/*
 * Which panel shows.
 *
 * `~` rather than `+`: the panels are siblings of the radios but not adjacent to them, and the
 * order inside `.composer-tabs` is radio, label, radio, label, panel, panel. Written as two
 * explicit rules rather than something clever, because the clever version breaks the moment a
 * third tab is added and nobody notices which panel it opened.
 */
.composer-tabs > input:nth-of-type(1):checked ~ .composer-panel[data-panel='note'],
.composer-tabs > input:nth-of-type(2):checked ~ .composer-panel[data-panel='call'] {
  display: block;
}

.composer-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-12);
  margin-top: var(--space-12);
  flex-wrap: wrap;
}

.composer-foot .quiet {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/*
 * Closed, and saying so properly.
 *
 * The brief: never just disable the composer. A greyed-out textarea tells somebody their work is
 * unwelcome without saying why or offering anything instead, and the thing it is hiding — that a
 * closed ticket's history is a record somebody promised something in — is worth a sentence.
 */
.locked-panel {
  background: var(--sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--card-pad);
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  align-items: flex-start;
}

.locked-head {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  margin: 0;
  font: var(--type-callout);
}

.locked-head .icon {
  color: var(--muted);
}

/* ----------------------------------------------------------- fleet pickers -- */

/*
 * Change driver, change truck.
 *
 * A `details` element again, so it needs no script, and the panel explains the restriction rather
 * than merely enforcing it: somebody who cannot find a truck in this list needs to know that the
 * list is deliberately limited to one customer's fleet, or they will assume the data is missing.
 */
.picker {
  margin-top: var(--space-12);
}

.picker > summary {
  display: inline-flex;
  list-style: none;
}

.picker > summary::-webkit-details-marker {
  display: none;
}

.picker > summary::marker {
  content: '';
}

.picker-panel {
  margin-top: var(--space-12);
  padding: var(--space-12);
  background: var(--sunken);
  border-radius: var(--r-control);
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.picker-panel select {
  min-height: 0;
  padding: var(--space-4);
}

/* A menu hung off a header button rather than off the rail. */
.menu-wrap {
  position: relative;
}

.menu-wrap > summary {
  list-style: none;
}

.menu-wrap > summary::-webkit-details-marker {
  display: none;
}

.menu-wrap > summary::marker {
  content: '';
}

.menu-right {
  top: calc(100% + var(--space-4));
  bottom: auto;
  left: auto;
  right: 0;
  width: 260px;
  padding: var(--space-12);
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.menu-right .menu-heading {
  margin: 0;
}

/* ------------------------------------------------------------ narrow screens -- */

@media (max-width: 1080px) {
  /* The context column goes under the thread rather than squeezing both. */
  .ticket-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ------------------------------------------------------- Queue views (step 5) -- */

/*
 * The five views, as a row of tabs.
 *
 * Underlined rather than pill-shaped, so they read as "which list am I looking at" and not as
 * filters — the chips below them are the filters, and the two need to be visually distinct or
 * people clear the wrong one.
 */
.views {
  display: flex;
  gap: var(--space-4);
  margin-bottom: var(--space-16);
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
}

.view {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--space-12) var(--space-12);
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font: var(--type-callout);
  text-decoration: none;
  white-space: nowrap;
}

.view:hover {
  color: var(--ink);
}

.view.is-active {
  border-bottom-color: var(--accent);
  color: var(--accent);
}

.view-count {
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--sunken);
  color: var(--muted);
  font: var(--type-label);
  font-variant-numeric: tabular-nums;
}

.view.is-active .view-count {
  background: var(--accent-soft);
  color: var(--accent);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  margin-bottom: var(--space-16);
}

/*
 * The name of what a row of chips filters on.
 *
 * Needed once there is more than one row: eleven chips in a line do not say which of them are
 * alternatives to each other, and Punjabi next to Accounting reads as one list of nine things.
 * Aligned with the chips rather than sitting above them, so each row is one line on a narrow
 * screen instead of two.
 */
.chips-label {
  font: var(--type-label);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  /* Lines the label up with the text inside the chips beside it, not with their top edge. */
  align-self: center;
  min-width: 62px;
}

/* Rows of chips sit closer to each other than a row of chips sits to the table below. */
.chips + .chips {
  margin-top: calc(var(--space-16) * -1 + var(--space-8));
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--space-4) var(--space-12);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink-2);
  font: var(--type-label);
  text-decoration: none;
}

.chip:hover {
  border-color: var(--control);
}

.chip-active {
  background: var(--accent-soft);
  border-color: transparent;
  color: var(--accent);
}

/* The dismiss is a link, so clearing a filter needs no JavaScript. */
.chip-active a {
  color: inherit;
  text-decoration: none;
  font-size: 14px;
  line-height: 1;
  opacity: 0.7;
}

.chip-active a:hover {
  opacity: 1;
}

/*
 * The whole row is a link, implemented by stretching the subject's anchor over the row.
 *
 * A real anchor rather than a click handler on the `tr`: it is focusable, it has a useful
 * accessible name, middle-click opens it in a tab, and it works with JavaScript off. The stretch
 * is a pseudo-element so the other cells stay selectable text.
 */
tbody tr {
  position: relative;
}

.row-link {
  font-weight: 600;
  text-decoration: none;
}

.row-link::after {
  content: '';
  position: absolute;
  inset: 0;
}

.row-link:hover {
  text-decoration: underline;
}

/*
 * A number in a table cell that wants attention.
 *
 * Amber and semibold together, never amber alone: weight survives a monochrome printout and a
 * screen with the contrast turned down, and it is the same reason every pill carries a word.
 */
td.is-pressing {
  color: var(--high);
  font-weight: 600;
}
.row-sub {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font: var(--type-caption);
}

.assignee {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/*
 * Anything a row cell needs to be clickable has to sit above the stretched link.
 *
 * Nothing in the queue does today. It is here because the first thing anybody adds to a table like
 * this is a button in the last column, and the reason it does nothing is not obvious.
 */
td .button,
td button,
td .chip {
  position: relative;
  z-index: 1;
}

.more {
  display: inline-block;
  margin-top: var(--space-16);
  font: var(--type-callout);
}

/* --------------------------------------------------- Dashboard (step 5) -- */

.greeting {
  margin-bottom: var(--space-20);
}

.greeting h1 {
  margin: 0 0 var(--space-4);
  font: var(--type-display);
  letter-spacing: -0.025em;
}

/* Tiles are links, so they need the hover and focus affordances a link has. */
a.stat {
  text-decoration: none;
  color: inherit;
}

a.stat:hover {
  border-color: var(--control);
}

.dash-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--space-20);
  align-items: start;
  margin-top: var(--space-20);
}

.dash-side {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
}

/* ------------------------------------------------------- your work, in order -- */

/*
 * Rows rather than a table.
 *
 * A table answers "compare these"; this list answers "which one next", and the answer is the top
 * row. Each row carries the reason it is here, because a row that says only "2h ago" makes
 * somebody open it to find out whether it is theirs.
 */
.worklist {
  list-style: none;
  margin: 0;
  padding: 0;
}

.work-row {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  grid-template-areas: 'priority subject reason action' 'priority context reason action';
  align-items: center;
  gap: 2px var(--space-12);
  padding: var(--space-12) var(--card-pad);
  border-bottom: 1px solid var(--line);
}

.worklist li:last-child .work-row,
.worklist li:last-child {
  border-bottom: 0;
}

.work-row:hover {
  background: var(--sunken);
}

/* The one somebody is already holding. Accent-soft, and the only row with a button. */
.work-row.is-held {
  background: var(--accent-soft);
}

.work-row .badge {
  grid-area: priority;
}

.work-row .row-link {
  grid-area: subject;
  min-width: 0;
}

.work-subject {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.work-context {
  grid-area: context;
  min-width: 0;
  color: var(--muted);
  font: var(--type-caption);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.work-reason {
  grid-area: reason;
  color: var(--muted);
  font: var(--type-caption);
  white-space: nowrap;
}

/* Unassigned is the one reason that is also a problem, so it is the one that gets the colour. */
.work-reason.is-pressing {
  color: var(--high);
}

.work-row .button {
  grid-area: action;
  /* Above the stretched row link, or it cannot be clicked. */
  position: relative;
  z-index: 1;
}

.work-chevron {
  grid-area: action;
  color: var(--control);
  font-size: 18px;
}

/* ------------------------------------------------------------- side cards -- */

.team-now,
.recent-calls {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

.team-now li {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}

.team-who {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  line-height: 1.25;
}

.team-who strong {
  font: var(--type-callout);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.team-who small {
  font: var(--type-caption);
  color: var(--muted);
}

.recent-calls li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px var(--space-8);
  align-items: baseline;
}

.recent-calls .row {
  grid-column: 1 / -1;
  gap: var(--space-4);
}

@media (max-width: 1080px) {
  .dash-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The reason and the action stack under the subject rather than squeezing four columns. */
  .work-row {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: 'priority subject action' 'priority context action' 'priority reason action';
  }
}

/* ---------------------------------------------------- Forms (new ticket) -- */

.form-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--space-20);
  align-items: start;
}

.form-side {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
}

/*
 * A field is a label, a control and whatever has to be said about it.
 *
 * Grouped so the hint belongs to the input rather than floating between two of them — which is
 * what happens when a form is a flat stack and somebody adds one hint.
 */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.field + .field {
  margin-top: var(--space-16);
}

/*
 * "required" as a word, not an asterisk.
 *
 * An asterisk needs a legend somewhere else on the page explaining it, which nobody reads, and
 * screen readers announce it as "star". The word costs four characters and needs no legend.
 */
.req,
.opt {
  margin-left: var(--space-8);
  font: var(--type-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.req {
  color: var(--high);
}

.opt {
  color: var(--muted);
}

@media (max-width: 1080px) {
  .form-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --------------------------------------------- Transcript on a ticket -- */

/*
 * The whole transcript, collapsed when it is long.
 *
 * Ishar asked for it because reading a call is often quicker than playing it back. Open by default
 * for a short call and closed for a long one: a ninety-second call is a dozen turns and reads at a
 * glance, and a ten-minute call would otherwise bury everything below it in the thread.
 */
.transcript-wrap {
  margin: var(--space-8) 0;
}

.transcript-wrap > summary {
  cursor: pointer;
  padding: var(--space-4) 0;
  color: var(--accent);
  font: var(--type-callout);
}

.transcript-wrap > summary:hover {
  text-decoration: underline;
}

.transcript-inline {
  list-style: none;
  margin: var(--space-8) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

/*
 * The agent on the left, the caller on the right.
 *
 * The brief's shape for the call screen, used here too so a transcript reads the same wherever it
 * appears. The asymmetry is doing real work: on a Punjabi call with both speakers in Gurmukhi, the
 * side of the column is what tells you who is talking before you have read a word.
 */
.transcript-inline .turn {
  max-width: 85%;
  padding: var(--space-8) var(--space-12);
  border-radius: var(--r-row);
  background: var(--sunken);
}

.transcript-inline .turn-agent {
  align-self: flex-start;
  background: var(--accent-soft);
}

.transcript-inline .turn-caller {
  align-self: flex-end;
  background: var(--surface);
  border: 1px solid var(--line);
}

.transcript-inline .turn-who {
  display: block;
  margin-bottom: 2px;
  font: var(--type-label);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.transcript-inline .turn-text {
  margin: 0;
}

/* Gurmukhi gets its own face and line height, from the [lang="pa"] rule in section 1. */

/* --------------------------------------------------- Softphone (step 6) -- */

/*
 * The incoming-call capsule.
 *
 * The island tokens are the only ones in the palette that do not change with the theme, and this is
 * why: a ringing phone should look the same whichever theme somebody is in. It is not a surface in
 * the page, it is a thing that has arrived on top of it.
 */
.island {
  position: sticky;
  top: var(--space-16);
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-20);
  width: min(680px, calc(100% - var(--space-32)));
  margin: var(--space-16) auto 0;
  padding: var(--space-16) var(--space-20);
  background: var(--island);
  color: var(--island-ink);
  border-radius: var(--r-dialog);
  box-shadow: var(--shadow-lg);
}

.island-main {
  display: flex;
  align-items: flex-start;
  gap: var(--space-12);
  min-width: 0;
}

.avatar-island {
  background: var(--island-accent);
  color: var(--island);
}

.island-who {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.island-status {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  font: var(--type-label);
  color: var(--island-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/*
 * The live dot pulses, because a ringing phone is the one place in this product where motion is
 * information rather than decoration. It says: this is happening now.
 *
 * Honoured by the reduced-motion rule in the responsive section, which flattens every animation.
 */
.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--island-accent);
  animation: live-pulse 1.4s ease-in-out infinite;
}

@keyframes live-pulse {
  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.35;
  }
}

.island-name {
  margin: 0;
  font: var(--type-section);
}

.island-name span {
  color: var(--island-muted);
  font-weight: 400;
}

.island-meta {
  margin: 2px 0 0;
  display: flex;
  align-items: center;
  gap: var(--space-8);
  flex-wrap: wrap;
  font: var(--type-caption);
  color: var(--island-muted);
}

.island-chip {
  padding: 1px 8px;
  border: 1px solid var(--island-muted);
  border-radius: var(--r-pill);
  font: var(--type-label);
}

/* What the agent found out before handing the call over. */
.island-learned {
  margin: var(--space-8) 0 0;
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font: var(--type-caption);
  color: var(--island-ink);
}

.island-learned .icon {
  flex: 0 0 auto;
  margin-top: 2px;
  color: var(--island-accent);
}

/* One ticket at a time, said before they answer rather than after. */
.island-note {
  margin: var(--space-8) 0 0;
  font: var(--type-caption);
  color: var(--island-muted);
}

.island-actions {
  display: flex;
  gap: var(--space-16);
  flex-shrink: 0;
}

/*
 * Answer and Decline: 52px circles with the word underneath.
 *
 * The brief asks for both the circle and the label, and the label is the part that matters. A pair
 * of unlabelled coloured circles is the design where somebody declines a customer by muscle memory.
 */
.call-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-height: 0;
  padding: 0;
  background: none;
  border: 0;
  color: var(--island-ink);
  font: var(--type-label);
  cursor: pointer;
}

.call-button:hover {
  background: none;
}

.call-glyph {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  font-size: 20px;
  line-height: 1;
}

.call-button.answer .call-glyph {
  background: var(--island-accent);
  color: var(--island);
}

.call-button.decline .call-glyph {
  background: var(--island-danger);
  color: var(--island);
}

.call-button:hover .call-glyph {
  filter: brightness(1.1);
}

/* ------------------------------------------------- on a call, and wrap-up -- */

/*
 * A floating card at the top right, with the page usable behind it.
 *
 * Not a modal. The entire reason for taking a call in the browser is being able to read the ticket
 * while you talk.
 */
.softphone {
  position: fixed;
  top: var(--space-16);
  right: var(--space-16);
  z-index: 60;
  width: 380px;
  max-width: calc(100vw - var(--space-32));
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-dialog);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

.softphone-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
}

.softphone-timer {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font: var(--type-callout);
}

.softphone-timer .live-dot {
  background: var(--accent);
}

/* Amber on hold: the caller is listening to silence and somebody should know. */
.softphone-timer.is-held {
  color: var(--high);
}

.softphone-timer.is-held .live-dot {
  background: var(--high);
}

/* Tabular, so a running clock does not shuffle the words beside it every second. */
.tick {
  font-variant-numeric: tabular-nums;
}

.softphone-who {
  display: flex;
  align-items: center;
  gap: var(--space-12);
}

.softphone-name {
  margin: 0;
  font: var(--type-callout);
}

.softphone-who .quiet {
  margin: 0;
}

.call-controls {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-4);
}

.call-control {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-height: 0;
  padding: var(--space-4) 0;
  background: none;
  border: 0;
  border-radius: var(--r-s);
  color: var(--ink-2);
  font: var(--type-label);
  cursor: pointer;
}

.call-control:hover:not(:disabled) {
  background: var(--sunken);
  color: var(--ink);
}

.call-control .call-glyph {
  width: 52px;
  height: 52px;
  background: var(--sunken);
  font-size: 18px;
}

.call-control.is-on .call-glyph {
  background: var(--ink);
  color: var(--surface);
}

.call-control.is-amber .call-glyph {
  background: var(--high-soft);
  color: var(--high);
}

.call-control.danger .call-glyph {
  background: var(--danger);
  color: var(--on-danger);
}

/*
 * Keypad and Transfer are shown disabled rather than hidden.
 *
 * Both are in the brief and both need call control this platform does not have yet. A disabled
 * control that names itself beats a gap somebody has to ask about, and a gap would move the other
 * three controls every time it appeared.
 */
.call-control:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.softphone-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
  padding-top: var(--space-12);
  border-top: 1px solid var(--line);
  font: var(--type-caption);
  color: var(--muted);
}

/* ------------------------------------------------------------- wrap-up -- */

.outcome-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  border: 0;
}

.outcome-chip input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.outcome-chip span {
  display: inline-block;
  padding: var(--space-4) var(--space-12);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font: var(--type-label);
  color: var(--ink-2);
  cursor: pointer;
}

.outcome-chip input:checked + span {
  background: var(--accent-soft);
  border-color: transparent;
  color: var(--accent);
}

/* The focus ring lands on the label, because the radio itself is invisible. */
.outcome-chip input:focus-visible + span {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.check {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  font: var(--type-caption);
  color: var(--ink-2);
}

.check input {
  width: auto;
  min-height: 0;
}

@media (max-width: 768px) {
  /* Full width at the bottom, where a thumb is, rather than floating over the content. */
  .softphone {
    top: auto;
    bottom: 0;
    right: 0;
    left: 0;
    width: auto;
    max-width: none;
    border-radius: var(--r-dialog) var(--r-dialog) 0 0;
  }

  .island {
    flex-direction: column;
    align-items: stretch;
  }

  .island-actions {
    justify-content: center;
  }
}

/* -------------------------------------------------------- team (step 9) -- */

/*
 * The credentials panel.
 *
 * On accent-soft rather than as a modal. A modal is a thing people dismiss by reflex, and this is
 * the only moment these values exist — once this page reloads they are gone for good, because the
 * secret lives in the server's memory under a token that is destroyed by being read.
 */
.reveal {
  background: var(--accent-soft);
  border-color: var(--accent);
}

.reveal .fields dd {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}

/*
 * The warning that this is the only showing.
 *
 * Amber and bordered, because it is a statement about something irreversible and the person reading
 * it is one reload away from losing a credential. Not red: nothing has gone wrong.
 */
.warn-strip {
  background: var(--high-soft);
  color: var(--high);
  border-left: 3px solid var(--high);
  padding: var(--space-8) var(--space-12);
  border-radius: var(--r-s);
  margin: var(--space-12) 0;
}

/* A chip carrying a pill and a count: "Ready 3". */
.chip-count {
  gap: 6px;
}

.chip-count strong {
  font-variant-numeric: tabular-nums;
}

/*
 * Checkboxes that read as chips.
 *
 * Chips in the brief, real checkboxes underneath — so the control is keyboard-operable, submits
 * with no script at all, and announces itself correctly to a screen reader. A multi-select listbox
 * is the one form control almost nobody operates correctly on the first try.
 */
.chip-field {
  border: 0;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  align-items: center;
}

.chip-field legend {
  font: var(--type-callout);
  padding: 0;
  margin-bottom: var(--space-8);
  /* A legend is not a flex item in every engine, so it takes the whole row explicitly. */
  float: left;
  width: 100%;
}

.chip-check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border: 1px solid var(--control);
  border-radius: 999px;
  cursor: pointer;
  font: var(--type-caption);
  background: var(--surface);
}

/*
 * The box stays in the document rather than being hidden.
 *
 * `display: none` on a checkbox takes it out of the tab order and out of the accessibility tree,
 * which turns a working control into a decoration. It is small and sits inside the chip.
 */
.chip-check input {
  margin: 0;
  accent-color: var(--accent);
}

.chip-check:has(input:checked) {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

/* The focus ring belongs on the chip, not on the box inside it. */
.chip-check:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --------------------------------------------------- customers (step 8) -- */

/*
 * One field, one button, and the field takes the room.
 *
 * This sits above the table rather than in the topbar because it belongs to this screen and not
 * to the shell — a search box in the chrome implies it searches everything, and this one searches
 * customers. The field is wide because what gets typed into it is a company name, and a company
 * name truncated to twenty characters is unreadable while you are typing it.
 */
.searchbar {
  display: flex;
  gap: var(--space-8);
  align-items: center;
  margin-bottom: var(--space-12);
}

.searchbar input[type='search'] {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 460px;
}

/*
 * The fleet panels on a customer, side by side where there is room.
 *
 * Drivers and trucks are two lists of different widths about the same carrier, and stacking them
 * on a wide screen leaves half the page empty while making somebody scroll to compare them.
 */
.fleet {
  display: grid;
  gap: var(--space-16);
}

@media (min-width: 1200px) {
  .fleet {
    grid-template-columns: 1fr 1fr;
  }
}

/*
 * A list in a side panel: no bullets, no table, divided by hairlines.
 *
 * For things that are each a small group of facts rather than a row of columns — a caller and
 * their number and their usual truck. A table would need three headers to say what three words
 * already say, and in a 320px panel it would scroll sideways.
 */
.plain-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.plain-list > li + li {
  margin-top: var(--space-12);
  padding-top: var(--space-12);
  border-top: 1px solid var(--line);
}

.plain-list .panel-title {
  margin-bottom: 2px;
}
/* The device, under the unit number it is fitted to. Monospace: somebody reads it down a phone. */
.device {
  display: block;
  font: var(--type-mono);
  color: var(--muted);
}
/* ------------------------------------------------------- calls (step 7) -- */

/*
 * The summary strip.
 *
 * Five figures across the top of the calls list. Not cards: one surface divided by hairlines,
 * because these are five readings of one thing and five separate cards would say they were five
 * separate subjects. The rule in the brief — "not everything is a card" — is doing real work here.
 */
.strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-16);
}

.strip-figure {
  padding: var(--space-16);
  /*
   * Divider on the left of every figure but the first. Drawn with a box-shadow rather than a
   * border so it does not add to the width and throw the grid columns out by a pixel each.
   */
  box-shadow: inset 1px 0 0 var(--line);
}

.strip-figure:first-child {
  box-shadow: none;
}

.strip-value {
  margin: 0;
  font: var(--type-title);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.strip-label {
  font: var(--type-caption);
  color: var(--muted);
  margin: 4px 0 0;
}

/*
 * Amber on the abandoned figure, and only when there are any.
 *
 * Colour is never the only signal: the number itself is the signal, and this makes an existing
 * non-zero easier to find. Zero abandoned calls stays in the ordinary ink, because a highlighted
 * zero reads as a problem.
 */
.strip-figure.is-pressing .strip-value {
  color: var(--high);
}

/*
 * The second line in a table cell — a phone number under a name.
 *
 * Block rather than inline so it sits under rather than beside, and muted so the name stays the
 * thing the eye lands on.
 */
.row-sub {
  display: block;
  font: var(--type-caption);
  color: var(--muted);
}

/* ---------------------------------------------------- one call (step 7) -- */

/* The player on the call's own screen, where it is the reason somebody came. */
.player-lg {
  max-width: 100%;
  height: 44px;
  margin: var(--space-8) 0 var(--space-12);
}

/*
 * A card that is flush but still needs padded blocks inside it.
 *
 * The transcript runs edge to edge; its heading does not.
 */
.card-pad {
  padding: var(--card-pad);
}

.card-flush .card-pad + .chat {
  border-top: 1px solid var(--line);
}

/*
 * The transcript, as a conversation.
 *
 * Agent left, caller right. Two grounds and two alignments rather than two labels, so the shape of
 * the column alone tells you who was talking — which is what makes a long call skimmable at all.
 * The "who" label stays on every turn regardless: a tool turn is neither side, and somebody using a
 * screen reader gets no benefit from alignment.
 */
.chat {
  list-style: none;
  margin: 0;
  padding: var(--card-pad);
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

.chat-turn {
  display: flex;
  gap: var(--space-8);
  align-items: flex-start;
  max-width: 86%;
}

.chat-bubble {
  border-radius: var(--radius-s);
  padding: var(--space-8) var(--space-12);
  min-width: 0;
}

.chat-who {
  font: var(--type-label);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  margin: 0 0 2px;
}

/* The offset into the call, beside the speaker. Tabular so a column of them lines up. */
.chat-at {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
  margin-left: 6px;
}
.chat-text {
  margin: 0;
  /* A transcript line can be one long unbroken token from a transcriber. It must wrap, not clip. */
  overflow-wrap: anywhere;
}

/* The agent: left, on the accent-soft ground, with the avatar that marks it as not a person. */
.chat-agent {
  align-self: flex-start;
}

.chat-agent .chat-bubble {
  background: var(--accent-soft);
}

/* The caller: right, on the plain surface inside a hairline, so the two never read as one voice. */
.chat-caller {
  align-self: flex-end;
  flex-direction: row-reverse;
}

.chat-caller .chat-bubble {
  background: var(--surface);
  border: 1px solid var(--line);
}

.chat-caller .chat-who {
  text-align: right;
}

/*
 * A tool turn, and a system turn.
 *
 * Centred, small and sunken: what the agent *did* belongs in the record, and it is not something
 * anybody said. These were briefly rendered as caller turns, which put `{"ok":true}` on a ticket as
 * a sentence a customer had spoken.
 */
.chat-tool,
.chat-system {
  align-self: center;
  max-width: 100%;
}

.chat-tool .chat-bubble,
.chat-system .chat-bubble {
  background: var(--sunken);
  font: var(--type-caption);
}

.chat-tool .chat-text,
.chat-system .chat-text {
  font: var(--type-mono);
  color: var(--ink-2);
}

/* Square, so it cannot be mistaken for the round avatar a person gets. */
.chat-avatar {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: 6px;
  background: var(--accent);
  color: var(--on-accent);
  font: var(--type-label);
  display: grid;
  place-items: center;
  margin-top: 18px;
}

/*
 * "What happened" — four or five moments, not an event log.
 *
 * A rail with a dot per beat. Deliberately plain: a call has no events table, so an elaborate
 * timeline would imply there is more here than the five facts on the row.
 */
.beats {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--space-16);
  border-left: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

.beat {
  position: relative;
}

.beat::before {
  content: '';
  position: absolute;
  left: calc(var(--space-16) * -1 - 4px);
  top: 6px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--control);
}

.beat-what {
  margin: 0;
}

.beat .when {
  display: block;
  margin-top: 2px;
}

/* ----------------------------------------------------------------- Responsive -- */

/*
 * Two steps, and the order of what goes is deliberate.
 *
 * At 1200 the rail keeps every destination and loses the words — a support desk is operated by
 * people who use the same six items all day and recognise them by position and shape long before
 * they read them. At 768 the rail stops being a column at all and becomes a strip above the
 * content, because below that width a 72px gutter is a tenth of the screen.
 *
 * What never happens at either step is a destination disappearing behind a menu. A nav item you
 * have to open something to reach is a nav item people stop using.
 */

@media (max-width: 1200px) {
  .app {
    grid-template-columns: var(--rail-width-collapsed) minmax(0, 1fr);
  }

  .rail {
    padding: var(--space-16) var(--space-8);
    align-items: center;
  }

  .brand {
    padding: var(--space-4) 0 var(--space-16);
  }

  /* The words go; the mark stays, and stays clickable. */
  .brand-words,
  .rail-label,
  .user-who,
  .availability-label,
  .user-menu > summary .icon {
    display: none;
  }

  .rail-item {
    justify-content: center;
    padding: 0;
    width: 40px;
  }

  /*
   * The count becomes a dot on the icon.
   *
   * A two-digit badge does not fit a 40px square, and shrinking the type to fit produces a number
   * nobody can read. A dot says "there is something here" — which is all the collapsed rail is
   * claiming — and the number is one widening away.
   */
  .rail-count {
    position: absolute;
    top: 6px;
    right: 6px;
    min-width: 8px;
    width: 8px;
    height: 8px;
    padding: 0;
    overflow: hidden;
    text-indent: -20px;
    background: var(--accent);
  }

  .rail-item {
    position: relative;
  }

  .segmented {
    grid-template-columns: 1fr;
    gap: 0;
  }

  /* Only the state you are in, as a dot. Pressing it switches. */
  .segment {
    min-height: 28px;
  }

  .segment span {
    display: none;
  }

  .segment:not(.is-on) {
    display: none;
  }

  .availability-status {
    padding: 0;
    justify-content: center;
  }

  .availability-status span {
    display: none;
  }

  .user-menu > summary {
    justify-content: center;
    padding: var(--space-4);
  }

  /* The menu can no longer match the rail's width, so it sizes to itself and escapes the column. */
  .menu {
    left: 0;
    right: auto;
    width: max-content;
    min-width: 200px;
  }

  .search {
    width: 220px;
  }
}

@media (max-width: 768px) {
  .app {
    grid-template-columns: minmax(0, 1fr);
  }

  /*
   * The rail stacks above the content and stops being sticky.
   *
   * A sticky horizontal bar plus a sticky top bar is two rows of chrome over a phone-height
   * viewport, which leaves very little page.
   */
  .rail {
    position: static;
    height: auto;
    flex-direction: row;
    align-items: center;
    gap: var(--space-8);
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding: var(--space-8) var(--space-12);
    overflow-x: auto;
  }

  .brand {
    padding: 0;
  }

  .rail-nav {
    flex-direction: row;
    gap: var(--space-4);
  }

  .rail-foot {
    margin-top: 0;
    margin-left: auto;
    flex-direction: row;
    align-items: center;
    padding-top: 0;
  }

  /* Opens downward now that the rail is at the top. */
  .menu {
    top: calc(100% + var(--space-8));
    bottom: auto;
    right: 0;
    left: auto;
  }

  .topbar {
    flex-wrap: wrap;
    padding: var(--space-16) var(--space-16) 0;
  }

  .topbar-tools {
    width: 100%;
  }

  .search {
    flex: 1;
    width: auto;
  }

  .page {
    padding: var(--space-16) var(--space-16) 48px;
  }
}

@media (max-width: 1080px) {
  .split,
  .columns {
    grid-template-columns: minmax(0, 1fr);
  }
}

/*
 * Nothing moves for somebody who asked for nothing to move.
 *
 * The only transform in the shell is the chevron flip on the user menu, which is cosmetic. Honoured
 * globally rather than per-rule so anything added later inherits the respect by default.
 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Coaching plan (Slice 013) ---------------------------------------------------- */

/*
 * An informational strip, beside .warn-strip and .notice-line.
 *
 * Those two are the two ends — something to act on, and something that went well. A privacy note
 * is neither, and borrowing either one would tell the reader the wrong thing about a sentence that
 * is simply a statement of fact.
 */
.info-strip {
  background: var(--sunken);
  color: var(--muted);
  border-left: 3px solid var(--control);
  padding: var(--space-8) var(--space-12);
  border-radius: var(--r-s);
  margin: 0 0 var(--space-16);
  font: var(--type-caption);
}

/* A card title with one piece of status to the right of it. */
.card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-12);
  flex-wrap: wrap;
}

.card-head h2 {
  margin-bottom: var(--space-16);
}

.card h3 {
  margin: var(--space-16) 0 var(--space-8);
  font: var(--type-label);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

.goals {
  list-style: none;
  margin: 0 0 var(--space-16);
  padding: 0;
  display: flex;
  flex-direction: column;
}

.goals li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-12);
  padding: var(--space-8) 0;
  border-bottom: 1px solid var(--line);
}

.goals li:last-child {
  border-bottom: none;
}

/* So the tick and the Remove link do not stretch to the row's height. */
.goal-form {
  flex: 0 0 auto;
}

/*
 * The tick is a real button, sized to the brief's 44px minimum.
 *
 * Not a checkbox wired to script: this posts a form, so it works with the keyboard alone and with
 * script off, and it announces what it will do rather than its own state — hence the aria-label in
 * the template reading "Mark done: <the goal>".
 */
.goal-tick {
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: inherit;
}

.goal-tick:hover {
  background: none;
}

.goal-box {
  width: 22px;
  height: 22px;
  border: 1px solid var(--control);
  border-radius: var(--r-s);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  line-height: 1;
  color: var(--on-accent);
}

.goal-done .goal-box {
  background: var(--accent);
  border-color: var(--accent);
}

.goal-body {
  flex: 1 1 auto;
  /* So a long goal wraps instead of pushing the Remove link off the card. */
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  /* Lines the text up against the 44px tick rather than the top of the row. */
  padding-top: var(--space-12);
}

/*
 * A finished goal is quieter, and is still legible.
 *
 * No line-through: struck-out text is announced character by character by some screen readers, and
 * the tick already says it. Muted on surface clears AA comfortably, which muted on sunken only
 * just does — which is why this is not on a tinted ground.
 */
.goal-done .goal-body {
  color: var(--muted);
}

/*
 * Overdue carries a word as well as the colour.
 *
 * "Status, priority, outcome and presence always carry a word, and most an icon — never colour
 * alone." The template writes "Was due" instead of "Due", so this only has to do the emphasis.
 */
.goal-overdue {
  color: var(--high);
}
