/* ════════════════════════════════════════════════════════
   CORVUS — UI Components

   All component CSS references SEMANTIC tokens (--cv-color-*,
   --cv-radius-*) from corvus-tokens.css, never primitives directly.
   Variants follow the semantic vocabulary:
       state:  danger / warning / success / info / neutral
       button: primary / secondary / destructive / ghost
   ════════════════════════════════════════════════════════ */

/* ── Card ────────────────────────────────────────────── */
/* Feature surface — detail pages, forms, modal bodies, page sections. */

.cv-card {
  background: var(--cv-color-bg-card);
  border: 1px solid var(--cv-color-border);
  border-radius: var(--cv-radius-xl);
  box-shadow: var(--cv-shadow);
  transition: box-shadow .2s ease, transform .2s ease, border-color .15s;
}

.cv-card-hover:hover {
  box-shadow: var(--cv-shadow-md);
  border-color: var(--cv-color-border-strong);
  transform: translateY(-2px);
}

.cv-card-header {
  padding: .875rem 1.25rem;
  border-bottom: 1px solid var(--cv-color-border);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--cv-color-text-muted);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
}

/* ── Widget tile chrome ──────────────────────────────── */
/* Tighter sibling of .cv-card — denser padding/radius, suitable for
   widgets that live on a 12-col grid alongside many peers.
   Compose with col-span-N on the parent grid cell:
       <div class="col-span-2"><div class="cv-widget">...</div></div>  */

.cv-widget {
  background: var(--cv-color-bg-card);
  border: 1px solid var(--cv-color-border);
  border-radius: var(--cv-radius-lg);
  box-shadow: var(--cv-shadow);
  padding: 1rem 1.125rem;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: box-shadow .18s, border-color .15s;
}

.cv-widget:hover {
  box-shadow: var(--cv-shadow-md);
  border-color: var(--cv-color-border-strong);
}

.cv-widget-title {
  font-size: .75rem;
  font-weight: 600;
  color: var(--cv-color-text);
}

.cv-widget-sub {
  font-size: .6875rem;
  color: var(--cv-color-text-muted);
  margin-top: .125rem;
}

/* State modifiers — left border draws the eye to attention-needing tiles */
.cv-widget-danger  { border-left: 3px solid var(--cv-color-danger); }
.cv-widget-warning { border-left: 3px solid var(--cv-color-warning); }
.cv-widget-success { border-left: 3px solid var(--cv-color-success); }
.cv-widget-info    { border-left: 3px solid var(--cv-color-info); }

/* ── Typography helpers ──────────────────────────────── */

.cv-label {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--cv-color-text-muted);
}

.cv-stat {
  font-family: 'DM Serif Display', Georgia, serif;
  font-size: 3.25rem;
  line-height: .9;
  letter-spacing: -.04em;
  color: var(--cv-color-text);
}

.cv-stat-sm {
  font-family: 'DM Serif Display', Georgia, serif;
  font-size: 1.6rem;
  line-height: 1;
  letter-spacing: -.03em;
  color: var(--cv-color-text);
}

.cv-muted {
  color: var(--cv-color-text-muted);
}

/* Caption / meta-text tier — between Tailwind's text-xs (12px) and text-sm
   (14px). Used for table caption rows, field hints, inline status text. */
.cv-text-meta {
  font-size: .8125rem;        /* 13px */
  line-height: 1.4;
  color: var(--cv-color-text-muted);
}

/* ── Badges ──────────────────────────────────────────── */
/* Inline status / category indicators. Variants are SEMANTIC.

   Three emphasis levels — pick by how loud the badge needs to be:

     .cv-badge                          DEFAULT — coloured dot + plain text,
                                        no fill. The right choice 95% of the
                                        time. Reads as inline status, not as
                                        a clickable button.

     .cv-badge.cv-badge-outline         Transparent fill, coloured border +
                                        text. Use when status needs a little
                                        more visual weight (e.g. an "Active"
                                        pill on a card header).

     .cv-badge.cv-badge-solid           Filled background + dark text. RARE.
                                        Use only when the badge MUST shout —
                                        "OVERDUE" stamps, "LIVE" indicators.
                                        Colour-on-colour fills read as
                                        buttons; deploy sparingly. */

.cv-badge {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--cv-color-text);
  white-space: nowrap;
  line-height: 1.4;
  background: transparent;
  padding: 0;
  border: none;
  border-radius: 0;
}

/* The dot — rendered via ::before so markup stays one element */
.cv-badge::before {
  content: '';
  display: inline-block;
  width: .5rem;
  height: .5rem;
  border-radius: var(--cv-radius-full);
  background: var(--cv-color-neutral);
  flex-shrink: 0;
}

.cv-badge-danger::before  { background: var(--cv-color-danger); }
.cv-badge-warning::before { background: var(--cv-color-warning); }
.cv-badge-success::before { background: var(--cv-color-success); }
.cv-badge-neutral::before { background: var(--cv-color-neutral); }

