/* ==========================================================================
   PFC 2.0 — APP SHELL + COMPONENT STYLES  (Plan §11.6, WP-2.3)
   Consumes ONLY tokens from tokens.css (no raw hex/px/font here except layout
   primitives). Every selector maps to a styleguide component or the app shell.
   ========================================================================== */

/* ---- Base / light reset ------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: var(--color-primary); text-decoration: none; }
a:hover { text-decoration: underline; }
button { font: inherit; }
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ---- Icons -------------------------------------------------------------- */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.icon {
  width: 18px; height: 18px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  vertical-align: middle;
}

/* ==========================================================================
   APP SHELL LAYOUT
   ========================================================================== */
.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  min-height: 100vh;
}

/* Sidebar state has THREE values, not two.
     auto    — server default: expanded on desktop, off-canvas-closed on small
     open    — the operator opened it
     closed  — the operator collapsed it
   "auto" exists so the DEFAULT can differ by viewport without JavaScript having
   to run before first paint. The server renders one attribute for every screen;
   with only open/closed it had to pick, it picked "open", and every phone load
   landed with the nav overlaying the page. */
/* ONE column when collapsed, not a zero-width one.

   .app-shell has three children — .sidebar, .sidebar-backdrop, .app-main — and a
   `display: none` grid item is not a grid item: it is removed from the grid and
   takes no track. Collapsed on desktop, BOTH .sidebar (hidden just below) and
   .sidebar-backdrop (hidden outside the small-screen media query) are gone, which
   leaves .app-main as the only in-flow child. Auto-placement therefore puts it in
   the FIRST track. With `0 1fr` that track is 0px wide: the whole app renders into
   a zero-width column, overflowing to min-content — roughly 100px of wrapped text
   with the topbar crushed beside it. `1fr` is the only track, and it is the page.

   The template is what makes this subtle: the count of children the grid actually
   has is not the count in the markup, and it changes with the state. Anything that
   adds a fourth child to .app-shell has to re-check this. */
@media (min-width: 901px) {
  .app-shell[data-sidebar-state="closed"] { grid-template-columns: 1fr; }
  .app-shell[data-sidebar-state="closed"] .sidebar { display: none; }
}
.app-main { display: flex; flex-direction: column; min-width: 0; }

/* ---- Sidebar ------------------------------------------------------------ */
.sidebar {
  position: sticky; top: 0;
  height: 100vh;
  display: flex; flex-direction: column;
  background: var(--color-sidebar-bg);
  color: var(--color-sidebar-fg);
  border-right: 1px solid var(--color-border);
  overflow-y: auto;
  z-index: 30;
}
.sidebar-brand {
  display: flex; align-items: center; gap: var(--space-2);
  height: var(--topbar-height); flex: none;
  padding: 0 var(--space-4);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.sidebar-brand-mark { font-size: var(--text-lg); line-height: 1; }
.sidebar-brand-text {
  font-weight: var(--weight-bold); letter-spacing: 0.02em;
  color: var(--color-sidebar-fg-strong); font-size: var(--text-md);
  /* The wordmark is now interpolated (config.APP_NAME) rather than written with a literal
     &nbsp;, so the no-wrap has to live here — a two-word name must not break across lines. */
  white-space: nowrap;
}
/* Visible at every width — collapse works on desktop too, not just off-canvas.
   This used to carry `display: none`, which never applied: .icon-btn below sets
   `display: inline-flex`, same specificity, later in the file. The control was
   therefore ALWAYS on screen while the collapse CSS existed only under the
   900px media query — a button that did nothing on desktop. */
.sidebar-hide { margin-left: auto; color: var(--color-sidebar-fg); }

.sidebar-nav { padding: var(--space-4) var(--space-3); display: flex; flex-direction: column; gap: var(--space-5); }
.nav-group-label {
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--color-sidebar-fg); opacity: 0.6;
  padding: 0 var(--space-3) var(--space-2);
  font-weight: var(--weight-semibold);
}
.nav-list { display: flex; flex-direction: column; gap: 2px; }
.nav-item {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-sidebar-fg);
  font-size: var(--text-base); font-weight: var(--weight-medium);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.nav-item:hover { background: var(--color-sidebar-hover-bg); color: var(--color-sidebar-fg-strong); text-decoration: none; }
.nav-item.is-active {
  background: var(--color-sidebar-active-bg);
  color: var(--color-sidebar-fg-strong);
  box-shadow: inset 2px 0 0 var(--color-primary);
}
.nav-item-icon { opacity: 0.9; }
.nav-item-label { flex: 1; }
.nav-badge {
  min-width: 20px; height: 20px; padding: 0 6px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  font-variant-numeric: var(--numeric);
  color: var(--color-primary-contrast); background: var(--color-primary);
  border-radius: var(--radius-full);
}

/* ---- Top bar ------------------------------------------------------------ */
.topbar {
  position: sticky; top: 0; z-index: 20;
  height: var(--topbar-height); flex: none;
  display: flex; align-items: center; gap: var(--space-3);
  padding: 0 var(--space-5);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}
/* Same story as .sidebar-hide: the `display: none` here never won against
   .icon-btn. Now deliberate — it is how you bring a collapsed sidebar back. */
