/* App stylesheet. Be Vietnam Pro / JetBrains Mono, white paper surface, blue #255CFF primary CTA.*/
/* Fonts self-hosted (latin subset) under static/fonts/ — no third-party runtime request,
   works offline. JetBrains Mono is a single variable file covering the 400–600 range. */
@font-face {
  font-family: "Be Vietnam Pro";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/be-vietnam-pro-400.woff2") format("woff2");
}
@font-face {
  font-family: "Be Vietnam Pro";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/be-vietnam-pro-700.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/static/fonts/jetbrains-mono.woff2") format("woff2");
}

:root {
  --c-paper: #FFFFFF;
  --c-ink: #0A0A0A;
  --c-body: #444444;
  --c-mute: #767676;
  --c-accent: #255CFF;
  --f-sans: "Be Vietnam Pro", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --f-mono: "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace;
}

body {
  font-family: var(--f-sans);
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 40px;
  background: var(--c-paper);
  color: var(--c-ink);
}

img.logo {
  width: min(420px, 80vw);
  height: auto;
}

/* Signed-in home: a centered logo (like the landing) over a welcome + who-you-are, a
   fixed distance below the navbar rather than floated to the vertical center. */
main.home-hero {
  align-self: center;
  margin-top: 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 20px;
}
.home-welcome {
  margin: 0;
  font-size: 30px;
  font-weight: 700;
  color: var(--c-ink);
}
.home-sub {
  margin: 0;
  font-family: var(--f-mono);
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--c-mute);
}

/* The landing has no navbar, yet its logo sits where /home's does: 108px + 80px. */
body:has(main.landing) {
  justify-content: flex-start;
}
main.landing {
  margin-top: 188px;
  gap: 40px;
}

dl.card {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: 10px 20px;
  align-items: baseline;
}
dl.card dt {
  font-family: var(--f-mono);
  font-size: 14px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-mute);
  text-align: left;
  align-self: center;
}
dl.card dd {
  margin: 0;
  font-size: 14px;
  font-weight: 400;
  color: var(--c-ink);
}

p {
  color: var(--c-body);
}

/* Log out — upper-right, same mono type as the name/role/tenant labels. */
a.logout {
  position: fixed;
  top: 24px;
  right: 24px;
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-mute);
  text-decoration: none;
}
a.logout:hover,
a.logout:focus-visible {
  color: var(--c-ink);
}

/* Top navigation bar — brand left, tabs, logout right; a fixed full-width header. */
nav.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  height: 60px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 32px;
  padding: 0 28px;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid rgba(10, 10, 10, 0.08);
}
nav.nav .brand {
  font-family: var(--f-mono);
  font-weight: 400;
  font-size: 15px;
  letter-spacing: 0.3em;
  color: var(--c-ink);
  text-decoration: none;
}
nav.nav .nav-tabs {
  display: flex;
  gap: 26px;
  flex: 1;
  align-items: center;
}
nav.nav .nav-tab,
nav.nav .nav-logout {
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-mute);
  text-decoration: none;
  transition: color 0.15s ease;
}
/* Logout is a form post (ADR 0013); strip the button chrome so it reads as a tab. */
nav.nav .nav-logout-form {
  margin: 0;
}
nav.nav .nav-logout {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
nav.nav .nav-tab {
  padding: 20px 0;
  border-bottom: 2px solid transparent;
}
nav.nav .nav-tab[aria-current="page"] {
  color: var(--c-ink);
  border-bottom-color: var(--c-accent);
}
nav.nav .nav-tab:hover,
nav.nav .nav-logout:hover {
  color: var(--c-ink);
}

/* Signed-in pages carry the fixed nav: top-left align the content, clear of the header. */
body:has(nav.nav) {
  justify-content: flex-start;
  align-items: flex-start;
  padding: 108px 48px 48px 78px;
}

/* The Capture page stands on a grey ground, so each stage reads as one white card on it. */
body:has(main.provision),
body:has(main.pair) {
  padding-left: 48px;
  background: #F5F6F8;
}
/* One column for the stage and the Pair again docked over its card's bottom bar. */
main.provision,
main.pair {
  position: relative;
  width: 100%;
  max-width: 1000px;
  margin: 0 auto;
}
/* What the stage shows until a screen is picked: the Theia eye, its rings turning. */
.stage-loading {
  min-height: 420px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--c-accent);
}
.stage-loading circle[class] {
  transform-origin: 50px 50px;
  animation: searching-turn 3.2s linear infinite;
}
.stage-loading .r2 {
  animation-duration: 5s;
  animation-direction: reverse;
}
.stage-loading .r3 {
  animation-duration: 7s;
}
.stage-loading .r4 {
  animation-duration: 9s;
  animation-direction: reverse;
}
@media (prefers-reduced-motion: reduce) {
  .stage-loading circle[class] {
    animation: none;
  }
}
/* Named for a screen reader, not drawn: the page heading a tab names visually, and the
   results announcement /search makes where the rows and the pager speak for themselves. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* The small-caps header the unauthenticated /privacy page carries. The signed-in pages
   are named by their nav tab instead, so none of them prints its own title twice. */
