/*
 * ORGN Support portal styles.
 *
 * Tokens derived from the ORGN Design System bundle in the Claude Design project
 * 511195e3 (`_ds/orgn-design-system-29ba5ed4.../tokens/`), itself extracted verbatim from
 * apps/agent's production globals.css. Layout values are measured from
 * web/design/ORGN-Support-Portal.dc.html — 47px shell bands, 48px corner cells, 205.5px rail,
 * 38px rail band, 48px content band, 1fr/168px row grid.
 *
 * `scripts/pull-design.md` documents pulling the full bundle (and the Berkeley Mono
 * licensing gate) when the portal needs more of it.
 *
 * House rules, none negotiable: Berkeley Mono everywhere with a monospace fallback; square
 * corners (rounded-full only for dots and avatars, 4px only for kbd); flat — structure is
 * 1px hairlines and background steps, never shadows; tokens store complete colour values.
 */

/* ---------------------------------------------------------------- tokens: light */
:root {
  --app-font-family: 'Berkeley Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --app-font-mono: var(--app-font-family);

  --background: hsl(0 0% 100%);
  --foreground: hsl(240 10% 3.9%);
  --card: hsl(0 0% 100%);
  --popover: hsl(0 0% 100%);
  --primary: hsl(240 5.9% 10%);
  --primary-foreground: hsl(0 0% 98%);
  --secondary: hsl(240 4.8% 95.9%);
  --muted: hsl(240 4.8% 95.9%);
  --muted-foreground: hsl(240 5% 32%);
  --accent: hsl(240 4.8% 95.9%);
  --accent-foreground: hsl(240 5.9% 10%);
  --destructive: hsl(0 84.2% 60.2%);
  /* As TEXT the destructive fill is 3.76:1 on white. Same split as --brand-text. */
  --danger-text: hsl(0 72% 42%);
  /*
   * Borders, taken verbatim from ORGN Studio's globals.css — the same source this file's
   * tokens came from originally.
   *
   * Studio carries two weights on purpose, and it is not drift: WCAG 1.4.11 asks 3:1 on the
   * visual boundary of a control, and a structural hairline that met that bar would shout.
   * `--border` is the whisper that separates things; `--input` is the line that says a thing
   * can be typed into. Collapsing them to one value makes every rule in the app as loud as an
   * input, which is not what Studio looks like.
   */
  --border: hsl(240 5.9% 90%);
  /*
   * Controls share the structural hairline, by instruction. Studio splits these — its
   * --input sits at 3:1 for WCAG 1.4.11, because that success criterion asks for a
   * distinguishable boundary on anything you can type into, and a hairline pitched to
   * whisper is not one. Here the boundary is carried by the field's surface instead: every
   * control gets --surface-base, so it reads as a control by being a raised plane rather
   * than by being outlined louder than everything around it. Focus is where the contrast
   * goes — quiet at rest, unmistakable when it has you.
   */
  --input: var(--border);
  --ring: hsl(240 10% 3.9%);
  --link: hsl(221 83% 53%);

  --shell-panel-bg: rgb(250 250 250 / 0.9);
  --shell-strip-bg: rgb(15 23 42 / 0.03);
  --shell-border: hsl(220 13% 91%); /* Studio's --sidebar-border */
  --shell-muted-text: rgb(15 23 42 / 0.62);

  --surface-base: #fafafa;
  --surface-strong: #f5f5f5;
  --content-strong: hsl(240 10% 3.9%);
  --content-base: hsl(240 5% 26%);
  --content-weak: hsl(240 5% 38%); /* 6.63:1 on white, 6.03:1 on --surface-strong */
  --icon-base: hsl(240 5% 26%);
  --icon-weak-base: hsl(240 5% 38%);

  --color-brand-green: #00d492;
  --color-status-green: #00d492;
  /* The green as TEXT. The brand value is a fill — on white it measures 1.94:1, which is
     not a colour anyone can read. 5.36:1 here, and in dark the brand value already passes. */
  --brand-text: #067a55;
  /* Focus must clear 3:1. The brand green is 1.94:1 on white, so light theme uses the
     darker brand text; dark theme keeps the vivid green, which is 9.5:1 there. */
  --focus-ring: #067a55;
  --color-status-orange: #b93e05; /* 5.6:1 on white; the brighter fill is a dark-theme value */
  --color-status-purple: #7c3aed;
  --color-status-red: #dc2626;
  /* Ink for text sitting on brand green — near-black, never pure white. */
  --on-brand: #00110c;

  --code-inline-bg: #f5f5f5;
  --code-inline-border: #e0e0e0;
  --stream-text: hsl(240 10% 3.9% / 0.85);
  --terminal-background: hsl(240 4.8% 95.9%);
  --terminal-border: hsl(240 5.9% 85%);
  --terminal-text-command: hsl(192 91% 27%);
  --terminal-text-error: hsl(0 72% 44%);
  --terminal-text-muted: hsl(240 5% 38%);

  --nav-height: 47px;
  --cell: 48px;
  --rail-w: 205.5px;
  --control-h: 36px;
  --control-h-sm: 32px;
}

/* ----------------------------------------------------------------- tokens: dark */
.dark {
  --background: hsl(0 0% 0%); /* pure black, not slate, not zinc */
  --foreground: hsl(220 13% 91%);
  --card: hsl(0 0% 0%);
  --popover: hsl(240 10% 3.9%);
  --primary: hsl(0 0% 98%);
  --primary-foreground: hsl(240 5.9% 10%);
  --secondary: hsl(240 3.7% 15.9%);
  --muted: hsl(240 3.7% 15.9%);
  --muted-foreground: hsl(240 5% 64.9%);
  --accent: hsl(240 3.7% 15.9%);
  --accent-foreground: hsl(0 0% 98%);
  --destructive: hsl(358.7 100% 69.6%);
  --danger-text: hsl(358.7 100% 69.6%);
  --border: hsl(0, 0%, 17%);
  --input: var(--border);
  --ring: hsl(240 4.9% 83.9%);
  --link: hsl(204 94% 68%);

  --shell-panel-bg: rgb(5 5 5 / 0.9);
  --shell-strip-bg: rgb(255 255 255 / 0.015);
  --shell-border: hsl(240 3.7% 15.9%); /* Studio's --sidebar-border */
  --shell-muted-text: rgb(255 255 255 / 0.56);

  --surface-base: #141414;
  --surface-strong: #1a1a1a;
  --content-strong: #f1ecec;
  --content-base: #b7b1b1;
  --content-weak: #8a8484; /* WCAG 1.4.3: 5.72:1 on black, 5.02:1 on --surface-base */
  --icon-base: #b7b1b1;
  --icon-weak-base: #8a8484;

  --brand-text: var(--color-brand-green);
  --focus-ring: var(--color-brand-green);
  --color-status-orange: #ff8c00;
  --color-status-purple: #8b5cf6;
  --color-status-red: #ef4444;

  --code-inline-bg: rgba(255, 255, 255, 0.1);
  --code-inline-border: rgba(255, 255, 255, 0.2);
  --stream-text: #f8f8f2;
  --terminal-background: hsl(0 0% 4%);
  --terminal-border: hsl(0 0% 23%);
  --terminal-text-command: hsl(188 94% 82%);
  --terminal-text-error: hsl(0 91% 82%);
  --terminal-text-muted: hsl(0 0% 50%);
}

/* ------------------------------------------------------------------- base */
* {
  box-sizing: border-box;
}
html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}
body {
  background: var(--background);
  color: var(--foreground);
  font-family: var(--app-font-family);
  font-size: 13px;
  line-height: 20px;
  -webkit-font-smoothing: antialiased;
  font-synthesis: none;
}
a {
  color: var(--link);
  text-decoration: none;
}
:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}
svg {
  flex-shrink: 0;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  /* `clip` is deprecated and kept only for browsers that predate clip-path on this pattern. */
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Diagonal pin-stripes, 6px pitch at -45deg — the signature shell texture. */
.app-shell-stripes {
  background-image: repeating-linear-gradient(-45deg, var(--shell-strip-bg) 0 1px, transparent 1px 6px);
}

@keyframes orgnLive {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.3;
  }
}
/*
 * Referenced by .field-err and .fence-offer and never defined, so both entry animations were
 * silently no-ops and the reduced-motion guard on .fence-offer disabled a motion that did not
 * exist. Defining it is what the two declarations already asked for: a validation error that
 * appears is the one thing on the page that should draw the eye.
 */
@keyframes orgnUp {
  from {
    opacity: 0;
    transform: translateY(3px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes orgnSpin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  /* Keep state legible without looping: the dot holds at full opacity rather than vanishing. */
  .live-dot, .live-spin svg, .await-dot {
    animation: none !important;
    opacity: 1 !important;
  }
  /*
   * Not a blanket kill.
   *
   * `transition: none` on everything removes the colour and border changes that tell a
   * control it heard you — the composer lighting up on focus, a row acknowledging hover.
   * Someone who asked for less motion asked for less *movement*, not for an interface that
   * stops responding. Transforms and movement go; colour, border and outline stay, fast.
   */
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-property: color, background-color, border-color, outline-color, fill, stroke !important;
    transition-duration: 0.12s !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------- shell */
.shell {
  /*
   * The shell is the app's coordinate space, and it has to say so.
   *
   * Every `.sr-only` is `position: absolute`, and nothing else in the shell was positioned —
   * so their containing block was the *initial* containing block, which an ancestor's
   * `overflow: hidden` cannot clip. Each one then stretched the document's scroll area down
   * to its own static position: on a ticket record the composer's hidden label sits at
   * y=1398, so the page grew 679px past the viewport and the entire chrome — nav, rail,
   * footer — could be scrolled out of view. macOS overlay scrollbars meant nothing showed
   * that it was scrollable.
   *
   * One line, and every absolutely-positioned descendant is clipped by the shell that owns it.
   */
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

.shell-nav {
  display: flex;
  height: var(--nav-height);
  flex-shrink: 0;
  align-items: stretch;
  border-bottom: 1px solid var(--shell-border);
}
.nav-cell {
  display: flex;
  width: var(--cell);
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  color: var(--icon-weak-base);
}
.nav-cell--avatar {
  border-left: 1px solid var(--shell-border);
}
/* Account menu.
   Built on <details>/<summary> so it opens, closes, and takes focus with no JavaScript —
   the script below only adds Escape-to-close and click-outside, which are refinements
   rather than the mechanism. A menu that needs JS to open is a menu that strands anyone
   whose bundle failed. */
.user-menu {
  position: relative;
}
.user-menu > summary {
  list-style: none;
  cursor: pointer;
}
.user-menu > summary::-webkit-details-marker,
.user-menu > summary::marker {
  display: none;
  content: '';
}
.user-menu > summary:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.user-menu[open] > summary {
  background: var(--accent);
}
.user-menu-panel {
  position: absolute;
  top: calc(100% + 1px);
  /* Flush, not -1px: the avatar is the last cell in the row, so a negative offset pushes the
     panel's right border past the shell, which clips it (`.shell` is overflow:hidden). */
  right: 0;
  z-index: 40;
  min-width: 264px;
  border: 1px solid var(--border);
  background: var(--background);
  display: flex;
  flex-direction: column;
  text-align: left;
}
.user-menu-id {
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.user-menu-name {
  font-size: 13px;
  color: var(--foreground);
  overflow-wrap: anywhere;
}
.user-menu-sub {
  font-size: 11px;
  color: var(--content-weak);
  overflow-wrap: anywhere;
}
.user-menu-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 5px;
}
.user-menu-tag {
  border: 1px solid var(--border);
  padding: 1px 5px;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--content-weak);
}
/* Agent status is worth stating plainly: it decides whether the inbox is reachable. */
.user-menu-tag--agent {
  border-color: var(--color-brand-green);
  color: var(--color-brand-green);
}
/* Account menu detail blocks. Each is a labelled band, hairline-separated like the rest of
   the shell, so the panel reads as a small record rather than a list of links. */
.user-menu-block {
  padding: 10px 14px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.user-menu-block-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--content-weak);
}
.user-menu-team {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.user-menu-team-name {
  font-size: 12px;
  color: var(--foreground);
  overflow-wrap: anywhere;
}
.user-menu-team-meta {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}
.user-menu-chip {
  border: 1px solid var(--border);
  padding: 1px 5px;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
/* Plan decides response priority, so it is the one chip that earns colour. */
.user-menu-chip--plan {
  border-color: var(--color-brand-green);
  color: var(--color-brand-green);
}
/*
 * --danger-text, matching the P2 remediation that fixed .user-menu-item--danger and .field-err
 * and missed this chip: as TEXT the destructive fill is 3.76:1, and this is 9px uppercase.
 * A blocked team is the one state where reading the chip actually matters.
 */
.user-menu-chip--blocked {
  border-color: var(--danger-text);
  color: var(--danger-text);
}
.user-menu-stat {
  display: flex;
  gap: 12px;
  font-size: 11px;
  color: var(--content-weak);
}
.user-menu-stat strong {
  color: var(--foreground);
  font-weight: 600;
}
.user-menu-code {
  font-family: var(--app-font-mono);
  font-size: 10px;
  color: var(--content-base);
  background: var(--muted);
  padding: 4px 6px;
  overflow-wrap: anywhere;
  user-select: all;
}

.user-menu-item {
  display: block;
  width: 100%;
  padding: 9px 14px;
  border: 0;
  background: none;
  font: inherit;
  font-size: 12px;
  text-align: left;
  color: var(--foreground);
  text-decoration: none;
  cursor: pointer;
}
.user-menu-item:hover {
  background: var(--accent);
}
.user-menu-item:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -1px;
}
.user-menu-item--danger {
  border-top: 1px solid var(--border);
  color: var(--destructive);
}

/* Accounts.
   Its own band, whichever of the three states it is in — the picker link it is rendered
   with, the "Add account" of a browser holding one identity, or the switcher. */
.user-menu-accounts {
  border-top: 1px solid var(--border);
}
.user-menu-switch {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  color: var(--content-weak);
  cursor: pointer;
}
.user-menu-switch:hover {
  background: var(--accent);
}
.user-menu-switch:focus-visible {
  outline: 1px solid var(--color-brand-green);
  outline-offset: -1px;
}
/* Overlapping, not spaced. A row of separate avatars reads as a list of other people; a
   stack reads as one set of identities you can flip between — which is what it is. The ring
   is what keeps them legible as separate faces at 20px. */
.user-menu-faces {
  display: flex;
  flex-shrink: 0;
  align-items: center;
}
.user-menu-faces > * + * {
  margin-left: -7px;
}
.user-menu-face {
  display: flex;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
  box-shadow: 0 0 0 1px var(--background);
  background: var(--muted);
  color: var(--content-base);
  font-size: 9px;
  text-transform: uppercase;
  object-fit: cover;
}
.user-menu-switch-count {
  flex: 1;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}
.user-menu-switch svg {
  flex-shrink: 0;
  transition: transform 120ms ease;
}
.user-menu-switch[aria-expanded='true'] svg {
  transform: rotate(180deg);
}
@media (prefers-reduced-motion: reduce) {
  .user-menu-switch svg {
    transition: none;
  }
}
/* A bare `display: flex` outranks the UA's `[hidden] { display: none }`, so the collapsed
   state has to be spelled out — the same trap `.bulk-bar` fell into. */
.user-menu-list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--border);
}
.user-menu-list[hidden] {
  display: none;
}
.user-menu-account {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  color: var(--foreground);
  cursor: pointer;
}
.user-menu-account:hover {
  background: var(--accent);
}
.user-menu-account:focus-visible {
  outline: 1px solid var(--color-brand-green);
  outline-offset: -1px;
}
/* Where you already are: tinted, badged, and not a control at all. */
.user-menu-account--current,
.user-menu-account--current:hover {
  background: var(--muted);
  cursor: default;
}
.user-menu-account-id {
  display: flex;
  min-width: 0;
  flex: 1;
  flex-direction: column;
}
.user-menu-account-name {
  font-size: 12px;
  overflow-wrap: anywhere;
}
.user-menu-account-email {
  font-size: 10px;
  color: var(--content-weak);
  overflow-wrap: anywhere;
}
.user-menu-account-badge {
  flex-shrink: 0;
  border: 1px solid var(--border);
  padding: 1px 5px;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
/* The switch is in flight and the page is about to navigate. */
.user-menu-account[data-busy] {
  opacity: 0.6;
  cursor: progress;
}

.avatar {
  display: flex;
  width: 26px;
  height: 26px;
  border-radius: 9999px;
  align-items: center;
  justify-content: center;
  background: var(--muted);
  color: var(--content-base);
  font-size: 10px;
}
/* The lockup replaces the old icon + wordmark pair and the dead panel toggle beside it.
   Clearspace per the asset README is 0.25x cap height; at 15px tall that is 6px, and the
   14px side padding clears it comfortably. */
.nav-brand {
  display: flex;
  align-items: center;
  padding: 0 14px;
  color: var(--foreground);
  text-decoration: none;
}
.nav-brand svg {
  display: block;
  height: 15px;
  width: auto;
}
.nav-brand:hover {
  background: var(--muted);
}
.nav-brand:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -1px;
}
.nav-spacer {
  flex: 1;
  min-width: 0;
}
.nav-actions {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: 10px;
  padding: 0 12px;
}
.nav-search {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--shell-border);
  padding: 4px 10px;
  color: var(--content-weak);
}
.nav-search input {
  all: unset;
  width: 150px;
  font-family: inherit;
  font-size: 11px;
  color: var(--foreground);
}
.nav-search input::placeholder {
  color: var(--content-weak);
}
.nav-bell {
  display: flex;
  padding: 6px;
  color: var(--icon-weak-base);
  position: relative;
}
/*
 * The bell's ONLY unread signal, so it has to clear 3:1 as a non-text indicator. The vivid
 * fill is 1.94:1 on white; --brand-text carries it in light and collapses to the same vivid
 * green in dark, where the original was already fine.
 */
