/* ==========================================================================
   A4A: Aircraft Maintenance Assistance | quantumflight.ai
   Edgent LLC

   Design direction: the visual grammar of an aircraft technical manual and
   an airframe data plate. Condensed grotesque for stencilled display type,
   a screen-legible serif for body copy (the printed-manual register), and a
   monospace for specification data. The signature device is the
   WARNING / CAUTION / NOTE block used the way MIL-spec technical manuals
   actually use it: WARNING = risk to people, CAUTION = risk to equipment
   or to the mission, NOTE = advisory. It carries the honest content.

   Self-contained: all fonts are local subsets. No external requests.
   ========================================================================== */

/* --------------------------------------------------------------- fonts -- */
/* Liberation Sans Narrow: SIL Open Font License 1.1 */
@font-face {
  font-family: "A4A Display";
  src: url("fonts/disp-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* DejaVu Serif: Bitstream Vera / DejaVu license (permissive) */
@font-face {
  font-family: "A4A Body";
  src: url("fonts/body-reg.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "A4A Body";
  src: url("fonts/body-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* DejaVu Sans Mono: Bitstream Vera / DejaVu license (permissive) */
@font-face {
  font-family: "A4A Mono";
  src: url("fonts/mono-reg.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "A4A Mono";
  src: url("fonts/mono-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------- tokens -- */
:root {
  --ff-display: "A4A Display", "Liberation Sans Narrow", "Arial Narrow", sans-serif;
  --ff-body: "A4A Body", Georgia, "Times New Roman", serif;
  --ff-mono: "A4A Mono", ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;

  /* Light: engineering vellum, cool neutral. Not cream. */
  --bg: #e7eae9;
  --surface: #f2f4f3;
  --surface-2: #dde2e1;
  --ink: #14181b;
  --ink-2: #454e54;
  --rule: #b9c0c1;
  --rule-strong: #7b8587;
  --amber: #6f4106;      /* caution amber, darkened for text contrast */
  --amber-edge: #b8791a; /* amber for rules and non-text marks */
  --warn: #8c2818;
  --focus: #0b4f8a;

  /* Search-box operator color-coding (#1322). Four distinct, high-contrast
     hues on the --surface field: operators, negation, phrases, parentheses. */
  --qop: #0b4f8a;     /* AND / OR / NOT */
  --qneg: #8c2818;    /* a leading -term negation */
  --qphrase: #1f6f4a; /* "quoted phrase" */
  --qparen: #6a3f9c;  /* ( grouping ) */

  --measure: 34rem;
  --gutter: clamp(1.25rem, 5vw, 4rem);
  --shell: 74rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #13171a;
    --surface: #1a2024;
    --surface-2: #232b30;
    --ink: #e4e8ea;
    --ink-2: #a2adb3;
    --rule: #2d363c;
    --rule-strong: #647279;
    --amber: #eaa843;
    --amber-edge: #b0761f;
    --warn: #f2907a;
    --focus: #7cc0ff;
    --qop: #7cc0ff;
    --qneg: #f2907a;
    --qphrase: #6ee7b7;
    --qparen: #c4a6ff;
  }
}

/* Explicit overrides win in both directions for a manual theme toggle. */
:root[data-theme="light"] {
  --bg: #e7eae9; --surface: #f2f4f3; --surface-2: #dde2e1;
  --ink: #14181b; --ink-2: #454e54; --rule: #b9c0c1; --rule-strong: #7b8587;
  --amber: #6f4106; --amber-edge: #b8791a; --warn: #8c2818; --focus: #0b4f8a;
  --qop: #0b4f8a; --qneg: #8c2818; --qphrase: #1f6f4a; --qparen: #6a3f9c;
}
:root[data-theme="dark"] {
  --bg: #13171a; --surface: #1a2024; --surface-2: #232b30;
  --ink: #e4e8ea; --ink-2: #a2adb3; --rule: #2d363c; --rule-strong: #647279;
  --amber: #eaa843; --amber-edge: #b0761f; --warn: #f2907a; --focus: #7cc0ff;
  --qop: #7cc0ff; --qneg: #f2907a; --qphrase: #6ee7b7; --qparen: #c4a6ff;
}

/* ---------------------------------------------------------------- base -- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--ff-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  overflow-x: hidden;       /* body must never scroll sideways */
  text-rendering: optimizeLegibility;
}

p { max-width: var(--measure); }

a {
  color: inherit;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}
a:hover { text-decoration-thickness: 2px; }

/* Visible focus everywhere, not a color-only cue. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 1px;
}

::selection { background: var(--amber-edge); color: #fff; }

/* ------------------------------------------------------------- layout -- */
.shell {
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--bg);
  padding: 0.75rem 1.25rem;
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  z-index: 100;
}
.skip:focus {
  left: 0.5rem;
  top: 0.5rem;
}

/* ------------------------------------------------------------- header -- */
.masthead {
  border-bottom: 1px solid var(--rule);
  background: var(--bg);
  position: sticky;
  top: 0;
  z-index: 40;
}
.masthead__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-height: 3.5rem;
  flex-wrap: wrap;
}
.wordmark {
  font-family: var(--ff-mono);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
.wordmark span { color: var(--amber); }

.nav ul {
  display: flex;
  gap: clamp(0.85rem, 2.4vw, 1.75rem);
  list-style: none;
  margin: 0;
  padding: 0;
  flex-wrap: wrap;
}
.nav a {
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-decoration: none;
  padding: 0.35rem 0;
  display: inline-block;
  border-bottom: 2px solid transparent;
}
.nav a:hover,
.nav a:focus-visible { color: var(--ink); border-bottom-color: var(--amber-edge); }
/* The current page is marked with aria-current so the state is announced, not
   only shown. */
.nav a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--amber);
}
/* Pages that land later (Runs history, Search) sit in the nav as disabled
   markers rather than dead links, so the shape of the site is visible now. */
.nav__soon {
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0.35rem 0;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  opacity: 0.55;
  cursor: default;
}
.nav__badge {
  font-size: 0.5625rem;
  letter-spacing: 0.12em;
  padding: 0.05rem 0.3rem;
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--ink-2);
}

.theme-toggle {
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: transparent;
  color: var(--ink-2);
  border: 1px solid var(--rule-strong);
  padding: 0.4rem 0.7rem;
  cursor: pointer;
  border-radius: 0;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--ink-2); }

/* --------------------------------------------------------------- hero -- */
.hero {
  border-bottom: 1px solid var(--rule);
  padding-block: clamp(3rem, 9vw, 6.5rem) clamp(2.5rem, 6vw, 4rem);
  position: relative;
}
/* Faint drafting grid: atmosphere, never a carrier of meaning. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--rule) 1px, transparent 1px),
    linear-gradient(to bottom, var(--rule) 1px, transparent 1px);
  background-size: 5.5rem 5.5rem;
  opacity: 0.35;
  mask-image: radial-gradient(ellipse 70% 80% at 15% 0%, #000 10%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 70% 80% at 15% 0%, #000 10%, transparent 75%);
}
.hero > * { position: relative; }

.eyebrow {
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 0 0 1.25rem;
  max-width: none;
}

.hero__title {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: clamp(4.5rem, 19vw, 12rem);
  line-height: 0.82;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0;
}
.hero__expand {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: clamp(1.35rem, 4.6vw, 2.6rem);
  line-height: 1.05;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0.75rem 0 0;
  max-width: none;
}
.hero__lede {
  font-size: clamp(1.0625rem, 1.7vw, 1.3125rem);
  line-height: 1.55;
  max-width: 36rem;
  margin: 2rem 0 0;
}
.hero__lede strong { font-weight: 700; }

/* The home hero is compact so the working demonstration sits near the top of the
   page rather than below a full screen of display type. */
.hero--compact {
  padding-block: clamp(2rem, 5vw, 3.5rem) clamp(1.5rem, 4vw, 2.5rem);
}
.hero--compact .hero__title {
  font-size: clamp(2.25rem, 6.2vw, 4rem);
  line-height: 0.95;
}
.hero--compact .hero__lede { margin-top: 1.5rem; }
.hero__note {
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 52rem;
  margin: 1.25rem 0 0;
}
.hero__note a { color: inherit; }

/* ---------------------------------------------------- data plate (spec) -- */
/* The airframe data plate: the hero's thesis, and it rhymes with the
   credentials block at the foot of the page. */
.plate {
  margin-top: clamp(2.5rem, 6vw, 4rem);
  border: 1px solid var(--rule-strong);
  background: var(--surface);
}
.plate__cap {
  max-width: none;
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0.6rem 1rem;
  border-bottom: 1px solid var(--rule-strong);
  background: var(--surface-2);
}
.plate dl {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(9.5rem, 15rem) 1fr;
}
.plate dt,
.plate dd {
  margin: 0;
  padding: 0.7rem 1rem;
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  border-top: 1px solid var(--rule);
}
.plate dt {
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  border-right: 1px solid var(--rule);
}
.plate dd { overflow-wrap: anywhere; }

/* A plate holding prose rather than a data list still needs the caption's
   inset. Without it the copy sits flush against the plate border, because the
   only padding in this block belongs to .plate__cap and to the dl cells. The
   caption sets its own, so it is excluded. */
.plate > p:not(.plate__cap) { padding-inline: 1rem; }
.plate dl > dt:first-of-type,
.plate dl > dt:first-of-type + dd { border-top: 0; }
.plate .flag { color: var(--amber); font-weight: 700; }

@media (max-width: 34rem) {
  .plate dl { grid-template-columns: 1fr; }
  .plate dt { border-right: 0; padding-bottom: 0.15rem; }
  .plate dd { border-top: 0; padding-top: 0.15rem; }
  .plate dl > dt { border-top: 1px solid var(--rule); }
  .plate dl > dt:first-of-type { border-top: 0; }
}

/* ------------------------------------------------------------ sections -- */
.band { border-bottom: 1px solid var(--rule); padding-block: clamp(3rem, 8vw, 5.5rem); }
.band--tint { background: var(--surface); }

.band__head { margin-bottom: clamp(2rem, 4vw, 3rem); }

h2 {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: clamp(1.9rem, 5vw, 3.1rem);
  line-height: 1.02;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  margin: 0;
  max-width: 22ch;
}
h3 {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: clamp(1.1rem, 2.3vw, 1.4rem);
  line-height: 1.2;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  margin: 0 0 0.5rem;
}
h4 {
  font-family: var(--ff-mono);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin: 0 0 0.4rem;
  color: var(--ink-2);
}

.lead { font-size: 1.1875rem; line-height: 1.6; max-width: 38rem; }
.muted { color: var(--ink-2); }

/* Two-column prose that collapses cleanly. */
.cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: clamp(1.5rem, 4vw, 3rem);
}

