/* ============================================================================
   CivilHive Design System — 02 · Base
   ----------------------------------------------------------------------------
   Self-hosted type, a light reset, the typographic scale, and — the important
   part — form controls styled BY ELEMENT rather than by class.

   Why by element: the app has 126 hardcoded `'class': 'form-control'` widget
   attrs baked into civilhive/forms.py and hand-written <input> markup in 65
   templates. Styling `input`/`select`/`textarea` directly means every one of
   them inherits the new field design with zero Python edits and zero risk to
   the `name=` attributes the views parse. The Bootstrap class names are then
   matched as aliases purely to win specificity, not because anything depends
   on them.
   ========================================================================== */

/* ─── Inter Variable, self-hosted ──────────────────────────────────────────
   Vendored from npm (@fontsource-variable/inter) rather than Google Fonts:
   fonts.googleapis.com is blocked in some regions this product ships to, and a
   blocked font request means a flash of fallback on every first paint. Paths
   are relative to this file so they survive collectstatic's URL rewriting. */
@font-face {
  font-family: "Inter Variable";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(../../vendor/inter/inter-latin-wght-normal.woff2) format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter Variable";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(../../vendor/inter/inter-latin-ext-wght-normal.woff2) format("woff2-variations");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Native controls we don't repaint (range tracks, some checkbox states)
     otherwise pick up the UA blue. */
  accent-color: var(--cv-accent);
}
@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-color: var(--cv-bg);
  color: var(--cv-ink-2);
  font-family: var(--cv-font);
  font-size: var(--cv-text-md);
  font-weight: var(--cv-weight-regular);
  line-height: var(--cv-leading);
  letter-spacing: var(--cv-track-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img, svg, video, canvas { max-width: 100%; height: auto; vertical-align: middle; }
svg { fill: currentColor; }
hr { height: 1px; border: 0; margin: var(--cv-sp-6) 0; background-color: var(--cv-line); opacity: 1; }
:target { scroll-margin-top: calc(var(--cv-header-offset, var(--cv-header-h)) + var(--cv-sp-6)); }

/* ─── Typography ──────────────────────────────────────────────────────────*/
h1, h2, h3, h4, h5, h6,
.cv-h1, .cv-h2, .cv-h3, .cv-h4 {
  margin: 0 0 var(--cv-sp-3);
  color: var(--cv-ink);
  font-weight: var(--cv-weight-semibold);
  line-height: var(--cv-leading-tight);
  text-wrap: balance;
}
h1, .cv-h1 { font-size: var(--cv-text-h1); letter-spacing: var(--cv-track-h1); font-weight: var(--cv-weight-bold); }
h2, .cv-h2 { font-size: var(--cv-text-h2); letter-spacing: var(--cv-track-h2); font-weight: var(--cv-weight-bold); }
h3, .cv-h3 { font-size: var(--cv-text-h3); letter-spacing: var(--cv-track-h3); }
h4, .cv-h4 { font-size: var(--cv-text-h4); letter-spacing: var(--cv-track-h3); }
h5 { font-size: var(--cv-text-md); letter-spacing: var(--cv-track-body); }
h6 { font-size: var(--cv-text-sm); letter-spacing: var(--cv-track-body); }

.cv-display {
  font-size: clamp(2.25rem, 1.2rem + 3.6vw, var(--cv-text-display));
  font-weight: var(--cv-weight-bold);
  line-height: 1.05;
  letter-spacing: var(--cv-track-display);
  color: var(--cv-ink);
  text-wrap: balance;
}
.cv-lead {
  font-size: var(--cv-text-lg);
  line-height: 1.55;
  color: var(--cv-ink-2);
  text-wrap: pretty;
}

p { margin: 0 0 var(--cv-sp-4); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }
small, .cv-small { font-size: var(--cv-text-sm); }
strong, b { font-weight: var(--cv-weight-semibold); color: var(--cv-ink); }

.cv-text-sm  { font-size: var(--cv-text-sm) !important; }
.cv-text-xs  { font-size: var(--cv-text-xs) !important; }
.cv-text-2xs { font-size: var(--cv-text-2xs) !important; }
.cv-ink      { color: var(--cv-ink) !important; }
.cv-ink-2    { color: var(--cv-ink-2) !important; }
.cv-ink-3    { color: var(--cv-ink-3) !important; }
.cv-accent   { color: var(--cv-accent-text) !important; }
.cv-urgent   { color: var(--cv-urgent-text) !important; }

/* An eyebrow: the small tracked label above a section heading. */
.cv-eyebrow {
  display: block;
  margin-bottom: var(--cv-sp-2);
  font-size: var(--cv-text-2xs);
  font-weight: var(--cv-weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cv-accent-text);
}

/* Money, dates, IDs and any column of figures. Tabular numerals are the whole
   point — proportional digits make a column of bid amounts look ragged. */
.cv-num, .cv-money, td.cv-num, .cv-table td.cv-num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

a {
  color: var(--cv-ink);
  text-decoration: none;
  text-underline-offset: 0.18em;
  transition: color var(--cv-t-fast) var(--cv-ease);
}
a:hover { color: var(--cv-accent-text); }
/* Inline links inside running prose stay underlined — in a paragraph, colour
   alone is not an accessible affordance. */
.cv-prose a, p > a:not(.btn):not(.cv-btn) { text-decoration: underline; text-decoration-thickness: 1px; }

ul, ol { margin: 0 0 var(--cv-sp-4); padding-left: 1.25rem; }
li { margin-bottom: var(--cv-sp-1); }
.cv-list-plain { list-style: none; margin: 0; padding: 0; }
.cv-list-plain > li { margin: 0; }

code, kbd, samp, pre { font-family: var(--cv-font-mono); font-size: 0.9em; }
code {
  padding: 0.15em 0.4em;
  border-radius: var(--cv-r-xs);
  background-color: var(--cv-surface-sunk);
  color: var(--cv-ink);
}

::selection { background-color: var(--cv-amber-200); color: var(--cv-n-900); }

/* ─── Focus ───────────────────────────────────────────────────────────────
   One visible, amber, unmissable focus treatment for keyboard users. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--cv-accent);
  outline-offset: 2px;
  border-radius: var(--cv-r-xs);
}

/* ============================================================================
   Form controls — styled by element
   ============================================================================
   The selector list below is the contract. It deliberately excludes:
     · button-like inputs (submit/button/reset)   — those are buttons
     · checkbox / radio / range / file            — each styled separately
     · anything carrying .btn or .cv-btn          — opted out explicitly

   Every part of the element branch sits inside `:where()`, including the two
   opt-out `:not()`s, so that branch weighs 0,0,0 and ANY class rule beats it.
   That matters: while those `:not()`s sat outside, this rule weighed 0,2,0 and
   silently outranked `.cv-field--invalid input` (so an invalid field drew no
   red border), `.cv-field--compact input` (so compact fields were not compact)
   and `.cv-toolbar select` (so the sort select stretched full width). The
   `.form-control` / `.form-select` branches stay at 0,1,0 on purpose — they
   have to outrank Bootstrap's own rules for the same classes.
   ========================================================================== */

:where(
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not([type="hidden"]),
  select,
  textarea
):where(:not(.btn):not(.cv-btn)),
.form-control,
.form-select {
  display: block;
  width: 100%;
  min-height: 2.5rem;
  padding: 0.5rem 0.75rem;
  font-family: inherit;
  font-size: var(--cv-text-sm);
  font-weight: var(--cv-weight-regular);
  line-height: 1.5;
  letter-spacing: var(--cv-track-body);
  color: var(--cv-ink);
  background-color: var(--cv-surface);
  background-clip: padding-box;
  border: 1px solid var(--cv-line-strong);
  border-radius: var(--cv-r-md);
  box-shadow: none;
  appearance: none;
  transition: border-color var(--cv-t-fast) var(--cv-ease),
              box-shadow var(--cv-t-fast) var(--cv-ease),
              background-color var(--cv-t-fast) var(--cv-ease);
}

/* The chevron is painted, not a font glyph, so it cannot go missing and it
   recolours with the theme. */
select:not([multiple]):not([size]),
.form-select {
  padding-right: 2.25rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%237a7a73' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 1rem;
  cursor: pointer;
}
:root[data-theme="dark"] select:not([multiple]):not([size]),
:root[data-theme="dark"] .form-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2396968e' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
}
select[multiple], select[size]:not([size="1"]) { padding-right: 0.75rem; min-height: 6rem; }