.page-title {
  margin: 0 0 26px;
  font-size: 38px;
  font-weight: 600;
  font-variant: small-caps;
  letter-spacing: 0.06em;
  color: var(--c-ink);
}
/* One rule for a capture stage's title and for the /search and /settings page titles:
   the stage subheader is the scale the signed-in pages are named at. */
.stage-title,
.s-head,
.t-head {
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--c-ink);
}
.stage-title {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 11px;
}
/* 32px over the first block, and the only gap above it: on /search the form and the viewer
   both open directly under this, so one owner keeps the two reading identically. */
.s-head,
.t-head {
  margin: 0 0 32px;
  max-width: 460px;
}
/* A pair stage's content column: title, sub-copy, and its button(s). */
.panel {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 26px;
}
.stage-sub {
  margin: 0;
  font-size: 16px;
  line-height: 1.9;
  color: var(--c-body);
}
.stage-sub span {
  font-family: var(--f-mono);
  font-size: 13px;
  font-weight: 500;
  color: var(--c-ink);
}
/* Not-connected's wait line: a turning arc beside the words. */
.searching {
  display: flex;
  align-items: center;
  gap: 10px;
}
.searching svg {
  flex: none;
  color: var(--c-accent);
  animation: searching-turn 0.9s linear infinite;
}
@keyframes searching-turn {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .searching svg {
    animation: none;
  }
}
.stage-sub a,
.gate-steps a {
  color: var(--c-accent);
  text-decoration: none;
}
.stage-sub a:hover,
.gate-steps a:hover {
  text-decoration: underline;
}

/* A wait line's words sit in a span, which stays body text on one line. */
.searching span {
  font: inherit;
  color: inherit;
  white-space: nowrap;
}

/* Setup incomplete's checklist: one row per prerequisite, ticked, crossed, or dashed. */
.setup-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: 14px;
}
.setup-list li {
  display: flex;
  align-items: center;
  gap: 12px;
  color: #b91c1c;
}
.setup-list li.done,
.setup-list li.unknown {
  color: var(--c-body);
}
.setup-list svg {
  flex: none;
}
.setup-list li.done svg {
  color: #15803d;
}
.setup-list li.done .no,
.setup-list li.unknown .no,
.setup-list li:not(.done) .yes,
.setup-list li:not(.unknown) .na {
  display: none;
}
.setup-list b {
  width: 130px;
  font-weight: 500;
  color: var(--c-ink);
}

/* Status dot next to the stage title: live (green) or warn (amber). */
.dot {
  flex: none;
  width: 11px;
  height: 11px;
  border-radius: 50%;
}
.dot.live {
  background: #22c55e;
  box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.16);
}
.dot.warn {
  background: #f5a623;
  box-shadow: 0 0 0 4px rgba(245, 166, 35, 0.16);
}
[hidden] {
  display: none;
}
/* `hidden` must beat the button display rule below: not-connected hides its button. */
main.provision [hidden],
main.pair [hidden] {
  display: none;
}