/* --------------------------------------------------- capability entries -- */
/* A definition list, not a card grid: these are named functions, and the
   name/description relationship is the real structure. */
.functions { margin: 0; }
.functions > div {
  border-top: 1px solid var(--rule);
  padding-block: 1.5rem;
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: 0.5rem clamp(1.5rem, 4vw, 3rem);
  align-items: start;
}
.functions > div:last-child { border-bottom: 1px solid var(--rule); }
.functions dt {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: clamp(1.05rem, 2.1vw, 1.3rem);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  line-height: 1.15;
}
.functions dd { margin: 0; }
.functions dd p { margin: 0; max-width: 40rem; }

/* An entry may carry a qualifying note after its description or under its
   status tag, which is where this page says what a component does NOT yet do.
   The rule above zeroes p margins to keep the primary description tight, so
   the note needs spacing set by a selector strong enough to beat it. */
.functions dd p + p,
.functions dd .tag + p { margin-top: 0.85rem; }

.tag {
  display: inline-block;
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border: 1px solid var(--amber-edge);
  color: var(--amber);
  padding: 0.15rem 0.45rem;
  margin-top: 0.6rem;
  white-space: nowrap;
}

@media (max-width: 46rem) {
  .functions > div { grid-template-columns: 1fr; }
}

/* ------------------------------------------- WARNING / CAUTION / NOTE -- */
/* The signature device. Used the way a technical manual uses it:
   WARNING = risk to people. CAUTION = risk to equipment or mission.
   NOTE    = advisory. Type and border carry the level, never color alone. */
.advisory {
  border: 2px solid var(--ink-2);
  background: var(--surface);
  margin: 2rem 0;
  max-width: 46rem;
}
.advisory__label {
  max-width: none;
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  text-align: center;
  padding: 0.4rem 1rem;
  border-bottom: 2px solid var(--ink-2);
  color: var(--ink);
}
.advisory__body { padding: 1.15rem 1.35rem; }
.advisory__body > :first-child { margin-top: 0; }
.advisory__body > :last-child { margin-bottom: 0; }
.advisory p { max-width: none; }

.advisory--warning { border-color: var(--warn); }
.advisory--warning .advisory__label {
  border-bottom-color: var(--warn);
  color: var(--warn);
}
.advisory--caution { border-color: var(--amber-edge); }
.advisory--caution .advisory__label {
  border-bottom-color: var(--amber-edge);
  color: var(--amber);
}
.advisory--note { border-color: var(--rule-strong); }
.advisory--note .advisory__label {
  border-bottom-color: var(--rule-strong);
  color: var(--ink-2);
  letter-spacing: 0.24em;
}

/* -------------------------------------------------------------- tables -- */
.table-scroll { overflow-x: auto; margin-block: 2rem; }

table {
  border-collapse: collapse;
  width: 100%;
  min-width: 40rem;
  font-size: 0.9375rem;
}
caption {
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: left;
  color: var(--ink-2);
  padding-bottom: 0.75rem;
}
th, td {
  text-align: left;
  vertical-align: top;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--rule);
}
thead th {
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  border-bottom: 2px solid var(--rule-strong);
  color: var(--ink);
}
tbody th {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
}
tbody tr:last-child th,
tbody tr:last-child td { border-bottom: 0; }

/* ------------------------------------------------------- step sequence -- */
/* Numbered because a maintenance procedure genuinely is ordered, the
   number is the information, not decoration. */
.sequence { list-style: none; counter-reset: step; margin: 0; padding: 0; }
.sequence > li {
  counter-increment: step;
  display: grid;
  grid-template-columns: 3.75rem minmax(0, 1fr);
  gap: 0 1.5rem;
  padding-block: 1.5rem;
  border-top: 1px solid var(--rule);
}
.sequence > li:last-child { border-bottom: 1px solid var(--rule); }
.sequence > li::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--ff-mono);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--amber);
  padding-top: 0.15rem;
}
/* The li is a two-column grid with THREE children: the ::before counter, the
   h3 and the p. Auto-placement puts the counter in row 1 col 1, the heading in
   row 1 col 2, then wraps the paragraph to row 2 col 1 -- the 3.75rem number
   track -- rendering body copy about 60px wide. Pinning both text children to
   column 2 is the fix. max-width cannot help: the track is the constraint. */
.sequence h3,
.sequence p { grid-column: 2; }
.sequence h3 { margin-bottom: 0.35rem; }
.sequence p { margin: 0; max-width: 42rem; }

@media (max-width: 34rem) {
  .sequence > li { grid-template-columns: 1fr; gap: 0.5rem; }
  /* Single column now, so column 2 no longer exists. */
  .sequence h3,
  .sequence p { grid-column: 1; }
}

/* --------------------------------------------------------------- lists -- */
.plain { list-style: none; margin: 0; padding: 0; }
.plain li {
  padding-left: 1.35rem;
  position: relative;
  margin-bottom: 0.7rem;
  max-width: var(--measure);
}
.plain li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 0.55rem;
  height: 1px;
  background: var(--amber-edge);
}

/* Change-bar list: the vertical rule a TO revision puts in the margin. */
.changebar {
  border-left: 3px solid var(--amber-edge);
  padding-left: 1.25rem;
  margin: 1.75rem 0;
}
.changebar > :first-child { margin-top: 0; }
.changebar > :last-child { margin-bottom: 0; }

/* ---------------------------------------------------------- credentials -- */
.creds {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 0;
  border-top: 1px solid var(--rule-strong);
  border-left: 1px solid var(--rule-strong);
}
.creds > div {
  border-right: 1px solid var(--rule-strong);
  border-bottom: 1px solid var(--rule-strong);
  padding: 1.15rem 1.25rem;
  background: var(--surface);
}
.creds dt {
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 0.35rem;
}
.creds dd {
  margin: 0;
  font-family: var(--ff-mono);
  font-size: 0.9375rem;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

.codes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.codes li {
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  border: 1px solid var(--rule-strong);
  padding: 0.25rem 0.5rem;
  white-space: nowrap;
}
.codes li b { font-weight: 700; color: var(--amber); }

/* ------------------------------------------------------------- contact -- */
.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: clamp(1.5rem, 4vw, 3rem);
  margin-top: 2rem;
}
.contact-grid a { font-family: var(--ff-mono); font-size: 0.9375rem; }

/* -------------------------------------------------------------- footer -- */
.colophon {
  padding-block: 2.5rem 3.5rem;
  font-size: 0.875rem;
  color: var(--ink-2);
}
.colophon p { max-width: 46rem; }
.colophon a { color: var(--ink-2); }

/* --------------------------------------------------- entrance sequence -- */
/* One orchestrated page-load reveal, and nothing else moves. */
@media (prefers-reduced-motion: no-preference) {
  .reveal { animation: rise 620ms cubic-bezier(0.16, 0.84, 0.34, 1) both; }
  .reveal:nth-child(1) { animation-delay: 40ms; }
  .reveal:nth-child(2) { animation-delay: 110ms; }
  .reveal:nth-child(3) { animation-delay: 180ms; }
  .reveal:nth-child(4) { animation-delay: 250ms; }
  .reveal:nth-child(5) { animation-delay: 320ms; }
  @keyframes rise {
    from { opacity: 0; transform: translateY(0.75rem); }
    to   { opacity: 1; transform: none; }
  }
}