/* Outline modifier — adds a border, keeps the dot */
.cv-badge.cv-badge-outline {
  padding: .15rem .5rem;
  border: 1px solid var(--cv-color-border);
  border-radius: var(--cv-radius-full);
}
.cv-badge.cv-badge-outline.cv-badge-danger  { border-color: var(--cv-color-danger);  color: var(--cv-color-danger); }
.cv-badge.cv-badge-outline.cv-badge-warning { border-color: var(--cv-color-warning); color: #b45309; }
.cv-badge.cv-badge-outline.cv-badge-success { border-color: var(--cv-color-success); color: var(--cv-color-success); }
.cv-badge.cv-badge-outline.cv-badge-neutral { border-color: var(--cv-color-border-strong); color: var(--cv-color-text-muted); }

/* Solid modifier — filled pill. Sparingly. */
.cv-badge.cv-badge-solid {
  padding: .2rem .55rem;
  border-radius: var(--cv-radius-full);
  font-weight: 600;
  letter-spacing: .03em;
  font-size: .69rem;
}
.cv-badge.cv-badge-solid::before { display: none; }   /* solid speaks loud enough without the dot */
.cv-badge.cv-badge-solid.cv-badge-danger  { background: var(--cv-color-danger-soft);  color: #991b1b; }
.cv-badge.cv-badge-solid.cv-badge-warning { background: var(--cv-color-warning-soft); color: #c2410c; }
.cv-badge.cv-badge-solid.cv-badge-success { background: var(--cv-color-success-soft); color: #166534; }
.cv-badge.cv-badge-solid.cv-badge-neutral { background: var(--cv-color-neutral-soft); color: var(--cv-color-text-muted); }

/* ── Buttons ─────────────────────────────────────────── */
/* MONOCHROME — no state colours on buttons. Emphasis comes from weight
   (filled vs outline vs ghost), not from hue. The colour budget is spent
   on signals that carry information (severity, alerts, fingerprints) —
   buttons just need to read as clickable.

   Buttons are for ACTION TRIGGERS — submit, save, delete, run, approve,
   reject. Navigation uses .cv-link / .cv-nav-link (see corvus-layout.css).
   For inline / row utility actions, use .cv-icon-btn (below).

   There is NO .cv-btn-destructive variant. Delete actions use
   .cv-btn-secondary or .cv-btn-ghost — the WORD "Delete" + the confirm
   dialog carry the destructive intent. */

.cv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  font-size: .85rem;
  font-weight: 500;
  line-height: 1;
  padding: .55rem 1.1rem;
  border-radius: var(--cv-radius-lg);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s, box-shadow .12s;
  white-space: nowrap;
  text-decoration: none;
  user-select: none;
}

.cv-btn:focus-visible {
  outline: 2px solid var(--cv-color-focus-ring);
  outline-offset: 2px;
}

.cv-btn:disabled,
.cv-btn[aria-disabled="true"] {
  opacity: .5;
  cursor: not-allowed;
}

.cv-btn-sm { padding: .35rem .75rem; font-size: .78rem; }
.cv-btn-lg { padding: .75rem 1.5rem; font-size: .9375rem; font-weight: 600; }

/* Primary — filled navy. The page's MAIN action. */
.cv-btn-primary {
  background: var(--cv-navy);
  color: var(--cv-white);
  border-color: var(--cv-navy);
}

.cv-btn-primary:hover {
  background: var(--cv-navy-mid);
  border-color: var(--cv-navy-mid);
}

/* Secondary — outline. Alternative / Cancel / paired with primary. */
.cv-btn-secondary {
  background: var(--cv-color-bg-card);
  color: var(--cv-color-text);
  border-color: var(--cv-color-border-strong);
}

.cv-btn-secondary:hover {
  background: var(--cv-color-bg-alt);
  border-color: var(--cv-color-text-muted);
}

/* Ghost — borderless. Tertiary / dismiss / low-emphasis utility. */
.cv-btn-ghost {
  background: transparent;
  color: var(--cv-color-text-muted);
  border-color: transparent;
}

.cv-btn-ghost:hover {
  background: var(--cv-color-bg-alt);
  color: var(--cv-color-text);
}

/* ── Icon button — for inline / row / toolbar utility actions ──── */
/* Square (rounded) button with icon only. Always include aria-label.
   Used for: row edit/delete, "more" menu trigger, toolbar utilities.
   Distinct from .cv-btn so visual context tells the reader "this is an
   inline utility, not a page-level action." */

.cv-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.875rem;          /* 30px — comfortable touch target */
  height: 1.875rem;
  padding: 0;
  border-radius: var(--cv-radius-md);
  border: 1px solid transparent;
  background: transparent;
  color: var(--cv-color-text-muted);
  cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
  flex-shrink: 0;
}

.cv-icon-btn:hover {
  background: var(--cv-color-bg-alt);
  color: var(--cv-color-text);
}

.cv-icon-btn:focus-visible {
  outline: 2px solid var(--cv-color-focus-ring);
  outline-offset: 2px;
}

.cv-icon-btn:disabled,
.cv-icon-btn[aria-disabled="true"] {
  opacity: .4;
  cursor: not-allowed;
}

.cv-icon-btn-sm {
  width: 1.5rem;            /* 24px — for very dense rows */
  height: 1.5rem;
  font-size: .85rem;
}

.cv-icon-btn-lg {
  width: 2.25rem;           /* 36px */
  height: 2.25rem;
  font-size: 1.1rem;
}

.cv-icon-btn i { font-size: .95rem; line-height: 1; }
.cv-icon-btn-sm i { font-size: .8rem; }
.cv-icon-btn-lg i { font-size: 1.1rem; }

/* ── Loading state ───────────────────────────────────── */
/* Driven by aria-busy="true" — accessibility state and visual state are
   the same attribute. Always pair with the `disabled` attribute to prevent
   double-submit.

   <button class="cv-btn cv-btn-primary" aria-busy="true" disabled>
     Saving…
   </button>

   <button class="cv-icon-btn" aria-busy="true" disabled aria-label="Saving">
     <i class="bi bi-pencil"></i>
   </button>                                                              */

@keyframes cv-spin { to { transform: rotate(360deg); } }

/* Pill-button loading state — spinner inserted before existing content,
   text stays visible. */
.cv-btn[aria-busy="true"] {
  cursor: wait;
}

.cv-btn[aria-busy="true"]::before {
  content: '';
  width: .85rem;
  height: .85rem;
  border: 1.5px solid currentColor;
  border-top-color: transparent;
  border-radius: var(--cv-radius-full);
  animation: cv-spin .6s linear infinite;
  display: inline-block;
  flex-shrink: 0;
  /* opacity matches text-weight visually */
  opacity: .9;
}

/* Icon-button loading state — spinner replaces the icon entirely. */
.cv-icon-btn[aria-busy="true"] {
  cursor: wait;
}

.cv-icon-btn[aria-busy="true"] i {
  visibility: hidden;
}

.cv-icon-btn[aria-busy="true"]::after {
  content: '';
  position: absolute;
  width: .85rem;
  height: .85rem;
  border: 1.5px solid currentColor;
  border-top-color: transparent;
  border-radius: var(--cv-radius-full);
  animation: cv-spin .6s linear infinite;
  opacity: .9;
}

.cv-icon-btn { position: relative; }   /* needed for the ::after positioning */

/* ── Forms ───────────────────────────────────────────── */

.cv-form-label {
  display: block;
  font-size: .8rem;
  font-weight: 600;
  color: var(--cv-color-text);
  margin-bottom: .35rem;
  letter-spacing: .01em;
}

.cv-form-control,
.cv-form-select {
  display: block;
  width: 100%;
  font-size: .875rem;
  font-family: inherit;
  padding: .5rem .75rem;
  border: 1px solid var(--cv-color-border);
  border-radius: var(--cv-radius-lg);
  background: var(--cv-color-bg-card);
  color: var(--cv-color-text);
  transition: border-color .12s, box-shadow .12s;
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  outline: none;
}

.cv-form-control:focus,
.cv-form-select:focus {
  border-color: var(--cv-color-focus-ring);
  box-shadow: 0 0 0 3px rgba(29,114,232,.1);
}

.cv-form-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2364748b' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .75rem center;
  padding-right: 2.25rem;
}

.cv-form-text {
  font-size: .775rem;
  color: var(--cv-color-text-muted);
  margin-top: .3rem;
}

.cv-form-lg .cv-form-control {
  padding: .65rem 1rem;
  font-size: .9375rem;
}

/* ── Checkbox ────────────────────────────────────────── */
/* Custom-styled. The native input is hidden but kept for keyboard +
   screen-reader support. The visible box is the adjacent .cv-checkbox-box
   span; the label text sits in .cv-checkbox-label.

   <label class="cv-checkbox">
     <input type="checkbox">
     <span class="cv-checkbox-box"></span>
     <span class="cv-checkbox-label">Option text</span>
   </label>                                                              */

.cv-checkbox {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  cursor: pointer;
  font-size: .875rem;
  color: var(--cv-color-text);
  user-select: none;
}

.cv-checkbox input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 1rem;
  height: 1rem;
  margin: 0;
  pointer-events: none;
}