/* Buttons are flat: .btn is solid blue, .secondary pale blue, and .neutral grey. */
.btn,
button.cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 11px 17px;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-family: var(--f-sans);
  font-size: 14px;
  line-height: 20px;
  color: var(--c-paper);
  background: var(--c-accent);
  text-decoration: none;  /* the landing's Log in is a link */
  transition:
    filter 0.15s ease,
    transform 0.05s ease;
}
.btn:hover,
button.cta:hover {
  filter: brightness(1.06);
}
.btn:active {
  transform: translateY(1px);
}
.btn.secondary {
  color: #1E4FE0;
  background: #EEF2FF;
}
.btn.secondary:hover {
  filter: none;
  background: #DFE6FF;
}
/* A gated control must read as gated: a dead button wearing its live colours reads as a
   broken page, not a locked one (e.g. Settings' Save while a save is in flight). */
.btn:disabled,
.btn:disabled:hover {
  cursor: not-allowed;
  opacity: 0.45;
  filter: none;
}
.btn:disabled:active {
  transform: none;
}
/* A text link that is still a button: the action is a word, not a shape. Settings paints
   every one of its actions this way, so that page carries no filled button at all. */
.btn.link {
  padding: 0;
  border: 0;
  background: none;
  color: var(--c-accent);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(37, 92, 255, 0.4);
}
.btn.link:hover {
  filter: none;
  background: none;
  text-decoration-color: currentColor;
}
.btn.link:active {
  transform: none;
}
/* The quieter half of a pair: Cancel beside Save. */
.btn.link.quiet {
  color: var(--c-mute);
  text-decoration-color: rgba(10, 10, 10, 0.25);
}
/* A text-link action inside a sentence reads at the sentence's size. */
.gate-hint .btn.link,
.gate-steps .btn.link {
  font: inherit;
}

/* The pairing QR on the scan-to-pair screen. A camera has to read it, so it sits on flat
   white — no tint, no texture — and the symbol carries its own quiet zone. */
.qr-ph {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;  /* the hint chip sits on the symbol */
  box-sizing: border-box;
  flex: 1;  /* a square as tall as the card has room for under its line of copy */
  min-height: 180px;
  aspect-ratio: 1;
  padding: 6px;
  border: 1px solid rgba(10, 10, 10, 0.1);
  border-radius: 12px;
  background: var(--c-paper);
  appearance: none;
  font: inherit;
  color: inherit;
  cursor: zoom-in;
}
/* Enlarged, the symbol is what the glasses camera reads at wearing distance. The size is
   the one tunable: a QR carrying the WiFi network is denser, so it gets more room. */
.qr-ph[aria-expanded="true"] {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 50;
  width: min(90vmin, 680px);
  height: auto;
  padding: 16px;
  box-shadow: 0 24px 60px rgba(10, 10, 10, 0.35);
  cursor: zoom-out;
}
/* The affordance rides the symbol itself, since nothing else marks the QR as a control:
   a pill at its centre, with most of the symbol showing around it. */
.qr-hint {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 24px;
  border-radius: 999px;
  background: rgba(236, 236, 238, 0.95);
  box-shadow: 0 1px 3px rgba(10, 10, 10, 0.18);
  font-family: var(--f-mono);
  font-size: 15px;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--c-ink);
}
.qr-ph[aria-expanded="true"] .qr-hint {
  display: none;  /* enlarged, the symbol is all camera: nothing lies across it */
}
/* Behind an enlarged QR: blurs the console so the symbol is the only thing in focus, and
   covers the viewport so a click anywhere outside the QR lands here and restores it. */
.qr-backdrop {
  display: none;
}
.qr-ph[aria-expanded="true"] + .qr-backdrop {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 40;  /* under the QR's 50 */
  backdrop-filter: blur(6px);
}
.qr-ph img {
  width: 100%;
  height: auto;
}

/* A capture stage is one card: its title in a strip across the top, the stage's own column
   beneath. A card is as narrow as its few lines unless it carries a demo or heads the table. */
.stage > .panel {
  box-sizing: border-box;
  max-width: 560px;
  margin: 0 auto;
  padding: 0 40px 56px;
  border: 1px solid rgba(10, 10, 10, 0.1);
  border-radius: 14px;
  background: var(--c-paper);
}
.stage > .panel > .stage-title {
  align-self: stretch;
  margin: 0 -40px 10px;
  padding: 22px 28px;
  border-bottom: 1px solid rgba(10, 10, 10, 0.08);
}
.stage > .panel.wide {
  max-width: none;
  padding: 0;
  gap: 0;
  align-items: stretch;
}
.stage > .panel.wide > .stage-title {
  margin: 0;
}
/* A screen the operator works from is as tall as the gate cards, so the stage keeps one
   size from the extension through to packing. */