/* ------------------------------------------------------------- printing -- */
@media print {
  .masthead, .theme-toggle, .skip { display: none; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .band, .hero { border-color: #999; }
  .hero::before { display: none; }
}

/* ------------------------------------------------------- spacing utils -- */
/* These exist so the page can hold a strict Content-Security-Policy with no
   'unsafe-inline' in style-src. Inline style attributes would be blocked. */
.mt-3 { margin-top: 0.85rem; }
.mt-4 { margin-top: 1rem; }
.mt-5 { margin-top: 1.25rem; }
.mt-6 { margin-top: 2rem; }
.mt-7 { margin-top: 3rem; }
.mono-sm { font-family: var(--ff-mono); font-size: 0.9375rem; }

/* Contact fields are section subheadings (h3) but read as small labels. */
.field-label {
  font-family: var(--ff-mono);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0 0 0.4rem;
}

/* ------------------------------------------------- narrow-viewport fixes -- */
/* Code chips keep their code and label on one line where there is room, but
   must be allowed to wrap before they push the page sideways. */
@media (max-width: 40rem) {
  .codes li { white-space: normal; }
  .codes { gap: 0.35rem; }
}

/* A wide table scrolls inside its own container; the page body never does. */
.table-scroll { max-width: 100%; min-width: 0; }
.band, .hero, .shell { min-width: 0; }

/* Long unbroken strings (URLs, codes) must not set the page width. */
.creds dd, .plate dd, .contact-grid p { overflow-wrap: anywhere; }

/* ===================================================== working demo == */
/* The interactive pipeline. Every interactive control is a real button or
   textarea so keyboard and screen-reader behavior comes from the platform
   rather than from re-implemented ARIA. */

.demo { margin-top: 2rem; }
.demo__controls { display: grid; gap: 1.75rem; }

.samples { border: 0; margin: 0; padding: 0; }
.samples legend {
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0;
  margin-bottom: 0.75rem;
}
.samples__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 0.5rem;
}
.sample {
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  line-height: 1.45;
  text-align: left;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.sample:hover { border-color: var(--amber-edge); background: var(--surface-2); }
.sample:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.field { display: grid; gap: 0.4rem; }
.field-hint { margin: 0; font-size: 0.875rem; color: var(--ink-2); }
.field textarea {
  width: 100%;
  font-family: var(--ff-mono);
  font-size: 0.9375rem;
  line-height: 1.5;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
}
.field textarea:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }
.field__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: space-between;
}
.charcount { font-family: var(--ff-mono); font-size: 0.75rem; color: var(--ink-2); }
.charcount--over { color: var(--warn); font-weight: 700; }

/* Live run progress, directly beneath the control that started it. The output
   panel is well below the fold, so progress reported only down there reads as
   nothing happening: the reader clicked and the part of the page they were
   looking at did not change. Tight to the button on purpose: the separating
   rule reads as "this belongs to that", and the reset margins stop the shared
   .batch__* rules (written for the roomier output panel) from reintroducing the
   gap this exists to close. */
.field__progress {
  margin-top: 0.85rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--rule);
}
.field__progress .batch__state { margin-bottom: 0.5rem; }
.field__progress .batch__counts { margin-bottom: 0; }

.btn {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 0.9375rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.6rem 1.4rem;
  border: 1px solid var(--amber-edge);
  background: var(--amber);
  color: #13171a;
  cursor: pointer;
}
.btn:hover { filter: brightness(1.08); }
.btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.status {
  margin-top: 1.25rem;
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  color: var(--ink-2);
}

.demo__out { margin-top: 1.5rem; }
.demo__out:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; }
.engine {
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--amber);
  border-left: 3px solid var(--amber-edge);
  padding-left: 0.7rem;
  margin: 0 0 1.5rem;
}

/* Stages. Deliberately NOT a two-column grid with a counter pseudo-element:
   that layout wraps its third child into the number track. The number is an
   inline span inside the heading instead, so it cannot be mis-placed. */
.stages { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem; }
.stage {
  border: 1px solid var(--rule);
  background: var(--surface);
  padding: 1.25rem;
}
.stage__title {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin: 0 0 0.9rem;
  font-size: clamp(1rem, 2vw, 1.15rem);
}
.stage__n {
  font-family: var(--ff-mono);
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--amber);
  border: 1px solid var(--amber-edge);
  padding: 0.05rem 0.45rem;
  flex: none;
}
.stage__note {
  margin: 0.9rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 46rem;
}

.io { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem; }
.io__cap {
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0 0 0.35rem;
}
.io__text {
  margin: 0;
  font-family: var(--ff-mono);
  font-size: 0.875rem;
  line-height: 1.55;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--rule);
  background: var(--surface-2);
  overflow-wrap: anywhere;
}
.io__text--clean { border-left: 3px solid var(--amber-edge); }

.trace { margin-top: 1rem; }
.edits { list-style: none; margin: 0.6rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }
.edits li {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  border: 1px solid var(--rule);
  background: var(--surface-2);
  padding: 0.25rem 0.5rem;
}
.edit__from { text-decoration: line-through; color: var(--ink-2); }
.edit__arrow { color: var(--amber); }
.edit__to { font-weight: 700; }
.edit__rule {
  font-size: 0.625rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  border-left: 1px solid var(--rule);
  padding-left: 0.4rem;
}

.code__line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.75rem; margin: 0 0 0.35rem; }
.code__num {
  font-family: var(--ff-mono);
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--amber);
}
.code__title { font-family: var(--ff-display); font-weight: 700; letter-spacing: 0.03em; }

.recs { display: grid; gap: 1rem; }
.rec { border: 1px solid var(--rule); background: var(--surface-2); padding: 1rem; }
.rec__action { margin: 0 0 0.4rem; font-size: 1rem; line-height: 1.4; }
.rec__conf { margin: 0 0 0.3rem; font-family: var(--ff-mono); font-size: 0.875rem; }
.rec__conf strong { color: var(--amber); font-size: 1.05rem; }
.rec__basis { margin: 0 0 0.7rem; font-size: 0.75rem; color: var(--ink-2); }

.cites__wrap summary {
  cursor: pointer;
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--ink-2);
}
.cites__wrap summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.cites { list-style: none; margin: 0.7rem 0 0; padding: 0; display: grid; gap: 0.6rem; }
.cites li { border-top: 1px solid var(--rule); padding-top: 0.6rem; }
.cite__id, .cite__ata, .cite__flag, .cite__sim {
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  margin-right: 0.6rem;
}
.cite__id { font-weight: 700; color: var(--amber); }
.cite__ata { color: var(--ink-2); }
.cite__flag { text-transform: uppercase; border: 1px solid var(--rule-strong); padding: 0.05rem 0.35rem; }
.cite__flag--ok { color: var(--ink); }
.cite__flag--no { color: var(--warn); border-color: var(--warn); }
.cite__sim { color: var(--ink-2); }
.cite__text { margin: 0.35rem 0 0; font-size: 0.8125rem; line-height: 1.5; color: var(--ink-2); }

@media (max-width: 34rem) {
  .field__foot { flex-direction: column; align-items: stretch; }
  .btn { width: 100%; }
}

/* ---------------------------------------------------- Scan → Extract ---- */
/* The upload entry point and the stage that shows what came back from it.
   Every color is a token, so both themes and the theme toggle are covered
   without a second rule set. */

.file-input {
  font-family: var(--ff-mono);
  font-size: 0.875rem;
  color: var(--ink);
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  width: 100%;
  /* A file input's own box does not shrink below its intrinsic button+label
     width without this, and that is what pushes a 320px page sideways. */
  max-width: 100%;
  box-sizing: border-box;
}

.file-input:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }
.file-input:disabled { color: var(--ink-2); border-color: var(--rule); }

.file-input::file-selector-button {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.35rem 0.9rem;
  margin-right: 0.85rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface-2);
  color: var(--ink);
  cursor: pointer;
}

.file-input::file-selector-button:hover { border-color: var(--amber-edge); }

/* Sits above the numbered stages rather than inside them: scanning is how the
   text is OBTAINED, and the numbered pipeline is what then happens to it. It
   is deliberately not renumbered as "stage 0", a typed write-up skips this
   step entirely and its stages must stay 1, 2, 3. */
.stage--scan { margin-bottom: 1.5rem; border-left: 3px solid var(--amber-edge); }

.scan__page {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--rule);
  background: var(--surface-2);
}

.scan__meta {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 0.9rem;
  margin: 1rem 0 0;
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
}

.scan__meta dt {
  color: var(--ink-2);
  text-transform: uppercase;
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  font-weight: 700;
  padding-top: 0.15rem;
}

