/* Deepvue business verification form.
 *
 * Built from the approved design: the canvas at "Business Verification Form
 * .dc.html", worked up from `04-templates/order-form-fields-and-logic.md`. The
 * design system this replaces was deleted rather than migrated - none of its
 * tokens, class names or edge treatments survive here.
 *
 * The reader is a company secretary or a finance head, once, with a folder of
 * certificates open. So the page is quiet, the measure is bounded, and every
 * hue has one job:
 *
 *   blue   #2F6BFF  interaction. Focus, the current step, the primary action.
 *   green  #1D8256  two jobs, and they never appear on one control: a finished
 *                   step, and the confidentiality strip over a section that
 *                   takes identity documents.
 *   red             something is wrong and you must act. oklch, because the
 *                   design specified it and the four reds are one ramp.
 *   magenta         the logo, and nothing else on the page.
 *
 * TWO DELIBERATE DEVIATIONS FROM THE APPROVED DESIGN, both accessibility, both
 * agreed with the owner before they were made. Everything else is as drawn.
 *
 * 1. `--dv-control` is #7E90B2, not the design's #C6CEDD. A control's border is
 *    the whole visual definition of the control, which is what WCAG 2.2 SC
 *    1.4.11 means by "visual information required to identify user interface
 *    components". #C6CEDD is 1.58:1 on white against a 3:1 threshold. #7E90B2
 *    keeps the design's hue (219deg) and saturation (25%) exactly and drops
 *    lightness 82% -> 60%, giving 3.00:1 on the darkest surface a control sits
 *    on. Container edges - `--dv-line` and `--dv-hair` - are the design's own
 *    values untouched, because 1.4.11 does not reach a box that bounds nothing
 *    interactive, and they are what make the page as light as it was drawn.
 *
 * 2. `--dv-ok-ink` is #1D8256, not the design's #1F8A5B, which is 4.08:1 on its
 *    own #F2FAF5 tint at 13px against SC 1.4.3's 4.5:1. The shift is one step of
 *    lightness and reads as the same green.
 *
 * AND ONE FORCED BY THE THREAT MODEL rather than by taste: the design loads
 * Poppins from fonts.googleapis.com at weights 400-700 and sets body text in
 * 400. This form accepts Aadhaar and PAN uploads, so it makes no third-party
 * requests and self-hosts a latin subset instead. That subset carries no 400,
 * so body text is Poppins 500 - which is also the weight the metric-matched
 * fallback below is built against. Slightly darker text than drawn, on a
 * document that is read under scrutiny and printed.
 */

/* -- fonts ---------------------------------------------------------------
 *
 * Self-hosted, latin subset, same origin. `optional` rather than `swap`: the
 * fallback still renders about 10% wider, and a swap period on a form means text
 * reflowing under someone mid-sentence. `optional` gives a ~100ms block and then
 * no swap at all.
 */

