/* ============================================================
   SAACHO CLEARWAY / OPENWAY - app stylesheet
   ------------------------------------------------------------
   ClearWay and OpenWay are child apps of saacho.com, so this file
   is a thin layer, not a theme. /brand/css/saacho.css loads
   first and supplies every token (--brand, --gold, --ink,
   --radius-*, --control-h*, --fs-*, --shadow-*, --focus-ring)
   plus the typography, the buttons (.btn-brand / .btn-outline /
   .btn-ghost / .btn-gold), the cards (.card-brand), the table
   (.saacho-table), the badges and the masthead + footer.

   Only what saacho.css does not already provide is defined here,
   and it is defined in terms of the same custom properties, so
   both apps follow the brand if the brand ever changes.

   THE TWO APPS SHARE THIS FILE BY DESIGN. ClearWay and OpenWay
   are the same codebase under two route segments; their app trees
   differ in exactly three files (README.md, config.php,
   t_formio.php) and this stylesheet is byte-identical between
   public/assets/clearway/ and public/assets/openway/. Edit one,
   copy it to the other, and keep the md5s matching.

   Naming: everything app-specific is prefixed cw- to keep it out
   of the way of the shared stylesheet. No class was renamed or
   removed in the Ant pass - the forms are built at runtime from
   Form.io / SurveyJS definitions, so the class surface is
   data-driven and fragile. Everything here is a restyle in place.

   ANT PASS (2026-10-02)
   ------------------------------------------------------------
   Moved the app layer from ad-hoc rem values and four hardcoded
   hexes onto the shared tokens, and closed five gaps:

     - --radius-lg (8px) for card-sized surfaces; controls at
       --radius-md (6px). No pill radii on any control.
     - --control-h / --control-h-lg on every control, --fs-base
       on control text, --lh-body on control line-height.
     - --focus-ring on form fields, plus :focus-visible on the
       app's own interactive elements. This closes a real defect:
       Choices.js (Form.io's select/radio/checkbox engine) sets
       `outline: 0` on .choices__button and .choices__input, so
       before this pass a keyboard user tabbing through a select
       got NO focus indicator at all. These are keyboard-heavy
       research forms, so that mattered more than anything else
       in this file.
     - @media (prefers-reduced-motion: reduce) - the app layer
       now animates cards and buttons, so it opts out explicitly.
       saacho.css also carries a global reduce block; this one is
       scoped to the app's own motion.

   VENDOR CSS AND CASCADE ORDER
   ------------------------------------------------------------
   Two vendor stylesheets reach these pages:

     - vendor/survey-core.min.css  - linked in <head> BEFORE this
       file, so a tie is won here.
     - vendor/formio.form.min.css  - linked from cw_foot(), which
       emits it in the BODY, therefore AFTER this file. A tie is
       LOST to Form.io.

   So every Form.io override below is scoped to .cw-survey-wrap
   and carries enough specificity to out-specify the vendor rule it
   replaces - no !important is used anywhere. Where a vendor rule
   has two classes, a third is added to the scoped selector; each
   such case is noted inline. SurveyJS needs no such fight: it
   declares its design tokens on `:where(.sd-theme-root)`, which
   has ZERO specificity, so a single class in this file overrides
   the entire SurveyJS palette cleanly.

   COLOUR DISCIPLINE
   ------------------------------------------------------------
   --brand and --gold are FILL tokens and are never used as a text
   colour; --brand-text / --gold-text carry brand text. Semantic
   state comes from --success / --warning / --danger / --info with
   their -soft and -border partners, which is what makes the pills,
   flashes and status text follow the dark-mode ramp. No colour
   anywhere in this file is the only carrier of meaning: the pills
   are labelled, the flashes carry a sentence, the studio status
   prints its own text, and required fields keep Form.io's "*"
   character cue.

   DARK MODE
   ------------------------------------------------------------
   This file adds no theme-dark block. saacho.css carries a
   complete dark ramp under `html.theme-dark, .saacho-dark`
   (view.php sets theme_toggle => true, so dark mode is live on
   every route here) and redeclares its own aliases inside it, so
   --hairline and friends follow --border correctly. Everything
   below reads tokens directly at the use site rather than through
   a local alias, which is what avoids the trap where an alias
   declared at :root freezes its light value.
   ============================================================ */

/* ── In-app navigation ─────────────────────────────────────
   Sits under the shared masthead. Not part of saacho.css because
   only multi-page child apps need it. Ant Menu at control height:
   the current page is marked by --brand-text on a --brand-light
   fill plus a 600 weight, so it survives a greyscale print. */