.stage > .panel.tall {
  min-height: 579px;
}
.stage > .panel.tall > .card-body,
.stage > .panel.tall > .gate-main {
  flex: 1;
}
.card-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 22px;
  padding: 28px;
}

/* An extension gate's two wide screens: numbered steps beside a demo of them, over a bar
   holding the hint and the buttons (ADR 0011). */
.gate-main {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 28px 44px;
  padding: 28px;
}
.gate-steps {
  flex: 1 1 300px;
  min-width: 0;
  box-sizing: border-box;
  margin: 0;
  padding: 0 40px;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 22px;
  font-size: 16px;
  line-height: 1.6;
  color: var(--c-body);
}
/* The steps start a tenth of the way down the demo beside them, not flush with its top. */
.gate-steps::before {
  content: "";
  flex: 0 0 10%;
  margin-bottom: -22px;
}
.gate-steps li {
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.gate-steps b {
  font-weight: 700;  /* the sans ships 400 and 700 only: anything between reads as 400 */
  color: var(--c-ink);
}
.gate-n {
  flex: none;
  width: 18px;
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--c-mute);
}
/* Steps beside a demo narrower than their own keep to their half and sit closer. */
.gate-steps.half {
  flex: 0 1 50%;
  gap: 18px;
}
.gate-steps.half::before {
  margin-bottom: -18px;
}
.gate-demo {
  flex: 1 1 380px;
  align-self: flex-start;
  min-width: 0;
  max-width: 500px;
  height: auto;
  margin: 0 auto;  /* centred in whatever room is left: its half, or its own row once wrapped */
  border: 1px solid rgba(10, 10, 10, 0.12);
  border-radius: 12px;
}
/* A demo beside half-width steps is shown at the size it was made. */
.gate-steps.half + .gate-demo {
  flex: none;
}
/* Too narrow for the two halves, the steps take the card's width and the demo goes under. */
@media (max-width: 900px) {
  .gate-steps.half {
    flex-basis: 100%;
  }
  .gate-steps.half::before {
    content: none;
  }
}
.gate-bar,
.gate-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 24px;
}
.gate-bar {
  justify-content: space-between;
  min-height: 42px;  /* a bar holding only the docked Pair again is still a button tall */
  padding: 16px 28px;
  border-top: 1px solid rgba(10, 10, 10, 0.08);
}
.gate-hint {
  margin: 0;
  font-size: 13px;
  color: #5f5f5f;
}
/* The way out of a screen, beside the button that says its step is done. */
.btn.neutral {
  color: var(--c-body);
  background: #F1F2F4;
}
.btn.neutral:hover {
  filter: none;
  background: #E6E8EC;
}

.field {
  font-family: var(--f-mono);
  font-size: 14px;
  color: var(--c-ink);
  padding: 11px 14px;
  border: 1px solid rgba(10, 10, 10, 0.2);
  border-radius: 10px;
  background: var(--c-paper);
}
.field:focus {
  outline: none;
  border-color: var(--c-accent);
}

/* Pair again, docked over the bottom bar of whichever paired screen is on show: one button
   outside the stage serves both, so nothing in a card moves when the glasses connect. */
.corner-dock {
  position: absolute;
  right: 28px;
  bottom: 16px;
  display: flex;
  align-items: center;
  gap: 24px;
  max-width: calc(100% - 56px);  /* the bar's side padding, so a long line wraps inside it */
}
#btn-repair {
  flex: none;  /* the button keeps its shape; the line beside it is what wraps */
}
#btn-repair,
#btn-retry {
  gap: 9px;
}
/* A failed generate's line: left of the button that was pressed, on the button's 20px line. */
.pair-error {
  line-height: 20px;
  text-align: right;
}
/* Narrow, the line goes under its button instead: the markup keeps the line first, so the
   column is reversed. The gap is the line's own margin, so an empty line takes no room. */