textarea, textarea.form-control {
  min-height: 6.5rem;
  line-height: var(--cv-leading);
  resize: vertical;
}

input::placeholder, textarea::placeholder { color: var(--cv-ink-4); opacity: 1; }

:where(input, select, textarea):hover:not(:disabled):not([readonly]) {
  border-color: var(--cv-n-400);
}

:where(input, select, textarea):focus,
.form-control:focus,
.form-select:focus {
  border-color: var(--cv-accent);
  box-shadow: var(--cv-ring);
  outline: none;
}

:where(input, select, textarea):disabled,
:where(input, textarea)[readonly],
.form-control:disabled, .form-control[readonly], .form-select:disabled,
.form-control.readonly {
  background-color: var(--cv-surface-sunk);
  border-color: var(--cv-line);
  color: var(--cv-ink-3);
  cursor: not-allowed;
  opacity: 1;
}
/* A readonly field is still readable data — only truly disabled ones dim. */
:where(input, textarea)[readonly], .form-control.readonly { color: var(--cv-ink-2); cursor: default; }

/* Date/time pickers: make the native indicator match our ink instead of the
   browser's default blue-grey. */
input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] {
  min-height: 2.5rem;
}
input::-webkit-calendar-picker-indicator {
  cursor: pointer;
  opacity: 0.55;
  transition: opacity var(--cv-t-fast) var(--cv-ease);
}
input::-webkit-calendar-picker-indicator:hover { opacity: 1; }
input[type="search"]::-webkit-search-cancel-button { cursor: pointer; }

