/* =====================================================================
   Instrument shell — rail, drawer, section headers, footer, completion.

   The form CONTROLS are entirely the Haggai forms package (`hif-*`); this
   file is only the page chrome around them, per design/CD_HANDOFF.md.
   Values reference design-system tokens — never hand-written near-miss
   hexes. Do not edit forms.tokens.css to rebrand; override --hif-* instead.
   ===================================================================== */

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

/* The forms package sets `.hif-notice { display: flex }`, which beats the UA
   stylesheet's `[hidden] { display: none }` on specificity — so every notice
   rendered with the `hidden` attribute was showing on load. Restore the
   attribute's meaning globally rather than patching each component. */
[hidden] { display: none !important; }

body.svy {
  margin: 0;
  font-family: var(--font-body);
  color: var(--fg-body);
  background: var(--bg-elevated);
  line-height: var(--lh-base);
}

/* --------------------------------------------------------------- shell */
.svy-shell {
  display: flex;
  align-items: flex-start;
  min-height: 100vh;
}

.svy-maincol {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.svy-content {
  flex: 1 0 auto;
  min-width: 0;
  padding: 0 var(--sp-6) var(--sp-8);
}

.svy-submit { margin: var(--sp-7) 0 var(--sp-8); }

/* ---------------------------------------------------------------- rail */
.svy-rail {
  position: sticky;
  top: 0;
  flex: 0 0 236px;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: var(--sp-5) 20px 28px;
  background: var(--hi-paper);
  border-right: 1px solid var(--border);
  max-height: 100vh;
  overflow-y: auto;
}

.svy-rail__logo { display: block; height: auto; }

.svy-rail__formtitle {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  line-height: var(--lh-snug);
  color: var(--fg-heading);
}

.svy-rail__est {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--fg-muted);
}

.svy-rail__nav { display: flex; flex-direction: column; }

.svy-rail__item,
.svy-drawer a {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 9px 10px;
  text-decoration: none;
  border-left: 3px solid var(--border);
}

.svy-rail__item:hover { background: var(--hi-stone-100); }

/* Left-border state: not started / in progress / complete */
.svy-rail__item.is-progress,
.svy-drawer a.is-progress { border-left-color: var(--hi-gold); }

.svy-rail__item.is-complete,
.svy-drawer a.is-complete {
  border-left-color: var(--status-success);
  background: var(--status-success-bg);
}

.svy-rail__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  color: var(--fg-heading);
}

.svy-rail__meta { font-size: 11px; color: var(--fg-muted); }

.svy-rail__saved {
  margin: 0;
  font-size: 11px;
  color: var(--fg-muted);
}

/* ------------------------------------------------------ mobile top bar */
.svy-topbar,
.svy-drawer { display: none; }

.svy-topbar__logo { display: block; height: auto; }

.svy-topbar__toggle {
  min-height: 44px;
  padding: 0 var(--sp-3);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: var(--tracking-caps-btn);
  text-transform: uppercase;
  color: var(--fg-heading);
  background: var(--bg-elevated);
  border: 1px solid var(--hi-dark-blue);
  border-radius: var(--r-md);
  cursor: pointer;
}

.svy-topbar__progress {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 1 1 auto;
  min-width: 0;
}

.svy-topbar__pct {
  font-size: 12px;
  color: var(--fg-muted);
  white-space: nowrap;
}

/* ------------------------------------------------------ section header
   The gold rule hangs in the gutter rather than pushing the header inward, so
   the section title, its intro, and every question below share one left edge.
   Indenting the questions to meet the header instead would align them just as
   well but cost 18px of measure on every line — which the 24- and 25-option
   checklists cannot spare now that they run two columns. */
.svy-section__head {
  margin-bottom: var(--sp-5);
  margin-left: -18px;              /* 14px gap + 4px rule */
  padding-left: 14px;
  border-left: 4px solid var(--hi-gold);
}

.hif-section--bordered { border-top: 1px solid var(--border); padding-top: var(--sp-6); }

/* ------------------------------------------------------- sticky footer */
.svy-footer {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: 14px var(--sp-6);
  background: var(--hi-dark-blue);
}

/* Time remaining is the primary signal; the percentage is secondary. */
.svy-footer__time {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: var(--tracking-caps-lg);
  text-transform: uppercase;
  color: var(--hi-gold);
}

.svy-footer__pct { font-size: 13px; color: var(--fg-on-brand); }

.svy-footer__copy {
  font-size: 11px;
  color: var(--fg-on-brand);
  opacity: 0.6;
  white-space: nowrap;
}