@media (max-width: 900px) {
  .corner-dock,
  .gate-actions:has(> .pair-error) {
    flex-direction: column-reverse;
    align-items: flex-end;
    gap: 0;
  }
  .pair-error:not(:empty) {
    margin-top: 12px;
  }
  /* One row, no gap beside the empty wait: a line as wide as the bar would otherwise wrap
     Glasses on under it, 12px down and off the bar's right edge. */
  .gate-bar:has(> .gate-actions > .pair-error) {
    flex-wrap: nowrap;
    column-gap: 0;
  }
  /* The dock cannot push the bar open, so the bar makes the room: 16 + 20 + 12. */
  main.pair:has(.corner-dock > .pair-error:not(:empty)) .gate-bar {
    padding-bottom: 48px;
  }
}

/* Captures table: one row per capture on the pairing, newest first, inside the Ready to
   capture card. */
.captures {
  align-self: stretch;
  overflow-x: auto;
}
.cap-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.cap-table th {
  text-align: left;
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--c-mute);
  padding: 8px 14px;
  border-bottom: 1px solid rgba(10, 10, 10, 0.12);
}
.cap-table td {
  padding: 11px 14px;
  color: var(--c-body);
  border-bottom: 1px solid rgba(10, 10, 10, 0.06);
}
/* Order number, WMS id and tote are all codes, so all three read in the mono face. */
.cap-table td:first-child,
.cap-table td:nth-child(2),
.cap-table td:nth-child(3) {
  font-family: var(--f-mono);
  color: var(--c-ink);
}
/* The open capture — the one still recording — stands out from the closed rows. These rows
   open nothing, so a tint cannot be read as a selection the way it could on /search. */
.cap-table tr.active td {
  background: rgba(37, 92, 255, 0.06);
  font-weight: 600;
  color: var(--c-ink);
}

/* Settings page: a left-aligned column of sections (Account, WMS, Warehouse WiFi). */
main.settings {
  width: 100%;
  max-width: 520px;
}
/* `hidden` must beat the field and button display rules below. */
main.settings [hidden] {
  display: none;
}
.settings-section {
  margin-top: 40px;
  gap: 10px;
}
/* Space alone divides one section from the next. Three sections on one short page do not
   need a rule drawn between them as well as the air. */
.settings-section + .settings-section {
  margin-top: 44px;
}
/* Section captions stop borrowing .stage-title's page-state voice: sentence case at the
   scale below the page title, so "WMS Configuration" stops shouting. */
main.settings .stage-title {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: none;
}
.settings-config {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  align-self: stretch;
  gap: 12px;
}
/* The card's last row clears the Update link below it by 18px (6px + the section's 12px). */
.settings-config > dl.card {
  margin-bottom: 6px;
}
/* One label column for the saved card and for the form that replaces it: the caption sits
   left of its input, so a name holds its place when Update swaps one for the other. */
main.settings dl.card,
.settings-field {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr);
}
/* Configure form: labelled fields, then Save and an inline error line. It fills the
   section width so a full base URL fits without clipping. */
.settings-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
}
/* The picked backend's secret fields — revealed only once a WMS is chosen; the reveal
   gap matches the form's, so WMS → Base URL → API token → Save are evenly spaced. */
.wms-fields:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* Dropdown reads as a grey italic placeholder until a WMS is picked, then upright black. */
.wms-select {
  font-style: italic;
  color: var(--c-mute);
}
.wms-select.chosen {
  font-style: normal;
  font-weight: 400;
  color: var(--c-ink);
}
.wms-select option {
  font-style: normal;
  color: var(--c-ink);
}
.wms-select option[value=""] {
  font-style: italic;
  color: var(--c-mute);
}
/* 132px matches the search viewer's .facts boxes; a longer caption wraps inside it. */
.settings-field {
  align-items: center;
  gap: 0 20px;
}
/* A saved name and the caption that edits it are one thing in two states, so one rule
   sets both — they cannot drift into two values that only happen to match. */
main.settings dl.card dt,
.settings-field {
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--c-mute);
}
/* An underline, not a box: at this row height a bordered box reads as a pill, and the
   rows then sit at the card's rhythm rather than twice it. */
