/* ============================================================================
   VTS — application-specific components.

   Everything here builds on the THSTI token set (tokens.css) and the shared
   component library. Nothing below hard-codes a brand colour; the only literal
   values are geometry.

   This is a task application rather than a dashboard, so the weight sits on the
   working surface: the setup row, the drop target, the file table and the
   console. There are no KPI tiles, because there is no figure to report — there
   is a job to finish.
   ========================================================================= */

/* ------------------------------------------------- corrections to the shell

   Two collisions with the shared library, fixed here because vts.css loads
   last rather than by editing the files DRISHTI also uses.

   1. components.css sizes .icon inside .btn, .alert and .empty, but not inside
      a card heading. An unsized inline SVG renders at its natural size, which
      is how you get a 60px magnifying glass in a 16px title.
   2. .drawer-close sets display:none at layout.css:51 and .icon-btn sets
      display:grid at layout.css:244. Same specificity, later wins, so the
      drawer's close button was visible on desktop. A two-class selector
      settles it without touching the shared file.
   ------------------------------------------------------------------------ */

.card-head h3 {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
}
.card-head h3 .icon {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  color: var(--thsti-maroon);
}

.app-sidebar .drawer-close { display: none; }
@media (max-width: 991px) {
  .app-sidebar .drawer-close { display: grid; }
}

/* 3. The topbar toggle holds both chevrons and shows whichever way the panel
      is about to move. DRISHTI keeps these rules in drishti.css, which is
      dashboard-specific and not loaded here, so without them both arrows
      render stacked on top of each other. */
.sidebar-toggle .when-rail,
body[data-sidebar="rail"] .sidebar-toggle .when-expanded { display: none; }
body[data-sidebar="rail"] .sidebar-toggle .when-rail { display: block; }

/* 4. A class that sets `display` outranks the hidden attribute's default, so
      anything toggled with `hidden` needs saying explicitly. Same convention
      as auth.css:257 and components.css:319 in the shared library. */
.session-summary[hidden],
.verify-state[hidden] { display: none; }

/* Title and its explanation belong together on the left; anything the page
   puts after them (a count, a control) still floats right. */
.card-head .head-text { min-width: 0; }
.card-head .head-text .sub { margin-top: 2px; }

/* ------------------------------------------------------------------ setup */

.setup-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) auto;
  gap: var(--sp-5);
  align-items: start;
}
.setup-actions .row { padding-top: 1px; }

@media (max-width: 880px) {
  .setup-grid { grid-template-columns: minmax(0, 1fr); }
  .setup-actions label { display: none; }
}

.verify-state {
  margin-top: var(--sp-4);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: 700;
}
.verify-state .icon { width: 17px; height: 17px; }
.verify-state.is-ok      { color: var(--thsti-success); }
.verify-state.is-bad     { color: var(--thsti-error); }
.verify-state.is-working { color: var(--thsti-navy); font-weight: 400; }
/* Already transferred. Not an error - the job is finished - so it reads as
   information rather than something the operator has done wrong. */
.verify-state.is-done {
  color: var(--thsti-navy);
  background: var(--thsti-navy-tint, rgba(64, 64, 126, 0.07));
  border-radius: var(--radius-sm, 4px);
  padding: var(--sp-3) var(--sp-4);
}

/* The locked summary that replaces the form once a session is open. */
.session-summary {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  justify-content: space-between;
  border-top: 3px solid var(--thsti-maroon);
  background: var(--thsti-panel);
}
.summary-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-6);
  min-width: 0;
}
.summary-grid > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.summary-grid .label { font-size: 10.5px; }
.summary-grid .value {
  font-family: var(--thsti-font-heading);
  font-weight: 700;
  font-size: 15px;
  color: var(--thsti-text);
}
.summary-grid .value.small { font-size: 12px; font-weight: 400; color: var(--thsti-text-2nd); }

/* -------------------------------------------------------------- dropzone */

.dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-10) var(--sp-5);
  border: 2px dashed var(--thsti-border);
  border-radius: var(--r-2);
  background: var(--thsti-panel);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--dur-2) var(--ease), background var(--dur-2) var(--ease);
}
.dropzone:hover,
.dropzone:focus-visible { border-color: var(--thsti-maroon); background: var(--thsti-maroon-100); }
.dropzone.is-over {
  border-color: var(--thsti-maroon);
  border-style: solid;
  background: var(--thsti-maroon-100);
}
.dropzone-icon { width: 30px; height: 30px; color: var(--thsti-maroon); }
.dropzone-text strong {
  display: block;
  font-family: var(--thsti-font-heading);
  font-weight: 600;
  font-size: 16px;
  color: var(--thsti-text);
}
.dropzone-text span { font-size: var(--fs-sm); color: var(--thsti-text-2nd); }
.dropzone-note { font-size: var(--fs-xs); color: var(--thsti-muted); }