.cw-subnav {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0 0 24px;
  padding: 0 0 8px;
  border-bottom: var(--hairline);
}
.cw-subnav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h);
  padding: 0 12px;
  border-radius: var(--radius-md);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  font-weight: 500;
  color: var(--ink-dim);
  text-decoration: none;
  transition: background-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.cw-subnav a:hover {
  color: var(--brand-text);
  background: var(--brand-light);
  text-decoration: none;
}
.cw-subnav a:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}
.cw-subnav a[aria-current="page"] {
  color: var(--brand-text);
  background: var(--brand-light);
  font-weight: 600;
}

/* ── Prose helpers ─────────────────────────────────────────
   --fs-md keeps these at the 16px prose size; 14px is reserved
   for control text. */
.cw-lede {
  color: var(--ink-dim);
  font-size: var(--fs-md);
  line-height: var(--lh-body);
  max-width: 62ch;
  margin-block: 0 16px;
}
.cw-note {
  background: var(--surface-2);
  border: var(--hairline);
  border-left: 2px solid var(--brand);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink-dim);
  margin: 0 0 16px;
}
.cw-note b {
  color: var(--ink);
}
.cw-warn {
  background: var(--warning-soft);
  border: 1px solid var(--warning-border);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink-dim);
  margin: 0 0 16px;
}
/* The warning's meaning is carried by this label, not by the wash
   alone, and --warning is a text colour here measured against its
   own --warning-soft background (4.91:1 in light, and the dark
   ramp moves both together). */
.cw-warn strong,
.cw-warn b {
  color: var(--warning);
}

/* ── Flash messages ────────────────────────────────────────
   The message text IS the label; the tinted left rule only
   reinforces it, so nothing here depends on hue alone. */
.cw-flash {
  border: var(--hairline);
  border-left-width: 3px;
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin: 0 0 16px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
}
.cw-flash-ok    { background: var(--success-soft); border-color: var(--success-border); border-left-color: var(--success); color: var(--success); }
.cw-flash-error { background: var(--danger-soft);  border-color: var(--danger-border);  border-left-color: var(--danger);  color: var(--danger);  }
.cw-flash-info  { background: var(--info-soft);    border-color: var(--info-border);    border-left-color: var(--info);    color: var(--info);    }

/* ── Buttons: small + danger variants ──────────────────────
   saacho.css has .btn-brand / .btn-outline / .btn-ghost /
   .btn-gold but no size or destructive variant. */
.cw-btn-sm {
  min-height: var(--control-h-sm);
  padding: 0 8px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
}

/* Destructive variant. Deliberately a DESTRUCTIVE-SECONDARY fill
   (--danger text on --danger-soft, --danger-border hairline)
   rather than the solid --danger fill a filled button would use.
   A solid fill needs white text, and white on the dark ramp's
   --danger (#e05555) measures 3.84:1, which fails AA. The soft
   pairing measures 5.6:1 in light and 4.52:1 in dark, so one
   declaration is correct in both modes with no theme-dark block.
   The button label names the action outright, so the cue is not
   colour-alone. */
.cw-btn-danger {
  background: var(--danger-soft);
  color: var(--danger);
  border: 1px solid var(--danger-border);
  border-radius: var(--radius-md);
  min-height: var(--control-h-lg);
  padding: 0 16px;
  font-weight: 500;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  cursor: pointer;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.cw-btn-danger:hover {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger);
  text-decoration: none;
}
.cw-btn-danger:focus-visible {
  outline: 2px solid var(--danger);
  outline-offset: 2px;
}

/* Action rows. The primary button gets --control-h-lg because a
   consent or submission decision is a deliberate click. The
   secondary gets the SAME height: on the consent screens the
   decline path is a real, equally weighted way out of the flow,
   and it must not read as a lesser control. */
.cw-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}
.cw-actions.cw-actions-center { justify-content: center; }
.cw-actions .btn-brand,
.cw-actions .btn-outline,
.cw-actions .btn-gold {
  min-height: var(--control-h-lg);
}

/* ── Layout helpers ──────────────────────────────────────── */
.cw-grid { display: grid; gap: 16px; }
@media (width >= 700px) {
  .cw-grid-2 { grid-template-columns: 1fr 1fr; }
}
/* display:none has to beat both vendor engines, which set display
   on the elements this hides (the locked questionnaire and the
   signed-out preview). Kept from the original file. */
.cw-hide { display: none !important; }

/* ── Forms ─────────────────────────────────────────────────
   saacho.css already styles input/textarea/select/label
   globally; these are layout wrappers only. */
.cw-field { margin: 0 0 16px; }
.cw-field-row { display: flex; gap: 8px; align-items: flex-end; }
.cw-field-row > * { flex: 1; }
.cw-label {
  display: block;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 8px;
}
.cw-check {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink);
  cursor: pointer;
  margin: 0 0 16px;
}
.cw-check input {
  margin-top: 2px;
  width: 16px;
  height: 16px;
  flex: none;
  accent-color: var(--brand);
}
.cw-check input:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}
/* Consent text is participant-facing and must stay at prose size
   and full measure - it is the thing people are agreeing to. */