/* ---------------------------------------------------------- completion
   The completion screen replaces the whole document, so it owns the viewport
   and cannot inherit the shell's centring. Band, body and footer each run full
   bleed while their contents share one centred measure. */
.svy-done {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.svy-done__inner {
  width: 100%;
  max-width: 660px;
  margin-inline: auto;
  padding-inline: var(--sp-6);
}

.svy-done__band {
  padding: var(--sp-8) 0 var(--sp-7);
  background: var(--hi-dark-blue);
}

.svy-done__rule {
  width: 44px;
  height: 4px;
  margin: var(--sp-5) 0;
  background: var(--hi-gold);
}

.svy-done__headline {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 34px;
  line-height: var(--lh-snug);
  color: var(--fg-on-brand);
  text-wrap: balance;
}

.svy-done__body {
  flex: 1 0 auto;
  padding: var(--sp-8) 0 var(--sp-8);
  font-size: 17px;
  line-height: 1.6;
}

/* These lines are what the respondent just told us, read back to them — the
   one moment in the instrument that is about them rather than about us. They
   carry the display face and enough size to be read rather than skimmed. */
.svy-done__stage1 p {
  margin: 0 0 var(--sp-5);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 20px;
  line-height: 1.45;
  color: var(--hi-dark-blue);
  text-wrap: pretty;
}

.svy-done__stage2 {
  padding: var(--sp-4);
  background: var(--hi-paper);
  border-left: 4px solid var(--hi-gold);
}

.svy-done__closing {
  margin: var(--sp-6) 0 0;
  padding-top: var(--sp-6);
  border-top: 1px solid var(--border);
  color: var(--fg-muted);
  text-wrap: pretty;
}

.svy-done__foot {
  padding: var(--sp-4) 0;
  font-size: 11px;
  color: var(--fg-muted);
  border-top: 1px solid var(--border);
  /* The copyright reads left, the build stamp sits hard right. */
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
}

/* ---------------------------------------------------------------- build stamp
   Which build rendered this page. It exists for the moment somebody sends a
   screenshot saying the form did something odd — a respondent has no use for it,
   and `aria-hidden` in the markup keeps it out of a screen reader.

   UNDERSTATED MEANS SMALL, NOT DIM. The first cut was 10px white at 0.55
   opacity on the dark-blue bar, and opacity blends toward the BACKGROUND — so
   white at 55% over dark blue is mid-blue, which reads as dark text rather than
   as faint text. Reported as "black on blue". Size is the right lever for
   understatement; transparency against a dark ground is not.

   `tabular-nums` so the digits do not change width between builds, and the
   corner of the page does not twitch on every release. */
.svy-build {
  font-size: 8px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--fg-muted);
  white-space: nowrap;
}

/* On the form the footer is the sticky progress bar, so the stamp is pinned to
   the corner rather than joining the bar's flex row — it is a marginal note
   about the page, not one of the three things the bar is there to tell you.
   `.svy-footer` is `position: sticky` and therefore already a containing block. */
.svy-build--inline {
  position: absolute;
  right: 6px;
  bottom: 2px;
  color: var(--hi-white);
  opacity: 0.7;
}

/* ------------------------------------------------------------ bare root
   haggaiforms.org/ — a logo, centred, nothing else. Styled here rather than
   inline because the CSP is `style-src 'self'` with no 'unsafe-inline', so a
   <style> block on the page would be dropped and the logo would render
   top-left at full size. */
body.svy-bare {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  margin: 0;
  padding: var(--sp-6);
  background: var(--bg-elevated);
}

.svy-bare__mark {
  width: 280px;
  max-width: 100%;
  height: auto;
}

@media (max-width: 559px) {
  .svy-bare__mark { width: 220px; }
}

/* ------------------------------------------------------- policy / notice
   /privacy and /data-use. Long-form prose, so it gets a reading measure and
   real heading rhythm rather than the completion screen's short blocks. */
.svy-policy { max-width: 62ch; }

.svy-policy__lede {
  font-size: 19px;
  line-height: 1.55;
  color: var(--hi-dark-blue);
  text-wrap: pretty;
}

.svy-policy__h {
  margin: var(--sp-7) 0 var(--sp-3);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--hi-stone-200);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 19px;
  color: var(--hi-dark-blue);
}

.svy-policy p { margin: 0 0 var(--sp-4); text-wrap: pretty; }