.linkish {
  background: none;
  border: 0;
  padding: 0;
  color: var(--thsti-maroon);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.linkish:hover { color: var(--thsti-navy); }

/* ----------------------------------------------------------- file table */

#file-table .col-name  { min-width: 300px; }
#file-table .col-status { min-width: 210px; }
#file-table td { vertical-align: middle; }

.file-name {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.file-name .primary {
  font-family: var(--thsti-font-mono);
  font-size: var(--fs-xs);
  color: var(--thsti-text);
  word-break: break-all;
}
.file-name .secondary { font-size: 11px; color: var(--thsti-text-2nd); }

/* Status is the column the operator watches, so it carries both a word and a
   shape: pill for state, bar for how far along. */
.status-cell { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.status-cell .meter { height: 5px; }
.status-cell .detail {
  font-size: 11px;
  color: var(--thsti-text-2nd);
  font-variant-numeric: tabular-nums;
}
.status-cell .detail.is-error { color: var(--thsti-error); font-weight: 700; }
.status-cell .detail.is-warn  { color: #A85D0E; font-weight: 700; }

/* A full maroon bar next to "Transferred" reads as still-in-progress. Once a
   video is filed the bar becomes the outcome, not the progress. */
.status-cell .meter.done > span { background: var(--thsti-success); }
.status-cell .meter.warn > span { background: var(--thsti-orange); }

.pill-running { background: var(--thsti-navy-100); color: var(--thsti-navy); }
.pill-running::before { animation: vts-pulse 1.4s var(--ease) infinite; }

@keyframes vts-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.25; }
}
@media (prefers-reduced-motion: reduce) {
  .pill-running::before { animation: none; }
}

tr.row-done    td:first-child { box-shadow: inset 3px 0 0 var(--thsti-success); }
tr.row-failed  td:first-child { box-shadow: inset 3px 0 0 var(--thsti-error); }
tr.row-active  td:first-child { box-shadow: inset 3px 0 0 var(--thsti-navy); }
tr.row-blocked td:first-child { box-shadow: inset 3px 0 0 var(--thsti-orange); }

/* ------------------------------------------------------- raw log pane */

/* Kept for the Activity Log screen only, which shows the server's log file
   verbatim. That IS a technical artefact and monospace is the right way to
   read it. The transfer screen used to borrow this look for its progress
   commentary; it no longer does - see the activity feed below. */

.console {
  background: var(--thsti-text);
  color: #E6E6E6;
  font-family: var(--thsti-font-mono);
  font-size: var(--fs-xs);
  line-height: 1.65;
  padding: var(--sp-4) var(--sp-5);
  max-height: 260px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.console-tall { max-height: 560px; }
.console-line { white-space: pre-wrap; word-break: break-word; }
.console-line.muted { color: #9A9A9A; }
.console::-webkit-scrollbar { width: 8px; }
.console::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.22); }
.console::-webkit-scrollbar-track { background: transparent; }

/* ------------------------------------------------------------- brand */

/* The mark is sized by WIDTH, not height. Its artwork is 128x84 - wider than
   tall - and the source PNG used to carry 22px of transparent padding above
   and below, so a height rule left the visible mark at two thirds the size it
   looked like it should be. The padding is cropped out of the file now and
   the width is set here, which is what actually governs how large it reads.
   76px is double the 38px it occupied before.

   Height stays auto: forcing both would stretch the artwork. */
.sidebar-brand .brand-logo {
  width: 76px;
  height: auto;
  max-height: calc(var(--topbar-h) - 12px);
}

/* --------------------------------------------------------- page head */

/* The topbar already names the page. Every screen also carried an h1 with the
   same words at 32px, twenty pixels below it - the same title twice, and about
   seventy pixels of head before any content. The h1 is gone; what remains is
   the line that actually says something, so it leads. */
.page-head {
  margin-bottom: var(--sp-4);
  align-items: center;
}
.page-head .sub {
  margin-top: 0;
  font-size: var(--fs-base, 14px);
  color: var(--thsti-text-2nd);
  max-width: 68ch;
}

/* ------------------------------------------------------------ filters */

/* components.css styles .filterbar as a standalone card - its own border,
   shadow, padding and bottom margin. Inside a card it is a card within a
   card, and the doubled chrome is most of why the filter strip was taller
   than the table header it sits above. Stripped back to a plain row here. */
.card-body .filterbar {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
  margin-bottom: 0;
  padding: 0;
  gap: var(--sp-3);
  align-items: flex-end;
}
.card-body.tight { padding: var(--sp-3) var(--sp-4); }

/* The label above each control was most of the strip's height: an uppercase
   line, a 5px gap, then the input. A placeholder says the same thing inside
   the control, so the labels are hidden visually and kept for screen readers -
   the row collapses to the height of one input. */
.card-body .filterbar .field {
  flex-direction: row;
  align-items: center;
  gap: 0;
  margin-bottom: 0;
}
.card-body .filterbar .field > label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.card-body .filterbar .input,
.card-body .filterbar .select {
  padding-top: 6px;
  padding-bottom: 6px;
}

/* "Cannot be opened (11)" does not fit the 168px components.css gives a
   filter field, and a truncated status reads as a different one. */
.card-body .filterbar .field { width: auto; }
.card-body .filterbar .field.wide { width: 220px; }
.card-body .filterbar .select { min-width: 190px; }

/* -------------------------------------------------------- query summary */

/* Everything for one record happens inside its own row: the folder button,
   what it found, and how far the send has got. */
.row-action { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 15rem; }
.row-status { line-height: 1.4; }
.row-status.is-ok  { color: var(--thsti-success); }
.row-status.is-bad { color: var(--thsti-error); }
.row-status.is-warn { color: var(--thsti-warning, #B26A00); }
.row-meter { height: 6px; }

.query-row.is-sent { background: rgba(46, 125, 50, 0.05); }
.query-row td { vertical-align: top; }

.query-detail > summary {
  cursor: pointer;
  color: var(--thsti-navy);
  margin-top: var(--sp-2);
}
.query-files { margin: var(--sp-2) 0 0; padding-left: var(--sp-4); }
.query-files li { margin-bottom: var(--sp-2); line-height: 1.4; }
.query-files li > span { display: block; }

/* ---------------------------------------------------------- summary */

.summary-table td.num-cell,
.summary-table th.num-cell {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.summary-table .row-name { font-weight: 500; white-space: nowrap; }

/* The total is the column people read first, so it is separated from the
   per-site figures rather than sitting in the run of them. */
.summary-table .col-total {
  font-weight: 600;
  border-right: 2px solid var(--thsti-rule);
}

/* Incomplete sets are the actionable row - everything else is context. */
.summary-table .row-attention td { background: rgba(178, 106, 0, 0.06); }
.summary-table .row-attention .row-name { color: var(--thsti-warning, #B26A00); }

.outstanding > summary {
  cursor: pointer;
  font-weight: 500;
  padding: var(--sp-2) 0;
}
.outstanding > summary:focus-visible { outline: 2px solid var(--thsti-navy); }
.outstanding[open] > summary { margin-bottom: var(--sp-3); }

/* A long list must not push the next scan's table off the screen. The header
   stays put so the columns are still readable halfway down. */
.outstanding-scroll {
  max-height: 320px;
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--thsti-rule);
  border-radius: var(--radius-sm, 4px);
}
.outstanding-scroll thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}

.small-title {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-sm);
  font-weight: 600;
}
.definitions { margin: 0 0 var(--sp-3); padding-left: var(--sp-5); }
.definitions li { margin-bottom: var(--sp-2); font-size: var(--fs-sm); }

/* ------------------------------------------------------------ pager */

/* components.css has no disabled state for a link, and Previous/Next are
   links rather than buttons so they stay right-clickable and bookmarkable. */
.btn.is-disabled {
  opacity: 0.4;
  pointer-events: none;
  cursor: default;
}

.pager {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
.pager-size { display: flex; align-items: center; gap: var(--sp-2); margin: 0; }
.pager-size .select-sm {
  width: auto;
  min-width: 4.5rem;
  padding-top: var(--sp-1);
  padding-bottom: var(--sp-1);
}
/* Pushes the page numbers to the right, whatever is to their left. */
.pager-count { margin-right: auto; font-variant-numeric: tabular-nums; }
.pager-pages { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; }
.pager-pages .btn { min-width: 2.25rem; justify-content: center; }
.pager-gap { padding: 0 var(--sp-1); color: var(--thsti-muted); }

.card-foot .row { align-items: center; gap: var(--sp-3); }

/* ------------------------------------------------------- activity feed */

/* Was a black terminal pane in monospace that autoscrolled away from whoever
   was reading it. The people using this screen are running scans, so it is now
   an ordinary list, newest first, in the page's own type. The terminal look
   said "something technical is happening here" to an audience for whom that is
   not reassuring. */

.activity-feed {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 280px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.activity-item {
  display: flex;
  gap: var(--sp-3);
  align-items: baseline;
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--thsti-rule);
  font-size: var(--fs-sm);
  color: var(--thsti-text);
}
.activity-item:last-child { border-bottom: 0; }

/* The newest line arrives at the top, so it is the one that gets the weight. */
.activity-item:first-child { font-weight: 500; }

.activity-time {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-xs);
  color: var(--thsti-muted);
  min-width: 4.5em;
}
.activity-text { flex: 1 1 auto; word-break: break-word; }

.activity-item.is-idle   { color: var(--thsti-muted); font-weight: 400; }
.activity-item.is-ok     { color: var(--thsti-success); }
.activity-item.is-error  { color: var(--thsti-error); }
.activity-item.is-warn   { color: var(--thsti-warning, #B26A00); }

.activity-feed::-webkit-scrollbar { width: 8px; }
.activity-feed::-webkit-scrollbar-thumb {
  background: var(--thsti-rule);
  border-radius: 4px;
}
.activity-feed::-webkit-scrollbar-track { background: transparent; }

/* ---------------------------------------------------------- misc chrome */

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

.card-foot {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  flex-wrap: wrap;
}
.card-foot .row.grow { min-width: 240px; }

.scroll-560 { max-height: 560px; overflow-y: auto; }

/* Record IDs are identifiers, not prose - never break one across two lines. */
.table td.mono, .table td.row-name { white-space: nowrap; }
.table td.col-name, .table td .file-name { white-space: normal; }

/* A two-word name was wrapping the whole topbar control onto two lines. */
.user-btn .who,
.user-btn .role { white-space: nowrap; }

.health-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--thsti-border);
  border-radius: var(--r-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--thsti-text-2nd);
  background: var(--thsti-surface);
  white-space: nowrap;
}
.health-chip .icon { width: 15px; height: 15px; }
.health-chip.is-ok   { color: var(--thsti-success); border-color: var(--thsti-success); }
.health-chip.is-bad  { color: var(--thsti-error);   border-color: var(--thsti-error); }

@media (max-width: 640px) {
  .health-chip .health-text { display: none; }
}

/* --------------------------------------------------------- sign-in page */

.signin-page { background: var(--thsti-surface); }

.signin {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  min-height: 100vh;
}

.signin-intro {
  background: var(--shell-bg);
  color: #fff;
  padding: var(--sp-12) var(--sp-12) var(--sp-10);
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  justify-content: center;
}
.signin-intro h1 {
  font-family: var(--thsti-font-heading);
  font-weight: 700;
  font-size: 40px;
  line-height: 1.12;
  color: #fff;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
.signin-intro p { color: rgba(255, 255, 255, 0.78); max-width: 46ch; }

.signin-brand { display: flex; align-items: center; gap: var(--sp-3); }
.signin-brand .brand-mark { width: 40px; height: 40px; font-size: 14px; }
.signin-brand .brand-name {
  display: block;
  font-family: var(--thsti-font-heading);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.signin-brand .brand-sub {
  display: block;
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.7);
}

.signin-points { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-4); }
.signin-points li { display: flex; gap: var(--sp-3); align-items: flex-start; max-width: 52ch; }
.signin-points .icon { width: 19px; height: 19px; flex: 0 0 auto; margin-top: 2px; color: var(--thsti-orange); }
.signin-points span { font-size: var(--fs-sm); color: rgba(255, 255, 255, 0.82); }
.signin-points strong { color: #fff; }

.signin-panel { display: grid; place-items: center; padding: var(--sp-10) var(--sp-6); }
.signin-card { width: 100%; max-width: 380px; display: flex; flex-direction: column; gap: var(--sp-4); }
.signin-card h2 { font-size: 26px; font-weight: 700; }
.signin-foot { margin-top: var(--sp-2); }

@media (max-width: 900px) {
  .signin { grid-template-columns: minmax(0, 1fr); }
  .signin-intro { padding: var(--sp-8) var(--sp-6); }
  .signin-intro h1 { font-size: 28px; }
  .signin-points { display: none; }
}

/* ============================================================ admin forms

   The project form is long by nature — it holds everything the desktop client
   hardcoded. What keeps it readable is that each card answers one question,
   and the fields inside share one grid rather than each choosing a width.
   ========================================================================= */

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-5);
  align-items: start;
  margin-bottom: var(--sp-4);
}
.form-grid .field.wide { grid-column: span 2; }

@media (max-width: 760px) {
  .form-grid { grid-template-columns: minmax(0, 1fr); }
  .form-grid .field.wide { grid-column: auto; }
}

.form-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-3);
  margin: var(--gutter) 0 var(--sp-10);
}

/* Scan rows: a table because they are records with columns, not a form. */
.scan-table { margin: var(--sp-4) 0; }
.scan-table th { background: transparent; }
.scan-table td { padding: 6px 8px 6px 0; border-bottom: none; }
.scan-table td:last-child { padding-right: 0; }

/* The filename preview is the one control that stops an administrator
   silently breaking every operator's uploads, so it gets real weight. */
.preview-box {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  background: var(--thsti-panel);
  border-left: 3px solid var(--thsti-navy);
}
.preview-box .mono { font-size: 15px; font-weight: 700; color: var(--thsti-text); }
.preview-box .mono.is-bad { color: var(--thsti-error); }

.access-grid { display: grid; gap: 2px; margin-bottom: var(--sp-3); }
.access-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--thsti-rule);
  background: var(--thsti-surface);
  flex-wrap: wrap;
}
.access-row:hover { border-color: var(--thsti-maroon); }
.access-row.is-off { opacity: 0.6; }
.access-row .check span { font-size: var(--fs-sm); }