.scan__meta dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; }
.scan__meta dd.ok { color: var(--ink); }
.scan__meta dd.warn { color: var(--warn); font-weight: 700; }

/* OCR output carries its own line breaks, the layout of the form is part of
   what was read, and collapsing it would misrepresent the extraction. */
#mxScanText { white-space: pre-wrap; }

@media (max-width: 34rem) {
  .scan__meta { grid-template-columns: 1fr; gap: 0.15rem; }
  .scan__meta dd { margin-bottom: 0.5rem; }
}

/* ---------------------------------------------- Scan → Extract: pages --- */
/* A PDF is many pages and each one is read and stored separately, so the
   result needs a way to move between them without stacking N full-height
   blocks down the page. */

.scan__doc { margin: 0 0 0.9rem; color: var(--ink-2); }

.pagebar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 1rem;
}

.pagechip {
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  padding: 0.35rem 0.8rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}

.pagechip:hover { border-color: var(--amber-edge); background: var(--surface-2); }
.pagechip:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* The selected page is marked with a real border and weight, not color
   alone, color on its own is not a distinction at AA. */
.pagechip[aria-current="true"] {
  border-color: var(--amber-edge);
  border-left-width: 3px;
  font-weight: 700;
  background: var(--surface-2);
}

/* A page that produced nothing, and a page that failed, must be legible as
   such from the bar itself rather than only after selecting it. Each carries a
   text marker as well as the color, for the same reason. */
.pagechip--empty { color: var(--ink-2); border-style: dashed; }
.pagechip--empty::after { content: " · blank"; font-size: 0.6875rem; }
.pagechip--failed { color: var(--warn); border-color: var(--warn); }
.pagechip--failed::after { content: " · failed"; font-size: 0.6875rem; }

/* A page that rendered blank is a different outcome from one that was read and
   found empty: nothing was uploaded and no record exists. It is marked
   distinctly so the two are never confused on screen. */
.pagechip--blank { color: var(--warn); border-color: var(--warn); border-style: dashed; }
.pagechip--blank::after { content: " · blank render"; font-size: 0.6875rem; }

/* ------------------------------------------------- Thumbnails and zoom --- */
/* A page that nobody looked at is how a blank render passed for a reading, so
   every page this site processes is shown back as an image. The trigger is
   always a real button: it has to take focus, announce itself, and respond to
   Enter and Space, none of which a clickable div does. */

.thumb,
.pagethumb,
.pageview {
  display: block;
  padding: 0;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  font: inherit;
}

.thumb:hover,
.pagethumb:hover,
.pageview:hover { border-color: var(--amber-edge); }

.thumb:focus-visible,
.pagethumb:focus-visible,
.pageview:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.pageview { width: 100%; position: relative; }
.pageview:disabled { cursor: default; border-style: dashed; color: var(--ink-2); }

/* The image inside the trigger keeps the border it had as a bare image, so the
   visual weight of this block does not change now that it is clickable. A tall
   page is capped rather than allowed to run the column off the screen; the
   whole page is still there, and the enlarged view is one click away. */
.pageview .scan__page {
  border: 0;
  max-height: 32rem;
  object-fit: contain;
  object-position: top center;
}

.pageview__hint {
  display: block;
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  border-top: 1px solid var(--rule);
  padding: 0.4rem 0.6rem;
}

.pageview:hover .pageview__hint { color: var(--amber); }
.pageview:disabled .pageview__hint { color: var(--ink-2); }

/* Where the image on screen came from, stated rather than assumed. It is the
   whole value of showing it. */
.pageview__prov {
  margin: 0.5rem 0 0;
  color: var(--ink-2);
  line-height: 1.5;
}

/* -------------------------------------------- Per-page thumbnail strip --- */
/* Replaces the text chips: the fastest way to see that page 4 came out white is
   to look at page 4. The status marker stays, because a thumbnail alone does
   not say whether the page was stored, and color alone is not a distinction. */

.pagethumb {
  width: 7.5rem;
  flex: 0 0 auto;
}

.pagethumb__img {
  display: block;
  width: 100%;
  height: 6rem;
  object-fit: cover;
  object-position: top center;
  background: var(--surface-2);
  border-bottom: 1px solid var(--rule);
}

.pagethumb__n,
.thumb__cap {
  display: block;
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  padding: 0.3rem 0.45rem 0;
}

.pagethumb__tag,
.thumb__tag {
  display: block;
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  color: var(--ink-2);
  padding: 0 0.45rem 0.35rem;
}

.pagethumb[aria-current="true"] {
  border-color: var(--amber-edge);
  border-left-width: 3px;
  background: var(--surface-2);
}

.pagethumb[aria-current="true"] .pagethumb__n { font-weight: 700; }

.pagethumb--empty { border-style: dashed; }
.pagethumb--empty .pagethumb__tag { color: var(--ink-2); }
.pagethumb--failed { border-color: var(--warn); }
.pagethumb--failed .pagethumb__tag { color: var(--warn); font-weight: 700; }
.pagethumb--blank { border-color: var(--warn); border-style: dashed; }
.pagethumb--blank .pagethumb__tag { color: var(--warn); font-weight: 700; }

/* --------------------------------------------------- The enlarged view --- */
/* A native dialog: Escape, focus containment and an inert background come with
   it. Only the appearance is set here. */

.lightbox {
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
  padding: 0;
  width: min(92vw, 60rem);
  max-width: 92vw;
  max-height: 92vh;
  overflow: auto;
}

.lightbox::backdrop { background: rgba(0, 0, 0, 0.72); }

/* A native dialog is hidden until opened by the UA rule dialog:not([open]), but
   .docviewer sets display:flex, and an author declaration beats the UA rule
   whatever its specificity, so a closed viewer would otherwise render as an
   empty box in normal flow. Restore the hidden-when-closed behavior with an
   author rule that only matches while the dialog is closed; showModal() adds the
   open attribute, so an open dialog keeps its flex layout. */
.lightbox:not([open]) { display: none; }

.lightbox__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--rule);
  position: sticky;
  top: 0;
  background: var(--surface);
}

.lightbox__title {
  margin: 0;
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  color: var(--ink);
}

.lightbox__close {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.4rem 1rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface-2);
  color: var(--ink);
  cursor: pointer;
}

.lightbox__close:hover { border-color: var(--amber-edge); color: var(--amber); }
.lightbox__close:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.lightbox__img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--surface-2);
}

.lightbox__note {
  margin: 0;
  padding: 0.7rem 0.9rem;
  border-top: 1px solid var(--rule);
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  color: var(--ink-2);
  line-height: 1.55;
}

/* ------------------------------------------------ The document view --- */
/* The lakehouse document control ported to this page: the scanned page and its
   extracted text side by side, with deep zoom, a resizable split, a page search
   and find-on-image. The dialog gets a definite height so each pane scrolls in
   place rather than the whole dialog scrolling. */
.docviewer {
  width: min(96vw, 84rem);
  max-width: 96vw;
  height: min(92vh, 60rem);
  max-height: 92vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.docviewer__body {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
}

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

.dvpane--image { flex: 0 0 56%; border-right: 1px solid var(--rule); }
.dvpane--ocr { flex: 1 1 auto; }

.dvpane__bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--rule);
  background: var(--surface-2);
}

.dvpane__controls { display: flex; align-items: center; gap: 0.4rem; }

.dvbtn {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.dvbtn:hover:not(:disabled) { border-color: var(--amber-edge); color: var(--amber); }
.dvbtn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.dvbtn:disabled { opacity: 0.45; cursor: default; }

.dvhint { font-family: var(--ff-mono); font-size: 0.72rem; color: var(--ink-2); }
.dvmatch { font-family: var(--ff-mono); font-size: 0.72rem; color: var(--amber); font-weight: 700; }

/* Result-set navigation in the viewer bar (#87): prev/next walk the ordering of
   the list the modal was opened from, no wraparound, and the position reads
   N of M. Hidden when the page was opened on its own. */
.dvnav { display: flex; align-items: center; gap: 0.5rem; }
.dvnav__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.35rem 0.55rem;
}
.dvnav__icon { width: 1em; height: 1em; display: block; }
.dvnav__pos {
  font-family: var(--ff-mono);
  font-size: 0.72rem;
  color: var(--ink-2);
  white-space: nowrap;
  min-width: 4.5em;
  text-align: center;
}

.dvcanvas {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  background: var(--surface-2);
  cursor: grab;
  touch-action: none;
}
.dvcanvas--grabbing { cursor: grabbing; }
/* At the resolution ceiling (#109) the page is shown at full detail: the
   grab/pan cursor stays, but zoom affordances read as exhausted via the
   disabled zoom-in button this class accompanies. */
.dvcanvas--maxzoom { cursor: grab; }

.dvcontent {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  will-change: transform;
}

.dvimg { display: block; width: 100%; height: 100%; user-select: none; }