/* Number inputs in a money context: hide the spinners, they invite mis-clicks
   on a bid amount. Add .cv-spin to opt back in. */
input[type="number"]:not(.cv-spin)::-webkit-outer-spin-button,
input[type="number"]:not(.cv-spin)::-webkit-inner-spin-button {
  appearance: none; margin: 0;
}
input[type="number"]:not(.cv-spin) { appearance: textfield; -moz-appearance: textfield; }

/* Sizes */
.form-control-sm, .cv-input-sm, select.form-select-sm, .form-select-sm {
  min-height: 2.125rem; padding: 0.3125rem 0.625rem; font-size: var(--cv-text-xs);
}
.form-control-lg, .cv-input-lg, .form-select-lg {
  min-height: 2.875rem; padding: 0.625rem 0.875rem; font-size: var(--cv-text-md);
}

/* ─── File input ──────────────────────────────────────────────────────────*/
input[type="file"] {
  display: block;
  width: 100%;
  padding: 0.4375rem 0.75rem;
  font-size: var(--cv-text-sm);
  color: var(--cv-ink-2);
  background-color: var(--cv-surface);
  border: 1px dashed var(--cv-line-strong);
  border-radius: var(--cv-r-md);
  cursor: pointer;
  transition: border-color var(--cv-t-fast) var(--cv-ease), background-color var(--cv-t-fast) var(--cv-ease);
}
input[type="file"]:hover { border-color: var(--cv-accent); background-color: var(--cv-accent-soft); }
input[type="file"]:focus { border-color: var(--cv-accent); box-shadow: var(--cv-ring); outline: none; }
input[type="file"]::file-selector-button {
  margin: -0.4375rem 0.75rem -0.4375rem -0.75rem;
  padding: 0.4375rem 0.75rem;
  border: 0;
  border-right: 1px solid var(--cv-line);
  background-color: var(--cv-surface-sunk);
  color: var(--cv-ink);
  font: inherit;
  font-weight: var(--cv-weight-medium);
  cursor: pointer;
}

/* ─── Checkbox & radio ────────────────────────────────────────────────────*/
input[type="checkbox"], input[type="radio"],
.form-check-input {
  appearance: none;
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  padding: 0;
  vertical-align: text-bottom;
  background-color: var(--cv-surface);
  background-repeat: no-repeat;
  background-position: center;
  background-size: 0.75rem;
  border: 1.5px solid var(--cv-line-strong);
  border-radius: var(--cv-r-xs);
  cursor: pointer;
  transition: background-color var(--cv-t-fast) var(--cv-ease),
              border-color var(--cv-t-fast) var(--cv-ease),
              box-shadow var(--cv-t-fast) var(--cv-ease);
}
input[type="radio"], .form-check-input[type="radio"] { border-radius: 50%; }

input[type="checkbox"]:hover:not(:disabled),
input[type="radio"]:hover:not(:disabled) { border-color: var(--cv-n-400); }