.topbar-menu { }
.breadcrumbs { min-width: 0; }
.breadcrumb-list { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1); font-size: var(--text-base); }
.breadcrumb-item { display: flex; align-items: center; color: var(--color-text-muted); }
.breadcrumb-item + .breadcrumb-item::before { content: "/"; margin: 0 var(--space-2); color: var(--color-text-subtle); }
.breadcrumb-item [aria-current="page"] { color: var(--color-text); font-weight: var(--weight-medium); }

.topbar-search {
  position: relative; margin-left: auto;
  display: flex; align-items: center; width: min(320px, 34vw);
}
.topbar-search-icon { position: absolute; left: var(--space-3); color: var(--color-text-subtle); }
.topbar-search input {
  width: 100%;
  padding: var(--space-2) var(--space-3) var(--space-2) calc(var(--space-6) + var(--space-2));
  font: inherit; color: var(--color-text);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.topbar-search input:disabled { cursor: not-allowed; opacity: 0.7; }

.topbar-actions { display: flex; align-items: center; gap: var(--space-2); margin-left: var(--space-2); }
.topbar-user { display: flex; align-items: center; gap: var(--space-2); padding-left: var(--space-2); }
.avatar {
  width: 32px; height: 32px; border-radius: var(--radius-full);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-primary); color: var(--color-primary-contrast);
  font-size: var(--text-xs); font-weight: var(--weight-bold);
}
.topbar-user-meta { display: flex; flex-direction: column; line-height: 1.2; }
.topbar-user-name { font-weight: var(--weight-semibold); color: var(--color-text); font-size: var(--text-sm); }
.topbar-user-role { color: var(--color-text-muted); font-size: var(--text-xs); }

/* Theme toggle icon swap */
.icon-sun { display: none; }
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] .icon-sun { display: inline-block; }
  :root[data-theme="auto"] .icon-moon { display: none; }
}
:root[data-theme="dark"] .icon-sun { display: inline-block; }
:root[data-theme="dark"] .icon-moon { display: none; }

/* ---- Content region ----------------------------------------------------- */
.content {
  flex: 1;
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--space-6) var(--space-6) var(--space-12);
}
/* Opt-out for a two-pane page (Mail). The reading-width cap exists so prose does not run
   to 2000px; a list beside a reading pane is the opposite case — the width IS the second
   pane. Still bounded, just far wider: past this the pane stops being a pane. */
.content--wide { max-width: min(1800px, 100%); }

/* ---- Sidebar backdrop (small screens only) ------------------------------ */
.sidebar-backdrop { display: none; }

/* ==========================================================================
   FLASH MESSAGES
   ========================================================================== */
.flash-region { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-5); }
.flash-region:empty { display: none; }
.flash {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-neutral-border);
  background: var(--color-neutral-bg); color: var(--color-neutral-fg);
  border-radius: var(--radius-md); box-shadow: var(--shadow-sm);
}
.flash-icon { margin-top: 1px; }
.flash-text { flex: 1; color: var(--color-text); }
.flash-dismiss { margin: -4px -4px 0 0; color: var(--color-text-muted); }
.flash--success { background: var(--color-success-bg); border-color: var(--color-success-border); }
.flash--success .flash-icon { color: var(--color-success-fg); }
.flash--warn    { background: var(--color-warn-bg);    border-color: var(--color-warn-border); }
.flash--warn .flash-icon { color: var(--color-warn-fg); }
.flash--error   { background: var(--color-error-bg);   border-color: var(--color-error-border); }
.flash--error .flash-icon { color: var(--color-error-fg); }
.flash--info    { background: var(--color-info-bg);    border-color: var(--color-info-border); }
.flash--info .flash-icon { color: var(--color-info-fg); }

/* ==========================================================================
   BUTTONS
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-base); font-weight: var(--weight-semibold);
  line-height: 1.2; white-space: nowrap;
  border: 1px solid transparent; border-radius: var(--radius-md);
  cursor: pointer; transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}
.btn:hover { text-decoration: none; }
.btn .icon { width: 16px; height: 16px; }
.btn--sm { padding: var(--space-1) var(--space-3); font-size: var(--text-sm); }

.btn--primary { background: var(--color-primary); color: var(--color-primary-contrast); }
.btn--primary:hover { background: var(--color-primary-hover); }

.btn--secondary { background: var(--color-surface); color: var(--color-text); border-color: var(--color-border-strong); }
.btn--secondary:hover { background: var(--color-surface-2); }

.btn--ghost { background: transparent; color: var(--color-text-muted); }
.btn--ghost:hover { background: var(--color-surface-2); color: var(--color-text); }

.btn--danger { background: var(--color-error-solid); color: #fff; }
.btn--danger:hover { filter: brightness(0.94); }

.btn:disabled, .btn.is-disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

/* Icon-only button (topbar/sidebar affordances) */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  color: var(--color-text-muted); background: transparent;
  border: 1px solid transparent; border-radius: var(--radius-md); cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.icon-btn:hover { background: var(--color-surface-2); color: var(--color-text); text-decoration: none; }

/* ==========================================================================
   STATUS CHIPS
   ========================================================================== */
.chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 2px var(--space-3); border-radius: var(--radius-full);
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  border: 1px solid var(--color-neutral-border);
  background: var(--color-neutral-bg); color: var(--color-neutral-fg);
  white-space: nowrap;
}
.chip-dot { width: 7px; height: 7px; border-radius: var(--radius-full); background: var(--color-neutral-solid); flex: none; }
.chip--success { background: var(--color-success-bg); color: var(--color-success-fg); border-color: var(--color-success-border); }
.chip--success .chip-dot { background: var(--color-success-solid); }
.chip--warn { background: var(--color-warn-bg); color: var(--color-warn-fg); border-color: var(--color-warn-border); }
.chip--warn .chip-dot { background: var(--color-warn-solid); }
.chip--error { background: var(--color-error-bg); color: var(--color-error-fg); border-color: var(--color-error-border); }
.chip--error .chip-dot { background: var(--color-error-solid); }
.chip--info { background: var(--color-info-bg); color: var(--color-info-fg); border-color: var(--color-info-border); }
.chip--info .chip-dot { background: var(--color-info-solid); }
.chip--neutral { background: var(--color-neutral-bg); color: var(--color-neutral-fg); border-color: var(--color-neutral-border); }

/* ==========================================================================
   PAGE HEADER
   ========================================================================== */
.page-header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4);
  margin-bottom: var(--space-6);
}
.page-header-titlerow { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.page-title { font-size: var(--text-2xl); font-weight: var(--weight-bold); color: var(--color-heading); line-height: var(--leading-tight); }
.page-subtitle { margin-top: var(--space-2); color: var(--color-text-muted); font-size: var(--text-md); max-width: var(--measure); }
.page-header-actions { flex: none; }

/* ==========================================================================
   CARDS
   ========================================================================== */
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.card-header { padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--color-border); }
.card-title { font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--color-heading); }
.card-subtitle { margin-top: var(--space-1); font-size: var(--text-sm); color: var(--color-text-muted); max-width: var(--measure); }
.card-body { padding: var(--space-5); color: var(--color-text); }
.card-footer { padding: var(--space-3) var(--space-5); border-top: 1px solid var(--color-border); background: var(--color-surface-2); font-size: var(--text-sm); color: var(--color-text-muted); }

/* ==========================================================================
   TABLES — dense (queues) + roomy (forms)
   ========================================================================== */
.table-wrap { overflow-x: auto; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); }
.table { width: 100%; border-collapse: collapse; font-size: var(--text-base); }
.table-caption { text-align: left; padding: var(--space-3) var(--space-4); color: var(--color-text-muted); font-size: var(--text-sm); caption-side: top; }
.table thead th {
  text-align: left; font-weight: var(--weight-semibold); color: var(--color-text-muted);
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.04em;
  background: var(--color-surface-2); border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}
.table td { color: var(--color-text); border-bottom: 1px solid var(--color-border); vertical-align: middle; }
.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr:hover { background: var(--color-surface-2); }
.table .is-numeric { text-align: right; font-variant-numeric: var(--numeric); }
.table .is-muted { color: var(--color-text-subtle); }

/* Scannable rows: a primary line (optional leading icon) plus a muted preview
   line beneath — a mail-client row inside the shared table. Additive; only the
   cells that set `icon`/`subtext` use these. */
.table .cell-line { display: inline-flex; align-items: center; gap: var(--space-2); }
.table .cell-icon { width: 14px; height: 14px; color: var(--color-text-subtle); flex: none; }
/* Unscoped from `.table` because the mail list is no longer a table. `max-width: 62ch`
   went with it: a nowrap line 62 characters long is 448px of forced width, which on a
   582px list was the single largest contributor to the horizontal scroll it was supposed
   to be a preview inside. It clamps to whatever room the row has instead. */
