/*
 * components.css — the CitedProof base component library.
 *
 * WHAT: every primitive the marketing site and the app share — buttons, cards,
 * tables, badges, form controls, the verdict pill, the score readout, empty
 * states and the responsive grid.
 *
 * WHY one file for both surfaces: the thing an agency buys is a deliverable it
 * shows to its own client. If the pricing page and the workspace screen use
 * different buttons, the product looks assembled rather than built.
 *
 * REQUIRES tokens.css. Every colour here is a LAYER 2 semantic alias, so dark
 * mode needs no changes in this file.
 *
 * MOBILE-FIRST IS THE DEFAULT, not a media query at the bottom. Base styles are
 * the 390px styles; media queries only ever add. Interactive targets are
 * --cp-tap-min (44px) tall; body text never drops below 16px.
 */

/* ==========================================================================
   1. BASE
   ========================================================================== */

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

/* The [hidden] attribute must always win.
   The UA stylesheet's `[hidden] { display: none }` has specificity 0,1,0 — the same as
   any single class — so a component that sets `display` overrides it and the element
   stays on screen with nothing in it. Not theoretical: `.cp-note { display: flex }`
   made every `[data-cp-form-error]` box (login, signup AND settings) render as an empty
   red panel on a page where nothing had gone wrong. app.js toggles `.hidden` to show
   and clear those messages, so this rule is what makes that mechanism work at all.
   Found by looking at a screenshot of the live login page, not by a test. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--cp-color-bg);
  color: var(--cp-color-text);
  font-family: var(--cp-font-sans);
  font-size: var(--cp-text-base);
  line-height: var(--cp-leading-normal);
  font-weight: var(--cp-weight-regular);
  letter-spacing: var(--cp-tracking-normal);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}

img, video { max-width: 100%; height: auto; display: block; }
/* NOT `height: auto` — that lets a flex container stretch an inline icon and
   silently overrides the svg's own width/height. Icons must keep their stated
   size. */
svg { display: block; max-width: 100%; }

hr, .cp-divider {
  border: 0;
  border-top: var(--cp-hairline) solid var(--cp-color-border);
  margin: var(--cp-space-6) 0;
}

a { color: var(--cp-color-link); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--cp-color-link-hover); }

::selection { background: var(--cp-color-primary-bg); color: var(--cp-color-primary-text); }

/* One focus treatment, everywhere. Keyboard users get a ring; mouse users do
   not, but the ring is never removed outright. */
:focus-visible {
  outline: 2px solid var(--cp-color-primary);
  outline-offset: 2px;
  border-radius: var(--cp-radius-xs);
}

.cp-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}

/* Logo swap. The two-tone mark is drawn for a paper ground — ink brackets sit
   at 1.3:1 on the dark surface and simply vanish. Ship both files and let CSS
   pick, rather than hoping every template remembers:
     <img class="cp-logo cp-logo--light" src="logo.svg" alt="CitedProof">
     <img class="cp-logo cp-logo--dark"  src="logo-inverse.svg" alt="" aria-hidden="true"> */
.cp-logo--dark { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-cp-theme='light']) .cp-logo--light { display: none; }
  :root:not([data-cp-theme='light']) .cp-logo--dark { display: block; }
}
:root[data-cp-theme='dark'] .cp-logo--light { display: none; }
:root[data-cp-theme='dark'] .cp-logo--dark { display: block; }

/* Forces a paper ground regardless of theme. For showing a light-only asset,
   a client's own logo, or a PDF preview. */
.cp-paper {
  background: var(--cp-neutral-0);
  color: var(--cp-neutral-900);
  border: var(--cp-hairline) solid var(--cp-neutral-200);
  border-radius: var(--cp-radius-lg);
  padding: var(--cp-space-5);
}

/* ==========================================================================
   2. LAYOUT
   ========================================================================== */

.cp-container {
  width: 100%;
  max-width: var(--cp-container);
  margin-inline: auto;
  padding-inline: var(--cp-gutter);
}
.cp-container--narrow { max-width: var(--cp-container-narrow); }
.cp-container--wide { max-width: var(--cp-container-wide); }

.cp-section { padding-block: var(--cp-section-y); }