.bell-dot {
  position: absolute;
  top: 4px;
  right: 3px;
  width: 6px;
  height: 6px;
  border-radius: 9999px;
  background: var(--brand-text);
}
.orgn-kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 20px;
  min-width: 20px;
  padding: 0 4px;
  border-radius: 4px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--muted) 50%, transparent);
  font-family: var(--app-font-mono);
  font-size: 10px;
  color: var(--muted-foreground);
}

.shell-body {
  display: flex;
  flex: 1;
  min-height: 0;
}

/* --------------------------------------------------------------- rail */
.shell-rail {
  width: var(--rail-w);
  flex-shrink: 0;
  border-right: 1px solid var(--shell-border);
  display: flex;
  flex-direction: column;
}
.rail-band {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 38px;
  padding: 0 12px;
  border-bottom: 1px dashed var(--shell-border);
}
.rail-band-title {
  flex: 1;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--shell-muted-text);
}
.rail-band-count {
  font-size: 10px;
  color: var(--shell-muted-text);
  font-variant-numeric: tabular-nums;
}
.rail-groups {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 6px;
  flex: 1;
  overflow-y: auto;
}
.orgn-nav-section-label {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  font-family: var(--app-font-mono);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  /* Quieter than a nav row, but by picking a dimmer token rather than by thinning this one
     with transparency. At 60% the label measured 3.41:1 — the smallest type in the app was
     also the least legible. `--content-weak` is a documented 5.72:1 on black / 6.63:1 on
     white and still sits below the rows' `--muted-foreground`, so the hierarchy is unchanged. */
  color: var(--content-weak);
}
.orgn-nav-row {
  position: relative;
  display: flex;
  width: 100%;
  min-width: 0;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  text-align: left;
  font-family: var(--app-font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted-foreground);
  transition: background 0.15s, color 0.15s;
  cursor: pointer;
  background: transparent;
  border: 0;
  text-decoration: none;
}
.orgn-nav-row:hover {
  background: color-mix(in srgb, var(--muted) 40%, transparent);
  color: var(--foreground);
}
.orgn-nav-row[data-active='true'] {
  background: var(--muted);
  color: var(--foreground);
}
.orgn-nav-row[data-active='true']::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 3px;
  background: var(--foreground);
}
/* The active row lifts its own background to --muted, which drops the count to 4.05:1 against
   it. The row brightens its label when active; its count belongs at the same emphasis. */
.orgn-nav-row[data-active='true'] .orgn-nav-count {
  color: var(--content-base);
}
.orgn-nav-count {
  margin-left: auto;
  font-size: 9px;
  /* Same reason as the section label: 80% measured 4.24:1, just under the bar. */
  color: var(--content-weak);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.05em;
}
.rail-foot {
  border-top: 1px dashed var(--shell-border);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.rail-foot a {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
.rail-foot a:hover {
  color: var(--foreground);
}

/* --------------------------------------------------------------- main */
.shell-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.main-band {
  display: flex;
  height: var(--cell);
  flex-shrink: 0;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px dashed var(--shell-border);
  padding: 0 24px;
}
.main-band-left {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.orgn-status-square {
  width: 8px;
  height: 8px;
  flex-shrink: 0;
  display: inline-block;
  background: var(--color-brand-green);
}
.main-band-title, .main-band-count {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--shell-muted-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.main-band-count {
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  padding-left: 16px;
}
.main-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* ------------------------------------------------------------ toolbar */
.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 24px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.toolbar-left {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.toolbar-search {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--input);
  height: var(--control-h-sm);
  padding: 0 10px;
  min-width: 280px;
  color: var(--content-weak);
}
.toolbar-search input {
  all: unset;
  flex: 1;
  font-family: inherit;
  font-size: 12px;
  color: var(--foreground);
}
.toolbar-search input::placeholder {
  color: var(--content-weak);
}
.toolbar-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: var(--control-h-sm);
  padding: 0 10px;
  border: 1px dashed var(--border);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}

.btn-new {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: var(--control-h-sm);
  padding: 0 14px;
  border: 1px solid transparent;
  background: var(--color-brand-green);
  color: var(--on-brand);
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s ease;
}
.btn-new:hover {
  background: color-mix(in srgb, var(--color-brand-green) 88%, black);
}
.btn-new:active {
  background: color-mix(in srgb, var(--color-brand-green) 78%, black);
}
.btn-new:disabled {
  background: var(--muted);
  color: var(--content-weak);
  cursor: default;
}
.btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--control-h-sm);
  padding: 0 14px;
  border: 1px solid var(--input);
  background: transparent;
  color: var(--foreground);
  font-family: inherit;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  cursor: pointer;
  text-decoration: none;
}
.btn-ghost:hover {
  background: var(--accent);
}

/* ------------------------------------------------------- ticket rows */
.rows {
  display: flex;
  flex-direction: column;
}
/* 24px flat, not 22px + a 2px border. Unread already says so three times — the tint, the
   dot before the subject, and the `needs response` pill — so the green edge was a fourth
   telling of the same fact, and the only thing holding every row 2px off the page's inset. */
.row {
  display: grid;
  grid-template-columns: 1fr 168px;
  gap: 20px;
  padding: 13px 24px;
  border-bottom: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
  transition: background 0.15s ease;
}
.row:hover {
  background: color-mix(in srgb, var(--foreground) 3%, transparent);
}
.row[data-unread='true'] {
  background: color-mix(in srgb, var(--color-brand-green) 5%, transparent);
}
.row[data-unread='true']:hover {
  background: color-mix(in srgb, var(--color-brand-green) 9%, transparent);
}
.row-main {
  min-width: 0;
}
.row-top {
  display: flex;
  align-items: center;
  gap: 8px;
}
.row-dot {
  width: 6px;
  height: 6px;
  border-radius: 9999px;
  background: var(--color-brand-green);
  flex-shrink: 0;
}
.row-subject {
  font-size: 14px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row-pill {
  display: inline-flex;
  align-items: center;
  height: 16px;
  padding: 0 5px;
  background: var(--color-brand-green);
  color: var(--on-brand);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  flex-shrink: 0;
}
/*
 * A ticket past its first-reply target, in the agent inbox.
 *
 * Outlined rather than filled, unlike the green "needs response" beside it. Two solid pills on
 * one row compete, and this one has to be the louder of the pair without shouting over the
 * subject — an outline in the warning hue reads as a flag, a second filled block reads as
 * decoration. Colour is never the whole signal: the pill says "no reply", so the state
 * survives a colourblind reader and the neutral skins that wash hue out.
 */
.row-pill--overdue {
  background: transparent;
  border: 1px solid var(--color-status-orange);
  color: var(--color-status-orange);
}
.toolbar-chip--overdue {
  border-style: solid;
  border-color: var(--color-status-orange);
  color: var(--color-status-orange);
}
.row-meta {
  margin-top: 5px;
  font-size: 11px;
  color: var(--content-weak);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row[data-unread='true'] .row-meta {
  padding-left: 14px;
}
.row-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}
.row-when {
  font-size: 10px;
  color: var(--content-weak);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.status-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 20px;
  padding: 0 9px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
  flex-shrink: 0;
}

/* ---------------------------------------------------------------- empty */
.empty-wrap {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px 32px;
}
.empty-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  border: 1px dashed var(--border);
  padding: 44px 56px;
  max-width: 460px;
}
.empty-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
}
.empty-desc {
  margin: 0;
  font-size: 12px;
  line-height: 1.7;
  color: var(--content-weak);
}

/* --------------------------------------------------------------- signin */

/* --------------------------------------------------------------- footer */
.shell-footer {
  display: flex;
  height: var(--nav-height);
  flex-shrink: 0;
  align-items: stretch;
  border-top: 1px solid var(--shell-border);
}
.foot-cell {
  display: flex;
  align-items: center;
  padding: 0 16px;
  border-right: 1px solid var(--shell-border);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.32em;
  color: var(--shell-muted-text);
}
.foot-mid {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 24px;
  padding: 0 16px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--shell-muted-text);
  overflow: hidden;
}
.live {
  display: flex;
  align-items: center;
  gap: 7px;
}
.live-dot {
  width: 7px;
  height: 7px;
  background: currentColor;
  display: none;
}
.live-spin, .live-off {
  display: none;
}
/*
 * The served resting state: no script has run, so nothing is connecting and nothing has failed.
 * Deliberately quiet — no dot, no spinner, no colour — because the honest message is simply
 * that the page updates when you reload it.
 */
.live[data-state='idle'] {
  color: var(--content-weak);
}

/* --brand-text, not the vivid fill: #00d492 is 1.94:1 on white and this is 10px text. */
.live[data-state='live'] {
  color: var(--brand-text);
}
.live[data-state='live'] .live-dot {
  display: block;
  animation: orgnLive 2s ease-in-out infinite;
}
.live[data-state='reconnecting'] {
  color: var(--color-status-orange);
}
.live[data-state='reconnecting'] .live-spin {
  display: flex;
  animation: orgnSpin 1s linear infinite;
}
.live[data-state='offline'] {
  color: var(--shell-muted-text);
}
.live[data-state='offline'] .live-off {
  display: flex;
}
.foot-toggle {
  width: var(--nav-height);
  flex-shrink: 0;
  justify-content: center;
  padding: 0;
  border: 0;
  border-left: 1px solid var(--shell-border);
  background: none;
  color: var(--icon-weak-base);
  cursor: pointer;
  font: inherit;
  letter-spacing: 0;
  -webkit-appearance: none;
  appearance: none;
}
.foot-toggle:hover {
  color: var(--foreground);
}

/* ----------------------------------------------------------- responsive */
@media (max-width: 900px) {
  .shell-rail {
    display: none;
  }
  .nav-search {
    display: none;
  }
  .row {
    grid-template-columns: 1fr auto;
    gap: 12px;
    padding: 12px 16px;
  }
  .row-when {
    display: none;
  }
  .main-band, .toolbar {
    padding-left: 16px;
    padding-right: 16px;
  }
  .thread, .composer {
    padding-left: 16px;
    padding-right: 16px;
  }
  .toolbar-search {
    min-width: 0;
    flex: 1;
  }
}
@media (max-width: 560px) {
  /* Name and timestamp stop sharing a line: side by side there is room for one of them, and
     the pair spent the width arguing over it. */
  .msg-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
  }
  .row {
    grid-template-columns: 1fr;
  }
  .row-side {
    flex-direction: row;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
  }
  .foot-mid {
    gap: 12px;
  }
  .foot-mid > span:first-child {
    display: none;
  }
}

/* Coarse pointers get 44px targets. Height only — the type scale is the design's. */
@media (pointer: coarse) {
  .btn-new, .btn-ghost, .toolbar-search, .toolbar-chip {
    height: 44px;
  }
  /* Share / make-private, which this block missed: a sliver between two strip cells. */
  .vis-btn {
    height: 44px;
  }
  .orgn-input, .orgn-select {
    height: 44px;
  }
  /* The filter group sizes its cells, so it has to take the taller target too — otherwise
     five 44px selects sit inside a 32px frame. */
  .qfilter-group, .qfilter-group .qf { height: 44px; }
  .toolbar-controls .orgn-select { height: 100%; }
  .nav-bell {
    padding: 14px;
  }
  .rail-foot a {
    min-height: 44px;
  }
  .orgn-nav-row {
    padding-top: 11px;
    padding-bottom: 11px;
  }
}


/* ------------------------------------------------------------ auth screen */
.auth-stage {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px 16px;
  min-height: 0;
}
/* Faint radial brand ambience in opposite corners — the house shell glow, kept subtle
   enough that it reads as depth rather than as a gradient. */
.orgn-ambient-glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(
      60ch 40ch at 8% 0%,
      color-mix(in srgb, var(--color-brand-green) 5%, transparent),
      transparent 70%
    ),
    radial-gradient(
    50ch 36ch at 100% 100%,
    color-mix(in srgb, var(--color-brand-green) 4%, transparent),
    transparent 70%
  );
}
.auth-card {
  position: relative;
  width: 400px;
  max-width: 100%;
  border: 1px solid var(--border);
  padding: 40px 36px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  background: var(--background);
}
/* Corner brackets: the house frame for an important card, at the cost of four 1px rules. */
.bracket {
  position: absolute;
  width: 9px;
  height: 9px;
}
.bracket--tl {
  top: -1px;
  left: -1px;
  border-top: 1px solid var(--foreground);
  border-left: 1px solid var(--foreground);
}
.bracket--tr {
  top: -1px;
  right: -1px;
  border-top: 1px solid var(--foreground);
  border-right: 1px solid var(--foreground);
}
.bracket--bl {
  bottom: -1px;
  left: -1px;
  border-bottom: 1px solid var(--foreground);
  border-left: 1px solid var(--foreground);
}
.bracket--br {
  bottom: -1px;
  right: -1px;
  border-bottom: 1px solid var(--foreground);
  border-right: 1px solid var(--foreground);
}