.cell-subtext {
  margin-top: 2px; font-size: var(--text-sm); color: var(--color-text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* A row that still needs a human — an un-actioned mail conversation. Stronger
   text + a left accent, the way an unread thread reads in a mail client, so the
   one row that needs attention does not blend into the handled ones. */
.table tr.is-unread td { font-weight: var(--weight-semibold); }
.table tr.is-unread td:first-child { box-shadow: inset 3px 0 0 0 var(--color-warn-solid, #d97706); }
/* A row hidden by the display-only table filter (components/table_filter.html).
   DISPLAY only, deliberately: the row's inputs stay in the DOM and still submit,
   which is what keeps filtering safe beside the lookup grid's delete-by-omission
   save (a row absent from the payload is a row deleted). */
.table tr.is-filter-hidden { display: none; }

/* Mail-client split view: a scannable conversation list beside a reading pane.
   The pane sticks while the list scrolls; on a narrow screen the columns stack so
   the pane drops below the list and nothing is lost. */
.split-view {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: var(--space-5); align-items: start;
}

/* ---- Mail rows: a conversation list that fits the pane it lives in ------------------
   Measured before writing: the list column is 582px wide and the six-column table it
   used to hold needed 990px, so 410px of it sat behind a horizontal scrollbar and the
   subject line was cut mid-word. A mail row is a block, not a table row — sender and
   date on one line, subject under it, snippet under that — which is why every mail
   client in a narrow column looks like this. */
.mail-list { list-style: none; margin: 0; padding: 0; }
.mail-row {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--space-1) var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}
.mail-row:last-child { border-bottom: none; }
.mail-row:hover { background: var(--color-surface-2); }
/* min-width:0 is what lets the long subject ellipsise instead of pushing the row wider
   — a flex item's default min-width:auto refuses to shrink below its content, which is
   the same trap the modal footer hit. */
.mail-open {
  flex: 1 1 20rem; min-width: 0;
  display: flex; flex-direction: column; gap: 2px;
  color: inherit; text-decoration: none;
}
/* The row is one big anchor, so the global `a:hover { text-decoration: underline }` would
   underline the sender, the date AND the snippet at once — four underlined lines, which
   reads as damage rather than as a hover. Only the subject, which is the thing you are
   about to open, takes the underline. */
.mail-open:hover, .mail-open:focus { text-decoration: none; }
.mail-open:hover .mail-subject > span { text-decoration: underline; }
.mail-head {
  display: flex; align-items: baseline; gap: var(--space-2);
  font-size: var(--text-sm);
}
.mail-from {
  color: var(--color-text); font-weight: var(--weight-semibold);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-count { color: var(--color-text-muted); font-weight: 400; }
/* The date is pushed to the end of its line and never shrinks: it is the one field
   read by comparing down the column, so a truncated one is worse than useless. */
.mail-when {
  margin-left: auto; flex: none;
  color: var(--color-text-muted); font-variant-numeric: var(--numeric);
}
/* Two lines, then ellipsis. One line loses the distinguishing half of an IP subject
   ("… Final Office Action issued with deadline due on 19 November 2026" is where the
   information is); three would stop the list being scannable. */
.mail-subject {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
  color: var(--color-heading);
}
/* BESIDE the message, not under it. Stacked under, the status chip and the three
   decision buttons each cost the row a whole line — 142px per conversation, so barely
   four fit a screen on the surface whose job is triage. Beside it they occupy vertical
   space the two-line subject was using anyway. `flex-wrap` on the row still drops them
   underneath when the list gets genuinely narrow, which is the right answer there. */
.mail-meta {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  gap: var(--space-2); margin-left: auto;
  max-width: 100%;
}
/* 1400px, not the split-view's own 900px breakpoint, because the constraint is the LIST
   column and not the window: at a 1280px window the list is 507px, and stacking the meta
   beside a message block that still wants 20rem squeezed the buttons into three wrapped
   lines — 174px per row, worse than the 142px it replaced. Measured at 1280 / 1512 / 1728. */
@media (min-width: 1400px) {
  .mail-meta { flex-direction: column; align-items: flex-end; }
}
.mail-status, .mail-actions {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  gap: var(--space-2);
}
.mail-ticket { font-variant-numeric: var(--numeric); font-size: var(--text-sm); }
/* The unread accent moves from the first cell to the row itself. */
.mail-row.is-unread { box-shadow: inset 3px 0 0 0 var(--color-warn-solid, #d97706); }
.mail-row.is-unread .mail-from,
.mail-row.is-unread .mail-subject { font-weight: var(--weight-semibold); }
.split-view-list { min-width: 0; }
.split-view-pane {
  position: sticky; top: var(--space-4);
  max-height: calc(100vh - 140px); overflow: auto; min-width: 0;
}
@media (max-width: 900px) {
  .split-view { grid-template-columns: 1fr; }
  .split-view-pane { position: static; max-height: none; }
}

/* Collapsible email-thread message: a <details> styled as a card, its <summary>
   as the card header. Latest open, older collapsed — the mail-client default. */
.thread-msg > summary { cursor: pointer; list-style-position: inside; }
.thread-msg > summary::-webkit-details-marker { color: var(--color-text-subtle); }
.thread-msg[open] > summary { border-bottom: 1px solid var(--color-border); }
.thread-msg-count { margin-left: auto; font-size: var(--text-sm); font-variant-numeric: var(--numeric); }

/* Density variants */
.table--dense th, .table--dense td { padding: var(--space-2) var(--space-4); }
.table--dense { font-size: var(--text-sm); }
.table--roomy th, .table--roomy td { padding: var(--space-4) var(--space-5); }

/* ==========================================================================
   FORM FIELDS
   ========================================================================== */
.field { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.field-label { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--color-text); }
.field-required { color: var(--color-error-solid); }
.field-control {
  width: 100%; padding: var(--space-2) var(--space-3);
  font: inherit; color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.field-control::placeholder { color: var(--color-text-subtle); }
.field-control:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 3px var(--color-focus-ring); }
textarea.field-control { resize: vertical; min-height: 76px; }
/* Monospace block for values a person must copy or read verbatim — a generated setup
   token, a rendered template body. Scrolls inside itself so a long line never widens the
   page (same contract as .table-wrap). */
.code-block {
  margin: var(--space-3) 0 0; padding: var(--space-3);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-family: var(--font-mono); font-size: var(--text-sm);
  white-space: pre-wrap; word-break: break-word; overflow-x: auto;
}
.field-check { display: flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.field-check input { flex: none; }
.field-check span { color: var(--color-text); }
.is-disabled .field-check { cursor: not-allowed; }
.field-help { font-size: var(--text-sm); color: var(--color-text-muted); }

.field.has-error .field-control { border-color: var(--color-error-solid); }
.field.has-error .field-control:focus { box-shadow: 0 0 0 3px var(--color-error-border); }
.field-error { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-error-fg); }
.field-error .icon { width: 15px; height: 15px; }

/* Disabled-with-reason (§11.3): field dims, caption explains WHY it's disabled */
.field.is-disabled .field-label { color: var(--color-text-muted); }
.field-control:disabled { background: var(--color-surface-2); color: var(--color-text-muted); cursor: not-allowed; }
.field-reason { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-text-muted); font-style: italic; }
.field-reason .icon { width: 15px; height: 15px; color: var(--color-info-fg); }

/* A control just populated by verified auto-fill. "Apply verified to form" swaps only
   the auto-fill panel, so the inputs below it never visibly changed and a successful
   apply read as if nothing happened. The ring flashes on arrival to show WHICH fields
   landed, then settles to a border tint marking the value as AI-supplied-and-verified
   rather than typed; the mark clears on first edit (doc_review.js). Ring, not
   background, so it composes with the control's own surface in either theme. The
   global prefers-reduced-motion rule already collapses the animation. */
.field-control.is-autofilled { border-color: var(--color-success-border); }
.field-control.is-autofilled { animation: pfc-autofill-land 1.4s ease-out 1; }
@keyframes pfc-autofill-land {
  from { box-shadow: 0 0 0 3px var(--color-success-border); }
  to   { box-shadow: 0 0 0 3px rgba(0, 0, 0, 0); }
}

/* ==========================================================================
   EMPTY STATE
   ========================================================================== */
.empty-state {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: var(--space-12) var(--space-6);
  border: 1px dashed var(--color-border-strong); border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.empty-state-icon {
  width: 48px; height: 48px; margin-bottom: var(--space-4);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--color-text-subtle); background: var(--color-surface-2); border-radius: var(--radius-full);
}
.empty-state-icon .icon { width: 24px; height: 24px; }
.empty-state-title { font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--color-heading); }
.empty-state-message { margin-top: var(--space-2); max-width: 42ch; color: var(--color-text-muted); }
.empty-state-action { margin-top: var(--space-5); }

/* ==========================================================================
   PROSE — long-form rendered content (user manual / Help / docs)
   Re-establishes the typographic rhythm the base reset zeroes out, using TOKENS
   only, scoped to `.prose` so it never leaks into component-built pages. Tables
   inside prose reuse the `.table` component and are not restyled here. This is the
   §11.6 mechanism: a page that needs new typography adds it to the shared library
   (here + /styleguide) first — never as page-local CSS.
   ========================================================================== */
.prose { color: var(--color-text); font-size: var(--text-base); line-height: var(--leading-normal); }
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose h1, .prose h2, .prose h3, .prose h4 {
  color: var(--color-heading); font-weight: var(--weight-semibold);
  line-height: var(--leading-tight); margin: var(--space-6) 0 var(--space-3);
}
.prose h1 { font-size: var(--text-xl); }
.prose h2 { font-size: var(--text-lg); }
.prose h3 { font-size: var(--text-md); }
.prose h4 { font-size: var(--text-base); }
.prose p { margin: var(--space-3) 0; }
.prose ul, .prose ol { margin: var(--space-3) 0; padding-left: var(--space-6); }
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }
.prose li { margin: var(--space-1) 0; }
.prose li::marker { color: var(--color-text-muted); }
.prose a { color: var(--color-primary); text-decoration: underline; }
.prose strong { font-weight: var(--weight-semibold); color: var(--color-heading); }
.prose code {
  font-family: var(--font-mono); font-size: 0.9em;
  padding: 0.1em var(--space-1); border-radius: var(--radius-sm);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
}
.prose pre {
  margin: var(--space-4) 0; padding: var(--space-4); overflow-x: auto;
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.prose pre code { padding: 0; border: 0; background: none; font-size: var(--text-sm); }
.prose blockquote {
  margin: var(--space-4) 0; padding: var(--space-2) var(--space-4);
  border-left: 3px solid var(--color-border-strong);
  color: var(--color-text-muted); font-style: italic;
}
.prose hr { border: 0; border-top: 1px solid var(--color-border); margin: var(--space-6) 0; }
.prose .table-wrap { margin: var(--space-4) 0; }

/* ==========================================================================
   HTMX LOADING INDICATOR
   htmx toggles `.htmx-request` on the indicator (or triggering element); until
   then the indicator is display:none via htmx's own base rule + ours.
   ========================================================================== */
.loading-indicator { display: none; align-items: center; gap: var(--space-2); color: var(--color-text-muted); font-size: var(--text-sm); }
.htmx-request .loading-indicator, .loading-indicator.htmx-request { display: inline-flex; }
.spinner { animation: pfc-spin 0.8s linear infinite; }
@keyframes pfc-spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   MODAL / DIALOG  (shared shell for the confirm-dialog + add-change-dialog
   htmx primitives). A trigger hx-gets the dialog partial into a slot; the
   `.modal-overlay` is a fixed backdrop so it centres regardless of the slot's
   position in the page. `.modal` alone (no overlay) is the styleguide anatomy.
   ========================================================================== */
.modal-overlay {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-4);
  background: var(--color-scrim);
}
.modal {
  width: 100%; max-width: 32rem;
  display: flex; flex-direction: column;
  max-height: calc(100vh - var(--space-8));
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
/* A dialog whose body is a FORM has a <form> sitting between .modal and its header/body/
   footer — the quick-add dialog is the first such. A plain block element there breaks the
   column: .modal-body's flex rules address a parent that is no longer a flex container, so
   the form grows to its full content height and carries the footer outside the panel. This
   makes the form the column instead, which is what it visually is. */
.modal > form { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

/* The header and footer keep their size; the BODY is the part that gives.
   Without `min-height: 0` a flex item refuses to shrink below its content (the default is
   `min-height: auto`), so `.modal`'s max-height clamped the box while `.modal-body` kept
   its full natural height and pushed `.modal-footer` straight off the bottom of the screen
   — measured at 850–909px in a 900, a 700 AND a 560px-tall viewport, i.e. the submit
   buttons were unreachable at every size. The confirm dialogs never showed it because
   their bodies are two lines; the quick-add dialog with "More options" open is the first
   body long enough to overflow. `overflow-y: auto` on .modal-body was always meant to
   scroll it — this is what lets it. */
.modal-header {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}
.modal-head-icon { color: var(--color-text-muted); }
.modal-head-icon--warn { color: var(--color-warn-fg); }
.modal-title { flex: 1; font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--color-heading); }
.modal-close { margin: calc(-1 * var(--space-1)) calc(-1 * var(--space-2)) calc(-1 * var(--space-1)) 0; }
.modal-body { flex: 1 1 auto; min-height: 0; padding: var(--space-5); color: var(--color-text); overflow-y: auto; }
.modal-body > * + * { margin-top: var(--space-4); }
.modal-message { color: var(--color-text); }
.modal-note { font-size: var(--text-sm); color: var(--color-text-muted); font-style: italic; }
.modal-footer {
  flex: 0 0 auto;
  display: flex; justify-content: flex-end; gap: var(--space-3);
  /* The buttons wrap rather than overflow: the quick-add dialog has three of them plus a
     spinner, and a 32rem panel on a laptop has no room to put them on one line. */
  flex-wrap: wrap; align-items: center;
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface-2);
}

/* ==========================================================================
   FORM ACTIONS (right-aligned button row for editor forms)
   ========================================================================== */
.form-actions { display: flex; justify-content: flex-end; gap: var(--space-3); margin-top: var(--space-5); }

/* ==========================================================================
   DEADLINE EDITOR + PICKER (structured deadline primitive)
   ========================================================================== */
.deadline-picker { margin-bottom: var(--space-2); }
.deadline-editor-result:empty { display: none; }
.deadline-editor-result { margin-top: var(--space-5); }

/* ==========================================================================
   SANITIZED EMAIL PREVIEW (sandboxed iframe primitive)
   ========================================================================== */
.email-preview {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface);
}
.email-preview-head {
  display: flex; flex-direction: column; gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface-2);
}
.email-preview-subject { font-weight: var(--weight-semibold); color: var(--color-heading); }
.email-preview-meta { display: flex; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-text); }
.email-preview-meta-label { color: var(--color-text-muted); min-width: 4.5rem; }
.email-preview-frame {
  display: block; width: 100%; height: 320px;
  border: 0; background: var(--color-surface);
}
.email-preview-note {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-xs); color: var(--color-text-muted);
}
.email-preview-note .icon { width: 14px; height: 14px; color: var(--color-info-fg); }