/* Vertical rhythm without margin collapse surprises. */
.cp-stack > * + * { margin-top: var(--cp-space-4); }
.cp-stack--tight > * + * { margin-top: var(--cp-space-2); }
.cp-stack--loose > * + * { margin-top: var(--cp-space-8); }

/* Horizontal group that wraps rather than overflowing. */
.cp-cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cp-space-3);
  align-items: center;
}
.cp-cluster--between { justify-content: space-between; }

/* One grid. Single column on a phone; columns appear only when there is room.
   auto-fit + minmax means no breakpoint list to maintain and no overflow. */
.cp-grid {
  display: grid;
  gap: var(--cp-space-4);
  grid-template-columns: 1fr;
}
@media (min-width: 640px) {
  .cp-grid { gap: var(--cp-space-5); grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
  .cp-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 960px) {
  .cp-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cp-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .cp-grid--sidebar { grid-template-columns: minmax(0, 1fr) 20rem; }
}

/* ==========================================================================
   3. TYPOGRAPHY
   ========================================================================== */

.cp-display, .cp-h1, .cp-h2, .cp-h3, .cp-h4 {
  margin: 0;
  font-family: var(--cp-font-sans);
  font-weight: var(--cp-weight-semibold);
  line-height: var(--cp-leading-tight);
  letter-spacing: var(--cp-tracking-tight);
  color: var(--cp-color-text);
  text-wrap: balance;
}
.cp-display { font-size: var(--cp-text-display); letter-spacing: var(--cp-tracking-tighter); font-weight: var(--cp-weight-bold); }
.cp-h1 { font-size: var(--cp-text-4xl); letter-spacing: var(--cp-tracking-tighter); }
.cp-h2 { font-size: var(--cp-text-3xl); }
.cp-h3 { font-size: var(--cp-text-2xl); }
.cp-h4 { font-size: var(--cp-text-xl); font-weight: var(--cp-weight-medium); }

.cp-lead {
  margin: 0;
  font-size: var(--cp-text-lg);
  line-height: var(--cp-leading-relaxed);
  color: var(--cp-color-text-muted);
  max-width: var(--cp-measure);
  text-wrap: pretty;
}
.cp-body { margin: 0; max-width: var(--cp-measure); text-wrap: pretty; }
.cp-muted { color: var(--cp-color-text-muted); }
.cp-subtle { color: var(--cp-color-text-subtle); }
.cp-small { font-size: var(--cp-text-sm); }

/* Micro-label. Uppercase mono, letterspaced. Used for column heads, engine
   names, section eyebrows — anything that is metadata rather than content. */
.cp-label {
  display: inline-block;
  font-family: var(--cp-font-mono);
  font-size: var(--cp-text-2xs);
  font-weight: var(--cp-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-text-subtle);
  line-height: var(--cp-leading-snug);
}

/* If it is data, it is monospaced. */
.cp-mono { font-family: var(--cp-font-mono); font-variant-numeric: tabular-nums; }
.cp-num { font-family: var(--cp-font-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }

/* Evidence quotation — a passage an engine actually returned, or a cited
   source. Serif marks it as "not our words". Never used for UI copy. */
.cp-quote {
  margin: 0;
  padding-left: var(--cp-space-5);
  border-left: 2px solid var(--cp-color-border-strong);
  font-family: var(--cp-font-quote);
  font-size: var(--cp-text-xl);
  line-height: var(--cp-leading-snug);
  color: var(--cp-color-text);
  max-width: var(--cp-measure);
}
.cp-quote cite {
  display: block;
  margin-top: var(--cp-space-3);
  font-family: var(--cp-font-mono);
  font-size: var(--cp-text-xs);
  font-style: normal;
  letter-spacing: var(--cp-tracking-wide);
  color: var(--cp-color-text-subtle);
}

/* ==========================================================================
   4. BUTTONS
   ========================================================================== */

.cp-btn {
  --_bg: var(--cp-color-surface);
  --_fg: var(--cp-color-text);
  --_bd: var(--cp-color-border-strong);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--cp-space-2);
  min-height: var(--cp-control-height);
  padding: 0 var(--cp-space-5);
  border: var(--cp-hairline) solid var(--_bd);
  border-radius: var(--cp-radius-md);
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--cp-font-sans);
  font-size: var(--cp-text-base);
  font-weight: var(--cp-weight-medium);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: var(--cp-transition-colors);
  -webkit-tap-highlight-color: transparent;
}
.cp-btn:hover { background: var(--cp-color-bg-subtle); }
.cp-btn:active { transform: translateY(0.5px); }
.cp-btn:disabled, .cp-btn[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.cp-btn--primary {
  --_bg: var(--cp-color-primary);
  --_fg: var(--cp-color-text-on-primary);
  --_bd: var(--cp-color-primary);
  box-shadow: var(--cp-shadow-xs);
}
.cp-btn--primary:hover { --_bg: var(--cp-color-primary-hover); --_bd: var(--cp-color-primary-hover); }
.cp-btn--primary:active { --_bg: var(--cp-color-primary-active); }

.cp-btn--secondary { --_bg: var(--cp-color-surface); --_fg: var(--cp-color-text); --_bd: var(--cp-color-border-strong); }

.cp-btn--ghost { --_bd: transparent; --_bg: transparent; --_fg: var(--cp-color-text-muted); }
.cp-btn--ghost:hover { --_fg: var(--cp-color-text); background: var(--cp-color-bg-subtle); }

.cp-btn--danger { --_bg: var(--cp-color-negative); --_fg: var(--cp-color-negative-on); --_bd: var(--cp-color-negative); }
.cp-btn--danger:hover { --_bg: var(--cp-color-negative-hover); --_bd: var(--cp-color-negative-hover); }

.cp-btn--lg { min-height: var(--cp-control-height-lg); font-size: var(--cp-text-lg); padding: 0 var(--cp-space-7); }
/* --sm is for dense desktop toolbars. It grows back to 44px on any touch device
   AND on any narrow viewport: a small button is a design choice, a small tap
   target is a bug. Both conditions are needed — a phone-width window on a
   laptop still reports a fine pointer, and a narrow viewport is where the
   fat-finger cases actually live. */
.cp-btn--sm { min-height: var(--cp-control-height-sm); font-size: var(--cp-text-sm); padding: 0 var(--cp-space-3); }
@media (pointer: coarse), (max-width: 639px) { .cp-btn--sm { min-height: var(--cp-tap-min); } }

.cp-btn--block { display: flex; width: 100%; }
.cp-btn--icon { padding: 0; width: var(--cp-control-height); }
@media (pointer: coarse), (max-width: 639px) { .cp-btn--icon { width: var(--cp-tap-min); } }

/* Async guard: the label stays (no layout jump), a spinner leads it. */
.cp-btn.is-loading { pointer-events: none; opacity: 0.75; }
.cp-btn.is-loading::before {
  content: '';
  width: 1em; height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--cp-radius-full);
  animation: cp-spin 620ms linear infinite;
}
@keyframes cp-spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   5. CARDS
   ========================================================================== */