/* ------------------------------------------------- confirmation code block
   Used by /received. The code is the one thing on that page a person may need
   to write down or read aloud, so it gets the gold rule and a face where a
   zero cannot be mistaken for an O. */
.svy-code {
  margin: var(--sp-6) 0;
  padding: var(--sp-5);
  background: var(--hi-paper);
  border-left: 4px solid var(--hi-gold);
}

.svy-code__label {
  margin: 0 0 var(--sp-2);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: var(--tracking-caps-lg);
  text-transform: uppercase;
  color: var(--fg-muted);
}

.svy-code__value {
  margin: 0;
  /* Monospace so 0/O and 1/l are distinguishable, and user-select so it can be
     copied cleanly in one gesture. */
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 24px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.04em;
  color: var(--hi-dark-blue);
  word-break: break-all;
  user-select: all;
}

.svy-code__hint {
  margin: var(--sp-3) 0 0;
  font-size: 13px;
  color: var(--fg-muted);
}

/* ----------------------------------------------------------- responsive
   The forms package is container-query driven; the one thing the application
   decides is the rail / drawer switch, and it is at 900px rather than 560px.

   The rail is a fixed 236px. It used to hold that width down to 560px, which
   meant a viewport just above the switch gave the QUESTIONS less room than a
   phone does — 260px of measure at a 560px viewport and 300px at 600px,
   against 358px on a 390px phone. A portrait tablet landed squarely in that
   band. Because the forms package sizes matrices and choice grids from their
   CONTAINER, the squeezed column also pushed every one of them into its
   narrowest layout, and 560px was consequently the tallest the Pre has ever
   rendered: 19,708px, against 13,755px on the phone it was beating.

   Measured 2026-08-25, before and after:

       viewport    measure before -> after    height before -> after
       560px          260px -> 528px           19,708px -> 12,112px
       600px          300px -> 568px           17,483px -> 11,945px
       700px          400px -> 668px           13,116px -> 11,874px
       800px          500px -> 768px           12,155px -> 11,885px

   900px, and not wider, is a deliberate choice rather than an optimum.

   Measured at the same time: the rail still costs measure between 900px and
   1199px, and the page is longer there than it would be full width — 11,617px
   against 9,578px at a 900px viewport. Moving the switch to 1200px would flatten
   that out. It is not done, because length is a weak thing to optimise for on a
   page the reader simply scrolls: the extra height is mostly matrices choosing
   their stacked layout for a narrower column, which is the right layout for that
   column and not a fault. Nothing in that band overflows or clips — verified
   across 360px to 1920px. The rail carries the section navigation and the
   progress indicator, and showing it to more respondents is worth more than a
   shorter scroll.

   Recorded so that a later reader who measures the heights and sees the same
   numbers knows they were seen and weighed, rather than missed. */
@media (max-width: 899px) {
  .svy-rail { display: none; }

  .svy-shell { flex-direction: column; }

  .svy-topbar {
    position: sticky;
    top: 0;
    z-index: 6;
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    width: 100%;
    padding: var(--sp-2) var(--sp-4);
    background: var(--bg-elevated);
    border-bottom: 1px solid var(--border);
  }

  .svy-drawer {
    display: block;
    position: sticky;
    /* Measured, not the 60px this used to hardcode: the bar wraps on a narrow
       screen and the drawer then opened underneath it. */
    top: var(--svy-bar-h, 56px);
    z-index: 6;
    width: 100%;
    background: var(--hi-paper);
    border-top: 1px solid var(--border);
  }

  .svy-drawer a { min-height: 44px; justify-content: center; }

  .svy-content { width: 100%; padding: 0 var(--sp-4) var(--sp-8); }

  /* The rule still hangs, but only as far as the narrower content padding. */
  .svy-section__head { margin-left: -16px; padding-left: 12px; }

  .svy-footer { padding: 14px var(--sp-4); }

  .svy-footer__copy { display: none; }
  .svy-build--inline { display: none; }

  .svy-done__inner { padding-inline: var(--sp-4); }

  .svy-done__headline { font-size: 27px; }

  .svy-done__stage1 p { font-size: 18px; }
}