/* ==========================================================================
   STAGED-CHANGE WORKBENCH (before -> after cards + add-change dialog)
   ========================================================================== */
.workbench-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}
.workbench-title { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--color-heading); }
.workbench-title .icon { color: var(--color-text-muted); }
.workbench-cards { padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); }

.staged-card { border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface); overflow: hidden; }
.staged-card-head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-2);
  border-bottom: 1px solid var(--color-border);
}
.staged-card-icon { color: var(--color-text-subtle); }
.staged-card-field { flex: 1; font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--color-text); }
.staged-card-remove { width: 28px; height: 28px; }
.staged-card-remove .icon { width: 15px; height: 15px; }

.staged-diff { display: grid; grid-template-columns: 1fr auto 1fr; }
.staged-diff-cell { padding: var(--space-3) var(--space-4); min-width: 0; }
.staged-diff-label { display: block; font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.04em; font-weight: var(--weight-semibold); margin-bottom: var(--space-1); opacity: 0.8; }
.staged-diff-value { font-family: var(--font-mono); font-size: var(--text-sm); word-break: break-word; }
.staged-diff-before { background: var(--color-error-bg); color: var(--color-error-fg); }
.staged-diff-before .staged-diff-value { text-decoration: line-through; text-decoration-color: var(--color-error-border); }
.staged-diff-after { background: var(--color-success-bg); color: var(--color-success-fg); }
.staged-diff-arrow { display: flex; align-items: center; justify-content: center; color: var(--color-text-subtle); padding: 0 var(--space-1); }
.staged-diff-arrow .icon { width: 16px; height: 16px; }