.dvoverlay { position: absolute; inset: 0; pointer-events: none; }

.dvbox { position: absolute; box-sizing: border-box; }
.dvbox--search {
  background: color-mix(in srgb, var(--amber) 30%, transparent);
  outline: 1px solid var(--amber-edge);
}
.dvbox--focus {
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  outline: 2px solid var(--amber);
}

/* Detect form (#77): alternating translucent stripes over each detected line
   band, with the band number tucked in the corner. */
.dvband {
  position: absolute;
  box-sizing: border-box;
  background: color-mix(in srgb, var(--accent, #2f6feb) 14%, transparent);
  outline: 1px solid color-mix(in srgb, var(--accent, #2f6feb) 45%, transparent);
}
.dvband--alt { background: color-mix(in srgb, var(--amber) 14%, transparent); outline-color: var(--amber-edge); }
.dvband::after {
  content: attr(data-band);
  position: absolute;
  right: 2px;
  top: 0;
  font-size: 9px;
  font-family: var(--mono, monospace);
  opacity: 0.75;
}
.dvschema { border-top: 1px solid var(--rule); padding: 8px 10px; max-height: 34%; display: flex; flex-direction: column; gap: 6px; }
/* display: flex above beats the UA [hidden] rule, so the hidden state needs
   its own rule (#116 follow-up): without it the empty draft-schema footer
   rendered on every viewer open even though scan.js sets hidden. */
.dvschema[hidden] { display: none; }
.dvschema__bar { display: flex; align-items: center; gap: 10px; }
.dvschema__label { font-size: 12px; color: var(--ink-soft, #667); flex: 1; }
.dvschema__json { margin: 0; overflow: auto; font-size: 11px; line-height: 1.45; background: var(--rule); border-radius: 6px; padding: 8px; }

.dvsplit {
  flex: 0 0 8px;
  cursor: col-resize;
  background: var(--rule);
  border-left: 1px solid var(--rule-strong);
  border-right: 1px solid var(--rule-strong);
}
.dvsplit:hover, .dvsplit:focus-visible { background: var(--amber-edge); outline: none; }

.dvsearch { display: flex; flex-direction: column; gap: 0.15rem; flex: 1 1 12rem; min-width: 8rem; }
.dvsearch__label {
  font-family: var(--ff-display);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.dvsearch__input {
  font-family: var(--ff-mono);
  font-size: 0.8rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
}
.dvsearch__input:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }

.dvtext {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 0.9rem 1rem;
  margin: 0;
  font-family: var(--ff-mono);
  font-size: 0.84rem;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--ink);
  user-select: text;
}
.dvempty { color: var(--ink-2); font-style: italic; }
.dvhl { background: color-mix(in srgb, var(--amber) 45%, transparent); color: var(--ink); }

/* Rendered extraction markdown (#103). A transcription that carries GENERATED
   markdown renders per-line blocks: markers are dropped at render time by the
   overlay in viewer-logic.js, so these classes only style what remains. The
   pane stays pre-wrap; each line is its own block, so hidden newlines still
   read as line breaks and blank lines become measured gaps. */
.dvmd-blk { margin: 0; }
.dvmd-gap { height: 0.7em; }
.dvmd-h { font-weight: 700; }
.dvmd-h1, .dvmd-h2 { font-size: 0.95rem; }
.dvmd-b { font-weight: 700; font-style: normal; }
.dvmd-li { position: relative; padding-left: 1.15em; }
.dvmd-li::before {
  content: '\2022';
  position: absolute;
  left: 0.25em;
  color: var(--ink-2);
}

/* Extracted-text skeleton (#97): paragraph-rhythm placeholder lines shown while
   the transcription fetch is in flight, so the pane never flashes "no extracted
   text" before a completed fetch. Reuses the .skel primitive, which is already
   static under prefers-reduced-motion. The pane is pre-wrap, so give each line
   a fixed height and its own rhythm rather than inheriting the text flow. */
.dvtext__skelline {
  height: 0.78rem;
  margin: 0.55rem 0;
  max-width: 100%;
}

/* Frame-shaped image skeleton (#97): fills the scan canvas while the image
   loads. Absolutely placed over the canvas (its positioned ancestor) so it
   covers the viewport and does not pan or zoom with dvcontent. */
.dvimgskel {
  position: absolute;
  inset: 0.75rem;
  border-radius: 6px;
  z-index: 1;
}
.dvimgskel[hidden] { display: none; }

.dvctxmenu {
  position: fixed;
  z-index: 40;
  display: flex;
  flex-direction: column;
  min-width: 11rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}
/* The class sets display:flex, which would otherwise beat the UA [hidden] rule
   and leave the menu on screen; restore hidden with the more specific selector. */
.dvctxmenu[hidden] { display: none; }
.dvctxitem {
  text-align: left;
  padding: 0.5rem 0.8rem;
  border: 0;
  background: none;
  color: var(--ink);
  font-family: var(--ff-mono);
  font-size: 0.8rem;
  cursor: pointer;
}
.dvctxitem:hover { background: var(--surface-2); color: var(--amber); }
.dvctxitem:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }

/* On a narrow screen the two panes stack: image over text, each scrolling. */
@media (max-width: 52rem) {
  .docviewer__body { flex-direction: column; }
  .dvpane--image { flex: 0 0 50%; border-right: 0; border-bottom: 1px solid var(--rule); }
  .dvsplit { display: none; }
}

/* ------------------------------------------------- Batch → Extract --- */
/* A batch that is still running must read differently from one that finished,
   at a glance and in the accessible name, never by color alone. */

.stage--batch { margin-bottom: 1.5rem; border-left: 3px solid var(--amber-edge); }

.batch__state {
  margin: 0 0 0.6rem;
  font-family: var(--ff-mono);
  font-size: 0.875rem;
  line-height: 1.55;
  padding-left: 0.8rem;
  border-left: 3px solid var(--rule-strong);
}

.batch__state--running { border-left-color: var(--amber-edge); }
.batch__state--done { border-left-color: var(--rule-strong); color: var(--ink); }
.batch__state--stalled { border-left-color: var(--warn); }
.batch__state--failed { border-left-color: var(--warn); color: var(--warn); font-weight: 700; }

/* A native progress element carries its value to assistive technology and needs
   no script writing a style attribute, which the CSP forbids in any case. */
.batch__meter {
  display: block;
  width: 100%;
  height: 0.6rem;
  appearance: none;
  border: 1px solid var(--rule-strong);
  background: var(--surface-2);
}

.batch__meter::-webkit-progress-bar { background: var(--surface-2); }
.batch__meter::-webkit-progress-value { background: var(--amber-edge); }
.batch__meter::-moz-progress-bar { background: var(--amber-edge); }

.batch__counts { margin: 0.5rem 0 1rem; color: var(--ink-2); line-height: 1.55; }

.batchgrid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: 0.8rem;
}

.batchitem {
  border: 1px solid var(--rule);
  background: var(--surface);
  padding: 0.5rem;
}

.batchitem .thumb { width: 100%; border-color: var(--rule); }

.batchitem .thumb__img {
  display: block;
  width: 100%;
  height: 7rem;
  object-fit: cover;
  object-position: top center;
  background: var(--surface-2);
  border-bottom: 1px solid var(--rule);
}

.batchitem__name {
  margin: 0.45rem 0 0.15rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.batchitem__state {
  margin: 0;
  font-family: var(--ff-mono);
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--ink-2);
}

/* Each outcome carries a border weight as well as a color, so the four states
   are distinguishable without relying on hue. */
.batchitem--queued { border-style: dashed; }
.batchitem--running { border-left: 3px solid var(--amber-edge); }
.batchitem--done { border-left: 3px solid var(--rule-strong); }
.batchitem--review { border-left: 3px solid var(--amber-edge); }
.batchitem--review .batchitem__state { color: var(--ink); }
.batchitem--failed { border-left: 3px solid var(--warn); }
.batchitem--failed .batchitem__state { color: var(--warn); font-weight: 700; }
/* A page that has been re-queued through the rerun-OCR controls, before the next
   poll reports its real state. Reuses the running weight so it reads as in flight. */
.batchitem--reprocessing { border-left: 3px solid var(--amber-edge); }
.batchitem--reprocessing .batchitem__state { color: var(--ink); font-weight: 700; }

/* ---- Rerun OCR controls (#1320) --------------------------------------- */
/* A compact disclosure under a rerunnable page: Retry / Escalate / Rotate. It
   stays collapsed so a grid of pages is not overwhelmed, and every control is a
   real form element styled through the sheet, never an inline style. */
.rerun {
  margin-top: 0.45rem;
  border-top: 1px solid var(--rule);
  padding-top: 0.4rem;
}
.rerun__summary {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--amber-edge);
  cursor: pointer;
}
.rerun__summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.rerun__body {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.45rem;
}
.rerun__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
}
.rerun__label {
  font-family: var(--ff-mono);
  font-size: 0.7rem;
  color: var(--ink-2);
}
.rerun__btn {
  font-size: 0.7rem;
  padding: 0.3rem 0.7rem;
  letter-spacing: 0.04em;
}
.rerun__btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* ---- Stylesheet-owned dropdown (#118) --------------------------------- */
/* The Escalate / Rotate pickers. A native select's option list is rendered by
   the browser at the browser's size: oversized for this bar's type and free
   to bleed past the bottom of the panel. This list is ours: rerun typography,
   a content-sized cap with its own scrollbar, and an upward-opening variant
   for a trigger near the bottom edge. scan.js clamps the max-height through
   the CSSOM to the room that actually exists. */
.qfdd { position: relative; display: inline-block; }
.qfdd__btn {
  font-family: var(--ff-mono);
  font-size: 0.72rem;
  padding: 0.25rem 1.1rem 0.25rem 0.35rem;
  border: 1px solid var(--rule);
  background: var(--surface-2);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.qfdd__btn::after {
  content: "";
  position: absolute;
  right: 0.4rem;
  top: 50%;
  margin-top: -0.1rem;
  border-left: 0.28rem solid transparent;
  border-right: 0.28rem solid transparent;
  border-top: 0.32rem solid var(--ink-2);
}
.qfdd__btn:disabled { opacity: 0.55; cursor: not-allowed; }
.qfdd__list {
  position: absolute;
  left: 0;
  top: calc(100% + 2px);
  z-index: 30;
  min-width: 100%;
  max-width: 16rem;
  margin: 0;
  padding: 0.1rem;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  box-shadow: 0 0.4rem 1.1rem rgba(0, 0, 0, 0.28);
  max-height: 12.5rem;
  overflow-y: auto;
}
/* display on the class must not beat the UA hidden rule. */
.qfdd__list[hidden] { display: none; }
.qfdd__list--up { top: auto; bottom: calc(100% + 2px); }
.qfdd__opt {
  display: block;
  width: 100%;
  font-family: var(--ff-mono);
  font-size: 0.72rem;
  line-height: 1.4;
  padding: 0.25rem 0.4rem;
  border: 0;
  background: transparent;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qfdd__opt:hover,
.qfdd__opt:focus { background: var(--surface-2); }
.qfdd__opt:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.qfdd__opt[aria-selected="true"] { font-weight: 700; color: var(--amber); }
.rerun__note {
  margin: 0;
  font-family: var(--ff-mono);
  font-size: 0.7rem;
  line-height: 1.5;
  color: var(--ink-2);
}

/* ------------------------------------------------------ Real documents --- */
/* What an example does NOT demonstrate is set at the same size and weight as
   what it does. Shrinking or footnoting the negative list is how a reader takes
   a good result on one capability as a claim about another. */

.examplegrid {
  display: grid;
  gap: 1.5rem;
  margin-top: 2rem;
}

.example {
  display: grid;
  grid-template-columns: 16rem 1fr;
  gap: 1.5rem;
  border: 1px solid var(--rule);
  background: var(--surface);
  padding: 1.2rem;
}

.example--poor { border-left: 3px solid var(--warn); }

.example__thumb { width: 100%; align-self: start; }

.example__thumb .thumb__img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--surface-2);
  border-bottom: 1px solid var(--rule);
}

.example__body { min-width: 0; }

.example__title {
  font-family: var(--ff-display);
  font-size: 1.15rem;
  margin: 0 0 0.2rem;
}

.example__sub {
  margin: 0 0 0.8rem;
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  color: var(--ink-2);
}

.example__flag {
  margin: 0 0 0.8rem;
  padding: 0.5rem 0.7rem;
  border-left: 3px solid var(--warn);
  background: var(--surface-2);
  font-size: 0.9375rem;
  line-height: 1.55;
}

.example__why { margin: 0 0 1rem; line-height: 1.6; }

.claims {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin: 0 0 1rem;
}

.claim { border-top: 2px solid var(--rule-strong); padding-top: 0.5rem; }
.claim--against { border-top-color: var(--warn); }

.claim__label {
  margin: 0 0 0.4rem;
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.claim__list { margin: 0; padding-left: 1.1rem; line-height: 1.55; }
.claim__list li { margin-bottom: 0.3rem; }

.example__text { margin: 1rem 0 0; }

.example__text summary {
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  cursor: pointer;
  color: var(--ink-2);
}

.example__text summary:hover { color: var(--ink); }
.example__text summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.example__text .io__text { white-space: pre-wrap; margin-top: 0.6rem; }

.example__src {
  margin: 1rem 0 0;
  color: var(--ink-2);
  line-height: 1.5;
}

@media (max-width: 52rem) {
  .example { grid-template-columns: 1fr; gap: 1rem; }
  .example__thumb { max-width: 20rem; }
  .claims { grid-template-columns: 1fr; }
}

@media (max-width: 34rem) {
  .pagethumb { width: 6rem; }
  .pagethumb__img { height: 4.5rem; }
  .batchgrid { grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); }
}

/* A transition is a convenience, not a requirement, and for some visitors it is
   a symptom trigger. Everything above is legible with none of it. */
@media (prefers-reduced-motion: no-preference) {
  .lightbox { transition: opacity 140ms ease-out; }
  .thumb,
  .pagethumb,
  .pageview { transition: border-color 120ms ease-out; }
}

/* ===================================================== corpus search == */
/* The Search page. It reuses the demo's field, button, tag and document-viewer
   styles; what follows is only the query form and the hit list. */

.searchform { margin-top: 0.5rem; }

.searchbox {
  width: 100%;
  font-family: var(--ff-mono);
  font-size: 1.0625rem;
  line-height: 1.5;
  padding: 0.8rem 0.95rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
}
.searchbox:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }

/* ---- the smart search box: operator color-coding (#1322) ---------------- */
/* A highlight layer is positioned BEHIND a transparent-text input. Both share
   identical typography, padding and a 1px border so the input's caret sits on
   top of the colored glyphs pixel-for-pixel. Every metric here also lives on
   .searchbox above; keep the two in lockstep. Colors only, never a weight
   change, so glyph advances match and nothing drifts after an operator. */
.searchbox-wrap { position: relative; }
.searchbox-wrap .searchbox {
  position: relative;
  z-index: 1;
  background: transparent;
  color: transparent;
  caret-color: var(--ink);
}
.searchbox-wrap .searchbox::placeholder { color: var(--ink-2); }
/* Normalize the search input so its text box matches the overlay exactly. */
.searchbox::-webkit-search-decoration,
.searchbox::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.searchbox-hl {
  position: absolute;
  inset: 0;
  z-index: 0;
  margin: 0;
  font-family: var(--ff-mono);
  font-size: 1.0625rem;
  line-height: 1.5;
  padding: 0.8rem 0.95rem;
  border: 1px solid transparent;
  background: var(--surface);
  color: var(--ink);
  white-space: pre;
  overflow: hidden;
  pointer-events: none;
}
/* Operator families, each distinct and theme-aware. Contrast holds on the
   --surface field in both light and dark. */
.qtok--op { color: var(--qop); }
.qtok--neg { color: var(--qneg); }
.qtok--phrase { color: var(--qphrase); }
.qtok--paren { color: var(--qparen); }

/* The live syntax hint: non-blocking, shown only when the query is malformed. */
.searchsyntax {
  margin: 0.5rem 0 0;
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--warn);
}
.searchsyntax::before { content: "\26A0\FE0E  "; }