.cw-consent-box {
  background: var(--surface-2);
  border: var(--hairline);
  border-radius: var(--radius-md);
  padding: 16px;
  font-size: var(--fs-md);
  line-height: var(--lh-body);
  color: var(--ink-dim);
  margin: 0 0 16px;
  max-height: 22rem;
  overflow-y: auto;
}
/* participate.php inlines `background:#f6f7f9` on one consent box
   to lift it off the canvas. That is a hardcoded light grey, so it
   stays near-white in dark mode and the --ink-dim body text on it
   falls well under AA. This is the one place in the app layer that
   needs !important: an inline style beats any selector, and the fix
   cannot be made in CSS otherwise without editing study-facing
   consent markup. Drop the inline style if you ever revisit that
   line - the rule then becomes redundant. */
.cw-consent-box[style*="background"] {
  background: var(--surface-2) !important;
}
.cw-consent-box h4 { margin: 16px 0 8px; font-size: var(--fs-base); font-weight: 600; color: var(--ink); }
.cw-consent-box h4:first-child { margin-top: 0; }
.cw-consent-box p { margin-block: 0 16px; }
.cw-consent-box ul { margin: 8px 0 16px; padding-left: 20px; }
.cw-consent-box li { margin-bottom: 8px; }

/* ── Tables ────────────────────────────────────────────────
   Two surfaces, both Ant, split by which header the page uses.
   consent.php and study_results.php put <th> inside <tbody> as a
   label/value pair, which is Ant Descriptions, not Ant Table: the
   label is --ink-mute at --fs-base with no uppercase and no fill,
   so a long question title wraps instead of shouting. researcher's
   study list is a real Ant Table and keeps saacho.css's thead. */
.cw-table-wrap { overflow-x: auto; }

.cw-table-wrap .saacho-table th,
.cw-table-wrap .saacho-table td {
  text-align: left;
  vertical-align: top;
  padding: 12px 16px;
}
.cw-table-wrap .saacho-table tbody th {
  width: 38%;
  font-size: var(--fs-base);
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--ink-mute);
  background: none;
  border-bottom: 1px solid var(--line-soft);
}
/* Participant answers are data, not prose: keep them at control
   size and monospaced-free but left-aligned and tabular so columns
   of numbers line up down the page. */
.cw-table-wrap .saacho-table tbody td {
  font-size: var(--fs-base);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.cw-table-wrap .saacho-table tbody tr:hover th,
.cw-table-wrap .saacho-table tbody tr:hover td { background: var(--surface-2); }
.cw-table-wrap .saacho-table tbody tr:last-child th,
.cw-table-wrap .saacho-table tbody tr:last-child td { border-bottom: 0; }

/* study_results.php labels the presented-order block with an
   inline `border-top: 1px solid #e5e7eb` on a colspan cell. Same
   reason as the consent box above: an inline style cannot be
   out-specified any other way, and the hardcoded grey is not on
   the token ramp. */
.cw-table-wrap .saacho-table th[colspan] {
  padding-top: 16px !important;
  border-top: var(--hairline) !important;
}

/* ── Tabs ──────────────────────────────────────────────────
   Ant Tabs: the active tab is a raised card edge on the canvas,
   the rest are quiet text. */
.cw-tabs {
  display: flex;
  gap: 8px;
  margin: 0 0 16px;
  flex-wrap: wrap;
}
.cw-tabs a {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  padding: 0 12px;
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--ink-dim);
  border: 1px solid transparent;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.cw-tabs a:hover { background: var(--brand-light); color: var(--brand-text); text-decoration: none; }
.cw-tabs a:focus-visible { outline: 2px solid var(--brand-text); outline-offset: 2px; }
.cw-tabs a.on {
  background: var(--card-bg);
  border-color: var(--border);
  color: var(--ink);
  font-weight: 600;
  box-shadow: var(--shadow-1);
}

/* ── Statistics ────────────────────────────────────────────
   Ant Statistic: a large --ink value over a quiet --ink-mute
   label. The value is set in the body face at 28px with tabular
   figures so a row of funnel counts reads as a column, and the
   whole tile is a card on the grey canvas rather than a bare box. */
.cw-stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: 8px;
  margin: 0 0 16px;
}
.cw-stat {
  background: var(--card-bg);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  padding: 16px 8px;
  text-align: center;
}
.cw-stat b {
  display: block;
  font-family: inherit;
  font-size: 28px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.cw-stat span {
  display: block;
  margin-top: 4px;
  font-size: var(--fs-xs);
  line-height: var(--lh-body);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-weight: 500;
  color: var(--ink-mute);
}

/* ── Status pills ──────────────────────────────────────────
   saacho.css has .badge-brand / .badge-gold; the neutral and
   negative states are app-specific. Every pill carries its state
   as a word (Open / Draft / Closed), so the tint is reinforcement
   rather than the message. */
.cw-pill {
  display: inline-flex;
  align-items: center;
  min-height: 20px;
  font-size: var(--fs-xs);
  line-height: 1.4;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .08em;
  padding: 0 8px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--ink-dim);
  white-space: nowrap;
}
.cw-pill-open   { background: var(--brand-light);  color: var(--brand-text); border-color: var(--brand-border);  }
.cw-pill-draft  { background: var(--warning-soft); color: var(--warning);    border-color: var(--warning-border); }
.cw-pill-closed { background: var(--danger-soft);  color: var(--danger);     border-color: var(--danger-border);  }