.auth-wordmark {
  display: block;
  color: var(--foreground);
  margin-bottom: 34px;
}
.auth-wordmark svg {
  height: 22px;
  width: auto;
  display: block;
}
.auth-form {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.auth-field {
  width: 100%;
  text-align: left;
}
.auth-btn {
  width: 100%;
  height: var(--control-h);
}
/* The SSO call to action, matching ORGN Studio's sign-in button
   (apps/agent/components/auth/signin-client.tsx): 48px tall, transparent, a 1px dashed
   hairline, and four solid 12px corner brackets laid over it.

   Studio hardcodes white because its sign-in is always on black. This is token-driven so it
   survives the portal's light theme, where a white-on-white button would be invisible. */
.btn-unlock {
  position: relative;
  display: flex;
  width: 100%;
  height: 48px;
  align-items: center;
  justify-content: center;
  border: 1px dashed color-mix(in srgb, var(--foreground) 30%, transparent);
  background: transparent;
  color: var(--foreground);
  font-family: var(--app-font-family);
  font-size: 21px;
  font-weight: 900;
  font-synthesis: weight;
  line-height: 1;
  letter-spacing: 0.15em;
  /* The tracking adds a trailing gap after the last glyph, which reads as off-centre. */
  text-indent: 0.15em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.btn-unlock:hover {
  border-color: color-mix(in srgb, var(--foreground) 60%, transparent);
  background: color-mix(in srgb, var(--foreground) 5%, transparent);
}
.btn-unlock:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
/* 12px arms, 1px thick, solid where the border is dashed — the same contrast Studio uses to
   pin the corners of a dashed frame. */
.btn-unlock::before,
.btn-unlock::after,
.btn-unlock > .unlock-corner::before,
.btn-unlock > .unlock-corner::after {
  content: '';
  position: absolute;
  width: 12px;
  height: 12px;
  border-color: var(--foreground);
  border-style: solid;
  border-width: 0;
  pointer-events: none;
}
.btn-unlock::before {
  top: -1px;
  left: -1px;
  border-top-width: 1px;
  border-left-width: 1px;
}
.btn-unlock::after {
  top: -1px;
  right: -1px;
  border-top-width: 1px;
  border-right-width: 1px;
}
.btn-unlock > .unlock-corner::before {
  bottom: -1px;
  left: -1px;
  border-bottom-width: 1px;
  border-left-width: 1px;
}
.btn-unlock > .unlock-corner::after {
  bottom: -1px;
  right: -1px;
  border-bottom-width: 1px;
  border-right-width: 1px;
}
.unlock-corner {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* The label is its own node so the scramble can rewrite text without destroying the
   bracket span next to it. */
.unlock-label {
  position: relative;
}

.error-status {
  margin: 0 0 4px;
  font-size: 40px;
  font-weight: 900;
  line-height: 1;
  letter-spacing: 0.06em;
  color: var(--content-weak);
}
.auth-lede {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
}
.auth-note {
  margin: 6px 0 0;
  font-size: 11px;
  line-height: 1.7;
  color: var(--content-weak);
}
.auth-note strong {
  color: var(--content-base);
  font-weight: 600;
}
.auth-note a {
  color: var(--content-base);
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* The field label. `.orgn-label` was applied throughout the markup and never defined, so
   every form label in the portal rendered as plain body copy — reading as a heading rather
   than as the small, quiet caption a field label should be. */
.orgn-label {
  display: block;
  margin-bottom: 6px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--content-weak);
}

/* A field with no visible focus state is unusable by keyboard. Brand green rather than the
   UA default so it belongs to this shell, and inset so it cannot be clipped by the row. */
.orgn-input:focus-visible,
.orgn-textarea:focus-visible,
.orgn-select:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -1px;
  border-color: var(--color-brand-green);
}
.btn-ghost:focus-visible,
.btn-new:focus-visible,
.trail-btn:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.foot-mid a {
  color: var(--shell-muted-text);
}
.foot-mid a:hover {
  color: var(--foreground);
  text-decoration: none;
}

/* ═══════════════════════════════════════════════ centred document column
   Thread and form sit in a bordered column rather than running full-bleed: the reading
   measure stays sane on a wide monitor, and the rules give the page an edge to hang from. */
.doc {
  width: 100%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  min-height: 100%;
  border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
}
/*
 * The agent's ticket page. Full-bleed, like the requester's record and for the same reason:
 * a 900px column centred in the content area left a 167px empty bordered strip hard against
 * the rail that read as a second, blank sidebar, and it made every message 336px narrower
 * than the row it sat in. That width is spent on a properties rail now — the one place this
 * page keeps everything that is not the conversation.
 */
.doc--thread {
  max-width: none;
  border-left: 0;
  border-right: 0;
  /*
   * One measure, two edges — the record page's rule, applied here. Prose holds the measure;
   * structure and controls span the column. So a message's name, its timestamp and its body
   * finish on one line, while the tint behind it, the system rules and the composer run to
   * the column's own edge.
   *
   * 624px carries 74ch of the thread's 13px body in a monospace face, and it is 26 × 24, so
   * it lands on the same 24px grid every other edge on this page uses.
   */
  --measure: 624px;
  /* The avatar column plus its gap: a message starts here, so it has to end this much
     earlier for the two to finish together. */
  --log-gutter: 42px;
}
.doc--form {
  max-width: 760px;
}
/* Takes the place of the band's status square on a page that is inside something else. Same
   8px optical weight, so the band's rhythm does not shift between views. */
.band-back {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 13px;
  height: 13px;
  margin-left: -2px;
  color: var(--shell-muted-text);
}
/* A 13px glyph is the right optical weight in the band and the wrong thing to ask a thumb to
   hit. The target grows to fill the band's height; the arrow does not move. */
.band-back::after {
  content: '';
  position: absolute;
  inset: -17px -14px;
}
.band-back:hover {
  color: var(--foreground);
}

.doc-head {
  border-bottom: 1px solid var(--border);
}
/*
 * The subject, and nothing competing with it.
 *
 * This block used to be a space-between row with the subject on one side and five controls
 * on the other: the controls wrapped into two ragged lines of different heights, and the
 * subject was left 398px of a 900px column to wrap in. The controls live in the rail now, so
 * the header is the one thing a header is for — which ticket this is, and what it says.
 */
.doc-title-block {
  padding: 22px 24px 20px;
  min-width: 0;
}
.doc-key-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.doc-key {
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--content-weak);
  font-variant-numeric: tabular-nums;
}
/* 24px, which is the size the record page and the new-request page already give their own
   headings. The three pages agree now, and the subject stops being the same size as the
   paragraph underneath it. */
.doc-title {
  margin: 0;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.25;
}
/* The subject now has the whole page to run across, and a subject that runs across the whole
   page is a subject nobody reads to the end of. */
.doc--thread .doc-title {
  max-width: 46ch;
}

/* ══════════════════════════════════════════════ the ticket page's two columns
 * The conversation on the left at a reading measure, everything else in a rail on the right.
 * The rail is sticky, because an agent's next move on a ticket is a status, an owner or a
 * snooze — and none of those should be forty messages up the page.
 */
.thread-grid {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 304px;
  align-items: stretch;
  min-height: 0;
}
.thread-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  /* The divider hangs off the column that stretches, so it runs the full height of the page
     whether the rail is 200px long or 700px. */
  border-right: 1px solid var(--border);
}
.thread-rail {
  position: sticky;
  top: 0;
  align-self: start;
  display: flex;
  flex-direction: column;
  min-width: 0;
  /*
   * Exactly the height of the scrolling area: the shell is a 47px nav, a 48px band, the
   * scroller, and a 47px footer. A rail with a form open in it can run past the bottom of
   * the window, and a sticky column that is taller than its scrollport pins its top and
   * hides its own end. It keeps its own scrollbar instead, so Take it is always in view and
   * Create task is always reachable.
   */
  max-height: calc(100dvh - (var(--nav-height) * 2) - var(--cell));
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.trail-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 18px 16px;
  border-bottom: 1px solid var(--border);
}
.trail-block:last-child {
  border-bottom: 0;
}
.trail-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 10px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--content-weak);
}
/* Not a control: the answer to "who owns this", when the answer is you. */
.trail-mark {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 7px;
  border: 1px solid var(--brand-text);
  color: var(--brand-text);
  font-size: 9px;
  letter-spacing: 0.1em;
}

/* One column, and that is the whole point of the rail: every control has the same left edge,
   the same right edge and the same height, however many of them there are. */
.trail-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.trail-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--content-weak);
}
.trail-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: var(--control-h);
  padding: 0 12px;
  border: 1px solid var(--input);
  background: var(--surface-base);
  color: var(--foreground);
  font-family: inherit;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  cursor: pointer;
}
.trail-btn:hover {
  background: var(--accent);
}
/* The action an agent takes ninety times in a hundred, and the only brand-marked control on
   the page other than Send. */
.trail-btn--take {
  border-color: var(--color-brand-green);
  color: var(--brand-text);
}
.trail-btn--take:hover {
  background: color-mix(in srgb, var(--color-brand-green) 12%, transparent);
}

/* A fact: a key that never wraps, and a value that ellipsizes with the whole of itself on
   the title attribute — so precision is a hover away rather than gone. */
/* Facts pack tighter than controls: they are read down the column in one pass, and the air
   a select needs around it only makes that list longer. */
.trail-prop {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
  line-height: 1.35;
}
.trail-prop + .trail-prop {
  margin-top: -2px;
}
.trail-k {
  flex: none;
  width: 80px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--content-weak);
}
.trail-v {
  flex: 1;
  min-width: 0;
  font-size: 11.5px;
  color: var(--content-base);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Tone is emphasis, never the signal: the value says "late by 2h" and "critical" in words,
   so the row survives a colour-blind reader, a screenshot and a monochrome print. */
.trail-v[data-tone='warn'] {
  color: var(--color-status-orange);
}
.trail-v[data-tone='bad'] {
  color: var(--danger-text);
}
.trail-note {
  margin: 0;
  font-size: 11px;
  line-height: 1.55;
  color: var(--content-weak);
}
/* The model is named, not hidden — an agent who disagrees with a classification can see what
   produced it without opening the database. */
.trail-note--by {
  font-size: 10px;
  letter-spacing: 0.04em;
}

/*
 * The two disclosures open in flow here rather than over the page: the rail has a column of
 * room under them, and a popover hanging off a sticky element is a popover that has to be
 * told about scrolling.
 */
.thread-rail .snooze,
.thread-rail .merge {
  position: static;
}
/*
 * Three weights in the rail, and they are the three kinds of thing in it: a filled field is
 * a property you set, a green edge is the action you take ninety times in a hundred, and a
 * hairline on nothing is an action you take rarely. Snooze and Merge are the third — they
 * were reading as two more fields with nothing to type in them.
 */
.thread-rail .snooze-btn,
.thread-rail .merge-btn {
  justify-content: center;
  width: 100%;
  height: var(--control-h);
  background: transparent;
  color: var(--content-weak);
}
.thread-rail .snooze-btn:hover,
.thread-rail .merge-btn:hover,
.thread-rail .snooze[open] .snooze-btn,
.thread-rail .merge[open] .merge-btn {
  background: var(--surface-base);
  color: var(--content-strong);
}
/* The actions are not the fields above them, and 4px of extra air says so without a rule. */
.trail-block > details:first-of-type {
  margin-top: 4px;
}
.thread-rail .snooze-panel,
.thread-rail .merge-panel {
  position: static;
  width: auto;
  min-width: 0;
  margin-top: 6px;
}
/* Five offsets in two columns rather than a five-row list: the same choice in a third of the
   rail. The hairlines between them are the 1px gaps, so no cell draws an edge against the
   panel's own border. */
.thread-rail .snooze-panel {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--border);
}
.thread-rail .snooze-opt {
  border-bottom: 0;
  background: var(--popover);
  text-align: center;
  font-size: 11px;
}
.thread-rail .snooze-opt:nth-child(odd):last-child {
  grid-column: 1 / -1;
}
.thread-rail .merge-panel .btn-new {
  width: 100%;
}

/*
 * Filing the ticket as engineering work, in the rail rather than as a popover.
 *
 * The control is the record page's, wearing the rail's geometry: full width, the same 36px
 * as every other control in the column, and a panel that opens in flow under it.
 */
.thread-rail .rec-esc {
  position: static;
}
.thread-rail .rec-esc-btn {
  justify-content: center;
  width: 100%;
  height: var(--control-h);
  padding: 0 12px;
  font-size: 10px;
  background: transparent;
}
.thread-rail .rec-esc-btn:hover,
.thread-rail .rec-esc[open] > .rec-esc-btn {
  background: var(--surface-base);
  color: var(--content-strong);
}
.thread-rail .rec-esc-panel {
  position: static;
  width: auto;
  margin-top: 6px;
  padding: 12px;
}
.thread-rail .rec-esc-actions {
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}
.thread-rail .rec-esc-actions .btn-new {
  width: 100%;
  order: -1;
}
.thread-rail .rec-esc-hint {
  text-align: center;
}
/* Once filed, the block holds the link to where it went — there is no second task to make. */
.thread-rail .rec-task {
  justify-content: center;
  width: 100%;
  height: var(--control-h);
  font-size: 10px;
}

/* ──────────────────────────────────────────────────────────────── messages */
.thread {
  flex: 1;
}
.msg {
  position: relative;
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 14px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--border);
}
/*
 * In the agent inbox the customer's turns are the ones worth finding, so those are tinted.
 * The requester's own page inverts it (see .rec-log): on your own ticket nearly every turn
 * is yours, so tinting yours tints the page and emphasises nothing.
 */
.thread .msg[data-author='customer'] {
  background: var(--surface-base);
}
/* .msg-main carries the measure, not .msg-body: the head is a space-between row, so capping
   the body alone leaves the timestamp hanging off the end of a line that stopped 300px
   earlier. Same rule, same reason, as the record's log. */