.cp-card {
  background: var(--cp-color-surface);
  border: var(--cp-hairline) solid var(--cp-color-border);
  border-radius: var(--cp-radius-lg);
  box-shadow: var(--cp-shadow-xs);
  overflow: hidden;
}
.cp-card__header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cp-space-3);
  padding: var(--cp-space-4) var(--cp-space-5);
  border-bottom: var(--cp-hairline) solid var(--cp-color-border-subtle);
}
.cp-card__title { margin: 0; font-size: var(--cp-text-lg); font-weight: var(--cp-weight-semibold); letter-spacing: var(--cp-tracking-tight); }
.cp-card__body { padding: var(--cp-space-5); }
.cp-card__footer {
  padding: var(--cp-space-4) var(--cp-space-5);
  border-top: var(--cp-hairline) solid var(--cp-color-border-subtle);
  background: var(--cp-color-surface-sunken);
}
.cp-card--flush .cp-card__body { padding: 0; }

/* Whole-card link. Hover changes colour and border only — never scale, which
   shifts the layout under the pointer. */
.cp-card--interactive { cursor: pointer; transition: var(--cp-transition-colors); }
.cp-card--interactive:hover { border-color: var(--cp-color-border-strong); box-shadow: var(--cp-shadow-md); }
.cp-card--interactive:focus-within { border-color: var(--cp-color-primary); box-shadow: var(--cp-shadow-focus); }

/* ==========================================================================
   6. BADGES
   ========================================================================== */