/* ── Empty state ─────────────────────────────────────────── */
.cw-empty {
  text-align: center;
  padding: 48px 24px;
  color: var(--ink-dim);
  background: var(--card-bg);
  border: 1px dashed var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  margin-bottom: 24px;
}
.cw-empty h2 { color: var(--ink); }

/* ── Study cards (participant view) ──────────────────────── */
.cw-card-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.cw-card-top h3 { margin: 0; }
.cw-teaser {
  color: var(--ink);
  font-size: var(--fs-md);
  line-height: var(--lh-body);
  margin: 0 0 8px;
}
.cw-card-meta {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink-mute);
  margin-bottom: 16px;
}
.cw-match-count {
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink-dim);
  margin: 0;
}

/* ── Questionnaire host ───────────────────────────────────
   The card every runtime renders into. SurveyJS and Form.io both
   land inside .cw-survey-wrap, so both get the Ant card surface
   and neither can inherit a mismatched background. */
.cw-survey-wrap {
  background: var(--card-bg);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  padding: 24px 16px;
}

/* ── SurveyJS theming ─────────────────────────────────────
   survey-core.min.css declares its whole design system on
   `:where(.sd-theme-root)`, which carries ZERO specificity, so
   one class here re-skins the entire runtime - no class-level
   overrides and no !important. The tokens are set on the theme
   root itself rather than on .cw-survey-wrap: a custom property
   declared on the theme root always beats one merely inherited
   from an ancestor, whatever their specificity. */
.cw-survey-wrap .sd-theme-root,
.cw-survey-wrap .sd-root-modern,
.cw-studio-preview .sd-theme-root,
.cw-studio-preview .sd-root-modern {
  /* Brand as fill; --brand-text is not offered here because
     SurveyJS derives its own on-primary foreground. */
  --sjs2-color-bg-brand-primary: var(--brand);
  --sjs2-color-bg-brand-primary-dim: var(--brand-dark);
  --sjs2-color-bg-brand-primary-disabled: var(--border);
  --sjs2-color-fg-brand-primary: var(--brand-text);
  --sjs2-color-border-brand-primary: var(--brand);

  /* Surfaces follow the card, so the runtime never paints its own
     canvas over ours and dark mode needs nothing extra. */
  --sjs2-color-utility-body: var(--card-bg);
  --sjs2-color-bg-basic-primary: var(--card-bg);
  --sjs2-color-bg-basic-secondary: var(--surface-2);
  --sjs2-color-bg-basic-tertiary: var(--bg);
  --sjs2-color-fg-basic-primary: var(--ink);
  --sjs2-color-fg-basic-secondary: var(--ink-dim);
  --sjs2-color-fg-basic-tertiary: var(--ink-mute);
  --sjs2-color-border-basic-primary: var(--border);
  --sjs2-color-border-basic-secondary: var(--line-soft);

  /* Semantic answers, on the same ramp as the app's own pills. */
  --sjs2-color-bg-positive-primary: var(--success-soft);
  --sjs2-color-fg-positive-primary: var(--success);
  --sjs2-color-border-positive-primary: var(--success-border);
  --sjs2-color-bg-warning-primary: var(--warning-soft);
  --sjs2-color-fg-warning-primary: var(--warning);
  --sjs2-color-border-warning-primary: var(--warning-border);
  --sjs2-color-bg-alert-primary: var(--danger-soft);
  --sjs2-color-fg-alert-primary: var(--danger);
  --sjs2-color-border-alert-primary: var(--danger-border);
  --sjs2-color-bg-note-primary: var(--info-soft);
  --sjs2-color-fg-note-primary: var(--info);
  --sjs2-color-border-note-primary: var(--info-border);

  /* 6px control radius everywhere the runtime draws a box. */
  --sjs2-base-unit-radius: var(--radius-md);
  --sjs2-radius-component-action: var(--radius-md);
  --sjs2-radius-component-formbox: var(--radius-md);
  --sjs2-radius-component-checkbox: var(--radius-sm);
  --sjs2-radius-component-boolean-item: var(--radius-sm);
  --sjs2-radius-component-drop: var(--radius-md);
  --sjs2-radius-component-drop-item: var(--radius-sm);
  --sjs2-radius-component-menu: var(--radius-md);
  --sjs2-radius-component-menu-item: var(--radius-sm);
  --sjs2-radius-component-message: var(--radius-md);
  --sjs2-radius-component-panel-simple: var(--radius-lg);
  --sjs2-radius-component-panel: var(--radius-lg);

  /* Action buttons at the Ant control metrics. */
  --sd-action-border-radius: var(--radius-md);
  --sd-action-title-font-size: var(--fs-base);
  --sd-action-title-line-height: var(--lh-body);
  --sd-action-title-font-weight: 400;
  --sd-action-boxshadow: none;
  --sd-action-hover-box-shadow: none;

  /* Panels are flat cards on the canvas, not floating boxes. */
  --sd-panel-border-radius: var(--radius-lg);
  --sd-panel-box-shadow: none;
  --sd-panel-background: var(--card-bg);
  --sd-question-container-border-radius: var(--radius-lg);
  --sd-question-container-box-shadow: none;
  --sd-question-title-color: var(--ink);
  --sd-question-description-color: var(--ink-mute);
  --sd-table-sticky-background-color: var(--surface-2);
}
/* Keyboard focus inside the runtime keeps a visible ring. The
   runtime does draw its own a11y shadow, so this only has to not
   remove it. */