.doc--thread .msg-main {
  max-width: calc(var(--measure) - var(--log-gutter));
}
.doc--thread .msg-body {
  max-width: none;
}
.msg-avatar {
  width: 28px;
  height: 28px;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--muted);
  color: var(--content-base);
  font-size: 10px;
}
.msg-avatar--agent {
  border-radius: 0;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--foreground);
}
.msg-avatar--agent svg {
  height: 13px;
  width: auto;
}
.msg-main {
  min-width: 0;
}
.msg-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 10px;
}
.msg-who {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  min-width: 0;
}
.msg-name {
  font-size: 12px;
  font-weight: 600;
  /* An author is an email address or an id-orgn subject — one unbreakable 32-character token
     that, left alone, runs straight through the timestamp on the other side of the head. It
     wraps instead. */
  overflow-wrap: anywhere;
}
.role-you {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
.role-badge {
  display: inline-flex;
  align-items: center;
  height: 16px;
  padding: 0 5px;
  border: 1px solid var(--border);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
.unread-badge {
  display: inline-flex;
  align-items: center;
  height: 16px;
  padding: 0 5px;
  background: var(--color-brand-green);
  color: var(--on-brand);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.msg-when {
  font-size: 11px;
  color: var(--content-weak);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.msg-body {
  font-size: 13px;
  line-height: 1.75;
  color: var(--stream-text);
  max-width: 72ch;
}
.msg-body p {
  margin: 0 0 14px;
}
.msg-body p:last-child {
  margin-bottom: 0;
}
.msg-body code {
  background: var(--code-inline-bg);
  border: 1px solid var(--code-inline-border);
  padding: 1px 5px;
  font-size: 12px;
}
.msg-body pre {
  margin: 0 0 14px;
  padding: 14px 16px;
  overflow-x: auto;
  font-family: inherit;
  font-size: 12px;
  line-height: 20px;
  white-space: pre-wrap;
}
.msg-body pre span {
  display: block;
}
.msg-body pre.term {
  border: 1px solid var(--terminal-border);
  background: var(--terminal-background);
}
.msg-body pre.code {
  border: 1px solid var(--border);
  background: var(--surface-strong);
}
.msg-body .tl-cmd {
  color: var(--terminal-text-command);
}
.msg-body .tl-err {
  color: var(--terminal-text-error);
}
.msg-body .tl-mut {
  color: var(--terminal-text-muted);
}

/* ── attachments in a thread ───────────────────────────────────────────────────
 * A screenshot is shown at a size you can recognise an error in, held to a HEIGHT so a tall
 * phone screenshot cannot push the next entry off a chronology people scan. Everything else
 * stays a chip.
 */
.attach-set {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}
.attach-shots {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.attach-shot {
  display: flex;
  flex-direction: column;
  max-width: 320px;
  border: 1px solid var(--border);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  cursor: zoom-in;
}
/*
 * A fixed height, not a maximum.
 *
 * With a maximum, a wide screenshot and a tall one produce different image heights, the flex
 * row stretches both tiles to the taller one, and the caption bars end up at different heights
 * with dead space under the shorter one. Uniform tiles scan; ragged ones read as unfinished.
 * `top left` keeps the corner where an error message usually starts.
 */
.attach-shot img {
  display: block;
  width: 100%;
  height: 150px;
  object-fit: cover;
  object-position: top left;
  background: var(--muted);
}
.attach-shot:hover {
  border-color: var(--color-brand-green);
}
.attach-shot:focus-visible {
  outline: 2px solid var(--color-brand-green);
  outline-offset: -2px;
}
.attach-shot-bar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 10px;
  border-top: 1px solid var(--border);
}
.attach-shot-bar .attach-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
 * The expanded view. A native dialog, so Escape and focus are the platform's job.
 *
 * No shadow and no radius: this world builds depth from hairlines and background steps, and a
 * floating rounded card with a drop shadow is the helpdesk look the product is defined against.
 */
.lightbox {
  border: 1px solid var(--border);
  background: var(--background);
  color: var(--foreground);
  padding: 0;
  max-width: min(92vw, 1200px);
  max-height: 92vh;
}
.lightbox::backdrop {
  background: color-mix(in srgb, #000 78%, transparent);
}
.lightbox-img {
  display: block;
  max-width: min(92vw, 1200px);
  max-height: calc(92vh - 38px);
  object-fit: contain;
}
.lightbox-cap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 0;
  padding: 0 12px;
  height: 38px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--content-weak);
}
.lightbox-download {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.lightbox-close-form {
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
}
.lightbox-close {
  all: unset;
  cursor: pointer;
  display: flex;
  padding: 8px;
  color: #fff;
  background: color-mix(in srgb, #000 55%, transparent);
}
.lightbox-close:focus-visible {
  outline: 2px solid var(--color-brand-green);
  outline-offset: -2px;
}

.attach-chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--border);
  padding: 7px 11px;
  margin-top: 4px;
  color: var(--icon-weak-base);
}
.attach-name {
  font-size: 12px;
  color: var(--foreground);
}
.attach-size {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
  font-variant-numeric: tabular-nums;
}

/*
 * System events are a rule between messages, never another message — and they read down the
 * same column the messages do.
 *
 * They used to be centred text with a dashed line running to each edge. That is a decent
 * device in a 900px column and a bad one at 1900, where it stranded four words in the middle
 * of an empty band, a thousand pixels from the message above and the message below. Now the
 * mark sits in the avatar gutter, the text starts where a name starts, and the time ends
 * where a timestamp ends. Nothing about it says "message": no avatar, no surface, no body.
 */
.sys-rule {
  display: flex;
  align-items: baseline;
  padding: 8px 24px;
  border-bottom: 1px solid var(--border);
}
/* Consecutive events are one list, not a stack of bands. Only the last of a run carries the
   rule that separates the group from whatever was said next. */
.sys-rule:has(+ .sys-rule) {
  border-bottom: 0;
}
/* The avatar's column, so the event's glyph lands under the face of whoever spoke last. */
.sys-mark {
  flex: none;
  width: 28px;
  margin-right: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  color: var(--content-weak);
}
.sys-text {
  min-width: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--content-base);
}
/* Beside the event rather than ranged right down a column of its own: the time belongs to
   the sentence it ends, and a right-ranged column of stamps on a 1900px page is two things
   at opposite ends of an empty row. */
.sys-when {
  flex: none;
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--content-weak);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.sys-when::before {
  content: '·';
  padding: 0 7px;
}
/*
 * A system event that reports a FAILURE — today, a notification that did not go out.
 *
 * Agent inbox only; the requester's timeline never renders these. Louder than the rules
 * around it on purpose: every other event is something that simply happened, and this one is
 * something somebody has to do about. The word "FAILED" is in the text, so the tint is
 * emphasis rather than the signal itself.
 */
.sys-rule[data-tone='warn'] .sys-text,
.sys-rule[data-tone='warn'] .sys-mark,
.sys-rule[data-tone='warn'] .sys-when {
  color: var(--danger-text);
}
.sys-square {
  width: 5px;
  height: 5px;
  background: currentColor;
  display: inline-block;
}

/* ────────────────────────────────────────────────────────────── composer */
.composer {
  border-top: 1px solid var(--border);
  padding: 16px 24px 18px;
  background: var(--background);
}
.composer-box {
  border: 1px solid var(--input);
}
.composer-box textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  border: 0;
  background: transparent;
  padding: 12px 14px;
  font-family: inherit;
  font-size: 13px;
  line-height: 1.7;
  color: var(--foreground);
  resize: vertical;
  outline: none;
}
.composer-box textarea::placeholder {
  color: var(--content-weak);
}
.composer-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border-top: 1px solid var(--border);
  padding: 8px 10px 8px 14px;
}
.composer-bar-left, .composer-bar-right {
  display: flex;
  align-items: center;
  gap: 14px;
}
.composer-hint {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
.kbd-hint {
  display: flex;
  align-items: center;
  gap: 5px;
}

/* The consequence sits above the box the user is about to type in, not after they send.
   One solid frame in the status's own colour: the callout is the warning, so the whole
   edge carries it rather than a 2px stub on the left. `--tone` is set on the element by the
   view, which keeps the colour data-driven without hard-coding properties in the markup. */
.reopen-callout {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  border: 1px solid var(--tone, var(--border));
  padding: 13px 16px;
  margin-bottom: 12px;
}
.reopen-callout-mark {
  display: flex;
  color: var(--tone, var(--content-weak));
}
.reopen-callout p {
  margin: 0;
  flex: 1;
  font-size: 12px;
  line-height: 1.7;
  color: var(--content-base);
}
.reopen-callout strong {
  color: var(--content-strong);
  font-weight: 600;
}

/* ────────────────────────────────────────────────────────────────── form */
.doc--form form {
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 26px;
}
.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.field > label, .field-label-row label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--content-weak);
}
.field-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.field-aside {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
.req {
  color: var(--color-status-red);
  margin-left: 4px;
}
.field-err {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--destructive);
  animation: orgnUp 0.2s ease-out;
}
.orgn-input, .orgn-textarea, .orgn-select {
  width: 100%;
  border: 1px solid var(--input);
  /* The surface is what identifies a control now that its edge matches every other edge. */
  background: var(--surface-base);
  border-radius: 0;
  font-family: var(--app-font-family);
  font-size: 14px;
  line-height: 20px;
  color: var(--foreground);
}
.orgn-input, .orgn-select {
  height: var(--control-h);
  padding: 0 12px;
}
.orgn-textarea {
  padding: 10px 12px;
  resize: vertical;
}
.orgn-textarea.desc {
  font-size: 13px;
  line-height: 1.7;
  min-height: 200px;
}
.orgn-input::placeholder, .orgn-textarea::placeholder {
  color: var(--content-weak);
}
/* 12% of --input used to do this, which resolved to near-black once --input became the
   hairline. --surface-base is a real step and is the same one the request body uses. */
.dark .orgn-input, .dark .orgn-select, .dark .orgn-textarea {
  background: var(--surface-base);
}
.orgn-input[aria-invalid='true'], .orgn-textarea[aria-invalid='true'], .orgn-select[aria-invalid='true'] {
  border-color: var(--destructive);
}
.form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  align-items: center;
}

/*
 * Below this the rail cannot hold a label and its control on one line, so it stops being a
 * column and becomes a band of properties directly under the subject. Still one home for all
 * of them, still above the conversation, still the first thing after the title — and the
 * composer stays at the foot of the page where it belongs.
 */
@media (max-width: 1120px) {
  .thread-grid {
    display: flex;
    flex-direction: column;
  }
  .thread-main {
    border-right: 0;
  }
  .thread-rail {
    position: static;
    /*
     * Follows the conversation in BOTH source and view, as it already does below 900px.
     *
     * `order: -1` pulled the rail visually above the thread while the DOM kept it last, after
     * the composer — so between 901 and 1120px a keyboard user tabbed through the thread and the
     * reply box and only then jumped back up to the Owner/Status controls sitting at the top of
     * the page. That is a WCAG 2.4.3 focus-order and 1.3.2 meaningful-sequence mismatch, and it
     * existed only in this range: the <=900px block already places the rail after the
     * conversation with `order: 1`.
     */
    max-height: none;
    overflow: visible;
    /* The column's `align-self: start` is a cross-axis instruction, and the cross axis is
       now the horizontal one: left alone it shrinks the band to the width of its widest
       control and parks it against the left edge. */
    align-self: stretch;
    flex-direction: row;
    flex-wrap: wrap;
    /*
     * The gaps ARE the hairlines between cells, so no cell draws an edge that would land on
     * the page's own. Wrapping rather than a grid of tracks for the same reason: cells grow
     * to fill their row, so a fourth block does not leave two empty tracks with the strip's
     * hairline colour showing through them.
     */
    gap: 1px;
    background: var(--border);
    border-bottom: 1px solid var(--border);
  }
  .trail-block {
    flex: 1 1 232px;
    background: var(--background);
    border-bottom: 0;
  }
}

@media (max-width: 900px) {
  .doc {
    border-left: 0;
    border-right: 0;
  }
  .doc-title-block, .msg, .sys-rule, .composer {
    padding-left: 16px;
    padding-right: 16px;
  }
  .doc--form form {
    padding: 20px 16px 28px;
  }
  .trail-block {
    padding-left: 16px;
    padding-right: 16px;
  }
  /*
   * On a phone the properties go BELOW the conversation.
   *
   * Stacked in one column they are two screens of controls, and leading with them means an
   * agent who opened a ticket to read it has to scroll past every field before reaching the
   * first sentence. The header still carries the key and the status, which is the state you
   * need in order to decide to read at all; the rest waits until after the reply box.
   */
  .thread-rail {
    order: 1;
    border-top: 1px solid var(--border);
    border-bottom: 0;
  }
  /* A system event names an address and a provider error, which is far longer than a phone
     is wide. The mark stands down and the line wraps under itself rather than pushing the
     whole document sideways. */
  .sys-rule {
    flex-wrap: wrap;
  }
  .sys-mark {
    width: 16px;
    margin-right: 10px;
  }
}

/* Agents carry both hats; the switch belongs in the nav, not buried in a menu. */
.nav-inbox-link {
  width: auto;
  padding: 4px 10px;
  border: 1px solid var(--shell-border);
  color: var(--content-weak);
  text-decoration: none;
}
.nav-inbox-link:hover {
  color: var(--foreground);
  background: transparent;
}
.nav-inbox-link[data-active='true']::before {
  content: none;
}

/* An internal note must never be mistakable for something the requester can read.
   A full frame rather than a stub of colour down one edge: this is the highest-stakes
   distinction in the app, and it should read as a different kind of object from any angle,
   not as a list item wearing an accent. `outline` with a negative offset draws the hairline
   inside the box, so the note stays on the same grid as every other message. */
.msg[data-internal='true'] {
  background: color-mix(in srgb, var(--color-status-orange) 6%, transparent);
  outline: 1px solid var(--color-status-orange);
  outline-offset: -1px;
}
.role-badge--internal {
  border-color: var(--color-status-orange);
  color: var(--color-status-orange);
}
.internal-toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
/* ── the AI draft
   The composer, when what is in it was written by a model. The strip is attached to the top
   of the box rather than floating above it, so the claim "this text is a draft" cannot be
   read as belonging to anything else on the page — and it stays attached as the agent edits.
   Purple because no ticket status uses it: an agent should never have to work out whether
   this band is telling them about the draft or about the ticket. */
.composer-box[data-drafted='true'] {
  border-color: var(--color-status-purple);
}
.draft-strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 7px 14px;
  border-bottom: 1px solid var(--color-status-purple);
  background: color-mix(in srgb, var(--color-status-purple) 7%, transparent);
}
.draft-badge {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-status-purple);
}
.draft-note {
  font-size: 10px;
  letter-spacing: 0.02em;
  color: var(--content-base);
}

/* ══════════════════════════════════════════════════════════ the ticket record
   The requester's ticket page is a filed document with correspondence attached, not a chat
   log. Everything here exists to keep that distinction visible: a header that states the
   facts, the request itself as the body of the page, and replies below it in a log that
   reads as subordinate to the thing it is about. */
/*
 * Full-bleed, unlike the thread and the form.
 *
 * A 900px column centred in the content area left a 167px empty bordered strip hard against
 * the rail, which read as a second, blank sidebar — and it made the ticket page the only one
 * that was not full width, so opening a request from the list jumped the canvas. The surfaces
 * and rules now run edge to edge and the reading measure is held on the content instead,
 * which is what was actually protecting legibility.
 */
.doc--record {
  max-width: none;
  border-left: 0;
  border-right: 0;
}
/*
 * One measure, two edges.
 *
 * Full bleed was right and the way it was applied was not: the request wrapped at 74ch, the
 * replies at 72ch, the message heads at 920px and the composer at 920px, so on a 1440 window
 * the page had five different right-hand edges — 853, 835, 1192, 1150, 1440 — and none of
 * them lined up with another. Content filled half the column and the rest was not margin,
 * it was slack.
 *
 * The rule now is one sentence. Prose holds the measure; structure and controls span the
 * page. Everything the reader treats as part of a paragraph — the request, a reply, and the
 * timestamp that addresses it — shares one right edge, and everything that frames the
 * document — the status line, the section rules, the composer — runs to the page's.
 *
 * 576px because it has to serve two sizes: 68ch of the request's 14px and 74ch of a reply's
 * 13px, both inside the measure a monospace face can carry. It is also 24 × 24, so the
 * column lands on the same 24px inset every other edge on this page uses.
 */
.doc--record {
  --measure: 576px;
  /* The avatar column plus its gap. A reply starts here, so it has to end this much earlier
     for the two to finish on the same line. */
  --log-gutter: 42px;
}
.doc--record .rec-request-body {
  max-width: var(--measure);
}
/* .msg-main carries it, not .msg-body: the head is a space-between row, so capping the body
   alone leaves the timestamp hanging off the end of a line that stopped 300px earlier. */
.doc--record .rec-log .msg-main {
  max-width: calc(var(--measure) - var(--log-gutter));
}
.doc--record .rec-log .msg-body {
  max-width: none;
}
.doc--record .composer-box {
  max-width: none;
}

.rec-title-block {
  padding: 20px 24px 18px;
}
/* The subject is the one thing on this page that is not metadata, and at 20px it was the same
   size as a paragraph of the request below it. 24px is the size the new-request page already
   gives its own heading, so the pair now agree. */
.doc--record .doc-title {
  font-size: 24px;
  letter-spacing: -0.025em;
  line-height: 1.25;
}
.rec-title-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Support asks for a link to the ticket constantly. Hidden until the script confirms a
   clipboard exists, so it is never a control that does nothing. */