.cv-checkbox-box {
  width: 1rem;
  height: 1rem;
  border: 1.5px solid var(--cv-color-border-strong);
  border-radius: var(--cv-radius-sm);
  background: var(--cv-color-bg-card);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background .12s, border-color .12s;
  position: relative;
}

.cv-checkbox:hover .cv-checkbox-box {
  border-color: var(--cv-color-text-muted);
}

.cv-checkbox input:focus-visible + .cv-checkbox-box {
  outline: 2px solid var(--cv-color-focus-ring);
  outline-offset: 2px;
}

.cv-checkbox input:checked + .cv-checkbox-box {
  background: var(--cv-navy);
  border-color: var(--cv-navy);
}

.cv-checkbox input:checked + .cv-checkbox-box::after {
  content: '';
  width: .5rem;
  height: .25rem;
  border-left: 1.5px solid var(--cv-white);
  border-bottom: 1.5px solid var(--cv-white);
  transform: rotate(-45deg) translate(1px, -1px);
}

/* Indeterminate — half-checked state (used for "some children selected") */
.cv-checkbox input:indeterminate + .cv-checkbox-box {
  background: var(--cv-navy);
  border-color: var(--cv-navy);
}

.cv-checkbox input:indeterminate + .cv-checkbox-box::after {
  content: '';
  width: .5rem;
  height: 1.5px;
  background: var(--cv-white);
  border: none;
  transform: none;
}

.cv-checkbox input:disabled ~ .cv-checkbox-box,
.cv-checkbox input:disabled ~ .cv-checkbox-label {
  opacity: .45;
  cursor: not-allowed;
}

.cv-checkbox input:disabled ~ * { cursor: not-allowed; }

/* ── Radio ───────────────────────────────────────────── */
/* Same pattern as checkbox; rounded box with a centred dot when selected.

   <label class="cv-radio">
     <input type="radio" name="group">
     <span class="cv-radio-box"></span>
     <span class="cv-radio-label">Option text</span>
   </label>                                                              */

.cv-radio {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  cursor: pointer;
  font-size: .875rem;
  color: var(--cv-color-text);
  user-select: none;
}

.cv-radio input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 1rem;
  height: 1rem;
  margin: 0;
  pointer-events: none;
}

.cv-radio-box {
  width: 1rem;
  height: 1rem;
  border: 1.5px solid var(--cv-color-border-strong);
  border-radius: var(--cv-radius-full);
  background: var(--cv-color-bg-card);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background .12s, border-color .12s;
}

.cv-radio:hover .cv-radio-box {
  border-color: var(--cv-color-text-muted);
}

.cv-radio input:focus-visible + .cv-radio-box {
  outline: 2px solid var(--cv-color-focus-ring);
  outline-offset: 2px;
}

.cv-radio input:checked + .cv-radio-box {
  border-color: var(--cv-navy);
  border-width: 1.5px;
}

.cv-radio input:checked + .cv-radio-box::after {
  content: '';
  width: .5rem;
  height: .5rem;
  border-radius: var(--cv-radius-full);
  background: var(--cv-navy);
}

.cv-radio input:disabled ~ .cv-radio-box,
.cv-radio input:disabled ~ .cv-radio-label {
  opacity: .45;
  cursor: not-allowed;
}

.cv-radio input:disabled ~ * { cursor: not-allowed; }

/* ── Switch ──────────────────────────────────────────── */
/* For binary on/off settings where the change is IMMEDIATE (no submit
   button). Use checkbox for "this is part of a form to submit." Use
   switch for "toggle this feature on/off right now."

   <label class="cv-switch">
     <input type="checkbox">
     <span class="cv-switch-track"></span>
     <span class="cv-switch-label">Enable notifications</span>
   </label>                                                              */

.cv-switch {
  display: inline-flex;
  align-items: center;
  gap: .625rem;
  cursor: pointer;
  font-size: .875rem;
  color: var(--cv-color-text);
  user-select: none;
}

.cv-switch input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 2rem;
  height: 1.125rem;
  margin: 0;
  pointer-events: none;
}