.cw-survey-wrap .sd-root-modern :focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

/* Runtime status line (set by clearway.js before/after a submit). */
.cw-survey-status {
  display: none;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  border: var(--hairline);
  border-left-width: 3px;
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin: 0 0 16px;
}
.cw-survey-status.show { display: block; }
.cw-survey-status.err  { background: var(--danger-soft);  color: var(--danger);  border-color: var(--danger-border);  border-left-color: var(--danger);  }
.cw-survey-status.ok   { background: var(--success-soft); color: var(--success); border-color: var(--success-border); border-left-color: var(--success); }
.cw-survey-status.info { background: var(--info-soft);    color: var(--info);    border-color: var(--info-border);    border-left-color: var(--info);    }

/* ── Form.io theming ──────────────────────────────────────
   formio.form.min.css is emitted in the BODY by cw_foot(), i.e.
   AFTER this file, so a tied selector LOSES to it. Every rule
   below is therefore scoped under .cw-survey-wrap, which puts one
   class ahead of the vendor's single-class rules; where the
   vendor rule itself carries two classes, a third is added and
   the case is noted. No !important is needed.

   The old #0a7a3d / #b42318 / #00bcd4 in this app were semantic
   and could not survive dark mode. They are now --success,
   --danger and --brand, which do. */
.cw-survey-wrap .formio-form {
  font-size: var(--fs-base);
  color: var(--ink);
}
.cw-survey-wrap .formio-component { margin-bottom: 16px; }
.cw-survey-wrap .formio-component:last-child { margin-bottom: 0; }

/* Native inputs. Vendor keeps .form-control itself almost
   unstyled, so the scoped class is enough. */
.cw-survey-wrap .form-control {
  min-height: var(--control-h);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 4px 11px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  font-family: inherit;
  color: var(--ink);
  background: var(--card-bg);
  width: 100%;
  box-sizing: border-box;
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.cw-survey-wrap textarea.form-control {
  padding: 6px 11px;
  min-height: 72px;
  line-height: var(--lh-body);
}
.cw-survey-wrap .form-control:hover { border-color: var(--brand-text); }
.cw-survey-wrap .form-control:focus {
  outline: none;
  border-color: var(--brand-text);
  box-shadow: var(--focus-ring);
}
/* Vendor paints .form-control.flatpickr-input white in both modes. */
.cw-survey-wrap .form-control.flatpickr-input { background: var(--card-bg); }

/* Labels and help text. */
.cw-survey-wrap .control-label,
.cw-survey-wrap .formio-component .control-label {
  display: block;
  margin-bottom: 8px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  font-weight: 500;
  color: var(--ink);
  text-align: left;
  width: auto;
}
/* The required "*" is Form.io's own text cue, so requiredness is
   never signalled by colour alone; only its colour is mapped. */
.cw-survey-wrap .field-required::after { color: var(--danger); }
.cw-survey-wrap .field,
.cw-survey-wrap .help-block {
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink-mute);
  margin-top: 8px;
}