.settings-field .field {
  width: 100%;
  box-sizing: border-box;
  padding-block: 7px;
  padding-left: 2px;
  border: 0;
  border-bottom: 1.5px solid rgba(10, 10, 10, 0.12);
  border-radius: 0;
  background: none;
  text-transform: none;
  letter-spacing: normal;
  font-family: var(--f-sans);
  font-variant-ligatures: none;
}
.settings-field .field:focus {
  border-bottom-color: var(--c-accent);
}
/* The Account card's Language row continues the card: it sits one card row (10px) below
   Tenant, not a section gap (26px), and its select drops the form fields' 7px padding so
   it is as tall as a card row; the -4px cancels the select's own inner inset so
   the value starts where the card's values do. */
dl.card + .settings-field {
  margin-top: -17.5px;
}
dl.card + .settings-field .field {
  padding: 0;
  margin-left: -4px;
  width: calc(100% + 4px);
}
/* A secret field's show/hide eye sits inside the field's right end, over its underline; the
   field keeps room for it so a long value never runs under the icon. */
.reveal {
  position: relative;
}
.reveal .field {
  padding-right: 30px;
}
.reveal-toggle {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  display: flex;
  padding: 4px;
  border: 0;
  background: none;
  color: var(--c-mute);
  cursor: pointer;
}
.reveal-toggle:hover,
.reveal-toggle[aria-pressed="true"] {
  color: var(--c-ink);
}
.reveal-toggle[aria-pressed="false"] .reveal-slash {
  display: none;
}
/* Cancel (left) + Save (right): two text links, so they need more air between them than
   two buttons would — nothing but the gap separates one word from the next. */
.settings-actions {
  display: flex;
  align-items: center;
  gap: 22px;
}
.settings-error,
.pair-error {
  margin: 0;
  font-size: 14px;
  color: #dc2626;
}
/* A quiet line in the label grey: why a non-admin has no Configure, or the QR's network. */
.note {
  margin: 0;
  font-size: 14px;
  color: var(--c-mute);
}
/* The hidden-network checkbox sits on one line with its label, unlike the stacked fields. */
.settings-check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--c-ink);
}
/* Search: the filter row, the results list and pager, and the viewer that replaces them. */
main.search {
  /* The air a result list stands in: above it, between it and the pager. */
  --s-gap: 22px;
  width: 100%;
  max-width: 880px;
}
/* `hidden` must beat the flex and button display rules below. */
main.search [hidden] {
  display: none;
}
/* Order number, the admin operator filter, and Search side by side; wraps when narrow. */
.search-form {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}
/* The order box and the operator dropdown share one width, height and text size. */
.search-form .field {
  flex: 0 0 240px;
  box-sizing: border-box;
  height: 44px;
  font-size: 12px;
}
/* The order box carries a magnifier inside its left padding, so the field itself says
   what the form is for and the caption above it has nothing left to add. */
.s-field-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: 0 0 240px;
}
.s-field-wrap .field {
  flex: 1 1 auto;
  width: 100%;
  padding-left: 38px;
}
.s-glyph {
  position: absolute;
  left: 13px;
  pointer-events: none;
  color: var(--c-mute);
}
/* A native select puts its arrow where the platform likes; this sets it the 13px in from
   the right edge that the magnifier sits in from the left, so the row reads symmetrically. */
.search-operator .field {
  appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='%23767676' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
}
/* The form and the results it produced are two things, not one list. */
.s-div {
  height: var(--s-gap);
}
.search-operator {
  display: flex;
  gap: 8px;
  align-items: center;
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--c-mute);
}
.search-status,
.page-info {
  font-family: var(--f-mono);
  font-size: 13px;
  color: var(--c-mute);
}
/* A hit is its own announcement, so the line is empty then and must claim no row. */
.search-status:empty {
  display: none;
}
/* No top margin: the divider above owns the gap between the form and the results. The
   pager below stands off by the same amount, so the list has equal air on both sides. */
.results {
  list-style: none;
  margin: 0 0 var(--s-gap);
  padding: 0;
}
.result-open {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 12px;
  padding: 12px 14px;
  text-align: left;
  font: inherit;
  color: var(--c-body);
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(10, 10, 10, 0.08);
  cursor: pointer;
}
.result-open:hover {
  background: rgba(37, 92, 255, 0.05);
}
.result-headline {
  font-family: var(--f-mono);
  color: var(--c-ink);
}
.result-meta {
  grid-column: 1 / -1;
  font-size: 13px;
  color: var(--c-mute);
}
.pager {
  display: flex;
  gap: 12px;
  align-items: center;
}
/* Two arrows and the range between them. A chevron has no accessible name of its own, so
   each carries an aria-label; the range beside them is paging context, not a result count. */