.cv-switch-track {
  position: relative;
  display: inline-block;
  width: 2rem;
  height: 1.125rem;
  background: var(--cv-color-border-strong);
  border-radius: var(--cv-radius-full);
  transition: background .15s;
  flex-shrink: 0;
}

.cv-switch-track::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: calc(1.125rem - 4px);
  height: calc(1.125rem - 4px);
  background: var(--cv-white);
  border-radius: var(--cv-radius-full);
  transition: transform .18s ease, background .15s;
  box-shadow: 0 1px 2px rgba(0,0,0,.15);
}

.cv-switch:hover .cv-switch-track {
  background: var(--cv-color-text-muted);
}

.cv-switch input:focus-visible + .cv-switch-track {
  outline: 2px solid var(--cv-color-focus-ring);
  outline-offset: 2px;
}

.cv-switch input:checked + .cv-switch-track {
  background: var(--cv-navy);
}

.cv-switch input:checked + .cv-switch-track::after {
  transform: translateX(calc(2rem - 1.125rem));
}

.cv-switch:hover input:checked + .cv-switch-track {
  background: var(--cv-navy-mid);
}

.cv-switch input:disabled ~ .cv-switch-track,
.cv-switch input:disabled ~ .cv-switch-label {
  opacity: .45;
  cursor: not-allowed;
}

.cv-switch input:disabled ~ * { cursor: not-allowed; }

/* ── Form field wrapper + validation ─────────────────── */
/* Groups label + control + helper/error in one consistent block.

   Standard markup:
       <div class="cv-form-field">
         <label class="cv-form-label" for="…">
           Field name
           <span class="cv-form-required" aria-hidden="true">*</span>
         </label>
         <input class="cv-form-control" id="…" type="text"
                aria-describedby="…-helper">
         <div class="cv-form-message" id="…-helper">Helper text.</div>
       </div>

   Error state — add .cv-is-invalid to the control, swap helper for error:
         <input class="cv-form-control cv-is-invalid" aria-invalid="true"
                aria-describedby="…-error">
         <div class="cv-form-message cv-form-message-error" id="…-error">
           <i class="bi bi-exclamation-circle"></i>
           <span>Risk name is required.</span>
         </div>                                                            */

.cv-form-field {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  margin-bottom: 1rem;
}

.cv-form-field:last-child { margin-bottom: 0; }

.cv-form-required {
  color: var(--cv-color-danger);
  margin-left: .15rem;
  font-weight: 600;
}

/* Helper / error message below the control */
.cv-form-message {
  display: flex;
  align-items: flex-start;
  gap: .3125rem;
  font-size: .72rem;
  color: var(--cv-color-text-muted);
  line-height: 1.4;
  margin-top: .15rem;
}

.cv-form-message i {
  font-size: .85rem;
  line-height: 1.2;
  flex-shrink: 0;
}

.cv-form-message-error {
  color: var(--cv-color-danger);
}

/* Invalid state — applies to text input / select / textarea */
.cv-form-control.cv-is-invalid,
.cv-form-select.cv-is-invalid {
  border-color: var(--cv-color-danger);
  background-color: rgba(220, 38, 38, .025);
}

.cv-form-control.cv-is-invalid:focus,
.cv-form-select.cv-is-invalid:focus {
  border-color: var(--cv-color-danger);
  box-shadow: 0 0 0 3px rgba(220, 38, 38, .12);
}

/* Optional success / valid state — same pattern in green. Rare but useful
   for fields that need explicit confirmation (e.g. password strength). */
.cv-form-control.cv-is-valid,
.cv-form-select.cv-is-valid {
  border-color: var(--cv-color-success);
}

.cv-form-control.cv-is-valid:focus,
.cv-form-select.cv-is-valid:focus {
  border-color: var(--cv-color-success);
  box-shadow: 0 0 0 3px rgba(22, 163, 74, .12);
}

/* ── Tables ──────────────────────────────────────────── */

.cv-table {
  width: 100%;
  border-collapse: collapse;
  font-size: .875rem;
}

.cv-table thead th {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--cv-color-text-muted);
  padding: .6rem .875rem;
  border-bottom: 2px solid var(--cv-color-border);
  text-align: left;
  white-space: nowrap;
}

.cv-table tbody td {
  padding: .75rem .875rem;
  border-bottom: 1px solid var(--cv-color-border);
  vertical-align: middle;
}

.cv-table tbody tr:last-child td { border-bottom: none; }
.cv-table tbody tr:hover td      { background: var(--cv-color-bg-alt); }

/* ── Breadcrumbs ─────────────────────────────────────── */

.cv-breadcrumb {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .78rem;
  color: var(--cv-color-text-muted);
  flex-wrap: wrap;
}

.cv-breadcrumb a         { color: var(--cv-color-text-link); }
.cv-breadcrumb a:hover   { color: var(--cv-color-text-link-hover); }
.cv-breadcrumb-sep       { color: var(--cv-color-border-strong); user-select: none; }
.cv-breadcrumb-active    { color: var(--cv-color-text-muted); }

/* ── Progress bars ───────────────────────────────────── */

.cv-progress {
  height: .4rem;
  background: var(--cv-color-border);
  border-radius: var(--cv-radius-full);
  overflow: hidden;
}

.cv-progress-bar {
  height: 100%;
  border-radius: var(--cv-radius-full);
  transition: width .4s ease;
}

/* ── Segmented progress bar — for activity widget stages ─────────
   One segment per workflow stage. Each segment takes a colour state
   matching the locked vocabulary:
       (no class)               → not started — grey
       .cv-progress-segment-in-progress → active — blue
       .cv-progress-segment-done        → complete — green
       .cv-progress-segment-overdue     → behind — amber
       .cv-progress-segment-failed      → rejected/blocked — red

   Each segment should carry a `title` attribute showing the stage name +
   owner so hover reveals detail without consuming tile space. */

.cv-progress-segmented {
  display: flex;
  height: .5rem;
  border-radius: var(--cv-radius-full);
  overflow: hidden;
  gap: 1px;
  background: var(--cv-color-border);
}