/* Buttons the form renders itself. */
.cw-survey-wrap .btn {
  min-height: var(--control-h);
  padding: 0 15px;
  border-radius: var(--radius-md);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  font-weight: 400;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.cw-survey-wrap .btn-primary,
.cw-survey-wrap .btn-success {
  background: var(--brand);
  border: 1px solid var(--brand);
  color: #fff;
}
.cw-survey-wrap .btn-primary:hover,
.cw-survey-wrap .btn-success:hover {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
  color: #fff;
}
.cw-survey-wrap .btn-default,
.cw-survey-wrap .btn-secondary {
  background: var(--card-bg);
  border: 1px solid var(--border);
  color: var(--ink);
}
.cw-survey-wrap .btn-default:hover,
.cw-survey-wrap .btn-secondary:hover {
  border-color: var(--brand-text);
  color: var(--brand-text);
}
.cw-survey-wrap .btn:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

/* Checkboxes and radios. */
.cw-survey-wrap .form-check,
.cw-survey-wrap .form-check-label {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink);
  margin: 0 0 8px;
  font-weight: 400;
}
.cw-survey-wrap .form-check-input {
  flex: none;
  margin: 2px 0 0;
  width: 16px;
  height: 16px;
  accent-color: var(--brand);
  border-radius: var(--radius-sm);
}
.cw-survey-wrap .form-check-input:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}

/* ── Choices.js (Form.io's select / radio / multi-select) ─
   This engine carried the hard parts: a 2.5px radius, a 44px
   control, a #f9f9f9 fill and a #00bcd4 teal that appears in
   neither the brand palette nor the dark ramp. */
.cw-survey-wrap .choices {
  font-size: var(--fs-base);
  margin-bottom: 16px;
}
/* Beats vendor .choices__inner (one class). */
.cw-survey-wrap .choices__inner {
  min-height: var(--control-h);
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 4px 11px;
  font-size: var(--fs-base);
  color: var(--ink);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.cw-survey-wrap .choices:hover .choices__inner { border-color: var(--brand-text); }
/* Three classes: beats the vendor's `.is-focused .choices__inner`
   and `.is-open .choices__inner` pair, which is two. */
.cw-survey-wrap .choices.is-focused .choices__inner,
.cw-survey-wrap .choices.is-open .choices__inner {
  border-color: var(--brand-text);
  box-shadow: var(--focus-ring);
}
.cw-survey-wrap .choices.is-open .choices__inner {
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.cw-survey-wrap .choices.is-flipped.is-open .choices__inner {
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}
/* The select's own field. Vendor sets it white with a bottom rule
   on `.choices[data-type*=select-one] .choices__input` — three
   specificity, because the attribute selector counts as a class,
   so three classes here would only tie and lose. The extra
   [data-type] takes it to four. */
.cw-survey-wrap .choices[data-type] .choices__input,
.cw-survey-wrap .choices .choices__input {
  background: transparent;
  border: 0;
  border-radius: 0;
  color: var(--ink);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  padding: 4px 0;
  margin-bottom: 0;
}
.cw-survey-wrap .choices .choices__placeholder { color: var(--ink-mute); opacity: 1; }

/* FOCUS. Vendor sets `outline: 0` on .choices__button and
   .choices__input, so before this pass a keyboard user tabbing
   into a select or a searchable dropdown had no focus indicator
   at all. This is the accessibility fix that mattered most in
   this file. */
.cw-survey-wrap .choices__button:focus,
.cw-survey-wrap .choices__button:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: -2px;
  opacity: 1;
}
/* Beats `.choices[data-type*=select-one] .choices__button:focus`
   (class + attribute + class) - the extra .choices class takes
   it from three to four. */
.cw-survey-wrap .choices[data-type] .choices__button:focus {
  outline: 2px solid var(--brand-text);
  outline-offset: -2px;
  box-shadow: none;
}
.cw-survey-wrap .choices .choices__input:focus,
.cw-survey-wrap .choices .choices__input:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: -2px;
  box-shadow: none;
}

/* The chevron trigger. Vendor gives it border-radius:10em on a
   20x20 hit area (`.choices[data-type*=select-one]
   .choices__button`, three specificity) - a pill on an
   interactive control. */
.cw-survey-wrap .choices[data-type] .choices__button {
  border-radius: var(--radius-sm);
  opacity: .6;
}
.cw-survey-wrap .choices[data-type] .choices__button:hover,
.cw-survey-wrap .choices[data-type] .choices__button:focus {
  opacity: 1;
}

/* The open dropdown list. Vendor backgrounds it #fff with a #ddd
   border on `.choices__list--dropdown, .choices__list[aria-expanded]`
   (two); three classes take it. */