.pg-arrow {
  width: 30px;
  height: 30px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(10, 10, 10, 0.12);
  border-radius: 7px;
  background: none;
  color: var(--c-ink);
  cursor: pointer;
}
.pg-arrow:hover {
  background: rgba(10, 10, 10, 0.05);
}
/* The viewer: the back arrow and title over the facts, with the video beside them. It
   opens where the form stood, so the page title's own gap is the only one above it. */
.detail-body {
  display: flex;
  align-items: flex-start;
  gap: 48px;
}
/* Title and facts share one column so the video's top edge reads level with the title. */
.detail-main {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.detail-head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 40px;
}
/* The back arrow in the viewer's top-left corner returns to the results. */
.back {
  flex: none;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 7px;
  color: var(--c-ink);
  background: rgba(10, 10, 10, 0.05);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.back:hover {
  background: rgba(10, 10, 10, 0.1);
}
/* A barcode or order id runs to 512 characters with no spaces, so it breaks mid-word
   rather than running across the video and out of the viewport. */
.detail-title {
  margin: 0;
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--c-ink);
  overflow-wrap: anywhere;
}
/* The glasses record portrait, so the video is a narrow panel beside the facts. */
.detail-video {
  flex: none;
  width: 288px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* One bordered box per group of facts, its heading over a rule across the whole box. */
.facts {
  box-sizing: border-box;
  border: 1px solid rgba(10, 10, 10, 0.11);
  border-radius: 12px;
  padding: 18px 20px;
}
.facts-title {
  margin: 0 -20px 14px;
  padding: 0 20px 11px;
  border-bottom: 1px solid rgba(10, 10, 10, 0.09);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--c-ink);
}
/* One term/value pair per row: a box beside the video is too narrow for two. */
.facts dl.card {
  grid-template-columns: 132px minmax(0, 1fr);
  gap: 8px 12px;
  margin: 0;
}
.facts dl.card dt {
  font-size: 11px;
  line-height: 1.5;
}
.facts dl.card dd {
  line-height: 1.5;
  overflow-wrap: anywhere;
}
/* The panel's width sets the box and the height follows the frames, so nothing is
   stretched, cropped, or left as ink down a portrait capture's sides. */
.player {
  display: block;
  width: 100%;
  height: auto;
  background: var(--c-ink);
  border-radius: 10px;
}
/* Too narrow to stand side by side: the video drops under the facts. */
@media (max-width: 700px) {
  .detail-body {
    display: block;
  }
  .detail-video {
    width: 100%;
    max-width: 288px;
    margin-top: 16px;
  }
}
/* A completeness label, coloured by kind: blue recording, grey uploading, green complete,
   amber incomplete, red failed. */
.label {
  font-family: var(--f-mono);
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
}
.label-recording {
  color: var(--c-accent);
  background: rgba(37, 92, 255, 0.1);
}
.label-uploading {
  color: var(--c-body);
  background: rgba(10, 10, 10, 0.06);
}
.label-complete {
  color: #106b2f;
  background: rgba(16, 107, 47, 0.1);
}
.label-incomplete {
  color: #8a5300;
  background: rgba(138, 83, 0, 0.1);
}
.label-failed {
  color: #b91c1c;
  background: rgba(185, 28, 28, 0.1);
}

/* Legal prose (the privacy policy): a readable measure, left-aligned, since the body
   centers its children and a full-width paragraph is unreadable. */
main.legal {
  align-self: center;
  max-width: 68ch;
  margin: 64px 24px;
  text-align: left;
}
main.legal h2 {
  margin: 34px 0 10px;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--c-ink);
}
main.legal p {
  margin: 0 0 12px;
  font-size: 15px;
  line-height: 1.65;
}
main.legal code {
  font-family: var(--f-mono);
  font-size: 13px;
}
.legal-date {
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--c-mute);
}
/* The contact address is the page's one link; without this it falls back to the
   browser default blue, which clashes with the accent. */
.legal a {
  color: var(--c-accent);
  text-decoration: none;
}
.legal a:hover {
  text-decoration: underline;
}