/* ==========================================================================
   LAYOUT UTILITIES (small, generic — used by pages + styleguide)
   ========================================================================== */
.stack > * + * { margin-top: var(--space-4); }
.stack-lg > * + * { margin-top: var(--space-6); }
.cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--space-4); }

/* ==========================================================================
   STYLEGUIDE PAGE
   ========================================================================== */
.sg-section { margin-top: var(--space-10); }
.sg-section:first-of-type { margin-top: var(--space-6); }
.sg-section-title { font-size: var(--text-lg); font-weight: var(--weight-bold); color: var(--color-heading); padding-bottom: var(--space-2); border-bottom: 2px solid var(--color-border); margin-bottom: var(--space-2); }
.sg-section-note { color: var(--color-text-muted); font-size: var(--text-sm); margin-bottom: var(--space-5); }
.sg-block { margin-top: var(--space-5); }
.sg-block-label { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-subtle); font-weight: var(--weight-semibold); margin-bottom: var(--space-3); }
.sg-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--space-3); }
.sg-swatch { border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; background: var(--color-surface); font-size: var(--text-xs); }
.sg-swatch-chip { height: 46px; }
.sg-swatch-meta { padding: var(--space-2) var(--space-3); }
.sg-swatch-name { font-weight: var(--weight-semibold); color: var(--color-text); }
.sg-swatch-var { color: var(--color-text-muted); font-family: var(--font-mono); }
.sg-scale-row { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-2) 0; border-bottom: 1px dashed var(--color-border); }
.sg-scale-demo { background: var(--color-primary-soft-bg); border-radius: var(--radius-sm); height: 16px; }
.sg-scale-name { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--color-text-muted); width: 8rem; flex: none; }
.sg-type-row { padding: var(--space-2) 0; border-bottom: 1px dashed var(--color-border); }
.sg-type-row .sg-scale-name { display: inline-block; }