.cp-badge {
  --_bg: var(--cp-color-bg-subtle);
  --_fg: var(--cp-color-text-muted);
  --_bd: var(--cp-color-border);
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-2);
  padding: 3px var(--cp-space-2);
  border: var(--cp-hairline) solid var(--_bd);
  border-radius: var(--cp-radius-sm);
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--cp-font-mono);
  font-size: var(--cp-text-2xs);
  font-weight: var(--cp-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  line-height: 1.5;
  white-space: nowrap;
}
.cp-badge--proof    { --_bg: var(--cp-color-proof-bg);    --_fg: var(--cp-color-proof-text);    --_bd: var(--cp-color-proof-border); }
.cp-badge--warn     { --_bg: var(--cp-color-warn-bg);     --_fg: var(--cp-color-warn-text);     --_bd: var(--cp-color-warn-border); }
.cp-badge--negative { --_bg: var(--cp-color-negative-bg); --_fg: var(--cp-color-negative-text); --_bd: var(--cp-color-negative-border); }
.cp-badge--primary  { --_bg: var(--cp-color-primary-bg);  --_fg: var(--cp-color-primary-text);  --_bd: var(--cp-color-primary-border); }
.cp-badge--nodata   { --_fg: var(--cp-color-nodata-text); --_bd: var(--cp-color-nodata-border); background: var(--cp-pattern-nodata-soft); }

/* Evidence grade. Every recommendation carries one; it is what makes the
   agency's deliverable defensible to their client. */
.cp-badge--grade {
  width: 22px; padding: 0;
  justify-content: center;
  font-weight: var(--cp-weight-semibold);
  letter-spacing: 0;
}

/* Status dot for engine liveness. Colour is never the only signal — it always
   sits next to a text label. */
.cp-dot {
  width: 8px; height: 8px;
  border-radius: var(--cp-radius-full);
  background: var(--cp-color-nodata);
  flex: none;
}
.cp-dot--ok { background: var(--cp-color-proof); }
.cp-dot--degraded { background: var(--cp-color-warn); }
.cp-dot--down { background: var(--cp-color-negative); }

/* ==========================================================================
   7. THE VERDICT PILL
   The three states a re-measurement is allowed to return. There is no fourth,
   and "NOT ENOUGH DATA" is never styled as a failure: it is a neutral,
   hatched, un-taken reading. Refusing to colour it red is the product.
   ========================================================================== */

.cp-verdict {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-2);
  min-height: 32px;
  padding: 0 var(--cp-space-3) 0 var(--cp-space-2);
  border: var(--cp-hairline) solid var(--cp-color-border);
  border-radius: var(--cp-radius-full);
  background: var(--cp-color-bg-subtle);
  color: var(--cp-color-text-muted);
  font-family: var(--cp-font-mono);
  font-size: var(--cp-text-xs);
  font-weight: var(--cp-weight-semibold);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
}
.cp-verdict__glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px; height: 20px;
  border-radius: var(--cp-radius-full);
  /* The disc takes the pill's own colour; the glyph inside is knocked out to
     the surface colour. Do NOT set `color` on this element — `currentColor` in
     `background` resolves against this element's own colour, so overriding it
     here would paint the disc and the glyph the same value and render the
     glyph invisible. Caught by looking at screenshots/preview-1440.png. */
  background: currentColor;
  flex: none;
}
.cp-verdict__glyph svg { width: 12px; height: 12px; stroke: var(--cp-color-surface); }

.cp-verdict--real {
  background: var(--cp-color-proof-bg);
  border-color: var(--cp-color-proof-border);
  color: var(--cp-color-proof-text);
}
.cp-verdict--none {
  background: var(--cp-color-bg-subtle);
  border-color: var(--cp-color-border-strong);
  color: var(--cp-color-text-muted);
}
.cp-verdict--insufficient {
  background: var(--cp-pattern-nodata-soft);
  border-color: var(--cp-color-nodata-border);
  color: var(--cp-color-nodata-text);
  border-style: dashed;
}
/* The hatch behind the pill would show through a `currentColor` disc, so this
   variant pins an opaque one. */
.cp-verdict--insufficient .cp-verdict__glyph { background: var(--cp-color-nodata); }
.cp-verdict--insufficient .cp-verdict__glyph svg { stroke: var(--cp-color-nodata-on); }