main.provision code {
  font-family: var(--f-mono);
  font-size: 13px;
  color: var(--c-ink);
  overflow-wrap: anywhere;
}
.provision-details {
  align-self: stretch;
  font-size: 13px;
  color: #5f5f5f;
}
.status-head {
  margin: 0;
}
.provision-log {
  margin: 8px 0 0;
  padding-left: 20px;
  font-family: var(--f-mono);
  font-size: 12px;
  line-height: 1.8;
  color: var(--c-body);
  white-space: pre-wrap;
}
/* Numbered instructions in a card or beside a demo: .gate-steps without its side
   padding, flex basis and lead. */
.gate-steps.plain {
  flex: none;
  padding: 0;
}
.gate-steps.plain::before {
  content: none;
}
.gate-steps.plain.tight {
  gap: 14px;
}
/* The column beside a demo: an intro line over its steps. */
.gate-col {
  flex: 1 1 300px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 22px;
}
/* A warning that belongs to the step above it: the number slot holds the triangle. */
.gate-warn {
  margin-top: -10px;
}
.gate-steps.plain.tight .gate-warn {
  margin-top: -4px;
}
.gate-warn .gate-n {
  align-self: flex-start;
  margin-top: 4px;
}
.gate-warn svg {
  display: block;
  color: #f5a623;
}
/* The mark beside a finished or a failed step's title. */
.title-tick {
  flex: none;
  color: #15803d;
}
.title-error {
  flex: none;
  color: #dc2626;
}
/* A do-not-unplug line: ink behind an amber triangle. */
.stage-alert {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  color: var(--c-ink);
}
.stage-alert svg {
  flex: none;
  margin-top: 3px;
  color: #f5a623;
}

/* ==== THE-660 share links (ADR 0017) ==== */
/* The public page: one centred column at every width (the 460px .s-head measure). */
main.share {
  align-self: center;
  width: 100%;
  max-width: 508px;
  box-sizing: border-box;
  margin: 64px auto;
  padding: 0 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
main.share [hidden] {
  display: none;
}
/* Pack date and expiry side by side on the .back tint, rounded like the player below. */
.share-bar {
  margin: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-radius: 10px;
  background: rgba(10, 10, 10, 0.05);
}
.share-bar > div {
  padding: 12px 16px;
  min-width: 0;
}
.share-bar > div + div {
  border-left: 1px solid rgba(10, 10, 10, 0.09);
}
.share-bar dt {
  font-family: var(--f-mono);
  font-size: 11px;
  line-height: 1.5;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-mute);
}
.share-bar dd {
  margin: 2px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--c-ink);
}
main.share .player {
  margin-top: 8px;
}
/* The refused link: a muted link-off tile, then the sentence at .stage-title's size. */
.share-invalid-mark {
  width: 32px;
  height: 32px;
  border-radius: 7px;
  background: rgba(10, 10, 10, 0.05);
  color: var(--c-mute);
  display: flex;
  align-items: center;
  justify-content: center;
}
#share-invalid {
  margin: 0;
  font-size: 20px;
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: -0.02em;
  color: var(--c-ink);
  padding-bottom: 20px;
  border-bottom: 1px solid rgba(10, 10, 10, 0.09);
}

/* The Share box (a .facts box under Capture): the line and the button share one row that never wraps. */
.share-row {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 16px;
}
.share-row > .share-status {
  flex: 1 1 0;
  min-width: 0;
}
.share-status {
  margin: 0;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--c-body);
}
.share-status .share-icon svg {
  margin-top: 3px;
}
/* A failed mint: the whole line in .settings-error red. */
.share-status.is-err {
  color: #dc2626;
}
.share-row .btn {
  flex: none;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.share-icon {
  display: inline-flex;
  flex: none;
}
/* Copied: the quiet .btn.neutral, its tick in the Complete label's green. */
.share-row .btn.neutral svg {
  color: #106b2f;
}
/* Clipboard blocked: the link field takes the line's place. */
.share-row.is-manual .field {
  flex: 1 1 0;
  min-width: 0;
  box-sizing: border-box;
  line-height: 20px;
  border-color: var(--c-accent);
}
.share-press {
  flex: none;
  margin-left: auto;
  font-size: 14px;
  color: var(--c-mute);
}