/* ------------------------------------------------------------ embedded
   Set on <html> by embed.js ONLY once the host page has confirmed it is sizing
   the frame to this document. Being framed is not enough on its own: a frame
   nobody is resizing has a viewport of its own, scrolls internally, and wants
   exactly the chrome below. Standalone delivery never sees any of this.

   The host sizes the frame to the document (embed/pathwright.js), so the frame
   has no viewport of its own to scroll. Two consequences, and together they are
   why the embed needs its own rules rather than reusing the narrow-screen
   layout:

   1. Every viewport-height rule has to go. `min-height: 100vh` resolves against
      the frame; the host grows the frame to fit; 100vh grows with it; the
      document reports a taller height. The loop never settles.

   2. Nothing sticky can stick. With no scroll container inside the frame, a
      sticky rail, bar or footer simply occupies space where it was declared.
      The chrome is hidden rather than left in place, because a progress rail
      that cannot follow the reader is 236px of column spent on a control that
      no longer does anything.

   Section navigation and the progress indicator are therefore absent when the
   form is embedded. On a form that renders around 9,500px tall that is a real
   loss, and it is the price of the host owning the scroll rather than the
   respondent scrolling a pane inside a page. */
html.svy-embed,
html.svy-embed body {
  min-height: 0;
  height: auto;
}

.svy-embed .svy-rail,
.svy-embed .svy-topbar,
.svy-embed .svy-drawer { display: none; }

.svy-embed .svy-shell {
  flex-direction: column;
  min-height: 0;
}

.svy-embed .svy-maincol { min-height: 0; }

/* Full width at every size. The rail is gone, so the 236px it reserved above
   560px belongs to the questions — which is also what closes the gap where a
   560-800px frame rendered a NARROWER column than a phone did (324px at 560px
   against 390px on a phone), and read as the tallest render of the form at
   17,483px. */
.svy-embed .svy-content { width: 100%; }

.svy-embed .svy-done { min-height: 0; }

.svy-embed body.svy-bare { min-height: 0; }

/* Static, not sticky: see (2) above. `bottom: 0` on a root that does not scroll
   has nothing to stick to, and leaving the declaration in place would invite a
   later reader to assume it still does something. */
.svy-embed .svy-footer,
.svy-embed .svy-section__strip {
  position: static;
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}


/* =====================================================================
   CD amendment (described 2026-08-06) — IMPLEMENTED FROM DESCRIPTION.

   The changed partials never reached us: the file supplied was
   forms.unlayered.css, which is only the @import index and is byte-identical
   to ours, and the design-system project's own forms/forms.field.css does not
   carry these rules either. So these are written here rather than into the
   package, which keeps forms/*.css a faithful copy and makes this trivially
   removable once the real partials land.

   REMOVE THIS BLOCK when CD's updated forms.field.css / forms.matrix.css arrive.
   ===================================================================== */

/* Stems take the display face against Roboto options — a face-level change,
   not a weight nudge. */
.svy .hif-label,
.svy .hif-legend,
.svy .hif-matrix__stem {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 17px;
  color: var(--hi-dark-blue);
}

.svy .hif-option__label,
.svy .hif-matrix__col {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  color: var(--hi-black);
}

/* ---------------------------------------------------------------- measure

   Option text was running ~96 characters per line at desktop width. 45-75 is
   the readable band, and the cost of exceeding it lands hardest on the readers
   this instrument is hardest for — non-native English speakers tracking a long
   line back to its start.

   Legends stop at 54ch rather than matching the options. Narrower was tried and
   is worse: at 42ch the long stems shred into four short lines, which reads as
   four fragments rather than one sentence. */
.svy .hif-option__label { max-width: 60ch; }

.svy .hif-field .hif-legend,
.svy .hif-field .hif-label {
  max-width: 54ch;
  /* Set here, not inline. An inline line-height on a legend beats this sheet
     and silently reverts the measure work. */
  line-height: 1.35;
}