/* ==========================================================================
   8. THE SCORE READOUT
   A number appears only when the data supports one. The --nodata variant draws
   a hatch where the number would be, because a "0" would be a lie about a
   reading we never took.
   ========================================================================== */

.cp-score { display: flex; align-items: flex-end; gap: var(--cp-space-3); }
.cp-score__value {
  font-family: var(--cp-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--cp-text-score);
  font-weight: var(--cp-weight-semibold);
  line-height: var(--cp-leading-none);
  letter-spacing: var(--cp-tracking-tighter);
  color: var(--cp-color-text);
}
.cp-score__unit {
  font-family: var(--cp-font-mono);
  font-size: var(--cp-text-lg);
  color: var(--cp-color-text-subtle);
  padding-bottom: 0.35em;
}
.cp-score__meta { display: flex; flex-direction: column; gap: 2px; padding-bottom: 0.4em; }
.cp-score--sm .cp-score__value { font-size: var(--cp-text-score-sm); }

/* No reading taken. Deliberately quieter than a real score: it steps down to
   the small size and prints the instrument's own "--" in visible ink on the
   hatch. A blank block at full score size shouts louder than the numbers it
   sits beside, which inverts the hierarchy — the readings should dominate. */
.cp-score--nodata .cp-score__value {
  display: inline-block;
  min-width: 2.6ch;
  padding: 0 var(--cp-space-2);
  border: var(--cp-hairline) dashed var(--cp-color-nodata-border);
  border-radius: var(--cp-radius-sm);
  background: var(--cp-pattern-nodata);
  color: var(--cp-color-nodata-text);
  font-size: var(--cp-text-score-sm);
  text-align: center;
  user-select: none;
}
.cp-score--nodata .cp-score__unit { color: var(--cp-color-nodata-text); }

.cp-delta { font-family: var(--cp-font-mono); font-variant-numeric: tabular-nums; font-size: var(--cp-text-sm); font-weight: var(--cp-weight-medium); }
.cp-delta--up { color: var(--cp-color-proof-text); }
.cp-delta--down { color: var(--cp-color-negative-text); }
.cp-delta--flat { color: var(--cp-color-text-subtle); }

/* Horizontal meter. Same rule: no data means a hatch, not a zero-width bar. */
.cp-meter {
  height: 8px;
  border-radius: var(--cp-radius-full);
  background: var(--cp-color-surface-sunken);
  border: var(--cp-hairline) solid var(--cp-color-border-subtle);
  overflow: hidden;
}
.cp-meter__fill { height: 100%; background: var(--cp-color-primary); border-radius: inherit; }
.cp-meter__fill--proof { background: var(--cp-color-proof); }
.cp-meter--nodata { background: var(--cp-pattern-nodata-dense); border-style: dashed; border-color: var(--cp-color-nodata-border); }

/* ==========================================================================
   9. TABLES
   Desktop: a dense, aligned instrument read-out.
   Phone (<640px): each row becomes a stacked record using the data-label on
   every cell. No horizontal scroll, no 9px text, no pinch-zoom.
   ========================================================================== */

.cp-table-wrap { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }

.cp-table { width: 100%; border-collapse: collapse; font-size: var(--cp-text-sm); }
.cp-table thead th {
  position: sticky; top: 0; z-index: var(--cp-z-raised);
  background: var(--cp-color-surface);
  padding: var(--cp-space-3) var(--cp-space-4);
  text-align: left;
  border-bottom: var(--cp-hairline) solid var(--cp-color-border);
  font-family: var(--cp-font-mono);
  font-size: var(--cp-text-2xs);
  font-weight: var(--cp-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-text-subtle);
  white-space: nowrap;
}
.cp-table tbody td {
  padding: var(--cp-space-3) var(--cp-space-4);
  border-bottom: var(--cp-hairline) solid var(--cp-color-border-subtle);
  vertical-align: middle;
  color: var(--cp-color-text);
}
.cp-table tbody tr:last-child td { border-bottom: 0; }
.cp-table tbody tr:hover td { background: var(--cp-color-bg-subtle); }
.cp-table .cp-td-num { text-align: right; font-family: var(--cp-font-mono); font-variant-numeric: tabular-nums; }
.cp-table caption { caption-side: top; text-align: left; padding-bottom: var(--cp-space-3); color: var(--cp-color-text-muted); font-size: var(--cp-text-sm); }