.cv-progress-segment {
  flex: 1;
  height: 100%;
  background: var(--cv-color-neutral);
  opacity: .25;                     /* default = not started — muted grey */
  transition: background .2s, opacity .2s;
  cursor: help;                     /* hint that hover reveals title */
}

.cv-progress-segment-in-progress { background: var(--cv-color-primary); opacity: 1; }
.cv-progress-segment-done        { background: var(--cv-color-success); opacity: 1; }
.cv-progress-segment-overdue     { background: var(--cv-color-warning); opacity: 1; }
.cv-progress-segment-failed      { background: var(--cv-color-danger);  opacity: 1; }

/* ── Empty states ────────────────────────────────────── */

.cv-empty {
  text-align: center;
  padding: 3.5rem 1.5rem;
  color: var(--cv-color-text-muted);
}

.cv-empty-icon {
  font-size: 2.25rem;
  margin-bottom: .85rem;
  display: block;
  opacity: .2;
}

.cv-empty h5 {
  font-family: 'DM Sans', sans-serif;
  font-size: .9375rem;
  font-weight: 600;
  color: #334155;
  margin: 0 0 .4rem;
}

.cv-empty p {
  font-size: .85rem;
  color: var(--cv-color-text-muted);
  max-width: 28ch;
  margin: 0 auto;
  line-height: 1.6;
}

.cv-empty-positive .cv-empty-icon { opacity: .4; color: var(--cv-color-success); }
.cv-empty-prompt   .cv-empty-icon { opacity: .25; color: var(--cv-color-info); }

/* ── Alerts ──────────────────────────────────────────── */
/* Page-level messages. Variants follow the same semantic vocabulary as badges. */

.cv-alert {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  padding: .875rem 1rem;
  border-radius: var(--cv-radius-lg);
  font-size: .875rem;
  line-height: 1.5;
  border: 1px solid transparent;
}