/* Two-tier option label: taxonomy name, then its examples beneath. */
.svy .hif-option__label {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.svy .hif-option__label-name {
  font-weight: 500;
  color: var(--hi-dark-blue);
}

.svy .hif-option__note {
  font-size: 13px;
  line-height: 1.45;
  color: var(--hi-taupe);
}

/* Each question opens on a stone hairline with room beneath. Suppressed on the
   first question in a section — the section header already carries one, and the
   gold bar is deliberately NOT reused here: it is the section device and loses
   meaning if repeated per question. */
.svy .hif-block {
  border-top: 1px solid var(--hi-stone-200);
  padding-top: 26px;
  /* The rule belongs to the question BELOW it, so it needs more air above than
     below. Without this the previous question's last input sat flush against
     the next hairline. */
  padding-bottom: 34px;
}

.svy .svy-section__head + .hif-block {
  border-top: 0;
  padding-top: 0;
}

.svy .hif-block:last-child { padding-bottom: 0; }


/* =====================================================================
   Long checklists (the 25- and 24-option activity lists)

   Two columns where there is room, and the group heading sticks while you
   scroll its options. Both shorten the page WITHOUT hiding anything —
   which matters here because these two questions feed the activity-breadth
   aggregate. Check-all already under-reports; anything that reduces option
   visibility depresses the count in a way that reads as a real finding
   rather than a UI artifact. So: no collapse-by-default.
   ===================================================================== */

/* The two-column treatment itself is the PACKAGE's — `hif-options--columns`,
   applied by the renderer to the long lists only. It is CSS multi-column behind
   a container query, so reading order stays vertical (right for scanning a
   taxonomy) and a narrow container collapses it with no viewport query. An
   earlier version of this file hand-rolled an auto-fit grid here; that fought
   `.hif-optgroup`'s flex layout, read left-to-right, and reached every
   checklist including the seven full-sentence hindrance options.

   What remains below is only what the package does not have. */

/* The group heading stays put while its own options scroll past, so you never
   lose track of which block you are answering in a 25-option list. */
.svy .hif-checklist--columns .hif-optgroup__title {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg-elevated);
}

/* The filter bar sits above the groups and should stay reachable too. */
.svy .hif-checklist__bar {
  position: sticky;
  top: 0;
  z-index: 3;
  padding: var(--sp-2) 0;
  background: var(--bg-elevated);
}

/* Below the rail/drawer breakpoint three things stack at the top of the screen,
   and each has to clear the ones above it:

       bar  ->  section strip  ->  filter  ->  group heading

   The offsets are MEASURED into these properties by trackStickyOffsets() rather
   than written here. Hardcoding them (they were 56px and 112px) is wrong on
   real hardware — the bar wraps to two lines on a narrow screen and every
   heading below slides underneath it. The fallbacks only apply for the instant
   before the first measurement. */
@media (max-width: 559px) {
  .svy .hif-checklist__bar {
    top: calc(var(--svy-bar-h, 56px) + var(--svy-strip-h, 0px));
  }
  .svy .hif-checklist--columns .hif-optgroup__title {
    top: calc(var(--svy-bar-h, 56px) + var(--svy-strip-h, 0px) + var(--svy-filter-h, 0px));
  }
}

/* Two columns of the two-tier labels need more room than the package's 620px
   container breakpoint assumes — the second line (examples) wraps to three or
   four lines at half measure. Hold single column until there is room for both. */
@container hif-field (max-width: 759px) {
  .svy .hif-checklist--columns .hif-options--columns { columns: 1; }
}


/* =====================================================================
   Number fields

   Two shapes, because two situations. A lone count sits under a stem that
   already asks the question; a money block asks two things at once and needs
   its rows labelled to tell them apart.
   ===================================================================== */

/* The lone-count case: input directly under the stem, no row label, no rule to
   cross. `.hif-fields--bare` is emitted by the renderer, which also moves the
   label text to aria-label so the input keeps a name for assistive tech. */
.svy .hif-fields--bare .hif-input-group { border: 0; padding: 0; }

/* A count field has no business being as wide as the page — an input sized for
   40 characters invites the respondent to wonder what else belongs in it. */
.svy .hif-input--number { max-width: 8rem; }

/* The labelled table.

   A GRID, not the package's flex row. Flex wraps per row on its own content, so
   "Through social media" stayed inline while "Through a large group
   presentation" pushed its input onto the next line — three rows asking one
   question in three different shapes. A grid puts every input in the same
   column regardless of how long its label runs. */
.svy .hif-fields:not(.hif-fields--bare) { max-width: 34rem; }

.svy .hif-fields:not(.hif-fields--bare) .hif-input-group {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--sp-3);
  padding: 8px 0;
  border-top: 1px solid var(--hi-stone-200);
}
.svy .hif-fields:not(.hif-fields--bare) .hif-input-group:first-child { border-top: 0; }

.svy .hif-input-group__control { display: flex; align-items: center; gap: 8px; }

/* A ROW label is not a question stem, and must not wear its face. Inheriting
   the stem's Montserrat 700 / 17px made three short labels shout over the
   question they belong to and pushed the inputs out of alignment. */
.svy .hif-fields .hif-input-group > .hif-label {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.35;
  color: var(--hi-dark-blue);
  max-width: none;
}

/* Below the grid's comfortable width, stack — a 1fr/auto grid on a phone
   squeezes the label to a couple of words per line. */