/* Force the (normally hidden) htmx loading indicator visible for documentation only. */
.sg-show-indicator .loading-indicator { display: inline-flex; }

/* Anatomy frame: shows a dialog panel (the `.modal` without its fixed overlay) in
   flow, so the styleguide documents the dialog's look without an interaction. */
.sg-demo-frame {
  display: flex; justify-content: center;
  padding: var(--space-6);
  background: var(--color-surface-2);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-lg);
}

/* ==========================================================================
   RESPONSIVE — sidebar becomes an off-canvas overlay on small screens
   ========================================================================== */
@media (max-width: 900px) {
  .app-shell { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0;
    width: var(--sidebar-width);
    transform: translateX(-100%);
    transition: transform var(--transition);
    box-shadow: var(--shadow-lg);
  }
  .app-shell[data-sidebar-state="open"] .sidebar { transform: translateX(0); }

  /* The backdrop is driven by the SHELL STATE, never by the `hidden` attribute.
     `hidden` only maps to display:none in the UA stylesheet, so an author
     `display: block` beats it — and this rule used to be unconditional. The
     result: on every viewport <=900px the backdrop was position:fixed, inset:0,
     z-index:25 AT ALL TIMES, sitting over the content and swallowing every tap.
     The app was unclickable on a phone, and it looked like nothing was wrong
     because the backdrop is transparent-ish and the markup said hidden. */
  .app-shell[data-sidebar-state="open"] .sidebar-backdrop {
    display: block; position: fixed; inset: 0;
    background: rgba(2, 6, 23, 0.5); z-index: 25;
  }
  .topbar-search { width: auto; }
  .topbar-search input { width: 44vw; }
  .topbar-user-meta { display: none; }
  .content { padding: var(--space-4) var(--space-4) var(--space-10); }
}

@media (max-width: 560px) {
  .page-header { flex-direction: column; }
  .topbar-search { display: none; }
}

/* ---- Reduced motion ----------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}


/* ---- Skip link (keyboard bypass) ---------------------------------------------------
   Offscreen until focused, then pinned top-left. Sighted keyboard-only / switch users
   have no landmark shortcut, so this is their only way past the sidebar. */
.skip-link {
  position: absolute; left: var(--space-3); top: -100vh; z-index: 100;
  padding: var(--space-2) var(--space-4);
  background: var(--color-surface); color: var(--color-text);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); text-decoration: none;
}
.skip-link:focus { top: var(--space-3); }
/* The bypass target is tabindex="-1" purely so focus can land on it; never paint a ring. */
.content:focus { outline: none; }


/* ---- Visually hidden (announced, not painted) ---------------------------------------
   For text that must reach a screen reader but not the page. Status-chip hints use it:
   `title` alone is announced by almost no screen reader and is unreachable by touch, so
   hints like "value will change on approval" — one click from a live dispatch — were
   invisible to exactly the people most likely to need them.
   Not `display:none` and not `visibility:hidden`; both remove the text from the
   accessibility tree, which is the opposite of the point. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}


/* ---- Measured prose (long-form only) -------------------------------------------------
   The in-app user manual is the one place with continuous multi-paragraph reading, and it
   ran the full 1180px column. Applied as a MODIFIER rather than to `.prose` itself, because
   that class also renders email bodies (whose own wrapping would fight a narrow cap) and two
   short fallback sentences that need no measure at all. Tables inside stay full width. */
.prose--measured { max-width: var(--measure); }
.prose--measured .table-wrap, .prose--measured table { max-width: none; }


/* ---- Surface levels: exactly two, and no nesting beyond them ------------------------
   Design review finding 09. Page header, section, sub-section and per-item container were
   ALL `.card`, and cards nested two and three deep — a nested card was --color-surface on
   --color-surface separated only by a 1px border and a --shadow-sm that is effectively
   invisible in dark mode. With one border and one shadow doing every job, nothing on a page
   said what to look at first.

   Level one (`.card`, above): the primary work object. --color-surface, --shadow-sm.
   Level two (`.card--nested`): grouped supporting detail. --color-surface-2, a border, and
   NO shadow — a shadow at level two reads as another primary object.

   There is deliberately no level three. A third would need a fourth background, and the
   token file only carries three (--color-bg / --color-surface / --color-surface-2), which is
   the point: the constraint is the design. */