.cw-survey-wrap .choices .choices__list,
.cw-survey-wrap .choices .choices__list[aria-expanded] {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.cw-survey-wrap .choices.is-open .choices__list {
  border-color: var(--border);
  border-top: 0;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}
.cw-survey-wrap .choices.is-flipped.is-open .choices__list {
  border-top: 1px solid var(--border);
  border-bottom: 0;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.cw-survey-wrap .choices .choices__list .choices__item,
.cw-survey-wrap .choices .choices__list[aria-expanded] .choices__item {
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink);
}
.cw-survey-wrap .choices .choices__list .choices__item--selectable:hover,
.cw-survey-wrap .choices .choices__list .choices__item--selectable.is-highlighted {
  background: var(--brand-light);
  color: var(--brand-text);
}
/* Multi-select chips. Vendor draws these as #00bcd4 pills at
   20px radius; squared to --radius-sm and moved onto the brand
   ramp so they read as the same control family. */
.cw-survey-wrap .choices__list .choices__item {
  border-radius: var(--radius-sm);
  background: var(--brand-light);
  border: 1px solid var(--brand-border);
  color: var(--brand-text);
  font-size: var(--fs-xs);
  font-weight: 500;
  padding: 2px 8px;
  margin: 0 4px 4px 0;
}
.cw-survey-wrap .choices__list .choices__item.is-highlighted {
  background: var(--brand-light);
  color: var(--brand-text);
  border-color: var(--brand-text);
}
.cw-survey-wrap .choices .choices__heading {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-mute);
  border-bottom: 1px solid var(--line-soft);
}

/* ── First-party Form.io components (components.js) ──────
   Ranking and Range are the app's own question types, injected by
   components.js inside .cw-survey-wrap. They had NO styling at
   all before this pass, so a ranking question rendered as a bare
   <ol> with unlabelled ▲/▼ glyphs and a rating scale as a row of
   default radios. Both are data-entry controls, so they get the
   same Ant treatment as everything else. Purely additive: no class
   is renamed and no markup changes. */
.cw-survey-wrap .cw-rank-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: var(--hairline);
  border-radius: var(--radius-md);
  background: var(--card-bg);
  overflow: hidden;
}
.cw-survey-wrap .cw-rank-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line-soft);
}
.cw-survey-wrap .cw-rank-item:last-child { border-bottom: 0; }
.cw-survey-wrap .cw-rank-pos {
  flex: none;
  min-width: 24px;
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink-mute);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.cw-survey-wrap .cw-rank-label {
  flex: 1;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink);
}
.cw-survey-wrap .cw-rank-moves {
  display: flex;
  gap: 4px;
  flex: none;
}
/* The reorder buttons carry an aria-label ("Move X up") as well as
   the glyph, so the control is not identified by the triangle
   alone; the glyph is kept as the visible cue at --ink-dim. */
.cw-survey-wrap .cw-rank-move {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-h-sm);
  height: var(--control-h-sm);
  min-height: var(--control-h-sm);
  padding: 0;
  font-size: 10px;
  line-height: 1;
  color: var(--ink-dim);
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.cw-survey-wrap .cw-rank-move:hover:not(:disabled) {
  color: var(--brand-text);
  border-color: var(--brand-text);
  background: var(--brand-light);
}
.cw-survey-wrap .cw-rank-move:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}
.cw-survey-wrap .cw-rank-move:disabled {
  color: var(--ink-mute);
  border-color: var(--line-soft);
  cursor: not-allowed;
  opacity: .55;
}

/* Range: a real <input type=range>, styled to Ant's slider. */
.cw-survey-wrap .cw-range-slider {
  display: block;
  width: 100%;
  min-height: var(--control-h);
  accent-color: var(--brand);
  cursor: pointer;
}
.cw-survey-wrap .cw-range-slider:focus-visible {
  outline: 2px solid var(--brand-text);
  outline-offset: 4px;
}

/* Range: the enumerated form. The radio inputs stay real radios -
   components.js notes this is what buys arrow-key navigation and
   the correct screen-reader announcement - so they are visually
   hidden rather than replaced, and the LABEL carries the Ant
   control. Selection is therefore never colour-alone: it also
   carries a 600 weight and the brand border. */
.cw-survey-wrap .cw-range-scale {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.cw-survey-wrap .cw-range-choice {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-h);
  min-width: var(--control-h);
  padding: 0 12px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink);
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  user-select: none;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.cw-survey-wrap .cw-range-choice:hover {
  border-color: var(--brand-text);
  color: var(--brand-text);
}
.cw-survey-wrap .cw-range-opt {
  /* Kept in the accessibility tree and the tab order; only the box
     is hidden. Not display:none or visibility:hidden, either of
     which would drop the radio and with it arrow-key navigation. */
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}
.cw-survey-wrap .cw-range-choice:has(.cw-range-opt:checked) {
  background: var(--brand-light);
  border-color: var(--brand-border);
  color: var(--brand-text);
  font-weight: 600;
}
.cw-survey-wrap .cw-range-choice:has(.cw-range-opt:focus-visible) {
  outline: 2px solid var(--brand-text);
  outline-offset: 2px;
}
/* Scale end labels ("Not at all" / "Very much"). */
.cw-survey-wrap .cw-range-end {
  font-size: var(--fs-xs);
  line-height: var(--lh-body);
  color: var(--ink-mute);
}
.cw-survey-wrap .cw-range-end-min { margin-right: 16px; }
.cw-survey-wrap .cw-range-end-max { margin-left: 16px; }