.rec-copy {
  appearance: none;
  height: 20px;
  padding: 0 9px;
  border: 1px solid var(--border);
  border-radius: 0;
  background: transparent;
  color: var(--content-weak);
  font-family: inherit;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.rec-copy:hover {
  color: var(--foreground);
  border-color: var(--input);
}
.rec-copy[data-copied] {
  border-color: var(--color-brand-green);
}
.rec-copy-done {
  display: none;
  color: var(--color-brand-green);
}
.rec-copy[data-copied] .rec-copy-idle {
  display: none;
}
.rec-copy[data-copied] .rec-copy-done {
  display: inline;
}

/* Status is an enum; this says whose move it is, which is the question people actually have.
   The status chip beside it already carries the colour, so this one stays quiet. */
.await {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 20px;
  padding: 0 9px;
  border: 1px dashed var(--border);
  color: var(--content-weak);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.await-dot {
  width: 5px;
  height: 5px;
  border-radius: 9999px;
  background: var(--color-brand-green);
  animation: orgnLive 2.4s ease-in-out infinite;
}

/* The record's facts as a status line: one row of cells divided by hairlines, wrapping only
   when the window is genuinely too narrow. Roughly a fifth of the height of the table it
   replaced, and it scans left-to-right in one pass instead of four. */
.rec-meta {
  margin: 0;
  display: flex;
  flex-wrap: nowrap;
  border-top: 1px solid var(--border);
}
/*
 * Every cell grows and every cell shrinks, so the strip is exactly the width of the page at
 * any size — no short row stopping mid-air, no one oversized cell mopping up the slack, and
 * never a second line. Spare width is shared out; when there is none, values ellipsize
 * (labels never do) and the title attribute keeps the full text reachable.
 */
.rec-meta-item {
  flex: 1 1 auto;
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
  padding: 8px 11px;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.rec-meta-item:first-child {
  padding-left: 24px;
}
.rec-meta-item:last-child {
  border-right: 0;
}
.rec-meta-k {
  flex: none;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--content-weak);
  white-space: nowrap;
}
.rec-meta-v {
  margin: 0;
  min-width: 0;
  font-size: 12px;
  color: var(--content-base);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Section rule: label, dashed line to the margin, meta on the right. The page's structural
   grammar — it is what makes "request ends here, correspondence starts here" readable
   without parsing five near-identical cards. */
.rec-rule {
  display: flex;
  align-items: center;
  gap: 14px;
  height: 34px;
  padding: 0 24px;
  border-bottom: 1px solid var(--border);
}
.rec-rule-label {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-shrink: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--foreground);
}
.rec-rule-label::before {
  content: '';
  width: 5px;
  height: 5px;
  background: currentColor;
}
.rec-rule-line {
  flex: 1;
  height: 1px;
  border-top: 1px dashed var(--border);
}
.rec-rule-meta {
  flex-shrink: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
  font-variant-numeric: tabular-nums;
}
.rec-rule-new {
  color: var(--color-brand-green);
}

/* The request. Tinted and set larger than the log, with no avatar and no name badge — the
   header above already says who filed it and when. */
/* Three steps of surface, and they carry the hierarchy: the request sits highest, agent
   replies one step down, your own replies flat on the page. `--surface-base` is a whisper
   against white, so the request takes the stronger of the two. */
.rec-request {
  padding: 24px 24px 26px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-strong);
}
.rec-request .rec-request-body {
  font-size: 14px;
  line-height: 1.85;
  color: var(--foreground);
}
.rec-request .rec-request-body p {
  margin: 0 0 16px;
}
.rec-request .rec-request-body p:last-child {
  margin-bottom: 0;
}

/* Takes the slack in the column so the composer sits at the foot of a short ticket rather
   than floating halfway up with dead space beneath it. */
.rec-log {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}
/* Inverted from the inbox: here the agent's replies are what you came to read. */
.rec-log .msg[data-author='agent'] {
  background: var(--surface-base);
}
/* A spine down the avatar gutter, so the replies read as one running log rather than a
   stack of unrelated cards. Stops at the last reply — a stub under the final avatar reads
   as a rendering fault. */
.rec-log .msg::before {
  content: '';
  position: absolute;
  left: 37.5px;
  top: 54px;
  bottom: 0;
  width: 1px;
  background: var(--border);
}
.rec-log .msg:last-of-type::before {
  display: none;
}
/* Never a shadow: the unread mark is a rule on the edge, laid over the padding so nothing
   reflows when a reply arrives. */
.rec-log .msg[data-unread='true']::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: -1px;
  width: 2px;
  background: var(--color-brand-green);
}
.rec-log .msg:target {
  background: color-mix(in srgb, var(--color-brand-green) 9%, transparent);
}
.msg-when:hover {
  color: var(--foreground);
  text-decoration: none;
}

/* Where the first reply will be: the log's own text column, hard under the Activity rule.
   Centred in the space, it read as a notice adrift in a few hundred pixels of nothing; sat on
   the line reply one will occupy, the same blank space reads as a log with nothing in it. */
.rec-empty {
  padding: 26px 24px 34px calc(24px + var(--log-gutter));
  border-bottom: 1px solid var(--border);
}
.rec-empty-title {
  margin: 0 0 9px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--content-base);
}
.rec-empty-desc {
  margin: 0;
  max-width: calc(var(--measure) - var(--log-gutter));
  font-size: 12px;
  line-height: 1.75;
  color: var(--content-weak);
}

/* Shown instead of the auto-refresh when there is a draft in the composer — reloading the
   page under someone who is mid-sentence throws their reply away. */
.rec-stale {
  margin: 0;
  padding: 11px 24px;
  border-top: 1px solid var(--color-brand-green);
  background: color-mix(in srgb, var(--color-brand-green) 10%, transparent);
  font-size: 12px;
  line-height: 1.7;
  color: var(--content-base);
}
.rec-stale[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .await-dot {
    animation: none !important;
  }
}

/*
 * The strip's own breakpoint, wider than the page's, and measured rather than picked.
 *
 * Re-measured when visibility joined the strip as an eighth cell. Eight cells need 1329px of
 * content — visibility is the widest of them at 268px, because it carries a button as well as
 * a value — and with the 206px rail that is a 1535px window before anything has to give. The
 * switch sits at 1600 so there is ~65px of headroom for a long team or project name, the same
 * slack the seven-cell strip was given at 1240.
 *
 * Below it, squeezing all eight values to two characters and an ellipsis is not a status line,
 * it is a puzzle — so it becomes a labelled list, which is what that width wanted anyway.
 * Above it, nothing ever truncates.
 */
@media (max-width: 1600px) {
  .rec-meta {
    flex-direction: column;
  }
  .rec-meta-item {
    flex: 0 0 auto;
    border-right: 0;
  }
  .rec-meta-item:last-child {
    border-bottom: 0;
  }
  .rec-meta-k {
    width: 86px;
  }
}

@media (max-width: 900px) {
  .rec-title-block,
  .rec-request,
  .rec-rule,
  .rec-empty,
  .rec-stale {
    padding-left: 16px;
    padding-right: 16px;
  }
  .rec-meta-item {
    padding: 7px 16px;
  }
  .rec-meta-item:first-child {
    padding-left: 16px;
  }
  .rec-log .msg::before {
    left: 29.5px;
  }
  /* Every phone browser has share-and-copy-link built in, and here the button only ever
     wraps onto a line of its own. The escalate control stays: an agent triaging from a phone
     still needs to be able to file the task. */
  .rec-copy {
    display: none;
  }
}

/* ─────────────────────────────────────────────── escalation to an ORGN Studio task */
.rec-esc {
  position: relative;
}
.rec-esc > summary {
  list-style: none;
  cursor: pointer;
}
.rec-esc > summary::-webkit-details-marker,
.rec-esc > summary::marker {
  display: none;
  content: '';
}
.rec-esc-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 20px;
  padding: 0 9px;
  border: 1px solid var(--border);
  color: var(--content-weak);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.rec-esc-btn:hover,
.rec-esc[open] > .rec-esc-btn {
  color: var(--foreground);
  border-color: var(--input);
}
.rec-esc-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  width: 340px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 14px;
  border: 1px solid var(--border);
  background: var(--background);
}
.rec-esc-lede {
  margin: 0 0 4px;
  font-size: 11px;
  line-height: 1.6;
  color: var(--content-weak);
}
.rec-esc-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
.rec-esc-panel .orgn-input,
.rec-esc-panel .orgn-select {
  height: var(--control-h-sm);
  font-size: 12px;
}
.rec-esc-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 5px;
}
.rec-esc-hint {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}

/* Once filed, the control is replaced by where it went — there is no second task to create. */
.rec-task {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 20px;
  padding: 0 9px;
  border: 1px solid var(--color-brand-green);
  color: var(--color-brand-green);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.rec-task:hover {
  background: color-mix(in srgb, var(--color-brand-green) 12%, transparent);
  text-decoration: none;
}
.rec-task--flat {
  border-color: var(--border);
  color: var(--content-weak);
}

.rec-notice {
  margin: 0;
  padding: 10px 24px;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  line-height: 1.7;
  color: var(--content-base);
}
/* Banded top and bottom rather than tipped with colour on the left: this is a full-bleed
   bar in a page whose whole grammar is horizontal rules, so the tone belongs on the rules
   it already has. */
.rec-notice[data-tone] {
  border-top: 1px solid var(--tone);
  border-bottom-color: var(--tone);
  background: color-mix(in srgb, var(--tone) 9%, transparent);
}
.rec-notice[data-tone='good'] {
  --tone: var(--color-brand-green);
}
.rec-notice[data-tone='warn'] {
  --tone: var(--color-status-orange);
}

@media (max-width: 900px) {
  .rec-notice {
    padding-left: 16px;
    padding-right: 16px;
  }
  /* The panel is wider than a phone; pin it to the viewport rather than the button. */
  .rec-esc-panel {
    position: fixed;
    left: 12px;
    right: 12px;
    top: auto;
    width: auto;
  }
}

/* ══════════════════════════════════════════════════════ record: seams and weight
   Fixes found by looking at the live page rather than the markup. */

/* The double rule under the metadata: .doc-head drew a bottom border and every meta cell drew
   one too, so the two stacked into a 2px seam. The strip owns its own edges now. */
.doc--record .doc-head {
  border-bottom: 0;
}
.doc--record .rec-meta {
  border-bottom: 1px solid var(--border);
}
.doc--record .rec-meta-item {
  border-bottom: 0;
}

/* Whose move it is, said once. Dashed is reserved for `snoozed` on the status chip, where it
   is the colourblind-safe second signal; spending it here diluted it to decoration. */
.await {
  border-style: solid;
}

/* The log still takes the slack — the composer belongs at the foot of the page, not floating
   halfway up it — and the slack now falls below the notice rather than around it. */
.rec-empty {
  margin-bottom: auto;
  border-bottom: 0;
}

/* The composer was the only bright-bordered object on the page. It keeps the contrast the
   control needs and gains the state that earns it: the whole box responds when you type in
   it, instead of an outline appearing on a textarea that has no visible edge of its own. */
.composer-box {
  transition: border-color 0.15s ease;
}
.composer-box:focus-within {
  border-color: var(--color-brand-green);
}
.composer-box textarea:focus-visible {
  outline: none;
}

/* Stacked, the cells still need dividers between them — the strip's own top and bottom
   edges only bound the group. */
@media (max-width: 1600px) {
  .doc--record .rec-meta-item + .rec-meta-item {
    border-top: 1px solid var(--border);
  }
}

/* The reply line moves in with the messages: 16px inset plus the avatar gutter, which the
   log keeps at every width. */
@media (max-width: 900px) {
  .rec-empty {
    padding-left: calc(16px + var(--log-gutter));
  }
}

/* ════════════════════════════════════════════════════════════════ audit remediation */

/*
 * P0 — the account cell was pushed off a 375px viewport entirely, and because .shell is
 * overflow:hidden that means gone, not scrollable: no account menu and no way to sign out
 * on a phone. The brand may now shrink, the account cell may never, and the agent-inbox
 * link stands down because the account menu already carries it one tap away.
 */
@media (max-width: 900px) {
  .nav-brand {
    min-width: 0;
    overflow: hidden;
  }
  .nav-brand svg {
    height: 13px;
  }
  .nav-cell--avatar {
    flex-shrink: 0;
  }
  .nav-inbox-link {
    display: none;
  }
  .nav-actions {
    gap: 6px;
    padding: 0 8px;
  }
}

/* P2 — WCAG 2.5.8 wants 24px on a target. The chips were 20, and lifting only the
   interactive ones would have split a row that just got its geometry unified. */
.status-chip,
.await,
.rec-copy,
.rec-esc-btn,
.rec-task,
.role-badge,
.unread-badge {
  height: 24px;
}
.msg-when {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

/* P1 — the green as text. Fill uses stay on the brand value with --on-brand ink. */
.rec-rule-new,
.rec-task {
  color: var(--brand-text);
}
.rec-task {
  border-color: var(--brand-text);
}
.user-menu-chip--plan {
  color: var(--brand-text);
  border-color: var(--brand-text);
}

/* P2 — the section rules are the record's structure, so they are headings, not divs. */
.rec-rule-label {
  margin: 0;
  font-weight: inherit;
}

/* The remaining light-theme text that used a fill colour: green and red both need their
   text variants here, and the agent tag was the last brand-green-on-white holdout. */
.user-menu-tag--agent {
  color: var(--brand-text);
  border-color: var(--brand-text);
}
.user-menu-item--danger {
  color: var(--danger-text);
}
.field-err {
  color: var(--danger-text);
}

/* ══════════════════════════════════════════════════════ new request
   The page's only hard part is the description; everything else is a dropdown. So the form
   takes the reading measure and the width that used to sit empty carries the two things the
   form cannot say for itself — what happens after Send, and whether this is a report an agent
   can actually act on. */
.doc--form {
  max-width: 1100px;
  /* No side borders: the column's left edge landed exactly on the rail's right edge at any
     width where it filled the content area. The section rules carry the structure. */
  border-left: 0;
  border-right: 0;
}
.doc--form form {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 0;
}
.form-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 288px;
  align-items: start;
  min-height: 100%;
}
.form-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 100%;
  border-right: 1px solid var(--border);
}

.form-head {
  padding: 30px 30px 26px;
}
.form-title {
  margin: 0 0 10px;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.2;
}
.form-lede {
  margin: 0;
  max-width: 58ch;
  font-size: 13px;
  line-height: 1.8;
  color: var(--content-weak);
}

.form-alert {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  margin: 0 30px 6px;
  padding: 12px 14px;
  border: 1px solid var(--destructive);
  background: color-mix(in srgb, var(--destructive) 8%, transparent);
  color: var(--destructive);
}
.form-alert p {
  margin: 0;
  font-size: 12px;
  line-height: 1.7;
  color: var(--content-base);
}

.form-section {
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 24px 30px 26px;
}
.form-section--pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 22px 24px;
}

.select-shell {
  position: relative;
  display: flex;
}
.select-shell select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 36px;
  width: 100%;
}
.select-shell svg {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--icon-weak-base);
}
.select-shell:focus-within svg {
  color: var(--foreground);
}
.field-desc {
  margin: 0;
  font-size: 11px;
  line-height: 1.6;
  color: var(--content-weak);
}

.textarea-shell {
  position: relative;
  display: flex;
}
.textarea-shell textarea {
  width: 100%;
}
.textarea-hint {
  position: absolute;
  right: 10px;
  bottom: 8px;
  padding: 2px 6px;
  background: var(--background);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--content-weak);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.textarea-shell:focus-within .textarea-hint {
  opacity: 1;
}

.fence-offer {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 26px;
  padding: 0 10px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--content-base);
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  animation: orgnUp 0.18s ease-out;
}
.fence-offer:hover {
  color: var(--foreground);
  background: var(--surface-base);
}
.fence-offer[hidden] {
  display: none;
}