.card--nested {
  background: var(--color-surface-2);
  box-shadow: none;
}
/* Its own header/footer must not re-introduce the level-one fill, or the nesting inverts. */
.card--nested > .card-footer { background: transparent; }

/* ---- The hidden attribute actually hides --------------------------------------------
   `[hidden]` maps to `display: none` only in the USER-AGENT stylesheet, so any author
   rule that sets `display` on the same element wins — silently. That is not theoretical:
   `.doc-review-canvas { display: block }` meant `canvas.hidden = true` in the PDF viewer
   did nothing, so a PDF that failed to load left a 314px blank box on screen with the
   explanation ("the server could not find this PDF") pushed below it, out of the pane —
   which is exactly what "the PDF pane is blank" turned out to be (2026-09-04).
   One baseline rule, because every `el.hidden = true` in the codebase depends on it. */
[hidden] { display: none !important; }

/* ---- Explainer: "How this works" on a rules tab -----------------------------------
   Every Rules Editor tab edits a table whose column headers are database names and
   whose effect lands two screens away (classification, a deadline, a report draft).
   The explainer is a collapsed <details> at level two: one line to open, a short
   ordered list of how the engine reads the table, and one worked example. Collapsed by
   default so a daily operator pays nothing; a tab with no rules yet opens it, because
   the first rule is where the guessing happens (an email address typed as a domain). */
.explainer {
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-3);
}
/* One compact line when closed — it is a door, not a heading, and the work area is
   what the page is for. */
.explainer > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: var(--space-2);
  font-weight: 600; font-size: var(--text-sm);
}
.explainer > summary::-webkit-details-marker { display: none; }
.explainer > summary::after {
  content: "Show"; margin-left: auto;
  font-weight: 400; font-size: var(--text-sm); color: var(--color-text-muted);
}
.explainer[open] > summary::after { content: "Hide"; }
.explainer-body { margin-top: var(--space-3); font-size: var(--text-sm); }
.explainer-body > * + * { margin-top: var(--space-2); }
.explainer-body ol, .explainer-body ul { margin: 0; padding-left: 1.25rem; }
.explainer-body li + li { margin-top: var(--space-1); }
.explainer-body dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: var(--space-1) var(--space-3); }
.explainer-body dt { font-family: var(--font-mono, ui-monospace, monospace); font-size: var(--text-sm); }
.explainer-body dd { margin: 0; }
.explainer-example {
  padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--color-accent, #4f8cff);
  background: var(--color-surface);
  border-radius: var(--radius-sm, 4px);
}

/* ---- Tabular figures on dates and references ----------------------------------------
   Almost every number in this product is a date or a docket reference, and both are read by
   COMPARING them down a column. Proportional digits make "11 Aug" and "01 Sep" different
   widths, so the eye cannot scan them. `.is-numeric` already did this for right-aligned
   counts; `.is-figures` is the left-aligned equivalent for dates and identifiers. */
.table .is-figures { font-variant-numeric: var(--numeric); }

/* ---- PDF text layer ------------------------------------------------------------------
   The viewer rasterised each page into a canvas and stopped there, so the Office Action
   was a PICTURE: it could not be selected, copied or found with the browser's own Ctrl-F,
   and the application number two inches from its own field on the form had to be re-typed
   by hand. This is pdf.js's text layer — invisible, correctly-positioned spans over the
   canvas — which restores all three at once.

   Adapted from pdf.js 4.2's text_layer_builder.css (the vendored build is
   static/js/pdf.min.mjs). Everything is expressed against `--scale-factor`, which the
   viewer script sets on each page container: pdf.js writes each span's left/top/font-size
   as `calc(var(--scale-factor) * …)`, so a zoom change is a variable update rather than a
   re-layout. Lives here rather than in either page's <style> because BOTH viewers (the
   Documents pane and the second-screen pop-out) load app.css and must not drift.

   Shared with pages/_doc_review_pdf_viewer_script.html — pinned by PdfTextLayerTests. */
.doc-review-page { position: relative; margin: 0 auto var(--space-2); width: max-content; }
.doc-review-page .doc-review-canvas { margin: 0; }
.doc-review-textlayer {
  position: absolute; inset: 0; overflow: hidden;
  text-align: initial; line-height: 1; text-size-adjust: none;
  forced-color-adjust: none; transform-origin: 0 0;
  /* The spans are transparent, so without this the caret is invisible too. */
  caret-color: CanvasText;
}
.doc-review-textlayer span,
.doc-review-textlayer br {
  color: transparent; position: absolute; white-space: pre;
  cursor: text; transform-origin: 0% 0%;
}
/* The selection has to be visible even though the text is not — this IS the feature. */
.doc-review-textlayer ::selection { background: rgba(0, 90, 255, 0.28); }
.doc-review-textlayer br::selection { color: transparent; }
/* An end-of-content marker pdf.js appends; it must not be selectable itself. */
.doc-review-textlayer .endOfContent {
  position: absolute; inset: 100% 0 0; z-index: -1; cursor: default; user-select: none;
}
.doc-review-textlayer.is-selecting .endOfContent { inset: 0; }