@media (max-width: 639px) {
  .cp-table--stack thead { display: none; }
  .cp-table--stack tbody tr {
    display: block;
    padding: var(--cp-space-3) 0;
    border-bottom: var(--cp-hairline) solid var(--cp-color-border);
  }
  .cp-table--stack tbody tr:last-child { border-bottom: 0; }
  .cp-table--stack tbody td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cp-space-4);
    padding: var(--cp-space-2) var(--cp-space-4);
    border: 0;
    font-size: var(--cp-text-base);
  }
  .cp-table--stack tbody td::before {
    content: attr(data-label);
    flex: none;
    font-family: var(--cp-font-mono);
    font-size: var(--cp-text-2xs);
    letter-spacing: var(--cp-tracking-caps);
    text-transform: uppercase;
    color: var(--cp-color-text-subtle);
  }
  .cp-table--stack .cp-td-num { text-align: right; }
  .cp-table--stack tbody tr:hover td { background: transparent; }
}

/* ==========================================================================
   10. FORM CONTROLS
   ========================================================================== */

.cp-field { display: block; }
.cp-field + .cp-field { margin-top: var(--cp-space-5); }
.cp-field__label {
  display: block;
  margin-bottom: var(--cp-space-2);
  font-size: var(--cp-text-sm);
  font-weight: var(--cp-weight-medium);
  color: var(--cp-color-text);
}
.cp-field__hint { margin-top: var(--cp-space-2); font-size: var(--cp-text-sm); color: var(--cp-color-text-muted); }
.cp-field__error {
  display: flex; align-items: center; gap: var(--cp-space-2);
  margin-top: var(--cp-space-2);
  font-size: var(--cp-text-sm);
  color: var(--cp-color-negative-text);
}

.cp-input, .cp-select, .cp-textarea {
  display: block;
  width: 100%;
  min-height: var(--cp-control-height);
  padding: var(--cp-space-2) var(--cp-space-3);
  border: var(--cp-hairline) solid var(--cp-color-border-strong);
  border-radius: var(--cp-radius-md);
  background: var(--cp-color-surface);
  color: var(--cp-color-text);
  font-family: var(--cp-font-sans);
  /* 16px minimum: anything smaller makes iOS Safari zoom the page on focus. */
  font-size: var(--cp-text-base);
  line-height: 1.4;
  transition: var(--cp-transition-colors);
}
.cp-input::placeholder, .cp-textarea::placeholder { color: var(--cp-color-text-subtle); }
.cp-input:hover, .cp-select:hover, .cp-textarea:hover { border-color: var(--cp-color-text-subtle); }
.cp-input:focus, .cp-select:focus, .cp-textarea:focus {
  outline: none;
  border-color: var(--cp-color-primary);
  box-shadow: var(--cp-shadow-focus);
}
.cp-input:disabled, .cp-select:disabled, .cp-textarea:disabled {
  background: var(--cp-color-surface-sunken);
  color: var(--cp-color-text-subtle);
  cursor: not-allowed;
}
.cp-input[aria-invalid='true'], .cp-textarea[aria-invalid='true'] { border-color: var(--cp-color-negative); }
.cp-input--mono { font-family: var(--cp-font-mono); }
.cp-textarea { min-height: 7rem; resize: vertical; line-height: var(--cp-leading-normal); }

.cp-select {
  appearance: none;
  padding-right: var(--cp-space-10);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%236f7a8d' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--cp-space-3) center;
  background-size: 16px 16px;
}

/* Prefixed input, e.g. https:// + domain. Stacks nothing on mobile; the prefix
   stays inline because it is 6 characters. */
.cp-input-group { display: flex; align-items: stretch; }
.cp-input-group__prefix {
  display: inline-flex; align-items: center;
  padding: 0 var(--cp-space-3);
  border: var(--cp-hairline) solid var(--cp-color-border-strong);
  border-right: 0;
  border-radius: var(--cp-radius-md) 0 0 var(--cp-radius-md);
  background: var(--cp-color-surface-sunken);
  color: var(--cp-color-text-subtle);
  font-family: var(--cp-font-mono);
  font-size: var(--cp-text-sm);
  white-space: nowrap;
}
.cp-input-group .cp-input { border-radius: 0 var(--cp-radius-md) var(--cp-radius-md) 0; }