@media (max-width: 560px) {
  .svy .hif-fields:not(.hif-fields--bare) .hif-input-group {
    grid-template-columns: 1fr;
    gap: 6px;
  }
}

/* 16px is unconditional and load-bearing: iOS zooms the viewport on focus for
   anything smaller, and the zoom does not reverse on blur. The height drop is
   NOT unconditional — it applies only to a real pointer. Both conditions are
   required, because `hover: hover` alone matches some Android browsers that
   report a hover-capable touch pointer, and a 39px target on a phone is under
   the 44px floor. */
.svy .hif-input--number { font-size: 16px; }

/* The package sizes every input for a 44px tap target, which is right for a
   thumb and far too tall for a number box sitting beside 16px text — it read as
   a different component rather than a field in the same row. Trimmed on a real
   pointer only.

   Both media conditions are required. `hover: hover` alone matches Android
   browsers that report a hover-capable touch pointer, and 39px on a phone is
   under the tap floor. */
@media (pointer: fine) and (hover: hover) {
  .svy .hif-input--number {
    min-height: 39px;
    padding: 7px 10px;
  }
}


/* =====================================================================
   Page shell

   The form is a sheet on a page, not the page itself. Two reasons beyond
   taste: an unbounded content column puts the rail and the submit button at
   opposite ends of a 27-inch display, and a white form on a white background
   has no edge, so nothing tells the eye where the questionnaire begins.

   Engine-level, not per-instrument — every form this platform serves gets the
   same frame.
   ===================================================================== */
body.svy { background: var(--hi-stone-100); }

.svy-shell {
  max-width: 1207px;
  margin: 0 auto;
  background: var(--bg-elevated);
  border-inline: 1px solid var(--hi-stone-200);
}

/* Edge to edge below the point where a margin would just steal width. */
@media (max-width: 1207px) {
  .svy-shell { border-inline: 0; }
}


/* =====================================================================
   Sticky section strip (narrow only)

   Says which section you are in and how far through it you are, without
   costing a second sticky bar. It sticks WITHIN its own <section>, so it
   scrolls away as that section ends and the next one's strip takes its place.
   No scroll-spy: a strip cannot report a section you have already left,
   because it does not outlive it.

   Narrow only. On desktop the rail already carries this, and there is no bar
   above it to pin against — CD measured it floating at the window's top edge
   as an orphaned label.
   ===================================================================== */
.svy-section__strip { display: none; }

/* Paired with the shell switch above, not independent of it: the strip is
   positioned at `top: var(--svy-bar-h)`, which is the height of the mobile top
   bar. It has to move whenever the bar does, or the strip pins itself against a
   bar that is not on the page. */
@media (max-width: 899px) {
  /* The strip has to meet the section's OWN frame, so the section's inline
     padding is pinned to a known token here rather than left at the package's
     --sp-5. Otherwise the strip's negative margin is a guess about a value
     declared in another stylesheet, and it was: -16px against 24px of padding
     left the header floating 8px inside the border on both sides.

     Tightening 24px -> 16px is a gain in its own right on a small screen — it
     is inline padding stacked on top of the content gutter, and the measure is
     the scarce thing on a 360px display. Above phone widths it is no longer a
     gain, but it is not optional either: the strip's negative margin below is
     exactly this value, so the two have to agree wherever this block applies. */
  .svy .hif-section--bordered {
    padding-top: 0;
    padding-inline: var(--sp-4);
    /* One colour for all four sides. Our rule sets border-top and the package
       sets the rest, and a two-tone frame shows at the strip's top corners. */
    border: 1px solid var(--border);
  }

  .svy-section__strip {
    position: sticky;
    top: var(--svy-bar-h, 56px);
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: 10px var(--sp-4);
    /* Exactly the section's inline padding, so the header runs edge to edge
       inside the section's border. */
    margin-inline: calc(var(--sp-4) * -1);
    background: var(--hi-paper);
    border-bottom: 1px solid var(--border);
    /* Meets the frame's own corners at the top of the section. */
    border-radius: var(--hif-radius-lg) var(--hif-radius-lg) 0 0;
  }

  /* The section's top padding moved to the strip's underside, because the strip
     is now the section's top edge. */
  .svy-section__strip + .svy-section__head { padding-top: var(--sp-5); }

  .svy-section__strip-title {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 15px;
    color: var(--hi-dark-blue);
    /* One line. A wrapped strip changes its own height, which moves everything
       pinned below it. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .svy-section__strip-count {
    font-size: 13px;
    color: var(--hi-taupe);
    flex: 0 0 auto;
  }
}