.form-actions {
  margin-top: auto;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  padding: 16px 30px;
  border-top: 1px solid var(--border);
}
.form-actions-note {
  margin-right: auto;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
  transition: color 0.2s ease;
}
.form-actions-note[data-ready='true'] {
  color: var(--brand-text);
}

/* ─────────────────────────────────────────────────────────────── the rail */
.form-rail {
  position: sticky;
  top: 0;
  display: flex;
  flex-direction: column;
}
.rail-block {
  padding: 26px 24px;
  border-bottom: 1px solid var(--border);
}
.rail-title {
  margin: 0 0 16px;
  font-size: 10px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--content-weak);
}

/* A sequence, so it is numbered by the browser and the order carries the meaning. */
.rail-steps {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}
.rail-steps li {
  position: relative;
  padding: 0 0 18px 22px;
  counter-increment: step;
}
.rail-steps li:last-child {
  padding-bottom: 0;
}
.rail-steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 1px;
  font-size: 10px;
  color: var(--content-weak);
  font-variant-numeric: tabular-nums;
}
/* The thread between the steps, the same device the activity log uses. */
.rail-steps li:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 17px;
  bottom: 4px;
  width: 1px;
  background: var(--border);
}
.rail-step-k {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--foreground);
  margin-bottom: 3px;
}
.rail-step-v {
  display: block;
  font-size: 11px;
  line-height: 1.65;
  color: var(--content-weak);
}

/* The four signals are a meter, not a to-do list. Four empty checkboxes on an optional
   panel read as four things you have failed to do; a bar that is one quarter full reads as
   a quarter of the way there, which is the same fact told the way round that gets the fifth
   sentence typed. */
.rail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 11px;
}
.rail-head .rail-title {
  margin-bottom: 0;
}
.rail-score {
  flex-shrink: 0;
  font-size: 10px;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  color: var(--content-weak);
  transition: color 0.2s ease;
}
.rail-score b {
  font-weight: 600;
  color: var(--content-base);
  transition: color 0.2s ease;
}
.rail-score[data-on='true'],
.rail-score[data-on='true'] b {
  color: var(--brand-text);
}

/* Auto-flow rather than a hardcoded four, so the bar follows the list if it ever changes. */
.rail-meter {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 4px;
  margin-bottom: 11px;
}
.rail-meter-seg {
  position: relative;
  height: 3px;
  background: var(--border);
  overflow: hidden;
}
.rail-meter-seg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--brand-text);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.34s cubic-bezier(0.33, 1, 0.68, 1);
}
.rail-meter-seg[data-on='true']::after {
  transform: scaleX(1);
}
/* Staggered so it reads as one bar advancing rather than four lights coming on. */
.rail-meter-seg:nth-child(2)::after {
  transition-delay: 0.05s;
}
.rail-meter-seg:nth-child(3)::after {
  transition-delay: 0.1s;
}
.rail-meter-seg:nth-child(4)::after {
  transition-delay: 0.15s;
}
.rail-meter[data-complete='true'] .rail-meter-seg::after {
  box-shadow: 0 0 9px -2px var(--color-brand-green);
}

.rail-verdict {
  margin: 0 0 16px;
  font-size: 11px;
  line-height: 1.6;
  color: var(--content-weak);
  transition: color 0.2s ease;
}
.rail-verdict[data-level='4'] {
  color: var(--brand-text);
}

.rail-checks {
  margin: 0 0 14px;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.rail-check {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 11px;
  line-height: 1.55;
  color: var(--content-weak);
  transition: color 0.2s ease;
}
.rail-check-mark {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  margin-top: 1px;
  color: transparent;
  transition: color 0.2s ease;
}
/* Unmet is a dot rather than an empty box: the box is the part that asks to be ticked. */
.rail-check-mark::before {
  content: '';
  position: absolute;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--content-weak);
  opacity: 0.45;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.rail-check[data-on='true'] {
  color: var(--content-base);
}
.rail-check[data-on='true'] .rail-check-mark {
  color: var(--brand-text);
}
.rail-check[data-on='true'] .rail-check-mark::before {
  opacity: 0;
  transform: scale(0.3);
}
/* The one worth adding next, pointed at once instead of four times. */
.rail-check[data-next='true'] {
  color: var(--content-base);
}
.rail-check[data-next='true'] .rail-check-mark::before {
  opacity: 1;
  transform: scale(1.3);
}
.rail-note {
  margin: 0;
  font-size: 10px;
  line-height: 1.7;
  color: var(--content-weak);
}

@media (prefers-reduced-motion: reduce) {
  .fence-offer {
    animation: none;
  }
  .rail-meter-seg::after,
  .rail-check-mark,
  .rail-check-mark::before {
    transition: none;
  }
}

/* The rail is guidance, not chrome: below this it goes under the form rather than beside it,
   and it never competes with the fields for a narrow screen's width. */
@media (max-width: 1000px) {
  .form-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .form-main {
    border-right: 0;
  }
  .form-rail {
    position: static;
    border-top: 1px solid var(--border);
  }
  .rail-block:last-child {
    border-bottom: 0;
  }
}

@media (max-width: 900px) {
  .form-head,
  .form-section,
  .form-actions {
    padding-left: 16px;
    padding-right: 16px;
  }
  .form-alert {
    margin-left: 16px;
    margin-right: 16px;
  }
  .rail-block {
    padding-left: 16px;
    padding-right: 16px;
  }
  .form-section--pair {
    grid-template-columns: 1fr;
  }
}

/* ────────────────────────────────── tags, bulk selection, presence */
.row-tags { display: inline-flex; gap: 5px; margin-left: 6px; }
.tag-chip {
  display: inline-flex; align-items: center; height: 14px; padding: 0 5px;
  border: 1px solid var(--border); font-size: 9px; letter-spacing: 0.06em;
  color: var(--content-weak); text-transform: none;
}
/*
 * Pinned to the BOTTOM of the queue, not the top.
 *
 * The table's header row is sticky — eleven column labels scrolling away leaves eleven columns
 * of unlabelled text — and two elements pinned to the same top edge is one of them covering
 * the other. Rising over the rows it acts on is also where the hand already is.
 */
.bulk-bar {
  position: sticky; bottom: 0; z-index: 16;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 14px; border-top: 1px solid var(--border); background: var(--surface-strong);
}
/* A bare `display: flex` outranks the UA's `[hidden] { display: none }`, so the bar has to be
   told to stay down while nothing is selected. Same guard the other hidden-by-default blocks
   here carry. */
.bulk-bar[hidden] {
  display: none;
}
.bulk-count { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--content-weak); }
.bulk-count b { color: var(--content-strong); font-variant-numeric: tabular-nums; }
.bulk-tag { display: inline-flex; gap: 6px; align-items: center; }
.bulk-tag .orgn-input { width: 130px; height: var(--control-h-sm); font-size: 11px; }
/*
 * Somebody else is on this ticket. Before the thread, not beside the composer: the point is to
 * be seen before an answer is written rather than after.
 */
.viewers {
  margin: 0; padding: 9px 24px; border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--color-status-orange) 7%, transparent);
  font-size: 11.5px; color: var(--content-strong);
}
/* ─────────────────────────────────────────────────────── priority
 * Only urgent and high are ever shown: a chip on every row is wallpaper, and `low` is
 * information for the sort rather than for the eye. Outlined, so it does not compete with the
 * filled "needs response" beside it. Colour is never the whole signal -- the word is the chip.
 */
.prio-pill {
  display: inline-flex; align-items: center; height: 16px; padding: 0 5px;
  border: 1px solid currentColor; font-size: 9px; text-transform: uppercase;
  letter-spacing: 0.1em; flex-shrink: 0;
}
.prio-pill[data-p='urgent'] { color: var(--danger-text); }
.prio-pill[data-p='high'] { color: var(--color-status-orange); }
/* ─────────────────────────────────────────────────────────── snooze */
.snooze { position: relative; }

/* ─────────────────────────────────────────────────────── merged duplicates */
.merge-banner {
  margin: 0; padding: 11px 24px; border-bottom: 1px solid var(--border);
  background: var(--surface-base); font-size: 12px; line-height: 1.55; color: var(--content-base);
}
.merge-banner a { color: var(--link); }
.merge-banner--absorbed { background: transparent; border-bottom: 1px dashed var(--border); }
.merge-banner--error { background: color-mix(in srgb, var(--danger-text) 8%, transparent); color: var(--content-strong); }
.merge { position: relative; }
.snooze-btn,
.merge-btn {
  display: inline-flex; align-items: center; height: var(--control-h-sm); padding: 0 10px;
  border: 1px solid var(--border); background: var(--surface-base); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--content-base);
  cursor: pointer; list-style: none; user-select: none;
}
.snooze-btn::-webkit-details-marker { display: none; }
.snooze-btn:hover, .snooze[open] .snooze-btn { color: var(--content-strong); border-color: var(--content-weak); }
.snooze-panel {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 20; min-width: 150px;
  border: 1px solid var(--border); background: var(--popover); display: flex; flex-direction: column;
}
.snooze-opt {
  padding: 8px 12px; border: 0; border-bottom: 1px solid var(--border); background: transparent;
  text-align: left; font: inherit; font-size: 12px; color: var(--content-base); cursor: pointer;
}
.snooze-opt:last-child { border-bottom: 0; }
.snooze-opt:hover, .snooze-opt:focus-visible { background: var(--surface-strong); color: var(--content-strong); }

/* ────────────────────────────────────────────── saved replies (agent inbox)
 * A popover over the composer bar, not a dropdown in it: the useful thing to show is the
 * first line of each answer, which a <select> cannot render. Square, flat, hairlined —
 * structure from borders and background steps, never shadow.
 */
.saved-replies {
  position: relative;
}
.saved-replies-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: var(--control-h-sm);
  padding: 0 10px;
  border: 1px solid var(--border);
  background: var(--surface-base);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-base);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.saved-replies-btn::-webkit-details-marker {
  display: none;
}
.saved-replies-btn:hover {
  color: var(--content-strong);
  border-color: var(--content-weak);
}
.saved-replies[open] .saved-replies-btn {
  border-color: var(--content-weak);
  color: var(--content-strong);
}
.saved-replies-panel {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  z-index: 20;
  width: min(420px, 70vw);
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--border);
  background: var(--popover);
}
.saved-reply {
  display: block;
  width: 100%;
  padding: 9px 12px;
  border: 0;
  border-bottom: 1px solid var(--border);
  background: transparent;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.saved-reply:last-child {
  border-bottom: 0;
}
.saved-reply:hover,
.saved-reply:focus-visible {
  background: var(--surface-strong);
}
.saved-reply-title {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ────────────────────────────────────────── closing your own request
 * Sits above the composer, framed as an aside rather than a primary action: most requests are
 * closed by an agent, and this must read as a shortcut for the person who no longer needs one
 * — never as the thing the page wants you to do.
 */
.resolve-own {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 14px 24px;
  border-top: 1px solid var(--border);
  border-bottom: 1px dashed var(--border);
  background: var(--surface-base);
}
.resolve-own-main {
  min-width: 0;
}
.resolve-own-lede {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--content-strong);
}
.saved-reply-shortcut {
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--content-weak);
  border: 1px solid var(--border);
  padding: 0 4px;
}
/*
 * Two lines of the answer itself, which is what makes this quicker than remembering titles.
 * Clamped rather than truncated to one line: a canned reply's first sentence is usually the
 * greeting, and the second is the one that tells you whether this is the right template.
 */
/*
 * Clamped to two lines only when the script that reads the full body is running.
 *
 * inbox.ts documents the no-JS floor as "every body is selectable text, so the fallback is
 * copy and paste rather than a dead control" — but the clamp hid everything past the greeting
 * line, and the full text lived only in the data-body attribute the script reads. So for any
 * saved reply longer than two lines the documented fallback did not exist. The picker's script
 * marks the panel when it binds, so with no JS the body is simply readable.
 */
.saved-reply-preview {
  margin-top: 3px;
  font-size: 11px;
  line-height: 1.5;
  color: var(--content-weak);
  white-space: pre-wrap;
}
/* The clamp itself, and only the clamp, waits for the script that can reveal the full body. */
[data-js] .saved-reply-preview {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/*
 * A meta cell reporting something that has gone wrong — today, a first reply past its target.
 *
 * The value carries the word "overdue", so this is emphasis and not the signal. Applied to the
 * value only: the label still reads as one of a row of quiet keys, and tinting the pair would
 * make the whole cell look like an error rather than a fact about one of them.
 */
.rec-meta-item[data-tone='late'] .rec-meta-v {
  color: var(--color-status-orange);
}

/* The consequence, stated before the control and not in a toast afterwards. */
.resolve-own-desc {
  margin: 3px 0 0;
  font-size: 11px;
  line-height: 1.5;
  color: var(--content-weak);
  max-width: 62ch;
}
/*
 * The one question. Brand-tinted because it only ever appears on a good outcome — the request
 * is closed — and it is the single place in the portal that is allowed to sound pleased.
 */
.resolve-ask {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 12px 24px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--color-brand-green) 6%, transparent);
}

/* ─────────────────────────────────────── who else can read this request
 * A cell of the meta strip rather than a band under it: who can read a ticket is one of the
 * ticket's facts, and it is answered where the reader already looks for facts. The value names
 * the audience, the button names the move — so the cell reads "Private · Share with team" and
 * never spends a line of the page on a sentence.
 */
.rec-meta-item--vis {
  /* The button must survive the squeeze; only the audience name ellipsizes. */
  overflow: visible;
}
.rec-meta-v--vis {
  display: flex;
  align-items: center;
  gap: 8px;
  overflow: visible;
}
.vis-state {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Shared is the state worth spotting from across the strip; private is the quiet default. */
.rec-meta-item--vis[data-shared='true'] .vis-state {
  color: var(--content-strong);
}
.vis-form {
  flex: none;
  display: flex;
}
/*
 * Quieter than a ghost button, because the strip is a status line and eight framed controls
 * would read as a toolbar. It earns its border on hover and focus, where it is about to be
 * used — until then it is a word you can click.
 */
/*
 * 24px, the WCAG 2.5.8 minimum the P2 pass applied to every comparable chip and missed here.
 * Un-sharing a ticket is exactly the control a touch user must not have to stab at.
 */
.vis-btn {
  height: 24px;
  padding: 0 6px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--content-weak);
  font-family: inherit;
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.vis-btn:hover {
  color: var(--content-strong);
  border-color: var(--border);
}
.vis-btn:focus-visible {
  outline: none;
  color: var(--content-strong);
  border-color: var(--color-brand-green);
}
.resolve-ask-q {
  margin: 0;
  font-size: 12px;
  color: var(--content-strong);
}
.resolve-ask-actions {
  display: flex;
  gap: 8px;
}

/* ─────────────────────────────────────────── live incident, above the new-request form
 * Above the form and not in the rail: during an incident the reader is already typing, and a
 * notice they scroll past is a notice that did not happen.
 *
 * Warning hue, hairline, square — the same grammar as every other surface here. It is a link,
 * not a dialog: it informs and gets out of the way, and it never blocks filing.
 */
.incident {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 13px 16px;
  margin-bottom: 20px;
  border: 1px solid var(--color-status-orange);
  background: color-mix(in srgb, var(--color-status-orange) 7%, transparent);
  color: var(--content-strong);
  text-decoration: none;
}
.incident:hover {
  background: color-mix(in srgb, var(--color-status-orange) 12%, transparent);
}
.incident > svg {
  flex: none;
  margin-top: 2px;
  color: var(--color-status-orange);
}
.incident-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}
.incident-lede {
  font-size: 12.5px;
  font-weight: 600;
}
.incident-desc {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--content-base);
}
.incident-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-status-orange);
  white-space: nowrap;
}
/*
 * Under 480px the status column goes, and the `needs response` pill stays.
 *
 * The pill is 98px and does not shrink, by design — it carries the WORD so the state survives
 * a colourblind reader. In a 187px cell that left the ticket's own name 53px: seven
 * characters, three rows reading "New Ti…", "Blocke…", "Incorr…" under three identical pills.
 * A row whose loudest element is a label common to half the queue, next to a name nobody can
 * read, has its priorities exactly backwards.
 *
 * Dropping status rather than the pill keeps colour from becoming the only signal, and status
 * is the one of the two already visible on the ticket you are about to open.
 */