/* Checkbox / radio: the whole label is the target, and it is 44px tall. */
.cp-check {
  display: flex;
  align-items: flex-start;
  gap: var(--cp-space-3);
  min-height: var(--cp-tap-min);
  padding-block: var(--cp-space-2);
  cursor: pointer;
}
.cp-check input {
  flex: none;
  width: 20px; height: 20px;
  margin: 2px 0 0;
  accent-color: var(--cp-color-primary);
  cursor: pointer;
}
.cp-check__text { font-size: var(--cp-text-base); line-height: var(--cp-leading-snug); }
.cp-check__note { display: block; margin-top: 2px; font-size: var(--cp-text-sm); color: var(--cp-color-text-muted); }

/* ==========================================================================
   11. NOTES / CALLOUTS
   ========================================================================== */

.cp-note {
  --_bg: var(--cp-color-bg-subtle);
  --_bd: var(--cp-color-border);
  --_fg: var(--cp-color-text);
  display: flex;
  align-items: flex-start; /* a leading icon sits with the title, not centred
                              against a paragraph that may be five lines long */
  gap: var(--cp-space-3);
  padding: var(--cp-space-4);
  border: var(--cp-hairline) solid var(--_bd);
  border-left-width: 3px;
  border-radius: var(--cp-radius-md);
  background: var(--_bg);
  color: var(--_fg);
  font-size: var(--cp-text-sm);
  line-height: var(--cp-leading-normal);
}
.cp-note__title { display: block; font-weight: var(--cp-weight-semibold); margin-bottom: var(--cp-space-1); }
.cp-note--proof    { --_bg: var(--cp-color-proof-bg);    --_bd: var(--cp-color-proof-border); }
.cp-note--warn     { --_bg: var(--cp-color-warn-bg);     --_bd: var(--cp-color-warn-border); }
.cp-note--negative { --_bg: var(--cp-color-negative-bg); --_bd: var(--cp-color-negative-border); }
.cp-note--primary  { --_bg: var(--cp-color-primary-bg);  --_bd: var(--cp-color-primary-border); }
.cp-note--nodata   { --_bd: var(--cp-color-nodata-border); background: var(--cp-pattern-nodata-soft); border-left-style: dashed; }

/* ==========================================================================
   12. EMPTY STATES
   An empty state always says what happened, why, and what to do next. It never
   says "No data" and stops.
   ========================================================================== */

.cp-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--cp-space-3);
  padding: var(--cp-space-10) var(--cp-space-5);
  border: var(--cp-hairline) dashed var(--cp-color-border-strong);
  border-radius: var(--cp-radius-lg);
  background: var(--cp-color-surface);
}
.cp-empty__icon {
  display: grid; place-items: center;
  width: 44px; height: 44px;
  border-radius: var(--cp-radius-md);
  background: var(--cp-color-surface-sunken);
  color: var(--cp-color-text-subtle);
}
.cp-empty__title { margin: 0; font-size: var(--cp-text-lg); font-weight: var(--cp-weight-semibold); }
.cp-empty__body { margin: 0; max-width: var(--cp-measure-narrow); color: var(--cp-color-text-muted); font-size: var(--cp-text-sm); }
.cp-empty--nodata { background: var(--cp-pattern-nodata-soft); border-color: var(--cp-color-nodata-border); }
.cp-empty--nodata .cp-empty__icon { background: var(--cp-color-surface); }

/* Loading placeholder. Reserves the space the content will occupy so nothing
   jumps when it arrives. */
.cp-skeleton {
  border-radius: var(--cp-radius-sm);
  background: var(--cp-color-surface-sunken);
  animation: cp-pulse 1.6s var(--cp-ease-in-out) infinite;
}
@keyframes cp-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }

/* ==========================================================================
   13. SMALL UTILITIES (kept deliberately few)
   ========================================================================== */

.cp-hatch { background: var(--cp-pattern-nodata); }
.cp-grid-bg { background-image: var(--cp-pattern-grid); background-size: var(--cp-pattern-grid-size); }
.cp-flow-tight > * + * { margin-top: var(--cp-space-2); }
.cp-nowrap { white-space: nowrap; }
.cp-right { text-align: right; }
.cp-full { width: 100%; }