/* The project picker sits above the setup row and reads as a scope selector,
   not another field to fill in. */
.project-picker {
  max-width: 420px;
  padding-bottom: var(--sp-4);
  margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--thsti-rule);
}

/* ==================================================== after drishti.css

   drishti.css is DRISHTI's own override layer, carried over so the two
   applications read as one system. Three things it does not account for,
   because DRISHTI does not have them.
   ========================================================================= */

/* 1. Nav group headings. layout.css colours them for a dark panel
      (rgba(255,255,255,.62)); drishti.css flips the panel to light but has no
      groups of its own, so the heading stayed white on white. */
.app-sidebar .nav-heading {
  color: var(--thsti-text-2nd);
  padding-top: var(--sp-2);
}
.app-sidebar .nav-group + .nav-group { margin-top: var(--sp-5); }
.app-sidebar .nav-group-rule { background: var(--thsti-border); }

/* 2. `.step` collides. components.css defines a compact inline wizard
      (.steps > .step > .dot); drishti.css redefines bare `.step` as a 50px
      stacked ring for its application-progress line. Re-assert the inline
      version under .steps, which outranks the bare selector. */
.steps .step {
  display: flex;
  flex: 0 0 auto;
  min-width: 0;
  justify-items: initial;
  align-items: center;
  gap: var(--sp-2);
  text-align: left;
}
.steps .step + .step::before,
.steps .step + .step::after { content: none; }
.steps .step .dot { position: static; }

/* 3. drishti.css adds `form .btn { margin-top: sp-5 }` for its stacked forms.
      The admin screens put buttons inside grids and table rows, where that
      margin misaligns them from the control they sit beside. */
form .row .btn,
form .card-foot .btn,
form .scan-table .btn,
form .form-actions .btn,
form .filterbar .btn { margin-top: 0; }

/* Likewise `.field + .field { margin-top: sp-4 }`: correct for a stacked form,
   double spacing inside a grid that already has a gap. */
.form-grid .field + .field,
.setup-grid .field + .field { margin-top: 0; }

/* 4. drishti.css makes `.hint` inline-block with a top pad, which is right for
      a hint sitting beside a question in its long application form. On these
      screens a hint explains the control above it, so it needs its own line. */
.card-body > .hint,
.check + .hint,
.btn + .hint,
.access-grid + .hint,
.scan-table + .btn + .hint { display: block; padding-top: var(--sp-2); }