@media (max-width: 480px) {
  .qt th.qt-status, .qt td.qt-status { display: none; }
}
@media (max-width: 640px) {
  .incident {
    flex-wrap: wrap;
  }
}

/* ─────────────────────────────────────── signed-out intake (/new, /new/sent)
 * Its own chrome. The portal shell is a nav, a rail of filters and a list — none of which
 * exists for somebody who is not signed in and cannot read anything. Same tokens, same square
 * hairlined grammar, one column.
 */
.pub-shell {
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: var(--background);
}
.pub-nav {
  display: flex;
  align-items: center;
  height: 47px;
  padding: 0 24px;
  border-bottom: 1px solid var(--shell-border);
}
.pub-nav .nav-brand svg {
  height: 15px;
  width: auto;
  color: var(--content-strong);
}
.pub-main {
  padding: 40px 24px 64px;
}
.pub-doc {
  max-width: 620px;
  margin: 0 auto;
}
.pub-doc--narrow {
  max-width: 480px;
}
.pub-head {
  margin-bottom: 26px;
}
.pub-title {
  margin: 0 0 10px;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--content-strong);
}
.pub-lede {
  margin: 0;
  font-size: 13px;
  line-height: 1.65;
  color: var(--content-base);
  max-width: 60ch;
}
.pub-signin {
  display: inline-block;
  margin-top: 14px;
  font-size: 12px;
  color: var(--link);
}
/* The key is the only reference this person is given, so it is the loudest thing on the page. */
.pub-key {
  margin: 14px 0;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--brand-text);
  font-variant-numeric: tabular-nums;
}
.pub-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.pub-next {
  margin: 18px 0 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--content-base);
}
.pub-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 26px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}
.pub-note {
  flex: 1;
  min-width: 260px;
  margin: 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--content-weak);
}
/*
 * The honeypot. Off-screen rather than display:none — some bots skip hidden inputs, and the
 * point is that they fill it in. aria-hidden and tabindex="-1" keep it away from anyone real.
 */
.pub-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
.pub-foot {
  display: flex;
  gap: 18px;
  padding: 14px 24px;
  border-top: 1px solid var(--shell-border);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.pub-foot a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--content-weak);
  text-decoration: none;
}
.pub-foot a:hover {
  color: var(--content-strong);
}
/* ─────────────────────────────────────────────────────────── attachments */
/*
 * The same height as every other control on the form.
 *
 * A file input is padding plus a button plus a filename, so left to itself it measured 50px
 * against the 36px of the inputs and selects above it — the one control on the page with its
 * own idea of how tall a control is. `--control-h` and centred content put it back in the row.
 */
.orgn-file {
  display: flex;
  align-items: center;
  width: 100%;
  height: var(--control-h);
  padding: 0 10px;
  border: 1px solid var(--input);
  background: var(--surface-base);
  color: var(--content-base);
  font: inherit;
  font-size: 12px;
}
/* ── the dropzone ─────────────────────────────────────────────────────────────
 * One rectangle is the click target, the drop target and the focus target, because the input
 * is stretched across it. Dashed hairline, square, flat: the same border vocabulary the
 * off-hours notice and the share-with-team block use, so it reads as a region of this form
 * rather than a widget bolted onto it.
 */
.dropzone {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 22px 16px;
  border: 1px dashed var(--input);
  text-align: center;
  transition: border-color 0.12s ease-out, background 0.12s ease-out;
}
/*
 * `inset: 0` alone, deliberately. Adding width/height 100% resolves them against the PADDING
 * box while the insets stretch to the border box, and the explicit sizes win — leaving a band
 * of the zone that looks clickable and is not.
 */
.dropzone-input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}
.dz-icon {
  display: flex;
  color: var(--icon-weak-base);
}
.dz-lede {
  margin: 0;
  font-size: 12px;
  color: var(--content-base);
}
.dz-browse {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.dz-note {
  margin: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
}
/* Hover, keyboard focus and a dragged file are the same invitation, so they look the same. */
.dropzone:hover,
.dropzone:focus-within,
.dropzone[data-drag='true'] {
  border-color: var(--color-brand-green);
  background: color-mix(in srgb, var(--color-brand-green) 5%, transparent);
}
.dropzone[data-drag='true'] {
  border-style: solid;
}
/* Once something is attached the zone stops advertising and the list below carries it. */
.dropzone[data-has='true'] {
  padding: 13px 16px;
}
.dropzone[data-has='true'] .dz-note {
  display: none;
}

/*
 * What is attached, once a script is there to say so.
 *
 * Empty and absent by default — with no JavaScript the file input stands alone exactly as it
 * did, and `:empty` keeps the gap from appearing before anything is picked.
 */
/*
 * The composer as a drop target.
 *
 * No dashed panel here: a reply is one box, and a second labelled block under it would make
 * answering look like filing all over again. The box it already has lights up instead, which
 * is the same signal the dropzone gives without adding a second thing to look at.
 */
.composer-box[data-drag='true'] {
  border-color: var(--color-brand-green);
  background: color-mix(in srgb, var(--color-brand-green) 5%, transparent);
}
/* Inside the box, above the bar: what is attached reads before Send does. */
.attach-list--composer {
  margin: 0 10px 8px;
}

.attach-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.attach-list:empty {
  display: none;
}
.attach-row {
  display: flex;
  align-items: center;
  gap: 12px;
  border: 1px solid var(--border);
  padding: 7px 10px;
  animation: orgnUp 0.2s ease-out;
}
.attach-row[data-state='err'] {
  border-color: var(--destructive);
  color: var(--destructive);
}
/* The picked file, before it is sent. Square, hairline, same vocabulary as the thread tile. */
.attach-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  overflow: hidden;
  color: var(--icon-weak-base);
}
.attach-row[data-state='err'] .attach-thumb {
  border-color: currentColor;
  color: inherit;
}
.attach-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.attach-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.attach-row-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.attach-row .attach-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attach-row[data-state='err'] .attach-name {
  color: inherit;
}
.attach-row-note {
  font-size: 11px;
  line-height: 1.5;
  color: var(--content-weak);
}
.attach-row[data-state='err'] .attach-row-note {
  color: inherit;
}
.attach-remove {
  all: unset;
  cursor: pointer;
  display: flex;
  padding: 3px;
  flex-shrink: 0;
  color: var(--icon-weak-base);
}
.attach-remove:hover {
  color: var(--foreground);
}
.attach-row[data-state='err'] .attach-remove {
  color: inherit;
}
/* Matches the other focus outlines in this file; #85 retunes them all together. */
.attach-remove:focus-visible {
  outline: 2px solid var(--color-brand-green);
}

/* Reference material, so it steps back from the sentence it follows. */
.attach-limits {
  display: block;
  margin-top: 4px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--content-weak);
}
.orgn-file::file-selector-button {
  margin-right: 10px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  background: var(--surface-strong);
  color: var(--content-strong);
  font: inherit;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  cursor: pointer;
}
.orgn-file:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
}
/*
 * The reply composer's attach control: a label wrapping a hidden input, so it sits inline in
 * the bar at the same weight as the "replying keeps this open" hint. A full file input there
 * would be the widest thing on the row and would read as the primary action.
 */
.composer-attach {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
  cursor: pointer;
}
.composer-attach:hover {
  color: var(--content-strong);
}
.composer-attach input[type='file'] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.composer-attach:focus-within {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
/* A chip that can be downloaded says so by behaving like a link; the flat one is unchanged. */
.attach-chip--link {
  text-decoration: none;
  color: inherit;
}
.attach-chip--link:hover {
  border-color: var(--content-weak);
  color: var(--content-strong);
}
.attach-chip--link:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
/* ────────────────────────────────── suggested documentation
 * Hidden until something matches. A panel that always shows three links teaches people to
 * ignore it; the value is entirely in it being rare.
 *
 * Rows, not a list of links. The first shape was a blue link with a loose grey sentence under
 * it, three times over, and it read as one paragraph you had to parse to find the boundaries
 * of — with the closing note absorbed into the third description — which is the opposite of a
 * glance on a panel whose whole job is to be glanced at. Each page is now a row with a
 * hairline under it and a target you can hit anywhere: the area it belongs to, what it is
 * called, and two lines of what it covers. Colour is not doing the work; structure is, the
 * same way it does everywhere else here.
 */
.rail-block--docs[hidden] {
  display: none;
}
.docs-hide {
  align-self: center;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Bigger than it looks: the negative margin buys a 26px target without moving the title. */
  width: 26px;
  height: 26px;
  margin: -7px -7px -7px 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--content-weak);
  cursor: pointer;
  opacity: 0.65;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.docs-hide:hover {
  opacity: 1;
  color: var(--foreground);
}

.docs-list {
  /* Pulled wider than the text column so a hovered row reads as a row and not as a highlight
     drawn around a sentence — still clear of the block's own edges, so its hairlines cannot be
     mistaken for the ones that divide the rail into blocks. */
  margin: 0 -10px;
  padding: 0;
  list-style: none;
}
.docs-item + .docs-item {
  border-top: 1px solid var(--border);
}
.docs-link {
  position: relative;
  display: block;
  /* Room on the right for the arrow, which is out of flow so that a title wrapping to two
     lines cannot push it around. */
  padding: 9px 28px 10px 10px;
  color: var(--content-strong);
  text-decoration: none;
}
.docs-link:hover,
.docs-link:focus-visible {
  background: var(--surface-strong);
}
/* A page you have already opened stops competing with the two you have not. Set on the link
   itself rather than on the title inside it — colour on the anchor is what :visited is
   allowed to change, and the title inherits it. */
.docs-link:visited {
  color: var(--content-weak);
}
.docs-crumb {
  display: block;
  margin-bottom: 3px;
  font-size: 9px;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--content-weak);
}
.docs-name {
  display: block;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  color: inherit;
}
.docs-link:hover .docs-name {
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* Two lines. The index writes descriptions of wildly different lengths, and one page that
   happens to have a paragraph should not push the other two out of sight. */
.docs-desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  margin-top: 2px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--content-weak);
}
.docs-go {
  position: absolute;
  top: 12px;
  right: 9px;
  display: flex;
  color: var(--icon-weak-base);
  opacity: 0.5;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.docs-link:hover .docs-go,
.docs-link:focus-visible .docs-go {
  opacity: 1;
  transform: translate(1px, -1px);
}
/* The note is advice about the list, not a fourth entry in it. Under three grey descriptions
   at nearly the same size, only a rule says so. */
.rail-block--docs .rail-note {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px dashed var(--border);
}

/* Arriving rather than appearing: the panel writes itself in beside a field somebody is
   typing in, and a hard pop there is read as a jump. */
@keyframes docs-in {
  from {
    opacity: 0;
    transform: translateY(-3px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.docs-item {
  animation: docs-in 0.24s cubic-bezier(0.33, 1, 0.68, 1) both;
}
.docs-item:nth-child(2) {
  animation-delay: 0.05s;
}
.docs-item:nth-child(3) {
  animation-delay: 0.1s;
}

/* Below this the rail sits under the form at full width, on the narrowest screens there are,
   where a block padded to 16px and rows pulled 10px wider than that would put a focus ring
   4px off the edge of the phone. Stated here rather than in the layout block above because
   these rules are written after it, and a media query does not outrank source order. */
@media (max-width: 900px) {
  .docs-list {
    margin-left: -6px;
    margin-right: -6px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .docs-item {
    animation: none;
  }
  .docs-go,
  .docs-hide {
    transition: none;
  }
}
/* ─────────────────────────────────────────────────────── desk report
 * One column of panels in the order the questions get asked. Same square hairlined grammar as
 * the rest of the desk; nothing here is a dashboard widget.
 */
.report { padding: 24px; display: flex; flex-direction: column; gap: 28px; max-width: 860px; }
.report-band { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.report-title { margin: 0; font-size: 18px; font-weight: 600; color: var(--content-strong); }
.report-range { display: flex; gap: 0; border: 1px solid var(--border); }
.report-range a {
  padding: 4px 12px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--content-weak); text-decoration: none; border-right: 1px solid var(--border);
}
.report-range a:last-child { border-right: 0; }
.report-range a[data-active='true'] { background: var(--surface-strong); color: var(--content-strong); }
.report-block { display: flex; flex-direction: column; gap: 12px; }
.report-h {
  margin: 0; font-size: 10px; text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--content-weak); font-weight: 600;
}
.report-note { margin: 0; font-size: 11.5px; line-height: 1.55; color: var(--content-weak); max-width: 62ch; }

.stats { display: flex; flex-wrap: wrap; gap: 0; border: 1px solid var(--border); }
.stat {
  flex: 1 1 150px; min-width: 0; padding: 12px 14px;
  border-right: 1px solid var(--border); display: flex; flex-direction: column; gap: 4px;
}
.stat:last-child { border-right: 0; }
.stat-k { font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--content-weak); }
.stat-v { font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--content-strong); }
/* Semantic, and never the only signal: the label always says what the number is. */
.stat[data-tone='good'] .stat-v { color: var(--brand-text); }
.stat[data-tone='warn'] .stat-v { color: var(--color-status-orange); }
.stat[data-tone='bad'] .stat-v { color: var(--danger-text); }

.ages { display: flex; flex-wrap: wrap; gap: 0; border: 1px solid var(--border); }
.age {
  flex: 1 1 90px; padding: 9px 12px; border-right: 1px solid var(--border);
  display: flex; align-items: baseline; gap: 8px;
}
.age:last-child { border-right: 0; }
.age-n { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--content-strong); }
.age-k { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--content-weak); }
.age[data-empty='true'] .age-n { color: var(--content-weak); }

.spark { width: 100%; height: 96px; border: 1px solid var(--border); background: var(--surface-base); }
.spark-key { display: flex; align-items: center; gap: 16px; margin: 0; font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--content-weak); }
.key { display: inline-flex; align-items: center; gap: 6px; }
.key::before { content: ''; width: 10px; height: 2px; }
.key--filed::before { background: var(--color-status-purple); }
.key--resolved::before { background: var(--brand-text); }
.spark-note { margin-left: auto; }

.report-table { width: 100%; border-collapse: collapse; border: 1px solid var(--border); }
.report-table th {
  text-align: left; padding: 8px 12px; font-size: 9.5px; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--content-weak);
  border-bottom: 1px solid var(--border); background: var(--surface-base);
}
.report-table td {
  padding: 8px 12px; font-size: 12px; border-bottom: 1px solid var(--border);
  font-variant-numeric: tabular-nums; color: var(--content-base);
}
.report-table tr:last-child td { border-bottom: 0; }
.report-table td:first-child { color: var(--content-strong); }
.report-empty { color: var(--content-weak); font-variant-numeric: normal; }
@media (max-width: 700px) { .report { padding: 16px; } .stat { flex-basis: 120px; } }