/* ── Criteria editor (researcher) ──────────────────────────
   The criteria rows sit on the canvas, not on a card, so they read
   as an inset region of the card that contains them. */
.cw-crit-row {
  display: grid;
  gap: 8px;
  padding: 16px;
  border: var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  margin: 0 0 8px;
}
@media (width >= 640px) {
  .cw-crit-row {
    grid-template-columns: 1.4fr .8fr 1.4fr auto;
    align-items: center;
  }
  .cw-crit-row > button { flex: none; }
}
.cw-crit-preview {
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink-dim);
  background: var(--brand-light);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin: 0 0 16px;
}
.cw-crit-preview b { color: var(--brand-text); }
.cw-crit-editor { margin-top: 16px; }

/* ── Typography helper ─────────────────────────────────────
   Nepali/Devanagari script fallback, for the district and
   institution names that are sometimes written in Nepali. */
.np {
  font-family: "Noto Sans Devanagari", "Nirmala UI", "Mangal", var(--pico-font-family);
}

/* ── Question builder (builder.js) ───────────────────────── */
.cw-q-card {
  border: var(--hairline);
  border-radius: var(--radius-lg);
  padding: 16px;
  margin: 0 0 8px;
  background: var(--surface-2);
}
.cw-q-card .cw-field:last-child { margin-bottom: 0; }

/* ── Questionnaire Studio (studio.php + formio.js builder) ──
   The builder is a design canvas full of third-party chrome
   (component palette, drag handles, a nested iframe preview), so
   this only supplies the containing surfaces and leaves the
   vendor's internal layout alone. */
.cw-studio-bar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.cw-studio-title { margin: 0 0 0 4px; font-size: 24px; }
.cw-studio-back {
  font-size: var(--fs-base);
  color: var(--ink-dim);
  text-decoration: none;
}
.cw-studio-back:hover { color: var(--brand-text); text-decoration: underline; }
.cw-studio-back:focus-visible { outline: 2px solid var(--brand-text); outline-offset: 2px; }
.cw-studio-actions { display: flex; align-items: center; gap: 8px; }
.cw-studio-status {
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink-dim);
  min-height: 1.5em;
}
/* The status line prints its own sentence ("Saved", "Error: ..."),
   so the colour only reinforces it. These replace the old
   hardcoded #0a7a3d / #b42318, which could not follow dark mode. */
.cw-studio-status.is-ok  { color: var(--success); }
.cw-studio-status.is-err { color: var(--danger); }
.cw-studio-note { margin: 4px 0 16px; }
.cw-studio-legacy {
  background: var(--warning-soft);
  border: 1px solid var(--warning-border);
  border-left: 3px solid var(--warning);
  color: var(--ink-dim);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin: 0 0 16px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
}
.cw-studio-legacy b,
.cw-studio-legacy strong { color: var(--warning); }
.cw-studio-preview {
  margin: 24px 0 0;
  padding: 16px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-lg);
  background: var(--card-bg);
}
.cw-studio-preview[hidden] { display: none; }
/* Preview failure text. builder.js and editor.js each build this
   <p class="cw-error"> when the form engine or the preview cannot be
   loaded, and it lands in whichever preview host they were given -
   .cw-studio-preview here, and .cw-survey-wrap for the screening
   preview on study_edit.php. So it is deliberately NOT scoped to a
   wrapper: both hosts need it. Styled like .cw-flash-error on the
   same --danger / -soft / -border triple, so a load failure reads as
   a failure rather than as ordinary body copy. The message is always
   a full sentence the script supplies, so the meaning never rests on
   the colour alone. */
.cw-error {
  background: var(--danger-soft);
  border: 1px solid var(--danger-border);
  border-left: 3px solid var(--danger);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin: 0 0 16px;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--danger);
}
.cw-studio-builder {
  min-height: 70vh;
  border: var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  overflow: hidden;
}

/* ── Reduced motion ───────────────────────────────────────
   saacho.css carries a global reduce block; this scopes the opt-out
   to the app's own hover and panel motion, so a card does not lift
   and a control does not cross-fade for a reader who has asked the
   platform for stillness. */
@media (prefers-reduced-motion: reduce) {
  .cw-subnav a,
  .cw-tabs a,
  .cw-btn-danger,
  .cw-survey-wrap .form-control,
  .cw-survey-wrap .choices__inner,
  .cw-survey-wrap .btn {
    transition: none;
  }
  .cw-tabs a.on { transform: none; }
  .cw-tabs a:hover,
  .cw-subnav a:hover { transform: none; }
}