/* The "Did you mean" line, streamed in from Mnemo over SSE. Same left-border
   device as the lexical-fallback notice, in the focus-blue family to read as a
   helpful suggestion rather than a warning. */
/* display:flex on the class BEATS the [hidden] attribute's UA display:none,
   so the empty, waiting suggest box rendered as a stray bordered strip under
   the search line. The attribute must win: hidden means gone. */
.suggest[hidden] { display: none; }

.suggest {
  margin-top: 1rem;
  border-left: 3px solid var(--focus);
  background: var(--surface);
  padding: 0.7rem 1rem;
  font-family: var(--ff-mono);
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink-2);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.6rem;
}
.suggest__label { color: var(--ink-2); }
.suggest__q { color: var(--ink); font-weight: 700; overflow-wrap: anywhere; }
.suggest__apply {
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--rule-strong);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}
.suggest__apply:hover { border-color: var(--amber-edge); background: var(--surface-2); }
.suggest__apply:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* The ranking control sits on the left of the form foot, the submit on the right,
   reusing the demo's .field__foot flex row. */
.modeset {
  border: 0;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1rem;
}
.modeset__legend {
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0;
  margin-right: 0.5rem;
}
.mode {
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
}
.mode input { accent-color: var(--amber-edge); }

/* A ghost button for a secondary action (Load more): the same shape as .btn but
   drawn as an outline, so it does not compete with the primary submit. */
.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule-strong);
}
.btn--ghost:hover { filter: none; border-color: var(--amber-edge); background: var(--surface-2); }