.share-team {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border: 1px dashed var(--border);
  font-size: 12px;
  line-height: 1.55;
  color: var(--content-base);
  cursor: pointer;
}
.share-team input {
  margin-top: 2px;
  flex: none;
  accent-color: var(--color-brand-green);
}
.share-team strong {
  color: var(--content-strong);
}
.merge-btn::-webkit-details-marker { display: none; }
.merge-btn:hover, .merge[open] .merge-btn { color: var(--content-strong); border-color: var(--content-weak); }
.merge-panel {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 20; width: 260px; padding: 12px;
  border: 1px solid var(--border); background: var(--popover);
  display: flex; flex-direction: column; gap: 8px;
}
.merge-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--content-weak); }
/* The consequence, before the button, per the house rule. */
.merge-hint { margin: 0; font-size: 11px; line-height: 1.5; color: var(--content-weak); }

/* ═══════════════════════════════════════════════════════ the queue table
 *
 * The agent inbox as a table.
 *
 * It replaced a stack of two-line rows because the work at this screen is COMPARISON — which
 * of these has waited longest, who owns which, what is urgent, what came in by email — and
 * comparison needs columns. A stack can only be read one item at a time, so every question
 * that spans the queue was answered by scrolling and remembering.
 *
 * Same house rules as everything else: 1px hairlines, square corners, flat surfaces, no zebra
 * striping. Structure comes from the grid the columns already are.
 */
.qt {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
  font-variant-numeric: tabular-nums;
}

/* Widths live on the header cells, not in a colgroup — a colgroup maps by POSITION, so
   hiding one cell at a narrow viewport slides every width after it one column left. */
.qt th.qt-pick { width: 34px; }
.qt th.qt-key { width: 72px; }
.qt th.qt-req { width: 168px; }
.qt th.qt-status { width: 104px; }
.qt th.qt-prio { width: 74px; }
.qt th.qt-owner { width: 106px; }
.qt th.qt-via { width: 66px; }
.qt th.qt-msgs { width: 52px; }
.qt th.qt-wait { width: 72px; }
.qt th.qt-upd { width: 104px; }
/* .qt-subj takes whatever is left, which is the only column whose content is worth the room. */

.qt thead th {
  position: sticky;
  top: 0;
  z-index: 12;
  height: 29px;
  padding: 0;
  text-align: left;
  vertical-align: middle;
  font-weight: 500;
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
  white-space: nowrap;
  /* Opaque, or the rows read straight through the header as it passes over them. */
  background: var(--surface-strong);
  border-bottom: 1px solid var(--border);
}
/* border-collapse is `separate` for exactly this: a collapsed border belongs to the table
   rather than to the cell, and does not travel with a sticky one. */

.qt-sort {
  display: flex;
  align-items: center;
  gap: 5px;
  height: 29px;
  padding: 0 10px;
  color: inherit;
  text-decoration: none;
}
.qt-sort:hover {
  color: var(--content-strong);
  background: color-mix(in srgb, var(--foreground) 5%, transparent);
}
.qt-sort:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
}
/*
 * One channel per state, and they must stay tellable apart.
 *
 * Hover moves the FILL and nothing else. The sorted column is the mark: absent when idle, a
 * hint of one under the cursor, and the brand colour at full strength on the column actually
 * in force — so a hovered header can never outshout the sorted one.
 */
.qt-sort-mark {
  display: inline-flex;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.qt-sort:hover .qt-sort-mark { opacity: 0.45; }
.qt-sort[data-active='true'] { color: var(--content-strong); }
/* --brand-text, not the vivid fill. #00d492 is 1.94:1 on white, and this mark is the only
   thing on the page that says which column the order came from — WCAG 1.4.11 wants 3:1 of a
   graphic that carries meaning. The token collapses back to the vivid green in dark. */
.qt-sort[data-active='true'] .qt-sort-mark {
  opacity: 1;
  color: var(--brand-text);
}

/*
 * 37px rows: 6px of padding either side of the tallest thing a cell can hold.
 *
 * That tallest thing is the status chip, which is 24px because WCAG 2.5.8 wants 24 on a target
 * and the chips were unified on that height deliberately. Row height is therefore not a free
 * dial — 6px is the smallest padding that still reads as a gap rather than a collision, and
 * anything below it buys pixels back from the one measurement that was set on purpose.
 */
.qt tbody td {
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 11.5px;
  line-height: 17px;
  color: var(--content-base);
  vertical-align: middle;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- row states. Exactly the three the stacked rows carried, in the same order of rank:
   overdue beats unread, and a selected row beats both because it is about to be acted on. */
.qt-row:hover td {
  background: color-mix(in srgb, var(--foreground) 3%, transparent);
}
.qt-row[data-unread='true'] td {
  background: color-mix(in srgb, var(--color-brand-green) 5%, transparent);
}
.qt-row[data-unread='true']:hover td {
  background: color-mix(in srgb, var(--color-brand-green) 9%, transparent);
}
.qt-row[data-overdue='true'] td,
.qt-row[data-unread='true'][data-overdue='true'] td {
  background: color-mix(in srgb, var(--color-status-orange) 4%, transparent);
}
.qt-row[data-overdue='true']:hover td,
.qt-row[data-unread='true'][data-overdue='true']:hover td {
  background: color-mix(in srgb, var(--color-status-orange) 8%, transparent);
}
.qt-row[data-overdue='true'] .row-dot { background: var(--color-status-orange); }
.qt-row[data-overdue='true'] .qt-wait-v { color: var(--color-status-orange); }
/*
 * Selected. The fill moves and the checkbox on the leading edge is already the marker, so
 * this stays one extra channel rather than three — no ring, no bar, no weight change.
 *
 * The `:hover` form is written out because the plain one LOST without it, and lost silently.
 * `.qt-row:has(input[name='seq']:checked) td` weighs (0,3,2); the row-state rules above reach
 * (0,4,1) once an unread-and-overdue row is hovered, so running the cursor across rows you had
 * just ticked repainted them orange and the selection fill vanished under the pointer. Adding
 * `:hover` here takes it to (0,4,2), which beats that on the element count rather than on
 * source order — the same reason overdue is written out against unread further up.
 */
.qt-row:has(input[name='seq']:checked) td,
.qt-row:has(input[name='seq']:checked):hover td {
  background: color-mix(in srgb, var(--color-brand-green) 13%, transparent);
}

/* ---- cells */
.qt th.qt-pick, .qt td.qt-pick { padding: 0; }
.qt-pick-hit {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 29px;
  height: 100%;
  cursor: pointer;
}
.qt-pick input { accent-color: var(--color-brand-green); margin: 0; }
/* `[hidden]` is a UA rule and loses to any author `display:`. The select-all ships hidden and
   is revealed by the script that gives it behaviour, so it has to actually stay down. */
.qt-pick input[hidden] { display: none; }

.qt-link { color: inherit; text-decoration: none; }
.qt-link:hover { text-decoration: underline; }
.qt-key .qt-link {
  color: var(--content-weak);
  font-size: 10.5px;
  letter-spacing: 0.04em;
}
.qt-subj-line {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}
.qt-subj-link {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--content-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.qt-req { color: var(--content-weak); }
.qt-msgs { text-align: right; }
.qt th.qt-msgs .qt-sort { justify-content: flex-end; }
.qt-owner-name { color: var(--content-base); }
.qt-via { color: var(--content-weak); }
.qt-upd { color: var(--content-weak); font-size: 10.5px; }
/* Nothing to say. The weak token and no opacity on top of it: half-alpha text took this to
   1.8:1 on the light theme, and an em dash IS the cell's value — it is not decoration. */
.qt-nil { color: var(--content-weak); }

/* Priority as a column rather than a pill: no border, because a row of eleven cells does not
   need a box drawn round one of them to find it. Colour only on the two that mean "move
   this up", and the word is always there for a reader who cannot see the colour. */
.prio-cell {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--content-weak);
}
.prio-cell[data-p='urgent'] { color: var(--danger-text); }
.prio-cell[data-p='high'] { color: var(--color-status-orange); }

/* ------------------------------------------------- toolbar: filters + readouts
 *
 * One row, two groups: the controls that narrow the queue, then the readouts that describe
 * what they did.
 *
 * The five selects had a band of their own below this one. It cost a ticket and a half of
 * vertical space to hold what fits on the line above it, and it put the search box on a
 * different line from the five things it composes with. The rail still keeps the handful of
 * views worth a single click; this is the rest — status × priority × owner × source × state
 * is six hundred combinations, and a rail listing those is not a rail.
 */
.toolbar-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  flex: 1 1 auto;
  min-width: 0;
}
.toolbar-readout {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* Narrower than the portal's, which is alone on its row and can afford 280. Here it shares the
   line with five selects, and the placeholder is a hint rather than the label. */
.toolbar-controls .toolbar-search {
  min-width: 0;
  flex: 0 1 190px;
}
/*
 * The five axes are ONE control with five cells, not five controls.
 *
 * Separately bordered and content-sized they measured 99, 113, 184, 197 and 197 — and each
 * one resized when it was used, because a select is as wide as whatever is chosen in it.
 * Picking "Pending customer" grew the status box by 85px and shoved the rest of the row,
 * readouts included. A row of controls that moves when you use it is the defect; equal cells
 * in a fixed frame is the fix, and it costs less width than the ragged version did.
 *
 * One border and one surface around the group, hairlines between the cells. Structure from
 * 1px rules and a background step, same as everything else here.
 */
.qfilter-group {
  display: flex;
  align-items: stretch;
  flex: 1 1 520px;
  min-width: 0;
  max-width: 720px;
  height: var(--control-h-sm);
  border: 1px solid var(--input);
  background: var(--surface-base);
}
.qf {
  position: relative;
  display: flex;
  align-items: stretch;
  flex: 1 1 0;
  min-width: 0;
}
.qf + .qf { border-left: 1px solid var(--border); }
.toolbar-controls .select-shell { flex: 1; min-width: 0; }
/*
 * Transparent and unbordered: the GROUP is the control now, and a box inside a box is two
 * edges saying one thing. `--surface-base` moved up to the group for the same reason — the
 * base rule puts a surface on a select precisely so it reads as a control, and an earlier
 * pass here overrode it to `--background`, which in dark is the page itself and left these
 * as outlines drawn on nothing.
 */
.toolbar-controls .orgn-select {
  width: 100%;
  min-width: 0;
  height: 100%;
  padding: 0 21px 0 9px;
  border: 0;
  background: transparent;
  font-size: 11px;
  color: var(--content-base);
  text-overflow: ellipsis;
}
.toolbar-controls .select-shell svg { right: 6px; }
.toolbar-controls .orgn-select:focus-visible { outline-offset: -2px; }
/*
 * Which cells are narrowing the queue.
 *
 * One idiom, three parts of the same statement: a marker on the group's own edge, a fill, and
 * the value promoted out of the weak tone the placeholders sit in. Hover moves the FILL and
 * nothing else, so a cell under the cursor can never outshout a cell that is actually on.
 */
.qf:hover { background: color-mix(in srgb, var(--foreground) 5%, transparent); }
.qf[data-on='true'] { background: color-mix(in srgb, var(--color-brand-green) 9%, transparent); }
.qf[data-on='true']::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--brand-text);
}
.qf[data-on='true'] .orgn-select { color: var(--content-strong); }
.qf[data-on='true'] .select-shell svg { color: var(--brand-text); }
.qf[data-on='true']:hover { background: color-mix(in srgb, var(--color-brand-green) 14%, transparent); }
.qf-apply { height: var(--control-h-sm); font-size: 10px; }
.qf-clear {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
  text-decoration: none;
  white-space: nowrap;
}
.qf-clear:hover { color: var(--foreground); text-decoration: underline; }

/* A chip that goes somewhere. The overdue count and the active sort are both links now —
   the count filters to what it counted, the sort chip drops itself. */
a.toolbar-chip { text-decoration: none; }
a.toolbar-chip:hover { color: var(--foreground); border-color: var(--content-weak); }
.toolbar-chip--sort {
  border-style: solid;
  color: var(--content-strong);
  text-transform: none;
  letter-spacing: 0.04em;
}
.toolbar-chip--sort svg { opacity: 0.75; }

/*
 * Narrow viewports drop columns rather than crushing them, least useful first: how it arrived
 * and how many messages, then the requester's address, then the owner. Key, subject, status
 * and the clock are the four an agent triages on and they never leave.
 */
@media (max-width: 1360px) {
  .qt th.qt-via, .qt td.qt-via,
  .qt th.qt-msgs, .qt td.qt-msgs { display: none; }
}
@media (max-width: 1180px) {
  .qt th.qt-req, .qt td.qt-req { display: none; }
}
@media (max-width: 1000px) {
  .qt th.qt-owner, .qt td.qt-owner { display: none; }
  .qt th.qt-prio, .qt td.qt-prio { display: none; }
}
@media (max-width: 820px) {
  .qt th.qt-upd, .qt td.qt-upd { display: none; }
  .qt tbody td { padding-left: 8px; padding-right: 8px; }
  .qt-sort { padding: 0 8px; }
  /* Two up rather than five down. Stacked, the filters were 225px of a phone screen — more
     than the queue they filter — which is a control taking the room from its own subject. */
  .toolbar-controls .toolbar-search { flex: 1 1 100%; }
  /* The group comes apart. Five cells sharing 340px is five slivers, and a segmented control
     narrower than the words in it is not one. */
  .qfilter-group {
    flex: 1 1 100%;
    max-width: none;
    height: auto;
    flex-wrap: wrap;
    gap: 6px;
    border: 0;
    background: transparent;
  }
  .qf {
    flex: 1 1 calc(50% - 3px);
    height: var(--control-h-sm);
    border: 1px solid var(--input);
    background: var(--surface-base);
  }
  .qf + .qf { border-left: 1px solid var(--input); }
}
@media (max-width: 640px) {
  /* The last column to go. Overdue still says so twice on the row itself — the tint and the
     `no reply` pill — and at this width the subject was narrower than the status chip. */
  .qt th.qt-wait, .qt td.qt-wait { display: none; }
  .qt th.qt-key { width: 62px; }
  .qt th.qt-status { width: 92px; }
}

/* ══════════════════════════════════════════ bulk confirmation
 *
 * A large status move, shown before it is made.
 *
 * A page rather than a browser confirm, for the reason every other page here is a document:
 * it survives scripting being off, and a dialog cannot say the thing worth saying — which of
 * the rows will actually move and how many of them write to Slack.
 */
.confirm-wrap {
  flex: 1;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 48px 32px;
}
.confirm-box {
  display: flex;
  flex-direction: column;
  gap: 18px;
  border: 1px solid var(--border);
  background: var(--surface-base);
  padding: 28px 32px;
  width: 100%;
  max-width: 620px;
}
.confirm-title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.confirm-facts {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--content-base);
}
.confirm-facts b {
  color: var(--content-strong);
  font-variant-numeric: tabular-nums;
}
/* The count that is hardest to take back, because somebody else is reading that channel.
   The word carries it as well as the colour — the hue is emphasis, never the message. */
.confirm-loud {
  color: var(--color-status-orange);
}
.confirm-loud b {
  color: var(--color-status-orange);
}
/* A `details`, so the list is available without a script and folded away by default: the
   numbers are the decision, and thirty subjects above the button bury it. */
.confirm-list > summary {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--content-weak);
  cursor: pointer;
}
.confirm-list > summary:hover {
  color: var(--foreground);
}
.confirm-list ul {
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  max-height: 260px;
  overflow-y: auto;
  border-top: 1px solid var(--border);
}
.confirm-list li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--content-base);
}
.confirm-key {
  flex-shrink: 0;
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--content-weak);
  font-variant-numeric: tabular-nums;
}
.confirm-from {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--content-weak);
}
.confirm-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}