.cv-alert-danger  { background: var(--cv-color-danger-soft);  color: #991b1b; border-color: #fecaca; }
.cv-alert-success { background: var(--cv-color-success-soft); color: #166534; border-color: #bbf7d0; }
.cv-alert-warning { background: #fef9c3;                      color: #854d0e; border-color: #fde68a; }

/* `info` alerts are now a neutral surface with a small blue accent stripe —
   blue is the brand colour, not a state. Use sparingly; most "info" content
   doesn't need an alert at all. */
.cv-alert-info    {
  background: var(--cv-color-neutral-soft);
  color: var(--cv-color-text);
  border-color: var(--cv-color-border);
  border-left: 3px solid var(--cv-color-primary);
}

.cv-alert i {
  font-size: 1rem;
  flex-shrink: 0;
  margin-top: .1rem;
}

/* ── Nav tile (programme areas) ──────────────────────── */

.cv-nav-tile {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .75rem 1rem;
  border: 1px solid var(--cv-color-border);
  border-radius: var(--cv-radius-lg);
  background: var(--cv-color-bg-card);
  color: var(--cv-color-text);
  font-size: .875rem;
  font-weight: 500;
  text-decoration: none;
  box-shadow: var(--cv-shadow);
  transition: box-shadow .18s, transform .18s, border-color .15s, background .15s, color .15s;
  min-height: 3.5rem;
}

.cv-nav-tile:hover {
  border-color: var(--cv-color-primary);
  background: var(--cv-color-primary-soft);
  color: var(--cv-color-primary);
  box-shadow: var(--cv-shadow-md);
  transform: translateY(-1px);
}

.cv-nav-tile i               { font-size: 1rem; flex-shrink: 0; transition: transform .15s; }
.cv-nav-tile:hover i         { transform: scale(1.1); }

/* ── Inline disclosure ───────────────────────────────── */
/* Row-level expandable detail. Chevron is its own click target — clicking
   it toggles a panel beneath the row WITHOUT navigating. The row body
   remains clickable for navigating to the detail page.

   Markup:
       <div class="cv-disclosure">
         <div class="cv-disclosure-trigger">
           <button class="cv-icon-btn cv-icon-btn-sm cv-disclosure-chevron"
                   aria-expanded="false"
                   aria-controls="related-r042"
                   aria-label="Show related items">
             <i class="bi bi-chevron-right"></i>
           </button>
           <a class="cv-disclosure-main" href="/risks/R-042/">
             … row content …
           </a>
         </div>
         <div class="cv-disclosure-panel" id="related-r042" hidden>
           … related content …
         </div>
       </div>

   HTMX integration — server returns the panel content on first expand:
       <button … hx-get="/risks/R-042/related/"
                 hx-target="#related-r042"
                 hx-trigger="click once">

   The JS helper toggles aria-expanded + hidden state. With HTMX's
   "click once" trigger, content fetches the first time; subsequent
   clicks just toggle visibility. */

.cv-disclosure {
  border-bottom: 1px solid var(--cv-color-border);
}

.cv-disclosure:last-child {
  border-bottom: none;
}

.cv-disclosure-trigger {
  display: flex;
  align-items: center;
  gap: .25rem;
  padding-left: .25rem;
}

/* The chevron — uses .cv-icon-btn styling; this class adds the rotation
   on expand. Use alongside .cv-icon-btn .cv-icon-btn-sm. */
.cv-disclosure-chevron i {
  transition: transform .2s ease;
}

.cv-disclosure-chevron[aria-expanded="true"] i {
  transform: rotate(90deg);
}

/* The clickable row body — navigates to detail page on click */
.cv-disclosure-main {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .625rem .75rem;
  color: inherit;
  text-decoration: none;
  border-radius: var(--cv-radius-md);
  transition: background .12s;
  min-width: 0;
}

.cv-disclosure-main:hover {
  background: var(--cv-color-bg-alt);
}

/* The expanding panel — indented under the row, distinguished background */
.cv-disclosure-panel {
  padding: .75rem 1rem .875rem 2.5rem;
  background: var(--cv-color-bg-alt);
  border-top: 1px solid var(--cv-color-border);
}

.cv-disclosure-panel[hidden] {
  display: none;
}

/* Common content inside the panel — a small section label, then content */
.cv-disclosure-panel-label {
  font-size: .65rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--cv-color-text-muted);
  margin-bottom: .5rem;
}

/* ── Toast / Snackbar ────────────────────────────────── */
/* Transient feedback after async actions ("Saved", "Evidence uploaded").
   Different from .cv-alert (page-level, persistent, demands attention).

   The container is created on demand by the cvToast() JS helper at the
   bottom of visual_inventory.html. Markup spawned per toast:

       <div class="cv-toast cv-toast-success" role="status">
         <i class="bi bi-check-circle cv-toast-icon"></i>
         <div class="cv-toast-message">Engagement saved.</div>
         <button class="cv-icon-btn cv-icon-btn-sm" aria-label="Dismiss">
           <i class="bi bi-x-lg"></i>
         </button>
       </div>

   Variants follow the colour vocabulary — neutral surface + coloured
   icon + coloured left border. NEVER solid-filled toasts (they'd read
   as buttons). */

.cv-toast-container {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  z-index: 100;
  display: flex;
  flex-direction: column-reverse;       /* newest toast at the bottom (closest to user) */
  gap: .625rem;
  pointer-events: none;                 /* container doesn't intercept clicks */
  max-width: calc(100vw - 3rem);
  width: 22rem;
}

.cv-toast {
  pointer-events: auto;                 /* but individual toasts do */
  display: flex;
  align-items: flex-start;
  gap: .625rem;
  padding: .75rem .875rem;
  background: var(--cv-color-bg-card);
  border: 1px solid var(--cv-color-border);
  border-left: 3px solid var(--cv-color-text-muted);
  border-radius: var(--cv-radius-lg);
  box-shadow: 0 6px 20px rgba(15,23,42,.12), 0 2px 4px rgba(15,23,42,.06);
  font-size: .82rem;
  line-height: 1.4;
  color: var(--cv-color-text);
  /* Entry animation */
  animation: cv-toast-in .22s cubic-bezier(.16, 1, .3, 1);
}

/* Exit state — applied by JS just before removal */
.cv-toast.cv-toast-leaving {
  animation: cv-toast-out .18s ease-in forwards;
}

@keyframes cv-toast-in {
  from { opacity: 0; transform: translateX(20px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes cv-toast-out {
  to { opacity: 0; transform: translateX(20px); }
}

/* Variants — left border + icon colour */
.cv-toast-success { border-left-color: var(--cv-color-success); }
.cv-toast-success .cv-toast-icon { color: var(--cv-color-success); }

.cv-toast-warning { border-left-color: var(--cv-color-warning); }
.cv-toast-warning .cv-toast-icon { color: var(--cv-color-warning); }

.cv-toast-danger  { border-left-color: var(--cv-color-danger); }
.cv-toast-danger  .cv-toast-icon { color: var(--cv-color-danger); }

.cv-toast-info    { border-left-color: var(--cv-color-primary); }
.cv-toast-info    .cv-toast-icon { color: var(--cv-color-primary); }

.cv-toast-icon {
  font-size: 1rem;
  line-height: 1.3;
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--cv-color-text-muted);
}

.cv-toast-message {
  flex: 1;
  min-width: 0;
}

.cv-toast-message strong {
  font-weight: 600;
  color: var(--cv-color-text);
}

/* Optional action button inline in the toast (e.g. "Undo") */
.cv-toast-action {
  background: transparent;
  border: none;
  padding: 0 .25rem;
  margin-left: .25rem;
  font: inherit;
  font-weight: 500;
  color: var(--cv-color-text-link);
  cursor: pointer;
  border-radius: var(--cv-radius-sm);
}

.cv-toast-action:hover {
  color: var(--cv-color-text-link-hover);
  text-decoration: underline;
}

.cv-toast-action:focus-visible {
  outline: 2px solid var(--cv-color-focus-ring);
  outline-offset: 2px;
}

/* ── Modal / Dialog ──────────────────────────────────── */
/* Built on the native HTML <dialog> element via .showModal() — which gives
   us a free focus trap, native Escape-to-close, body scroll lock, and the
   ::backdrop pseudo-element. We just style and wire open/close handlers.

   Markup:
       <dialog class="cv-modal" id="my-modal">
         <header class="cv-modal-header">
           <h2 class="cv-modal-title">Title</h2>
           <button class="cv-icon-btn cv-tooltip" data-tooltip="Close"
                   aria-label="Close" data-cv-modal-close>
             <i class="bi bi-x-lg"></i>
           </button>
         </header>
         <div class="cv-modal-body">…</div>
         <footer class="cv-modal-footer">
           <button class="cv-btn cv-btn-ghost" data-cv-modal-close>Cancel</button>
           <button class="cv-btn cv-btn-primary">Confirm</button>
         </footer>
       </dialog>

   Trigger:
       <button data-cv-modal-open="my-modal">Open</button>

   Size variants:
       .cv-modal-sm   ~24rem — confirm dialogs
       (default)      ~32rem — short forms
       .cv-modal-lg   ~48rem — multi-section forms / wider content    */

.cv-modal {
  /* Reset the dialog defaults */
  padding: 0;
  border: none;
  border-radius: var(--cv-radius-xl);
  background: var(--cv-color-bg-card);
  color: var(--cv-color-text);
  box-shadow: 0 20px 50px rgba(15,23,42,.25), 0 6px 12px rgba(15,23,42,.12);
  overflow: hidden;

  /* Sizing */
  width: 32rem;
  max-width: calc(100vw - 2rem);
  max-height: calc(100vh - 4rem);

  /* When open, become a flex column so header / scrollable body / footer
     layout correctly. The display change is necessary because <dialog>
     uses display: block by default which doesn't constrain body scroll. */
}

.cv-modal[open] {
  display: flex;
  flex-direction: column;
  animation: cv-modal-in .15s cubic-bezier(.16, 1, .3, 1);
}

.cv-modal::backdrop {
  background: rgba(15, 23, 42, .45);
  animation: cv-modal-backdrop-in .15s ease-out;
}

@keyframes cv-modal-in {
  from { opacity: 0; transform: translateY(-8px) scale(.98); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}

@keyframes cv-modal-backdrop-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Size modifiers */
.cv-modal.cv-modal-sm { width: 24rem; }
.cv-modal.cv-modal-lg { width: 48rem; }

/* Anatomy */
.cv-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem 1rem 1.5rem;
  border-bottom: 1px solid var(--cv-color-border);
  flex-shrink: 0;
}

.cv-modal-title {
  font-family: 'DM Serif Display', Georgia, serif;
  font-size: 1.15rem;
  font-weight: 400;
  letter-spacing: -.01em;
  color: var(--cv-color-text);
  margin: 0;
  line-height: 1.3;
}

.cv-modal-body {
  padding: 1.25rem 1.5rem;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
  line-height: 1.55;
}

.cv-modal-body p { margin-bottom: .75rem; }
.cv-modal-body p:last-child { margin-bottom: 0; }

.cv-modal-footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: .5rem;
  padding: 1rem 1.5rem;
  border-top: 1px solid var(--cv-color-border);
  background: var(--cv-color-bg-alt);
  flex-shrink: 0;
}

/* Optional helper for left-aligned info inside footer (e.g. "Step 2 of 3") */
.cv-modal-footer-info {
  margin-right: auto;
  font-size: .78rem;
  color: var(--cv-color-text-muted);
}

/* ── Tooltip ─────────────────────────────────────────── */
/* CSS-only tooltip — no JS dependency. Activates on hover, on keyboard
   focus, and on focus-within (so a tooltip on a wrapper element still
   appears when its child receives focus).

   Usage:
       <button class="cv-icon-btn cv-tooltip" data-tooltip="Edit">
         <i class="bi bi-pencil"></i>
       </button>

   Placement modifiers:
       (none)              → tooltip appears above (default)
       .cv-tooltip-bottom  → below
       .cv-tooltip-left    → to the left
       .cv-tooltip-right   → to the right

   The tooltip text comes from the data-tooltip attribute. Long text
   wraps at max-width 16rem. For interactive popovers (with links inside),
   use a popover component (TBC — different concept). */

.cv-tooltip {
  position: relative;
}

.cv-tooltip[data-tooltip]::after,
.cv-tooltip[data-tooltip]::before {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s ease;
  transition-delay: .25s;
  z-index: 100;
}

/* The tooltip body */
.cv-tooltip[data-tooltip]::after {
  content: attr(data-tooltip);
  background: var(--cv-navy);
  color: var(--cv-text-on-dark);
  padding: .375rem .625rem;
  border-radius: var(--cv-radius-md);
  font-size: .72rem;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: 0;
  white-space: normal;
  text-align: center;
  max-width: 16rem;
  width: max-content;
  box-shadow: var(--cv-shadow-md);
}

/* The arrow — small triangle pointing at the trigger */
.cv-tooltip[data-tooltip]::before {
  content: '';
  border: .3125rem solid transparent;
}

/* Default placement: above */
.cv-tooltip[data-tooltip]::after {
  bottom: calc(100% + .5rem);
  left: 50%;
  transform: translateX(-50%);
}
.cv-tooltip[data-tooltip]::before {
  bottom: calc(100% + .1875rem);
  left: 50%;
  transform: translateX(-50%);
  border-top-color: var(--cv-navy);
}

/* Below */
.cv-tooltip.cv-tooltip-bottom[data-tooltip]::after {
  bottom: auto;
  top: calc(100% + .5rem);
}
.cv-tooltip.cv-tooltip-bottom[data-tooltip]::before {
  bottom: auto;
  top: calc(100% + .1875rem);
  border-top-color: transparent;
  border-bottom-color: var(--cv-navy);
}

/* Left */
.cv-tooltip.cv-tooltip-left[data-tooltip]::after {
  bottom: auto;
  top: 50%;
  left: auto;
  right: calc(100% + .5rem);
  transform: translateY(-50%);
}
.cv-tooltip.cv-tooltip-left[data-tooltip]::before {
  bottom: auto;
  top: 50%;
  left: auto;
  right: calc(100% + .1875rem);
  transform: translateY(-50%);
  border-top-color: transparent;
  border-left-color: var(--cv-navy);
}

/* Right */
.cv-tooltip.cv-tooltip-right[data-tooltip]::after {
  bottom: auto;
  top: 50%;
  left: calc(100% + .5rem);
  transform: translateY(-50%);
}
.cv-tooltip.cv-tooltip-right[data-tooltip]::before {
  bottom: auto;
  top: 50%;
  left: calc(100% + .1875rem);
  transform: translateY(-50%);
  border-top-color: transparent;
  border-right-color: var(--cv-navy);
}

/* Show on hover, focus, focus-within */
.cv-tooltip[data-tooltip]:hover::after,
.cv-tooltip[data-tooltip]:hover::before,
.cv-tooltip[data-tooltip]:focus::after,
.cv-tooltip[data-tooltip]:focus::before,
.cv-tooltip[data-tooltip]:focus-within::after,
.cv-tooltip[data-tooltip]:focus-within::before {
  opacity: 1;
}

/* ── Tabs ────────────────────────────────────────────── */
/* Underline-style tabs. Visual state is driven by [aria-selected="true"]
   on the tab button — so the rendering and the accessibility state can
   never diverge. Pair with the tabs JS helper (see visual_inventory.html
   bottom) for click + keyboard activation.

   Markup:
       <div class="cv-tabs">
         <div class="cv-tablist" role="tablist">
           <button class="cv-tab" role="tab" id="t-overview"
                   aria-controls="p-overview" aria-selected="true" tabindex="0">
             Overview
           </button>
           <button class="cv-tab" role="tab" id="t-pm"
                   aria-controls="p-pm" aria-selected="false" tabindex="-1">
             Project Management
             <span class="cv-tab-count">12</span>
           </button>
         </div>
         <div class="cv-tabpanel" role="tabpanel" id="p-overview"
              aria-labelledby="t-overview">…</div>
         <div class="cv-tabpanel" role="tabpanel" id="p-pm"
              aria-labelledby="t-pm" hidden>…</div>
       </div>                                                              */

.cv-tabs {
  display: flex;
  flex-direction: column;
}

.cv-tablist {
  display: flex;
  border-bottom: 1px solid var(--cv-color-border);
  gap: 0;
}

.cv-tab {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  background: transparent;
  border: none;
  border-radius: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: .85rem;
  font-weight: 500;
  color: var(--cv-color-text-muted);
  padding: .75rem 1.25rem;
  margin-bottom: -1px;                /* overlap parent border so active underline doesn't double */
  border-bottom: 2px solid transparent;
  transition: color .12s, border-color .12s;
  white-space: nowrap;
}

.cv-tab:hover {
  color: var(--cv-color-text);
}

.cv-tab:focus-visible {
  outline: 2px solid var(--cv-color-focus-ring);
  outline-offset: -2px;
}

.cv-tab[aria-selected="true"] {
  color: var(--cv-color-text);
  border-bottom-color: var(--cv-navy);
}

.cv-tab[disabled],
.cv-tab[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
}

/* Tab icon — smaller, muted unless tab is active */
.cv-tab i {
  font-size: .95rem;
  line-height: 1;
}

/* Optional count badge inline in tab */
.cv-tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.125rem;
  padding: 0 .35rem;
  border-radius: var(--cv-radius-full);
  background: var(--cv-color-bg-alt);
  color: var(--cv-color-text-muted);
  font-size: .65rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.cv-tab[aria-selected="true"] .cv-tab-count {
  background: var(--cv-navy);
  color: var(--cv-text-on-dark);
}

/* Tabpanel — content area for each tab. Inactive panels use [hidden]. */
.cv-tabpanel {
  padding: 1.25rem 0;
}

.cv-tabpanel[hidden] {
  display: none;
}

.cv-tabpanel:focus-visible {
  outline: 2px solid var(--cv-color-focus-ring);
  outline-offset: 2px;
  border-radius: var(--cv-radius-md);
}

/* ── Stepper (process / workflow stages) ─────────────── */
/* Horizontal stepper for showing process progress — used inside the
   activity tile to display the stages of a workflow. Numbered circles
   connected by a line; current step has stronger weight.

   Markup:
       <ol class="cv-stepper">
         <li class="cv-stepper-step cv-stepper-step-done">
           <span class="cv-stepper-marker">✓</span>
           <span class="cv-stepper-label">Scope</span>
         </li>
         <li class="cv-stepper-step cv-stepper-step-current">
           <span class="cv-stepper-marker">3</span>
           <span class="cv-stepper-label">Evidence</span>
         </li>
         <li class="cv-stepper-step">  <!-- todo, default -->
           <span class="cv-stepper-marker">4</span>
           <span class="cv-stepper-label">Test</span>
         </li>
       </ol>                                                                  */

.cv-stepper {
  display: flex;
  align-items: flex-start;
  gap: 0;
  list-style: none;
  padding: 0;
  margin: 0;
  width: 100%;
}

.cv-stepper-step {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .375rem;
  position: relative;
  min-width: 0;
}

/* The connecting line — drawn as a ::after pseudo on each step except the
   last, anchored to the right of the marker */
.cv-stepper-step::after {
  content: '';
  position: absolute;
  top: .6875rem;            /* aligns to the centre of the 1.375rem marker */
  left: calc(50% + .8rem);  /* starts at marker edge */
  right: calc(-50% + .8rem);
  height: 1px;
  background: var(--cv-color-border);
}

.cv-stepper-step:last-child::after { display: none; }

/* Done step — line to the right is filled */
.cv-stepper-step-done::after { background: var(--cv-color-success); }

.cv-stepper-marker {
  width: 1.375rem;
  height: 1.375rem;
  border-radius: var(--cv-radius-full);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: .6875rem;
  font-weight: 600;
  background: var(--cv-color-bg-card);
  border: 1.5px solid var(--cv-color-border);
  color: var(--cv-color-text-muted);
  flex-shrink: 0;
  position: relative;
  z-index: 1;
}

.cv-stepper-step-done .cv-stepper-marker {
  background: var(--cv-color-success);
  border-color: var(--cv-color-success);
  color: var(--cv-white);
}

.cv-stepper-step-current .cv-stepper-marker {
  background: var(--cv-color-bg-card);
  border-color: var(--cv-color-primary);
  border-width: 2px;
  color: var(--cv-color-primary);
}

.cv-stepper-label {
  font-size: .68rem;
  color: var(--cv-color-text-muted);
  text-align: center;
  line-height: 1.2;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cv-stepper-step-current .cv-stepper-label {
  color: var(--cv-color-text);
  font-weight: 500;
}

.cv-stepper-step-done .cv-stepper-label {
  color: var(--cv-color-text-muted);
}

/* ── Donut / ring ────────────────────────────────────── */

.cv-ring-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.cv-ring-label {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

/* ── Avatar ──────────────────────────────────────────── */
/* NOT for representing individual people. Two-letter initials collide
   ("DS" = David Sherratt AND Doug Smith) and are useless identifiers.
   For people, use a person icon + "I. Surname" format instead — see the
   work_item_list widget's assignee meta-item.

   This class is retained for non-person uses: organisation logos,
   anonymous group indicators, "+3 others" counters, etc. */

.cv-avatar {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--cv-radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  flex-shrink: 0;
  background: var(--cv-navy);
  color: var(--cv-color-text-on-dark);
  letter-spacing: .05em;
}

/* ── Page fade-up animation ──────────────────────────── */

@media (prefers-reduced-motion: no-preference) {
  .cv-card {
    animation: cv-up .22s ease both;
  }

  .cv-stagger-1 { animation-delay:  40ms; }
  .cv-stagger-2 { animation-delay:  80ms; }
  .cv-stagger-3 { animation-delay: 120ms; }
  .cv-stagger-4 { animation-delay: 160ms; }

  @keyframes cv-up {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0);   }
  }
}