@font-face {
  font-family: "Poppins";
  src: url("./fonts/poppins-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: optional;
}
@font-face {
  font-family: "Poppins";
  src: url("./fonts/poppins-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: optional;
}
@font-face {
  font-family: "Poppins";
  src: url("./fonts/poppins-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: optional;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("./fonts/jetbrains-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: optional;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("./fonts/jetbrains-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: optional;
}

/* Metrics taken from Poppins-Medium.ttf: unitsPerEm 1000, x-height 551,
   ascender 1050, descender 350, lineGap 100. The overrides are pre-divided by
   the size-adjust so they land on Poppins' true proportions after scaling. */
@font-face {
  font-family: "Poppins Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Helvetica"), local("Liberation Sans");
  size-adjust: 106.2%;
  ascent-override: 98.8%;
  descent-override: 32.9%;
  line-gap-override: 9.4%;
}

/* -- tokens --------------------------------------------------------------- */

:root {
  color-scheme: light;

  --dv-font: "Poppins", "Poppins Fallback", system-ui, -apple-system, "Segoe UI", Roboto, Arial,
    sans-serif;
  --dv-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* surfaces */
  --dv-bg: #f5f7fb;
  --dv-card: #ffffff;
  --dv-sunken: #f9fafd;

  /* ink */
  --dv-ink: #0f1b3d;
  --dv-ink-2: #3c4766;
  --dv-ink-3: #66708c;

  /* lines. `--dv-line` bounds a container, `--dv-hair` separates two rows
     inside one, `--dv-control` bounds something you can operate. Only the third
     is under SC 1.4.11, which is why only the third was moved. */
  --dv-line: #e1e6f0;
  --dv-hair: #edf0f6;
  --dv-control: #7e90b2;

  /* interaction.
     The design's accent is #2F6BFF, which is 4.499:1 on white -- it misses SC
     1.4.3 by a thousandth, in both directions, because it is used as ink on
     white AND as the ground under white on the primary button. #2B68FF keeps
     the hue (223deg) and the saturation (100%) and takes one step of lightness,
     which reads as the same blue and clears at 4.64:1 either way. */
  --dv-accent: #2b68ff;
  --dv-accent-hover: #1f55e0;
  --dv-accent-tint: #eaf0ff;
  --dv-accent-line: #c3d3ff;
  --dv-accent-wash: #f3f6ff;

  /* done, and confidential */
  --dv-ok: #22a366;
  --dv-ok-ink: #1d8256;
  --dv-ok-tint: #e4f6ec;
  --dv-ok-line: #bde6d0;
  --dv-ok-wash: #f2faf5;

  /* wrong */
  --dv-bad: oklch(0.5 0.19 27);
  --dv-bad-edge: oklch(0.6 0.18 27);
  --dv-bad-ink: oklch(0.42 0.17 27);
  --dv-bad-line: oklch(0.8 0.1 27);
  --dv-bad-wash: oklch(0.97 0.02 27);

  /* type. The design's own sizes, named by the job rather than by a scale
     position, because the design does not follow one ratio and pretending it
     does would invite the next person to "fix" a size it chose. */
  --dv-t-hero: 38px;
  --dv-t-screen: 32px;
  --dv-t-head: 28px;
  --dv-t-part: 17px;
  --dv-t-lede: 17px;
  --dv-t-body: 15px;
  --dv-t-ui: 14px;
  --dv-t-small: 13px;
  --dv-t-tiny: 12px;

  /* radius, deepest first. The rule this stylesheet is held to: a component
     nested inside another uses a strictly smaller radius, so nothing inside a
     card can read as a second card. `web/tests/edges.test.mjs` enforces it. */
  --dv-r-page: 14px; /* a page-level card: a step part, the checklist */
  --dv-r-panel: 12px; /* a panel on the page: bundle, fold, summary, resume */
  --dv-r-box: 10px; /* a box inside a panel: slot, entity bar, rail item */
  --dv-r-ctl: 8px; /* a control: input, select, textarea, small button */
  --dv-r-pill: 999px;

  --dv-shadow-tip: 0 12px 32px rgba(15, 27, 61, 0.25);
  --dv-shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.25);
}

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

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

html {
  scroll-behavior: smooth;
  /* The screen is drawn after a round trip for `form-schema.json`, so the page
     starts short and then grows past the viewport -- and the scrollbar arriving
     at that moment moves the masthead, the content and the footer sideways at
     once. Reserving the gutter up front is most of this page's layout shift. */
  scrollbar-gutter: stable;
}

/* The two gradients are the whole of the page's decoration. `fixed` so they
   belong to the window rather than to a 2300px document, where they would
   stretch into invisibility. */
body {
  margin: 0;
  padding: 0;
  background: var(--dv-bg);
  background-image: radial-gradient(
      900px 400px at 15% -10%,
      rgba(47, 107, 255, 0.08),
      transparent 60%
    ),
    radial-gradient(700px 400px at 95% 0%, rgba(232, 66, 120, 0.06), transparent 60%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  font-family: var(--dv-font);
  font-weight: 500;
  font-size: var(--dv-t-body);
  line-height: 1.5;
  color: var(--dv-ink);
}

/* The parts nobody draws still carry the design. This form is read aloud over a
   phone call and marked up in pen, so the selection is used constantly. */
::selection {
  background: var(--dv-accent-tint);
  color: var(--dv-ink);
}
:root {
  scrollbar-color: var(--dv-control) transparent;
}
input,
select,
textarea,
button {
  font-family: inherit;
  font-weight: inherit;
}

a {
  color: var(--dv-accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
a:hover {
  color: var(--dv-accent-hover);
}

/* One focus treatment, said once. The design draws it on the control's own edge
   for a field and offset for a button, because a field's border is already the
   ring's diameter and a pill's is not. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
canvas:focus-visible {
  outline: 2px solid var(--dv-accent);
  outline-offset: 0;
  border-color: var(--dv-accent);
}
button:focus-visible,
a:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--dv-accent);
  outline-offset: 2px;
}
main:focus {
  outline: none;
}

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

.dv-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--dv-card);
  color: var(--dv-accent);
  padding: 10px 16px;
  border-radius: 0 0 var(--dv-r-ctl) 0;
  font-weight: 600;
  text-decoration: none;
}
.dv-skip:focus {
  left: 0;
}

/* -- shared primitives ---------------------------------------------------- */

.dv-eyebrow {
  font-size: var(--dv-t-small);
  font-weight: 600;
  color: var(--dv-ink-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.dv-mono {
  font-family: var(--dv-mono);
}

.dv-muted {
  color: var(--dv-ink-3);
}

/* Short prose breaks badly at every width and no measure fixes it: a 98
   character string was two lines at every width between 538 and 654, and
   widening made the break worse. `balance` is the only thing that does. */
.dv-balance {
  text-wrap: balance;
}
.dv-pretty {
  text-wrap: pretty;
}

/* Buttons. Four shapes, and the shape says what the press costs: a filled pill
   commits, an outlined pill navigates, bare text is a minor aside, and a dashed
   box adds something that is not there yet. */
.dv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: var(--dv-t-body);
  font-weight: 600;
  border-radius: var(--dv-r-pill);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}
.dv-btn:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

.dv-btn--primary {
  background: var(--dv-accent);
  color: #fff;
  border: none;
  padding: 12px 24px;
}
/* The colour is restated on hover, and it has to be.
   These pills are drawn as <a> on the done screen, and `a:hover` -- one element
   more specific than `.dv-btn--primary` -- was repainting the label
   `--dv-accent-hover` on an `--dv-accent-hover` ground. The button went blank
   under the pointer: same width, same pill, no words. `:hover:not(:disabled)`
   outruns it. */
.dv-btn--primary:hover:not(:disabled) {
  background: var(--dv-accent-hover);
  color: #fff;
}

.dv-btn--ghost {
  background: none;
  border: 1px solid var(--dv-control);
  color: var(--dv-ink-2);
  padding: 11px 20px;
}
.dv-btn--ghost:hover:not(:disabled) {
  border-color: var(--dv-ink);
  color: var(--dv-ink);
}

.dv-btn--small {
  padding: 6px 14px;
  font-size: var(--dv-t-small);
}

.dv-btn--link {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--dv-accent);
  font-size: var(--dv-t-small);
  font-weight: 600;
  border-radius: var(--dv-r-ctl);
}
.dv-btn--link:hover:not(:disabled) {
  color: var(--dv-accent-hover);
}

.dv-btn--quiet {
  background: none;
  border: none;
  padding: 0;
  color: var(--dv-ink-3);
  font-size: var(--dv-t-small);
  font-weight: 600;
  cursor: pointer;
  border-radius: var(--dv-r-ctl);
}
.dv-btn--quiet:hover:not(:disabled) {
  color: var(--dv-bad);
}

.dv-btn--dashed {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border: 1px dashed var(--dv-control);
  border-radius: var(--dv-r-ctl);
  background: none;
  font-size: var(--dv-t-ui);
  font-weight: 600;
  color: var(--dv-accent);
  cursor: pointer;
}
.dv-btn--dashed:hover:not(:disabled) {
  border-color: var(--dv-accent);
  background: var(--dv-card);
  color: var(--dv-accent);
}

/* A status pill. The checklist is a list of forty rows and the tag is the only
   thing a reader scans, so it carries its own ground rather than relying on the
   colour of its text alone. */
.dv-tag {
  flex: none;
  font-size: var(--dv-t-tiny);
  font-weight: 600;
  padding: 2px 8px;
  border-radius: var(--dv-r-pill);
  background: var(--dv-accent-tint);
  color: var(--dv-accent-hover);
  white-space: nowrap;
}
.dv-tag[data-tone="ok"] {
  background: var(--dv-ok-tint);
  color: var(--dv-ok-ink);
}
.dv-tag[data-tone="na"] {
  background: var(--dv-hair);
  color: var(--dv-ink-2);
}
.dv-tag[data-tone="opt"] {
  background: var(--dv-bg);
  color: var(--dv-ink-3);
}

/* -- shell ---------------------------------------------------------------- */

/* The bar spans the viewport; the row inside it does not.
 *
 * A navbar has to reach both edges to read as one, so the ground and the bottom
 * rule stay full-bleed here -- and the wordmark was starting at 24px from the
 * window while the content below it started 200px further in, which is what
 * `__inner` fixes. Same 1120px measure and same 24px side padding as `.dv-foot`
 * and `.dv-main`, so the header, the content and the footer share one left edge. */
.dv-mast {
  background: var(--dv-card);
  border-bottom: 1px solid var(--dv-line);
}
.dv-mast__inner {
  max-width: 1120px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px 16px;
  flex-wrap: wrap;
  padding: 12px 24px;
}
.dv-mast__brand {
  display: flex;
  align-items: center;
  gap: 12px;
}
.dv-mast__logo {
  height: 22px;
  width: auto;
  display: block;
}
.dv-mast__rule {
  width: 1px;
  height: 18px;
  background: var(--dv-line);
}
.dv-mast__label {
  color: var(--dv-ink-3);
  font-size: var(--dv-t-ui);
}
.dv-mast__tools {
  display: flex;
  align-items: center;
  gap: 12px 16px;
  flex-wrap: wrap;
}
.dv-mast__save {
  font-family: var(--dv-mono);
  font-size: var(--dv-t-tiny);
  color: var(--dv-ink-3);
}
.dv-mast__help {
  font-size: var(--dv-t-small);
  font-weight: 600;
  color: var(--dv-accent);
  text-decoration: none;
}
.dv-mast__help:hover {
  text-decoration: underline;
}

.dv-notice {
  max-width: 1040px;
  margin: 16px auto 0;
  padding: 0 24px;
}
.dv-notice__inner {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  align-items: center;
  padding: 10px 14px;
  border-radius: var(--dv-r-box);
  background: var(--dv-accent-tint);
  border: 1px solid var(--dv-accent-line);
  font-size: var(--dv-t-ui);
}
.dv-notice__inner[data-tone="error"] {
  background: var(--dv-bad-wash);
  border-color: var(--dv-bad-line);
  color: var(--dv-bad-ink);
}
/* A link on a tinted band needs the darker ink: the accent clears 4.5:1 on
   white and only 4.07:1 on the notice's own ground, which is the surface it is
   actually read against. On the error tone it takes the band's own ink, because
   the whole band is one semantic. */
.dv-notice__inner .dv-btn--link {
  color: var(--dv-accent-hover);
}
.dv-notice__inner[data-tone="error"] .dv-btn--link {
  color: var(--dv-bad-ink);
}

.dv-foot {
  max-width: 1120px;
  margin: 0 auto;
  padding: 24px 24px 40px;
  border-top: 1px solid var(--dv-line);
  display: flex;
  justify-content: space-between;
  gap: 16px 32px;
  flex-wrap: wrap;
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}
.dv-foot__links {
  display: flex;
  gap: 20px;
  align-items: center;
  flex-wrap: wrap;
}
.dv-foot a {
  text-decoration: none;
  color: var(--dv-ink-3);
}
.dv-foot a:hover {
  color: var(--dv-ink);
  text-decoration: underline;
}

/* -- gate ----------------------------------------------------------------- */

.dv-gate {
  max-width: 1040px;
  margin: 0 auto;
  padding: 56px 24px 80px;
  display: flex;
  flex-direction: column;
  gap: 36px;
}
.dv-gate__head {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 680px;
}
.dv-gate__title {
  margin: 0;
  font-size: var(--dv-t-hero);
  line-height: 1.15;
  font-weight: 600;
  letter-spacing: -0.02em;
}
.dv-gate__lede {
  margin: 0;
  font-size: var(--dv-t-lede);
  line-height: 1.55;
  color: var(--dv-ink-2);
}
/* "Four steps" and how long they take, on one line.
 *
 * The figure was the tail of the lede's last sentence, spelled out in words,
 * which is the one place somebody deciding whether to start right now will not
 * look. It belongs against the list of steps rather than against the page,
 * because the steps are what the time buys. `baseline` so the clock and the
 * eyebrow sit on the same line of text rather than on the box's centre. */
.dv-gate__planhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
/* Muted, and a size down. It is not information anybody needs to act on -- it
   answers "roughly how long" and then gets out of the way -- so it is set in the
   same muted ink and size as the eyebrow it sits beside rather than competing
   with the step names below it. The clock takes `currentColor` for the same
   reason: on the accent blue it read as something to press. */
.dv-gate__time {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
  flex: none;
}
.dv-gate__time svg {
  flex: none;
}
.dv-gate__timenum {
  /* Tabular, so "15-25" holds its width against any figure that lands beside
     it later. */
  font-variant-numeric: tabular-nums;
}

/* `.dv-gate__who` went on 2026-09-03, when its sentence was folded into the
   lede. It was set apart by a `border-top` because it answered a different
   question -- which held while the lede was three sentences long. With one
   sentence either side, the rule was a band across the screen separating two
   short lines, so the sentence moved instead of the divider staying. */

.dv-resume {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  align-items: center;
  flex-wrap: wrap;
  padding: 16px 20px;
  background: var(--dv-card);
  border: 1px solid var(--dv-accent-line);
  border-left: 4px solid var(--dv-accent);
  border-radius: var(--dv-r-panel);
}
.dv-resume__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dv-resume__title {
  font-weight: 600;
  font-size: var(--dv-t-body);
}
.dv-resume__sub {
  font-size: var(--dv-t-ui);
  color: var(--dv-ink-3);
}
.dv-resume__acts {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}

/* `wrap-reverse` so that when the two columns cannot sit side by side the step
   preview goes ABOVE the entity picker rather than below it: it is the thing
   that says what you are about to start, and underneath a six-item list it
   would never be read. */
.dv-gate__cols {
  display: flex;
  flex-wrap: wrap-reverse;
  gap: 48px;
  align-items: flex-end;
  align-content: flex-start;
}
.dv-gate__main {
  flex: 1 1 420px;
  display: flex;
  flex-direction: column;
  gap: 32px;
}
.dv-gate__side {
  flex: 1 1 260px;
  max-width: 340px;
  align-self: flex-end;
}
.dv-gate__sticky {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.dv-gate__pick {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* `.dv-gate__picksub` went with the sentence it styled, on 2026-09-03. Its
   `margin-top: -6px` existed only to pull that line up under the eyebrow above
   it, so keeping the rule would have left a negative margin waiting on an
   element that no longer exists. */

.dv-plan__row {
  display: flex;
  gap: 14px;
  padding: 12px 0;
  border-top: 1px solid var(--dv-line);
}
.dv-plan__num {
  font-family: var(--dv-mono);
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
  padding-top: 2px;
}
.dv-plan__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dv-plan__name {
  font-weight: 600;
  font-size: var(--dv-t-body);
}
.dv-plan__sum {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}

/* The entity picker. A single-select list with a disclosure, drawn as one: one
   column, each option its own height, and the constitutions nested inside the
   option that owns them. It was a two-column grid of equal cards once, and
   every problem that had came from that - opening the left card pushed its
   neighbour down and left a hole beside it. */
.dv-bundle {
  background: var(--dv-card);
  border: 1px solid var(--dv-line);
  border-radius: var(--dv-r-panel);
  overflow: hidden;
}
.dv-bundle + .dv-bundle {
  margin-top: 8px;
}
.dv-bundle__pick {
  display: flex;
  width: 100%;
  text-align: left;
  gap: 14px;
  align-items: flex-start;
  padding: 16px 18px;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
}
.dv-bundle__pick:hover {
  background: var(--dv-sunken);
}
.dv-bundle__text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dv-bundle__name {
  font-weight: 600;
  font-size: var(--dv-t-body);
}
.dv-bundle__hint {
  font-size: var(--dv-t-ui);
  color: var(--dv-ink-3);
}
.dv-bundle__mark {
  color: var(--dv-ink-3);
  font-size: 18px;
  line-height: 1.2;
  flex: none;
}
.dv-bundle__open {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 4px 18px 16px;
  border-top: 1px solid var(--dv-hair);
}
.dv-bundle__which {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
  padding-top: 10px;
}
.dv-con {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-start;
  text-align: left;
  padding: 12px 14px;
  border: 1px solid var(--dv-line);
  border-radius: var(--dv-r-box);
  background: var(--dv-card);
  cursor: pointer;
  color: inherit;
  width: 100%;
}
.dv-con:hover {
  border-color: var(--dv-accent);
  background: var(--dv-accent-wash);
}
.dv-con__name {
  font-weight: 600;
  font-size: var(--dv-t-ui);
}
.dv-con__hint {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}

/* -- main: entity bar, rail, content -------------------------------------- */

.dv-main {
  max-width: 1120px;
  margin: 0 auto;
  padding: 20px 24px 80px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.dv-entity {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  background: var(--dv-card);
  border: 1px solid var(--dv-line);
  border-radius: var(--dv-r-box);
  font-size: var(--dv-t-ui);
}
.dv-entity__name {
  font-weight: 600;
  margin-left: 8px;
}
/* The shield and the way out, at the end of the row. `flex: none` so a long
   entity label takes the wrapping and these two never do. */
.dv-entity__acts {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
}

.dv-cols {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  align-items: flex-start;
}

/* The rail is a grid inside a flex item, and that is what makes it responsive
   without a media query. Beside the content it is 240px wide, so `auto-fit`
   resolves to one column and it reads as a vertical rail. When the content's
   440px basis no longer fits next to it the row wraps, the rail becomes full
   width, and the same `auto-fit` gives it four or five columns - a horizontal
   stepper - with no second set of rules to keep in step. */
.dv-rail {
  flex: 1 1 240px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 6px;
  position: sticky;
  top: 16px;
}
.dv-rail__item {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  text-align: left;
  padding: 12px 14px;
  border-radius: var(--dv-r-box);
  border: 1px solid transparent;
  background: none;
  cursor: pointer;
  color: inherit;
  width: 100%;
}
.dv-rail__item:hover:not(:disabled) {
  background: var(--dv-card);
  border-color: var(--dv-line);
}
.dv-rail__item:disabled {
  cursor: default;
}
.dv-rail__badge {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  flex: none;
  font-size: var(--dv-t-tiny);
  font-weight: 600;
  font-family: var(--dv-mono);
  background: var(--dv-card);
  color: var(--dv-ink-3);
  border: 1px solid var(--dv-control);
}
.dv-rail__badge[data-state="current"] {
  background: var(--dv-accent);
  color: #fff;
  border-color: var(--dv-accent);
}
.dv-rail__badge[data-state="complete"] {
  background: var(--dv-ok-tint);
  color: var(--dv-ok-ink);
  border-color: var(--dv-ok-line);
}
.dv-rail__badge[data-state="error"] {
  background: var(--dv-bad-wash);
  color: var(--dv-bad);
  border-color: var(--dv-bad-line);
}
.dv-rail__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.dv-rail__name {
  font-weight: 600;
  font-size: var(--dv-t-ui);
  color: var(--dv-ink-3);
}
.dv-rail__item[data-reached="yes"] .dv-rail__name {
  color: var(--dv-ink);
}
.dv-rail__sub {
  font-size: var(--dv-t-tiny);
  color: var(--dv-ink-3);
}

/* `flex: 999` rather than `1`: when the rail and the content share a row the
   content should take every pixel the rail does not want, and when the row
   wraps the content is alone and its basis is what bounds it. 720px is the
   measure, and it is the same 720 whether the rail is beside it or above it -
   which is the whole reason the rail became a grid instead of disappearing. */
.dv-content {
  flex: 999 1 440px;
  min-width: 0;
  max-width: 720px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* -- step ----------------------------------------------------------------- */

/* The step's own stack, and nothing was setting it.
 *
 * `#dv-form` had no rule at all, so it laid out as a block with every child at
 * `margin: 0`: the step head, each part card and the actions row all stacked
 * flush, and two cards met at a 2px seam of their own borders. The 20px is the
 * measure `.dv-content` already uses one level up, so the rhythm between the
 * cards is the same one as between the rail and the cards.
 *
 * `.dv-nav` keeps its own `padding-top` on top of this: a little more air
 * before the button that files the application than between two questions. */
#dv-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.dv-step__head {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dv-step__meta {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  align-items: baseline;
}
.dv-step__of {
  font-family: var(--dv-mono);
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}
.dv-step__answered {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}
.dv-step__title {
  margin: 0;
  font-size: var(--dv-t-head);
  font-weight: 700;
  letter-spacing: -0.02em;
}
/* Said once per step, in place of an asterisk on thirty controls. */
.dv-step__rule {
  margin: 0;
  color: var(--dv-ink-3);
  font-size: var(--dv-t-ui);
}
.dv-step__rule b {
  font-weight: 600;
  color: var(--dv-ink-2);
}

.dv-fold {
  background: var(--dv-card);
  border: 1px solid var(--dv-line);
  border-radius: var(--dv-r-panel);
  overflow: hidden;
}
.dv-fold__btn {
  display: flex;
  width: 100%;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 18px;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  text-align: left;
  font-size: var(--dv-t-ui);
}
.dv-fold__btn b {
  font-weight: 600;
}
.dv-fold__body {
  padding: 0 18px 6px;
  border-top: 1px solid var(--dv-hair);
}

/* The error summary. A red border over a red tint is one semantic said in one
   vocabulary rather than an edge declared twice, and it is the pattern GOV.UK's
   error summary exists to enforce: every failure on the step, named, in source
   order, each one a link to the control that owns it. */
.dv-summary {
  border: 1px solid var(--dv-bad-line);
  background: var(--dv-bad-wash);
  border-radius: var(--dv-r-panel);
  padding: 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dv-summary__title {
  font-weight: 600;
  color: var(--dv-bad-ink);
}
.dv-summary__list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.dv-summary__item {
  background: none;
  border: none;
  padding: 0;
  text-align: left;
  cursor: pointer;
  font-size: var(--dv-t-ui);
  color: var(--dv-bad-ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  border-radius: var(--dv-r-ctl);
}
.dv-summary__item b {
  font-weight: 600;
}

/* A part. One idea per card, and the card is a page-level surface now - the
   single card that used to hold a whole step is gone, because thirteen fields
   under one heading gave the reader nothing to say where one idea ended and the
   next began. */
.dv-part {
  background: var(--dv-card);
  border: 1px solid var(--dv-line);
  border-radius: var(--dv-r-page);
  overflow: hidden;
  /* The card is what the two-column rule below asks about, and it has to be an
     ancestor of what the rule styles: an element cannot query itself, which is
     what a `container-type` on `.dv-part__fields` was silently doing. */
  container: dv-part / inline-size;
}
.dv-part[data-error="yes"] {
  border-color: var(--dv-bad-line);
}
.dv-part__wrap {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.dv-part__head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: flex-start;
  padding: 18px 22px;
  border-bottom: 1px solid var(--dv-hair);
}
.dv-part__head[data-bare="yes"] {
  border-bottom-color: transparent;
}
.dv-part__lead {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  min-width: 0;
}
.dv-part__num {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  flex: none;
  font-size: var(--dv-t-tiny);
  font-weight: 600;
  font-family: var(--dv-mono);
  margin-top: 1px;
  background: var(--dv-accent-tint);
  color: var(--dv-accent);
}
.dv-part__num[data-complete="yes"] {
  background: var(--dv-ok-tint);
  color: var(--dv-ok-ink);
}
.dv-part__text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.dv-part__titlerow {
  display: flex;
  gap: 10px;
  align-items: baseline;
  flex-wrap: wrap;
}
.dv-part__title {
  margin: 0;
  font-size: var(--dv-t-part);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.dv-part__badge {
  font-size: var(--dv-t-small);
  font-weight: 600;
  color: var(--dv-ink-3);
  font-family: var(--dv-mono);
}
.dv-part__badge[data-tone="ok"] {
  color: var(--dv-ok-ink);
}
.dv-part__badge[data-tone="go"] {
  color: var(--dv-accent);
}
.dv-part__who {
  font-size: var(--dv-t-ui);
  color: var(--dv-ink-3);
}
.dv-part__intro {
  margin: 0;
  font-size: var(--dv-t-ui);
  color: var(--dv-ink-3);
}
.dv-part__err {
  margin: 0;
  font-size: var(--dv-t-small);
  color: var(--dv-bad);
  font-weight: 600;
}
.dv-part__acts {
  display: flex;
  gap: 14px;
  flex: none;
  align-items: baseline;
  font-size: var(--dv-t-small);
  font-weight: 600;
}

/* One column, and two where two fit.
 *
 * A container query rather than a media query, for the reason the rail is built
 * the way it is: what decides this is how wide the CARD is, not how wide the
 * window is. The same card is drawn full-width on a phone, beside a 240px rail
 * on a laptop, and inside `.dv-narrow` on the foreign hand-off, and a media
 * query would have to guess all three from the viewport.
 *
 * 620px is arithmetic, not taste: two cells of 310, each spending 44 on its own
 * padding and the gutter it shares, leaves a 266px input. Below that an email
 * address stops fitting in its own box and one column is the better answer. */
.dv-part__fields {
  display: grid;
  grid-template-columns: 1fr;
}
/* Twelve columns so a line can be cut in two or in three. `data-run` is written
   by the renderer, which is the only place that can see a field's neighbours;
   no `data-run` means the whole row, which is every long answer, every document
   slot and every short answer with no short answer next to it. */
@container dv-part (min-width: 620px) {
  .dv-part__fields {
    grid-template-columns: repeat(12, 1fr);
  }
  .dv-field {
    grid-column: span 12;
  }
  .dv-field[data-run="2"] {
    grid-column: span 6;
  }
  .dv-field[data-run] {
    /* Bottom of the content, not the top. Only one of a group usually carries a
       line of help under its label, and aligning the tops puts the boxes at
       different heights -- the row reads as ragged. Aligning the ends puts them
       on one line and leaves each label directly above its own box, which is the
       grouping an eye is looking for. The cell still stretches, so the hairline
       above the row stays one straight line. */
    justify-content: flex-end;
  }
}
/* Three across needs the room for it. At 620 each cell is 206px and the box
   inside it 162px, which is not an email address; at 700 it is 189px, which is.
   Below that the three stack, which is honest, rather than splitting two-and-one
   and leaving a thin box alone on a line. */
@container dv-part (min-width: 700px) {
  .dv-field[data-run="3"] {
    grid-column: span 4;
  }
}

.dv-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 22px;
  border-top: 1px solid var(--dv-hair);
}
.dv-field:first-child {
  border-top: none;
}
/* A field that only one answer reveals is set in from the question that
   revealed it, so the relationship is visible rather than remembered. */
.dv-field[data-revealed="yes"] {
  padding-left: 44px;
  border-left: 2px solid var(--dv-hair);
  margin-left: 22px;
}

.dv-label {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: baseline;
  flex-wrap: wrap;
}
.dv-label__text {
  font-weight: 600;
  font-size: var(--dv-t-ui);
}
.dv-optional {
  font-weight: 500;
  font-size: var(--dv-t-tiny);
  /* Not `--dv-ink-3`: the muted ink is 4.93:1 on white and only 4.32:1 on this
     pill's own ground, which is the surface it is actually read against. */
  color: #626b86;
  background: var(--dv-hair);
  padding: 1px 7px;
  border-radius: var(--dv-r-pill);
  margin-left: 6px;
  vertical-align: middle;
}
.dv-help {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
  margin-top: -4px;
}
.dv-err {
  font-size: var(--dv-t-small);
  color: var(--dv-bad);
}

/* -- controls ------------------------------------------------------------- */

.dv-input,
.dv-select,
.dv-textarea {
  border: 1px solid var(--dv-control);
  border-radius: var(--dv-r-ctl);
  padding: 10px 12px;
  font-size: var(--dv-t-body);
  background: var(--dv-card);
  color: inherit;
  width: 100%;
}
.dv-input,
.dv-select {
  max-width: 440px;
}
.dv-textarea {
  resize: vertical;
  line-height: 1.5;
}
.dv-input[aria-invalid="true"],
.dv-select[aria-invalid="true"],
.dv-textarea[aria-invalid="true"] {
  border-color: var(--dv-bad-edge);
}
.dv-input[readonly] {
  background: var(--dv-bg);
}
/* An identifier is read back character by character over a phone call. */
.dv-input[data-mono="yes"] {
  font-family: var(--dv-mono);
}
.dv-count {
  font-size: var(--dv-t-tiny);
  color: var(--dv-ink-3);
  font-family: var(--dv-mono);
}

/* Yes/No is two radios, not a select and not a pair of toggles: it is one
   question with two mutually exclusive answers and no default, and a radio is
   the only control that says all three of those things.
   Under the question rather than beside it, and left with the other controls on
   the card, so the eye running down a column of boxes finds it. */
.dv-yesno {
  display: flex;
  gap: 18px;
  flex: none;
  margin-top: 2px;
}
.dv-pick {
  display: flex;
  gap: 7px;
  align-items: center;
  cursor: pointer;
  font-size: var(--dv-t-ui);
}
.dv-radio {
  width: 16px;
  height: 16px;
  accent-color: var(--dv-accent);
  margin: 0;
  flex: none;
}
.dv-checkbox {
  width: 18px;
  height: 18px;
  margin-top: 3px;
  accent-color: var(--dv-accent);
  flex: none;
}
.dv-check {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  cursor: pointer;
}
.dv-check__text {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.dv-check__label {
  font-size: var(--dv-t-ui);
  font-weight: 500;
  /* The authorisation sentence is the longest prose on the form and it was the
     only prose with no reading measure -- it ran the full width of the card, at
     roughly 95 characters a line, on the one paragraph a person is legally
     agreeing to. 62ch of Poppins lands near 70 real characters, because `ch` is
     the width of the "0" glyph and Poppins' zero is wide. */
  max-width: 62ch;
  text-wrap: pretty;
}
.dv-check__sub {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}
/* The terms link inside the authorisation sentence. It is part of the sentence
   being agreed to, so it is underlined but not coloured - a blue word inside a
   consent paragraph reads as the only thing worth reading. */
.dv-check__terms {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--dv-control);
  text-underline-offset: 3px;
  font-weight: 500;
}

/* -- uploads -------------------------------------------------------------- */

.dv-slot {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border: 1px dashed var(--dv-control);
  border-radius: var(--dv-r-box);
  padding: 12px 14px;
  background: var(--dv-card);
}
.dv-slot[data-filled="yes"] {
  border-style: solid;
  border-color: var(--dv-line);
  background: var(--dv-sunken);
}
.dv-slot[data-error="yes"] {
  border-color: var(--dv-bad-edge);
}
.dv-slot[data-over="yes"] {
  border-color: var(--dv-accent);
  background: var(--dv-accent-wash);
}
.dv-file {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 4px 12px;
  align-items: center;
  font-size: var(--dv-t-ui);
}
.dv-file__name {
  display: flex;
  gap: 10px;
  align-items: center;
  min-width: 0;
  /* Shrinkable, and it is the ONLY part of the row that is. The button must not
     be squeezed and the reason now has its own line, so this is what gives when
     a filename is long -- and `.dv-file__text` has the ellipsis to show for it. */
  flex: 1 1 auto;
}
/* The reason a row failed, on its own line, full width.
 *
 * `flex-basis: 100%` is what puts it there, and it is the whole fix: the reason
 * used to live inside `.dv-file__acts`, which is `flex: none`, so a long
 * sentence grew that block wider than the row and crushed `.dv-file__name` to
 * zero. Every part of the name -- the dot, the filename, the "Failed" tag --
 * then drew on top of each other and Remove was pushed past the card's edge. */
.dv-file__why {
  flex: 1 1 100%;
  min-width: 0;
}
.dv-file__text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dv-file__size {
  color: var(--dv-ink-3);
  font-size: var(--dv-t-small);
  flex: none;
}
.dv-file__acts {
  display: flex;
  gap: 12px;
  flex: none;
  align-items: center;
}
.dv-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--dv-ok);
  flex: none;
}
.dv-dot[data-state="failed"] {
  background: var(--dv-bad-edge);
}
.dv-dot[data-state="busy"] {
  background: var(--dv-accent);
}
.dv-slot__add {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
}
.dv-slot__choose {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--dv-control);
  border-radius: var(--dv-r-ctl);
  background: var(--dv-card);
  font-size: var(--dv-t-ui);
  font-weight: 600;
  cursor: pointer;
}
.dv-slot__choose:hover {
  border-color: var(--dv-accent);
  color: var(--dv-accent);
}
/* Not `display:none`: a hidden file input cannot be focused, and the label is
   the only thing that would be left to reach it from the keyboard. */
.dv-slot__file {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.dv-slot__note {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}
.dv-slot__gone {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  font-size: var(--dv-t-ui);
}
.dv-slot__gone b {
  font-weight: 600;
  color: var(--dv-ink-3);
}
.dv-hatch {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--dv-ink-3);
  font-size: var(--dv-t-small);
  text-decoration: underline;
  text-underline-offset: 2px;
  border-radius: var(--dv-r-ctl);
}
/* Tint, no edge. The design draws both; on a sunken ground inside a white card
   the border is the same boundary said twice, which is the one thing
   `web/tests/edges.test.mjs` exists to stop. */
.dv-reason {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border-radius: var(--dv-r-box);
  background: var(--dv-sunken);
}
.dv-reason__head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}
.dv-reason__title {
  font-size: var(--dv-t-ui);
  font-weight: 600;
}

/* -- signature ------------------------------------------------------------ */

.dv-sig {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dv-sig__pad {
  width: 100%;
  max-width: 640px;
  aspect-ratio: 640 / 180;
  border: 1px solid var(--dv-control);
  border-radius: var(--dv-r-box);
  background: var(--dv-card);
  touch-action: none;
  cursor: crosshair;
  display: block;
}
.dv-sig__pad[aria-invalid="true"] {
  border-color: var(--dv-bad-edge);
}
.dv-sig__foot {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  max-width: 640px;
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}
/* The keyboard way to sign. Sits under the pad rather than beside it, because it
   is the same act and not a different question. */
.dv-sig__typerow {
  display: flex;
  gap: 10px;
  align-items: center;
  max-width: 640px;
}
.dv-sig__type {
  max-width: 360px;
}

/* -- add another, and its tooltip ----------------------------------------- */

.dv-after {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
}
.dv-tipwrap {
  position: relative;
  display: flex;
  align-items: center;
}
.dv-tipbtn {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid var(--dv-control);
  background: var(--dv-card);
  color: var(--dv-ink-3);
  font-size: var(--dv-t-small);
  font-weight: 600;
  cursor: help;
  display: grid;
  place-items: center;
  padding: 0;
  font-family: var(--dv-mono);
}
.dv-tipbtn:hover {
  border-color: var(--dv-accent);
  color: var(--dv-accent);
}
/* The confidentiality lock. Green because the assurance is, and no tinted
   ground under the border: one edge treatment per edge. */
.dv-tipbtn[data-tone="ok"] {
  border-color: var(--dv-ok-line);
  color: var(--dv-ok-ink);
}
.dv-tipbtn[data-tone="ok"]:hover {
  border-color: var(--dv-ok-ink);
  color: var(--dv-ok-ink);
}
.dv-tip {
  position: absolute;
  left: 0;
  bottom: calc(100% + 10px);
  width: min(300px, calc(100vw - 64px));
  padding: 14px 16px;
  background: var(--dv-ink);
  color: #fff;
  border-radius: var(--dv-r-box);
  font-size: var(--dv-t-small);
  line-height: 1.5;
  box-shadow: var(--dv-shadow-tip);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dv-tip__title {
  font-weight: 600;
}
.dv-tip__body {
  color: var(--dv-accent-line);
}
.dv-tip__arrow {
  position: absolute;
  left: 8px;
  bottom: -6px;
  width: 12px;
  height: 12px;
  background: var(--dv-ink);
  transform: rotate(45deg);
}
/* The entity bar's lock is at the top of the page and at the right edge of its
   card, so the default -- upward, left-aligned -- would open into the masthead
   and off the right of the screen. */
.dv-tip[data-anchor="below-right"] {
  top: calc(100% + 10px);
  bottom: auto;
  left: auto;
  right: 0;
}
.dv-tip[data-anchor="below-right"] .dv-tip__arrow {
  left: auto;
  right: 8px;
  bottom: auto;
  top: -6px;
}
/* On a phone there is not 300px to the left of the lock, so a tip hung off the
   lock hangs off the screen -- and this page must never scroll sideways. Below
   560px it is hung off the CARD instead, edge to edge, and loses its arrow:
   there is only one thing under the entity bar it could be pointing at. */
@media (max-width: 560px) {
  .dv-entity {
    position: relative;
  }
  .dv-entity .dv-tipwrap {
    position: static;
  }
  .dv-tip[data-anchor="below-right"] {
    left: 14px;
    right: 14px;
    width: auto;
  }
  .dv-tip[data-anchor="below-right"] .dv-tip__arrow {
    display: none;
  }
}

/* -- step navigation ------------------------------------------------------ */

/* `wrap-reverse` keeps Continue on the top row when the two cannot share one,
   because Back is the rarer press and the one that should move. */
.dv-nav {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: center;
  padding-top: 8px;
  flex-wrap: wrap-reverse;
}

/* -- checklist ------------------------------------------------------------ */

.dv-ck {
  background: var(--dv-card);
  border: 1px solid var(--dv-line);
  border-radius: var(--dv-r-page);
  overflow: hidden;
}
.dv-ck__head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px 20px;
  border-bottom: 1px solid var(--dv-hair);
  background: var(--dv-sunken);
}
.dv-ck__progress {
  font-weight: 600;
  font-size: var(--dv-t-ui);
}
.dv-ck__optional {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}
.dv-ck__group {
  padding: 8px 20px 4px;
}
.dv-ck__grouphead {
  font-size: var(--dv-t-tiny);
  font-weight: 600;
  color: var(--dv-ink-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 10px 0 4px;
}
.dv-ck__row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: baseline;
  padding: 10px 0;
  border-top: 1px solid var(--dv-hair);
}
.dv-ck__label {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.dv-ck__name {
  font-size: var(--dv-t-ui);
}
.dv-ck__cond {
  font-size: var(--dv-t-small);
  color: var(--dv-ink-3);
}
.dv-ck__fold {
  border-top: 1px solid var(--dv-hair);
}
.dv-ck__foldbtn {
  display: flex;
  width: 100%;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 20px;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  text-align: left;
  font-size: var(--dv-t-ui);
}
.dv-ck__foldbtn b {
  font-weight: 600;
}
.dv-ck__foldsum {
  color: var(--dv-ink-3);
  margin-left: 8px;
}
.dv-ck__foldbody {
  padding: 0 20px 8px;
}

/* -- foreign, done, modal ------------------------------------------------- */

.dv-narrow {
  max-width: 720px;
  margin: 0 auto;
  padding: 32px 24px 80px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.dv-panel {
  background: var(--dv-card);
  border: 1px solid var(--dv-line);
  border-radius: var(--dv-r-page);
  padding: 32px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.dv-panel__head {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dv-panel__title {
  margin: 0;
  font-size: 24px;
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: -0.015em;
}
.dv-panel__lede {
  margin: 0;
  color: var(--dv-ink-2);
}
.dv-stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 420px;
}

.dv-done {
  max-width: 640px;
  margin: 0 auto;
  padding: 80px 24px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.dv-done__mark {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--dv-ok);
  color: #fff;
  display: grid;
  place-items: center;
}
.dv-done__title {
  margin: 0;
  font-size: var(--dv-t-screen);
  font-weight: 700;
  letter-spacing: -0.02em;
}
.dv-done__block {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.dv-done__body {
  margin: 0;
  color: var(--dv-ink-2);
  font-size: 16px;
}
.dv-done__ref {
  display: flex;
  gap: 10px;
  align-items: baseline;
  flex-wrap: wrap;
  padding: 14px 16px;
  background: var(--dv-card);
  border: 1px solid var(--dv-line);
  border-radius: var(--dv-r-box);
}
.dv-done__refid {
  font-family: var(--dv-mono);
  font-size: var(--dv-t-body);
  font-weight: 500;
}
.dv-done__acts {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.dv-modal {
  position: fixed;
  inset: 0;
  background: rgba(14, 26, 29, 0.45);
  display: grid;
  place-items: center;
  padding: 24px;
  z-index: 50;
}
.dv-modal__box {
  background: var(--dv-card);
  border-radius: var(--dv-r-page);
  padding: 24px;
  max-width: 460px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 18px;
  box-shadow: var(--dv-shadow-modal);
}
/* The box is a heading, a sentence and two buttons, so the heading carries the
   question and the sentence carries the cost. `window.confirm` had no room for
   that distinction: it put the whole thing in one grey paragraph under
   "localhost says". */
.dv-modal__title {
  margin: 0;
  font-size: var(--dv-t-part);
  font-weight: 600;
  color: var(--dv-ink);
  letter-spacing: -0.01em;
}
.dv-modal__text {
  margin: 0;
  font-size: var(--dv-t-body);
  color: var(--dv-ink-2);
}
.dv-modal__acts {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.dv-btn--danger {
  background: var(--dv-bad);
  color: #fff;
  border: none;
  padding: 10px 18px;
  font-size: var(--dv-t-ui);
}
.dv-btn--danger:hover:not(:disabled) {
  background: var(--dv-bad-ink);
  color: #fff;
}

/* The screen is drawn after `form-schema.json` arrives, which is a round trip.
   Without a reservation the page is one line of "Loading the form..." and then
   suddenly a thousand pixels of gate, and everything below it -- the footer
   most visibly -- jumps. A min-height on the mount point holds the space the
   first real screen is going to want, which took cumulative layout shift from
   1.02 to under 0.02.

   A full viewport rather than a fraction of one, and that is the whole trick:
   cumulative layout shift counts movement INSIDE the viewport, so reserving a
   screen's worth puts the footer below the fold at first paint and leaves it
   there. `min-height` rather than a fixed height, because every screen the form
   draws is taller than this. */
#dv-main {
  min-height: 100vh;
}

/* The human check.
 *
 * In normal flow and NOT visually hidden, which is the whole point: Turnstile
 * renders an iframe here when it decides a challenge is needed, and a 1px
 * clipped box gives it nowhere to draw one. It then issues no token, the
 * promise `boot()` waits on never resolves, and no draft ever opens -- so every
 * upload and the submission itself fail, silently, with the gate looking fine.
 *
 * It takes no room until there is something in it: `:has(iframe)` is what makes
 * an invisible pass cost nothing and a real challenge sit centred with air
 * around it.
 */
.dv-turnstile {
  display: flex;
  justify-content: center;
}
.dv-turnstile:has(iframe) {
  padding: 0 24px 32px;
}

.dv-loading {
  max-width: 1040px;
  margin: 0 auto;
  padding: 56px 24px;
  color: var(--dv-ink-3);
}

/* -- narrow --------------------------------------------------------------- */

/* The layout needs no breakpoint - it wraps on its own. These are the two
   things wrapping cannot do: give back the gutters a phone does not have, and
   stop a 38px headline taking four lines of a 390px screen. */
@media (max-width: 560px) {
  .dv-gate,
  .dv-main,
  .dv-narrow,
  .dv-done,
  .dv-foot,
  .dv-notice {
    padding-left: 16px;
    padding-right: 16px;
  }
  /* `__inner`, not `.dv-mast`. The padding moved onto the inner row when the
     bar went full-bleed, and this override left pointing at the outer element
     would have gone on matching and gone on doing nothing -- narrowing that
     nobody would see until a phone was in front of them. */
  .dv-mast__inner {
    padding: 12px 16px;
  }
  .dv-gate {
    padding-top: 32px;
  }
  .dv-gate__title {
    font-size: 30px;
  }
  .dv-done__title {
    font-size: 26px;
  }
  .dv-part__head,
  .dv-field {
    padding-left: 16px;
    padding-right: 16px;
  }
  .dv-field[data-revealed="yes"] {
    padding-left: 30px;
    margin-left: 16px;
  }
  .dv-ck__group,
  .dv-ck__head,
  .dv-ck__foldbtn,
  .dv-ck__foldbody {
    padding-left: 16px;
    padding-right: 16px;
  }
  .dv-panel {
    padding: 20px;
  }
}

/* -- print ---------------------------------------------------------------- */

/* It gets printed and marked up in pen. Give it the ink, and take away the
   things that mean nothing on paper. */
@media print {
  body {
    background: #fff;
    background-image: none;
  }
  .dv-mast__tools,
  .dv-rail,
  .dv-nav,
  .dv-skip,
  .dv-notice,
  .dv-modal {
    display: none !important;
  }
  .dv-part,
  .dv-ck,
  .dv-fold {
    break-inside: avoid;
    border-color: #999;
  }
  .dv-content {
    max-width: none;
  }
}