/* The lexical-fallback advisory. Left border and amber label, in the family of
   the WARNING/CAUTION/NOTE device without being one of them. */
.searchnotice {
  margin-top: 1.25rem;
  border-left: 3px solid var(--amber-edge);
  background: var(--surface);
  padding: 0.75rem 1rem;
}
.searchnotice p {
  margin: 0;
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 46rem;
}

.results { margin-top: 1.75rem; }
.results:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; }
.results__count { color: var(--ink-2); margin: 0 0 1rem; }
.results__foot { margin-top: 1.5rem; }

.hitlist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.85rem;
}

/* A hit is a full-width button so the whole card is one keyboard target that
   announces itself, exactly like the batch thumbnails. */
.hit__btn {
  width: 100%;
  text-align: left;
  display: grid;
  gap: 0.6rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  /* A result card is full of text worth copying (the key path, the marks, the
     snippet). Buttons suppress text selection by default; opt back in so a
     click-drag selects. search.js skips the open when a selection was made. */
  -webkit-user-select: text;
  user-select: text;
}
.hit__btn:hover { border-color: var(--amber-edge); background: var(--surface-2); }
.hit__btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

.hit__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.85rem;
}
.hit__source {
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: clamp(1.05rem, 2.1vw, 1.3rem);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.15;
}
.hit__type { margin-top: 0; }
.hit__score {
  margin-left: auto;
  color: var(--ink-2);
  white-space: nowrap;
}

.hit__marks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.6rem;
  font-size: 0.75rem;
  color: var(--ink-2);
}
.hit__mark {
  border: 1px solid var(--rule);
  padding: 0.05rem 0.4rem;
  letter-spacing: 0.04em;
}
.hit__mark--exact {
  border-color: var(--amber-edge);
  color: var(--amber);
  font-weight: 700;
}
.hit__key { overflow-wrap: anywhere; }

.hit__snippet {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.6;
  max-width: 60rem;
  color: var(--ink);
}
.hitmark {
  background: color-mix(in srgb, var(--amber) 45%, transparent);
  color: var(--ink);
  padding: 0 0.05em;
}

.hit__open {
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--amber);
}

@media (max-width: 40rem) {
  .hit__score { margin-left: 0; }
  .searchbox { font-size: 1rem; }
}

/* ------------------------------------------------------------- RUNS -- */
/* The run-history list (runs.html). One row per processed archive: what kind of
   run it was, its status, the processed/total/failed counts, the source archive
   and the run date. Built in the family of the search hit list, but a run is a
   static article rather than a button: there is no per-run drill-down to open,
   so nothing on the row claims to be interactive. Status is carried by type and
   border, never by color alone, the same rule the advisories follow. */
.runs { margin-top: 1.75rem; }
.runs:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; }

.runlist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.85rem;
}
.run__card {
  display: grid;
  gap: 0.7rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--rule-strong);
  background: var(--surface);
  color: var(--ink);
}

.run__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
}
/* The KIND badge reuses .tag; the two known kinds are drawn apart so A4A and
   Microfiche read as distinct at a glance without relying on the label alone. */
.run__type { margin-top: 0; }
.run__type--a4a { border-color: var(--amber-edge); color: var(--amber); }
.run__type--microfiche {
  border-color: var(--rule-strong);
  color: var(--ink-2);
  border-style: dashed;
}

/* Status chip. Border weight and style carry the state; the label carries it in
   words. Complete is solid, processing is dashed and in motion by nothing but
   its label, empty is muted. */
.run__stat {
  font-family: var(--ff-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.15rem 0.45rem;
  border: 1px solid var(--rule);
  color: var(--ink-2);
  white-space: nowrap;
}
.run__stat--complete { border-color: var(--amber-edge); color: var(--amber); }
.run__stat--processing { border-style: dashed; border-color: var(--rule-strong); color: var(--ink); }
.run__stat--empty { border-color: var(--rule); color: var(--ink-2); }
.run__stat--unknown { border-color: var(--rule); color: var(--ink-2); }

.run__date {
  margin-left: auto;
  color: var(--ink-2);
  white-space: nowrap;
}

.run__source {
  margin: 0;
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: clamp(1rem, 2vw, 1.2rem);
  letter-spacing: 0.02em;
  line-height: 1.2;
  overflow-wrap: anywhere;
}

.run__counts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.6rem;
  font-size: 0.75rem;
  color: var(--ink-2);
}
.run__count {
  border: 1px solid var(--rule);
  padding: 0.05rem 0.4rem;
  letter-spacing: 0.04em;
}
.run__count--failed { border-color: var(--warn); color: var(--warn); font-weight: 700; }
.run__id { overflow-wrap: anywhere; }

/* The progress track. Decorative and aria-hidden: the counts above say the same
   thing for assistive tech. The fill width is set on .style.width through the
   CSSOM in runs.js, which the CSP permits; there is no style attribute here. */
.run__bar {
  height: 0.4rem;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  overflow: hidden;
}
.run__fill {
  height: 100%;
  width: 0;
  background: var(--rule-strong);
}
.run__fill--done { background: var(--amber-edge); }
.run__fill--partial { background: var(--warn); }

@media (max-width: 40rem) {
  .run__date { margin-left: 0; }
}

/* ------------------------------------------------ word-locate (viewer) --- */
/* The clickable word tokens in the OCR pane. A token is real flowing text:
   it inherits font and spacing so the panel reads exactly like the plain
   rendering, and only the hover/hit states reveal that each word is live. */
.dvtok { cursor: pointer; border-radius: 2px; }
.dvtok:hover { background: color-mix(in srgb, var(--amber) 18%, transparent); }
.dvtok--hit { background: color-mix(in srgb, var(--amber) 45%, transparent); color: var(--ink); }

/* The flash a located word gets: an EDGE treatment, never a fill (#119). The
   old flash opened at a 70% amber fill, which in the light theme is a dark
   band that covered the very word the reader searched for. The highlight must
   never reduce legibility of the ink under it, so the interior stays
   near-transparent for the whole animation and the pulse lives on the edges:
   a wide soft ring that tightens into the resting focus outline. */
.dvbox--flash { animation: dvflash 1.1s ease-out 2; }
@keyframes dvflash {
  0% {
    background: color-mix(in srgb, var(--amber) 10%, transparent);
    outline-width: 3px;
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--amber-edge) 55%, transparent);
  }
  100% {
    background: color-mix(in srgb, var(--amber) 6%, transparent);
    outline-width: 2px;
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--amber-edge) 0%, transparent);
  }
}
@media (prefers-reduced-motion: reduce) {
  .dvbox--flash { animation: none; }
}

/* The word-click toggle reads as pressed/unpressed through its border. */
.dvbtn--toggle[aria-pressed="false"] { border-style: dashed; }

/* ------------------------------------------------- runs drill-down --- */
.run__open { margin-top: 0.6rem; }
.run__pages { margin-top: 0.75rem; border-top: 1px solid var(--rule); padding-top: 0.75rem; }
.run__pages-status { min-height: 1.2rem; }

/* ------------------------------------- handwritten example documents --- */
.hwlist {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.75rem;
}
.hwitem {
  border: 1px solid var(--rule-strong);
  border-radius: 6px;
  padding: 0.75rem 0.85rem;
  background: var(--panel);
}
.hwitem__title { margin: 0 0 0.3rem; font-weight: 600; }
.hwitem__title a { color: var(--amber); text-decoration-thickness: 1px; }
.hwitem__meta { margin: 0 0 0.35rem; }
.hwitem__desc { margin: 0; }

/* ------------------------------------------------ search skeletons --- */
/* Content-shaped placeholders that stand in for hit cards the instant a new
   search starts, so stale results never linger under a fresh query. */
.hit--skeleton .hit__btn { cursor: default; }
.skel {
  border-radius: 4px;
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  animation: skelpulse 1.2s ease-in-out infinite;
}
.skel--line { height: 0.85rem; margin: 0.35rem 0; }
.skel--w40 { width: 40%; }
.skel--w70 { width: 70%; }
.skel--block { height: 2.6rem; margin-top: 0.5rem; }
@keyframes skelpulse {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; }
}

/* ------------------------------------------- query-line intellisense --- */
/* The completion list renders INLINE below the input, pushing the mode and
   Search controls down rather than floating over them: an overlay dropdown
   sat exactly on top of the Search button and swallowed the click aimed at
   it (caught by the Playwright pass). Inline can never intercept an action. */
.searchcomplete {
  margin: 0.15rem 0 0;
  padding: 0.25rem;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--rule-strong);
  border-radius: 6px;
  max-height: 11rem;
  overflow-y: auto;
}
.searchcomplete__item {
  padding: 0.3rem 0.55rem;
  border-radius: 4px;
  cursor: pointer;
  font-family: var(--mono, monospace);
  font-size: 0.85rem;
}
.searchcomplete__item:hover,
.searchcomplete__item--active {
  background: color-mix(in srgb, var(--amber) 22%, transparent);
  color: var(--ink);
}