input[type="checkbox"]:checked, .form-check-input:checked {
  background-color: var(--cv-action);
  border-color: var(--cv-action);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23ffffff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5l3.5 3.5L13 4.5'/%3E%3C/svg%3E");
}
input[type="radio"]:checked, .form-check-input[type="radio"]:checked {
  background-color: var(--cv-surface);
  border-color: var(--cv-action);
  border-width: 5px;
  background-image: none;
}
input[type="checkbox"]:indeterminate {
  background-color: var(--cv-action);
  border-color: var(--cv-action);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23ffffff' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M4 8h8'/%3E%3C/svg%3E");
}
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible,
.form-check-input:focus { box-shadow: var(--cv-ring); border-color: var(--cv-accent); outline: none; }
input[type="checkbox"]:disabled, input[type="radio"]:disabled {
  background-color: var(--cv-surface-sunk); border-color: var(--cv-line); cursor: not-allowed;
}
:root[data-theme="dark"] input[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%231a1a1a' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5l3.5 3.5L13 4.5'/%3E%3C/svg%3E");
}

/* ─── Range ───────────────────────────────────────────────────────────────
   The reference design's value-range control: a thin rail with an amber thumb.
   Both vendor prefixes are required; there is no shorthand that covers them. */
input[type="range"], .form-range {
  appearance: none;
  width: 100%;
  height: 1.25rem;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px; border-radius: var(--cv-r-pill); background-color: var(--cv-surface-sunk);
}
input[type="range"]::-moz-range-track {
  height: 4px; border-radius: var(--cv-r-pill); background-color: var(--cv-surface-sunk);
}
input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 1.125rem; height: 1.125rem; margin-top: -0.4375rem;
  border-radius: 50%;
  background-color: var(--cv-accent);
  border: 2px solid var(--cv-surface);
  box-shadow: var(--cv-shadow-sm);
}
input[type="range"]::-moz-range-thumb {
  width: 1.125rem; height: 1.125rem;
  border-radius: 50%;
  background-color: var(--cv-accent);
  border: 2px solid var(--cv-surface);
  box-shadow: var(--cv-shadow-sm);
}
input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: var(--cv-ring); }
input[type="range"]:focus-visible::-moz-range-thumb { box-shadow: var(--cv-ring); }

/* ─── Labels, help and errors ─────────────────────────────────────────────
   The app carries three historical error conventions — `.form-error-message`
   (post_requirement / edit_requirement, previously defined only inside one
   page's inline <style> and therefore unstyled on the other), `.text-danger
   .small` (tender_form) and `.invalid-feedback.d-block` (the bid partial). All
   three are matched here so nothing renders unstyled during the migration;
   rebuilt pages use `.cv-field__error`. */
label, .form-label, .col-form-label, .cv-label {
  display: inline-block;
  margin-bottom: var(--cv-sp-2);
  font-size: var(--cv-text-xs);
  font-weight: var(--cv-weight-medium);
  line-height: 1.4;
  color: var(--cv-ink);
  letter-spacing: var(--cv-track-body);
}
.form-text, .cv-help, .cv-field__help {
  display: block;
  margin-top: var(--cv-sp-2);
  font-size: var(--cv-text-2xs);
  line-height: 1.45;
  color: var(--cv-ink-3);
}
.cv-field__error,
.form-error-message,
.invalid-feedback,
.invalid-feedback.d-block {
  display: block;
  margin-top: var(--cv-sp-2);
  font-size: var(--cv-text-2xs);
  font-weight: var(--cv-weight-medium);
  line-height: 1.45;
  color: var(--cv-danger-text);
}
/* Empty error containers must not open a gap under every field. */
.cv-field__error:empty, .form-error-message:empty { display: none; }

.is-invalid, .was-validated :invalid,
input.is-invalid, select.is-invalid, textarea.is-invalid {
  border-color: var(--cv-danger);
}
.is-invalid:focus { box-shadow: var(--cv-ring-danger); border-color: var(--cv-danger); }
.is-valid, input.is-valid, select.is-valid, textarea.is-valid { border-color: var(--cv-success); }

/* The required marker. Adding it via CSS keeps templates from hand-writing a
   red asterisk span forty times. */
.cv-label--required::after,
.cv-field--required > .cv-field__label::after {
  content: "*";
  margin-left: 0.2em;
  color: var(--cv-danger);
  font-weight: var(--cv-weight-semibold);
}
/* Its opposite, for the rare field where "you may skip this" is the useful
   thing to say — a whole optional sub-section, not every unrequired input. */