/* ----------------------------------------- opportunity framing (hero) --- */
.hero__opp {
  margin: 0.25rem 0 0.6rem;
  color: var(--amber);
  letter-spacing: 0.04em;
}

/* The evaluator's path: the notice's four asks, in its own order, each
   landing on the part of the site that answers it. Same technical-manual
   grammar as the rest of the page: ruled cards, numbered plates. */
.soonav {
  list-style: none;
  margin: 0 0 1.25rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.6rem;
}
.soonav__item a {
  display: block;
  height: 100%;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--rule-strong);
  border-radius: 6px;
  background: var(--panel);
  text-decoration: none;
  color: var(--ink);
  font-weight: 600;
}
.soonav__item a:hover { border-color: var(--amber-edge); }
.soonav__item a:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.soonav__n {
  display: inline-block;
  min-width: 1.4rem;
  margin-right: 0.35rem;
  text-align: center;
  border: 1px solid var(--amber-edge);
  border-radius: 4px;
  color: var(--amber);
  font-family: var(--mono, monospace);
  font-size: 0.85rem;
}
.soonav__d {
  display: block;
  margin-top: 0.25rem;
  font-weight: 400;
  font-size: 0.85rem;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
}

/* A page with text but no per-word geometry. The word tools are inert on it, so
   the panel is marked to match the stated reason in the note line: the text is
   still readable, searchable and copyable, it simply is not clickable word by
   word. Without this the panel looked identical to a geometry-backed page, so a
   dead control read as a broken one. */
.dvtext--nogeo .dvtok { cursor: text; }

/* The run's narration and closing summary, under the bar it belongs to. Sized
   like the counts line rather than the heavier state line: the state line says
   what is happening right now and should carry the eye, this says what it means. */
.batch__summary {
  margin: 0.5rem 0 0;
  color: var(--ink-2);
  line-height: 1.55;
}
.field__progress .stage--batch { margin-top: 1rem; }

/* ------------------------------------------------------ audit page --- */
/* The chain-proof panel and its readouts (audit.html). Same ruled-card
   grammar as the rest of the site; the badge states borrow the existing
   hues: the phrase green for an intact proof, --warn for a break, neutral
   rules for restricted/unavailable, so no new color tokens are introduced. */
.adpanel {
  border: 1px solid var(--rule-strong);
  border-radius: 6px;
  background: var(--surface);
  padding: 1rem 1.15rem;
  max-width: 46rem;
}
.adpanel__head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.adbadge {
  font-family: var(--ff-mono);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--rule-strong);
  border-radius: 4px;
  color: var(--ink);
}
.adbadge--intact { border-color: var(--qphrase); color: var(--qphrase); }
.adbadge--broken { border-color: var(--warn); color: var(--warn); }
.adbadge--restricted,
.adbadge--unavailable { border-style: dashed; color: var(--ink-2); }
.adverified { color: var(--ink-2); }
.adverify { margin-left: auto; }
.adpanel__detail {
  margin: 0.6rem 0 0;
  color: var(--ink-2);
  line-height: 1.55;
}
.adfacts {
  margin: 0.85rem 0 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0.6rem;
}
.adfacts > div {
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.5rem 0.65rem;
}
.adfacts dt {
  font-size: 0.8125rem;
  color: var(--ink-2);
  margin: 0 0 0.2rem;
}
.adfacts dd {
  margin: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.adcounts { margin-top: 1rem; }
.adcounts__title {
  font-size: 0.9375rem;
  margin: 0 0 0.5rem;
}
.adcounts__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.45rem;
}
.adcount__head {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
}
.adcount__label { font-size: 0.9375rem; }
.adcount__n { color: var(--ink-2); }
.adcount__bar {
  height: 0.35rem;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: var(--surface-2);
  overflow: hidden;
}
.adcount__fill {
  height: 100%;
  background: var(--amber-edge);
}
.adcounts__note { margin: 0.5rem 0 0; color: var(--ink-2); }

/* The live announcement strip: newest first, bounded, content never shown
   because the stream carries none. */
.adlive__count { margin: 0 0 0.5rem; color: var(--ink-2); }
.adlive__list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 46rem;
  border: 1px solid var(--rule-strong);
  border-radius: 6px;
  background: var(--surface);
  max-height: 22rem;
  overflow-y: auto;
}
.adlive__item {
  display: flex;
  gap: 0.9rem;
  align-items: baseline;
  padding: 0.45rem 0.85rem;
  border-bottom: 1px solid var(--rule);
}
.adlive__item:last-child { border-bottom: 0; }
.adlive__time { color: var(--ink-2); white-space: nowrap; }

/* The choke-point ladder. */
.adstages {
  list-style: none;
  margin: 0 0 1.25rem;
  padding: 0;
  display: grid;
  gap: 0.5rem;
  max-width: 46rem;
}
.adstage {
  border: 1px solid var(--rule-strong);
  border-radius: 6px;
  background: var(--surface);
  padding: 0.55rem 0.75rem;
}
.adstage__n {
  display: inline-block;
  min-width: 1.4rem;
  margin-right: 0.45rem;
  text-align: center;
  border: 1px solid var(--amber-edge);
  border-radius: 4px;
  color: var(--amber);
}
.adstage__d { color: var(--ink-2); }

/* The administrator deep link into the governance ledger. */
.adledgerlink { font-weight: 600; color: var(--amber); }
.adledgerlink__hint {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  color: var(--ink-2);
}

/* -------------------------------------------- run timing waterfall --- */
/* The per-run choke-point view on the Runs page: one bar per finished page,
   spanning that page's own start to its finish, on the run's shared clock. A
   bar is the page's duration, not its elapsed-since-the-run-started
   (quantumflight#89).

   The x-axis is TIME (quantumflight#93): the axis strip rides the same grid
   as a row, so a tick and the bars beneath it line up, and bars that overlap
   horizontally are pages that ran at the same time. All geometry (tick
   offsets, bar offset and width) is set through the CSSOM, never a style
   attribute, because the strict CSP forbids inline style. */
.run__timing { margin-top: 0.75rem; }
.rtsummary { margin: 0 0 0.5rem; color: var(--ink-2); }

/* The zoom controls, in the document viewer's order. */
.rtctl {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0.4rem;
}
.rtctl__btn { padding: 0.15rem 0.55rem; font-size: 0.85rem; }
.rtctl__caption { color: var(--ink-2); margin-left: 0.25rem; }

.rtplot {
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--surface-2);
  padding: 0.4rem 0.55rem;
  touch-action: pan-y;
}
.rtplot:focus-visible { outline: 2px solid var(--amber-edge); outline-offset: 2px; }
.rtplot--grabbing { cursor: grabbing; }

/* The axis strip: the row grid again, so the ticks sit over the tracks. */
.rtaxis {
  display: grid;
  grid-template-columns: 5.5rem 1fr 4.5rem;
  align-items: end;
  gap: 0.6rem;
  padding-bottom: 0.15rem;
  margin-bottom: 0.2rem;
  border-bottom: 1px solid var(--rule);
}
.rtaxis__track { position: relative; height: 1.15rem; }
.rttick {
  position: absolute;
  bottom: 0;
  width: 1px;
  height: 0.4rem;
  background: var(--rule);
}
.rttick__label {
  position: absolute;
  bottom: 0.45rem;
  left: 0;
  transform: translateX(-50%);
  white-space: nowrap;
  color: var(--ink-2);
}
.rtlist {
  list-style: none;
  margin: 0;
  padding: 0;
}
.rtrow {
  display: grid;
  grid-template-columns: 5.5rem 1fr 4.5rem;
  align-items: center;
  gap: 0.6rem;
  padding: 0.12rem 0;
}
.rtrow__label { color: var(--ink-2); white-space: nowrap; }
.rtrow__track { position: relative; height: 0.6rem; }
.rtrow__bar {
  position: absolute;
  top: 0;
  height: 100%;
  border-radius: 2px;
  background: var(--amber-edge);
  min-width: 2px;
}
.rtrow__bar--failed { background: var(--warn); }
.rtrow__bar--slowest { outline: 2px solid var(--warn); outline-offset: 1px; }
/* A bar running past an edge of the view loses its rounding there, so a
   clipped page never reads as one that finished at the edge. */
.rtrow__bar--clipl { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.rtrow__bar--clipr { border-top-right-radius: 0; border-bottom-right-radius: 0; }
/* The queue-wait segment of a bar. It has width only once the pipeline
   records a processing start (vforce-lakehouse#1682); until then every bar is
   single-tone, which is the honest picture of what is known today. */
.rtseg { position: absolute; top: 0; left: 0; height: 100%; border-radius: 2px 0 0 2px; }
.rtseg--queue { background: var(--rule); opacity: 0.85; }
.rtrow__dur { color: var(--ink-2); text-align: right; }