.cv-field__optional {
  margin-left: 0.4em;
  font-size: var(--cv-text-2xs); font-weight: var(--cv-weight-regular);
  letter-spacing: 0; text-transform: none;
  color: var(--cv-ink-3);
}

fieldset { min-width: 0; margin: 0; padding: 0; border: 0; }
legend {
  width: 100%;
  margin-bottom: var(--cv-sp-3);
  padding: 0;
  font-size: var(--cv-text-h4);
  font-weight: var(--cv-weight-semibold);
  color: var(--cv-ink);
}

/* Bootstrap's own form wrappers, brought into the system so untouched legacy
   markup does not look foreign next to a rebuilt page. */
.form-check {
  display: flex;
  align-items: flex-start;
  gap: var(--cv-sp-2);
  min-height: auto;
  margin-bottom: var(--cv-sp-2);
  padding-left: 0;
}
.form-check .form-check-input { margin-top: 0.1875rem; margin-left: 0; float: none; }
.form-check-label, .form-check label {
  margin-bottom: 0;
  font-size: var(--cv-text-sm);
  font-weight: var(--cv-weight-regular);
  color: var(--cv-ink-2);
  cursor: pointer;
}
.form-switch { padding-left: 0; }
.form-switch .form-check-input {
  width: 2.25rem;
  height: 1.25rem;
  border-radius: var(--cv-r-pill);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='%23a8a8a1'/%3E%3C/svg%3E");
  background-position: left center;
  background-size: 1.25rem;
  transition: background-position var(--cv-t-mid) var(--cv-ease),
              background-color var(--cv-t-fast) var(--cv-ease);
}
.form-switch .form-check-input:checked {
  background-position: right center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='%23ffffff'/%3E%3C/svg%3E");
}

/* Bootstrap input groups.
   The element-level rule above sets `display:block; width:100%` on every
   control, which breaks Bootstrap's flex row — the addon ends up stacked above
   the field instead of beside it. Restoring the flex-child sizing here is what
   keeps ₹ prefixes and search affordances on one line. */
.input-group {
  display: flex;
  flex-wrap: nowrap;
  align-items: stretch;
  width: 100%;
}
.input-group > .form-control,
.input-group > .form-select,
.input-group > input,
.input-group > select,
.input-group > textarea {
  position: relative;
  flex: 1 1 auto;
  width: 1%;
  min-width: 0;
}
/* Join the pieces into one control: only the outer edges keep their radius. */
.input-group > :not(:first-child) {
  margin-left: -1px;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.input-group > :not(:last-child) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.input-group > .form-control:focus,
.input-group > input:focus,
.input-group > select:focus { z-index: 2; }

.input-group-text {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  padding: 0.5rem 0.75rem;
  font-size: var(--cv-text-sm);
  font-weight: var(--cv-weight-medium);
  color: var(--cv-ink-3);
  background-color: var(--cv-surface-sunk);
  border: 1px solid var(--cv-line-strong);
  border-radius: var(--cv-r-md);
  white-space: nowrap;
}

/* ─── Tables (element level) ──────────────────────────────────────────────*/
table { border-collapse: collapse; width: 100%; }
caption { padding: var(--cv-sp-3) 0; color: var(--cv-ink-3); font-size: var(--cv-text-xs); text-align: left; }

/* ─── Accessibility helpers ───────────────────────────────────────────────*/
.cv-visually-hidden, .visually-hidden, .sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
/* Skip link — hidden until focused, then a real, visible target. */
.cv-skip-link {
  position: absolute;
  top: var(--cv-sp-2);
  left: var(--cv-sp-2);
  z-index: calc(var(--cv-z-toast) + 1);
  padding: var(--cv-sp-3) var(--cv-sp-4);
  background-color: var(--cv-action);
  color: var(--cv-action-ink);
  border-radius: var(--cv-r-md);
  font-size: var(--cv-text-sm);
  font-weight: var(--cv-weight-semibold);
  transform: translateY(-200%);
  transition: transform var(--cv-t-mid) var(--cv-ease);
}
.cv-skip-link:focus { transform: translateY(0); color: var(--cv-action-ink); }

@media print {
  .cv-no-print, .cv-header, .cv-rail, .cv-footer { display: none !important; }
  body { background: #fff; }
}
