/* The account-opening flow, on top of css/onboarding.css.

   css/onboarding.css, css/orbit.css and js/orbit.js are fasset-biz's, byte for
   byte, and stay that way. This file is what the KYB flow adds or overrides:
   16px inputs, the eyebrow, a six-section progress bar that fills, a steady
   step height, the camera table (data-cam instead of the
   fasset-biz data-scene names, which collide with our step ids), the stage
   layer system, a busy Continue, inline errors, notices, focus rings, the
   field components js/kyb/fields.js draws, and the reduced-motion block.

   Tokens only (scripts/check-design-system.mjs checks this file), and no
   font-weight of 600 or more. Group styles live in kyb-a.css, kyb-b.css and
   kyb-c.css.

   The law is fasset-ds/DESIGN.md, checked by
   docs/kyb-own-flow/DS-RULES.md. Where onboarding.css (vendored from
   fasset-biz, never edited here) departs from it, the correction is in the
   "law over reference" section below rather than in that file. The shared
   primitives (tag, status, monogram, done mark, key and value, skeleton) are
   at the end: groups use those rather than drawing their own. */

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

/* The stack primitive is fasset-ds's (tokens.css); its heading gap names
   generic headings, so the flow's own two heading classes join it here. */
.stack.stack > :is(.ob-h, .kyb-notice-h) + * { margin-top: var(--rhythm-heading-body); }

/* One column width on every step (--ob-form-width, onboarding.css). A wider column for the
   list steps was tried and dropped: the left edge moved on every visit. */

.kyb-vh {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Every direct child of a step joins the fasset-biz cascade, not only the
   classes onboarding.css lists, so a group's own blocks rise in with the
   heading. The delays for children 1 to 8 are onboarding.css's own. */
.ob-step > * {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 300ms var(--ease-out-soft), transform 300ms var(--ease-out-soft);
}
.ob-step.in > * { opacity: 1; transform: translateY(0); }
.ob-step.in > *:nth-child(n+9) { transition-delay: 280ms; }
.ob-step { min-height: 360px; }  /* fasset-biz's 300px, plus the eyebrow and 16px inputs, so the heading and Continue stay put */
/* The heading takes focus on every step change so a screen reader starts
   there. It is not a control, so it draws no ring: nothing about it can be
   pressed, and a ring would read as an input. */
.ob-step .ob-h:focus,
.ob-step .ob-h:focus-visible { outline: none; }

.kyb-eyebrow {
  font-size: var(--text-12);
  color: var(--text-subtle);
  margin-bottom: var(--space-8);
  letter-spacing: 0.01em;
}
.ob-sub { color: var(--text-subtle); }
.ob-sub:empty { display: none; }

/* ---------- progress: six sections that fill ---------- */

.ob-progress i::after { transform: scaleX(var(--fill, 0)); }
.ob-progress i.on::after { transform: scaleX(var(--fill, 1)); }
.ob-progress { margin-bottom: var(--space-8); }
/* A section reached can be gone to (js/kyb/flow.js, sectionTarget). The bar
   keeps its 2px look; a transparent border gives every section an 18px hit
   area without moving anything. */
.ob-progress i { box-sizing: content-box; border-block: var(--space-8) solid transparent; margin-block: calc(-1 * var(--space-8)); background-clip: padding-box; }
.ob-progress i[role="button"] { cursor: pointer; transition: background-color var(--dur-2) var(--ease-snappy); }
.ob-progress i[role="button"]:hover { background-color: var(--border-strong); }
.ob-progress i[role="button"]:hover::after { background: var(--text-default); }
.ob-progress i[role="button"]:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.kyb-progress-label { margin: 0 0 18px; font-size: var(--text-12); color: var(--text-subtle); }
.kyb-progress-label[hidden] { display: none; }

/* ---------- fields ---------- */

/* 16px or more on every input, at every width. onboarding.css sets 13px,
   which makes iOS zoom the page on focus and is below the size this flow
   promises. */
.ob-input,
.ob-step input,
.ob-step select,
.ob-step textarea { font-size: var(--text-16); }
.ob-input { line-height: 1.35; }
textarea.ob-input { resize: vertical; min-height: 88px; line-height: 1.5; }

/* The fasset-biz field label, 12px subtle with a 7px gap, on every group
   label too, so a question reads the same whether it is a field or a set of
   choices. */
.kyb-legend {
  display: block;
  font-size: var(--text-12);
  color: var(--text-subtle);
  margin-bottom: 7px;
}
/* A field's label is not a note (R12-15): it was drawn exactly like the hint
   under the field, so a long hint read as just another label. Stronger and one
   step up, over the input; the hint stays 12px subtle under it. */
.ob-field > label {
  display: block;
  font-size: var(--text-13);
  font-weight: var(--w-medium);
  color: var(--text-strong);
  margin-bottom: 7px;
}
/* The required mark (R12-11): the label's own colour, never red, which is for
   errors. Explained once per step by .kyb-required-note. */
.kyb-req { color: var(--text-subtle); font-weight: var(--w-body); }
.kyb-legend .kyb-req { color: var(--text-subtle); }
.kyb-required-note { margin: 0 0 var(--space-14); }
.kyb-hint {
  font-size: var(--text-12);
  color: var(--text-subtle);
  margin-top: var(--space-6);
  line-height: 1.45;
}
.kyb-counter {
  font-size: var(--text-12);
  color: var(--text-subtle);
  margin: 0 0 0 auto;
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* The error and the counter of one field share a row: the error on the left, the counter on the right, one gap above. */
.kyb-msgrow { display: flex; align-items: flex-start; gap: var(--space-12); margin-top: var(--space-6); }
.kyb-msgrow .kyb-error { margin-top: 0; flex: 1 1 auto; min-width: 0; }
.kyb-counter.over { color: var(--negative-text); }

.kyb-error {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-6);
  font-size: var(--text-12);
  color: var(--negative-text);
  margin-top: var(--space-6);
  line-height: 1.45;
}
.kyb-error[hidden] { display: none; }
/* The support contact and reference sit under the message, full width, never as a second column. */
.kyb-error > .support-reference { flex-basis: 100%; }
/* The one coloured border in the system: --negative on a field in error
   (fasset-biz .ob-otp.err). It stays while the field has focus, and the ink
   half-ring gives way to it rather than drawing a red glow. */
.ob-input[aria-invalid="true"],
.ob-input[aria-invalid="true"]:hover,
.ob-input[aria-invalid="true"]:focus { border-color: var(--negative); }
.ob-input[aria-invalid="true"]:focus { box-shadow: none; }

.kyb-fieldset { border: 0; padding: 0; margin: 0 0 var(--space-14); min-width: 0; }
.kyb-fieldset > legend { padding: 0; }

/* Figures that line up. The body font with tabular numerals, not a monospace:
   fasset-biz keeps monospace for one place, and codes and emails read better
   in the face everything else is set in. */
.kyb-mono, .kyb-mono input { font-variant-numeric: tabular-nums; }

/* phone: calling code + number */
.kyb-phone { display: grid; grid-template-columns: 124px 1fr; gap: var(--space-8); }

/* percent: a number with a % suffix inside the field */
.kyb-suffix { position: relative; }
.kyb-suffix .ob-input { padding-right: 34px; font-variant-numeric: tabular-nums; }
.kyb-suffix::after {
  content: "%";
  position: absolute; right: 12px; top: 50%;
  transform: translateY(-50%);
  color: var(--text-subtle);
  font-size: var(--text-14);
  pointer-events: none;
}

/* number: digits only, an optional unit inside the field on either side */
.kyb-affix { position: relative; }
.kyb-affix .ob-input { font-variant-numeric: tabular-nums; }
.kyb-affix.has-pre .ob-input { padding-left: 58px; }
.kyb-affix.has-suf .ob-input { padding-right: 88px; }
.kyb-affix-pre,
.kyb-affix-suf {
  position: absolute; top: 50%;
  transform: translateY(-50%);
  color: var(--text-subtle);
  font-size: var(--text-14);
  pointer-events: none;
}
/* A fixed unit, not a hint: a hairline sets it apart from the typed number
   and from the placeholder after it (R12-18). */
.kyb-affix-pre { left: 12px; padding-right: var(--space-8); border-right: 1px solid var(--border); line-height: 20px; font-variant-numeric: tabular-nums; }
.kyb-affix-suf { right: 12px; }

/* month: month and year side by side */
.kyb-month-row { display: grid; grid-template-columns: 1.4fr 1fr; gap: var(--space-8); }

/* choice cards, as radio or checkbox groups */
.kyb-choices { display: flex; flex-direction: column; }
.ob-choice { width: 100%; text-align: left; }
.ob-choice[aria-checked="true"],
.ob-choice[aria-pressed="true"] { border-color: var(--text-strong); background: var(--bg-hover); }
.ob-choice .oc-body small { color: var(--text-subtle); }
/* One answer: the .ob-radio mark, an ink ring whose dot scales in on
   --ease-out-soft. */
.ob-choice .oc-state::after {
  content: "";
  position: absolute; inset: 4.5px;
  border-radius: 50%;
  background: var(--text-strong);
  transform: scale(0);
  transition: transform var(--dur-2) var(--ease-out-soft);
}
.ob-choice[aria-checked="true"] .oc-state::before,
.ob-choice[aria-pressed="true"] .oc-state::before { border-color: var(--text-strong); }
.ob-choice[aria-checked="true"] .oc-state::after,
.ob-choice[aria-pressed="true"] .oc-state::after { transform: scale(1); }
/* Several answers: a square checkbox with the icon set's check. */
.ob-choice .kyb-oc-box::before { border-radius: var(--r-tag); transition: background var(--dur-2) var(--ease-snappy), border-color var(--dur-2) var(--ease-snappy); }
.ob-choice .kyb-oc-box::after { display: none; }
.ob-choice .kyb-oc-box svg { inset: 3px; color: var(--surface); }
.ob-choice[aria-checked="true"] .kyb-oc-box::before { background: var(--text-strong); }
.ob-choice[aria-checked="true"] .kyb-oc-box svg { opacity: 1; transform: scale(1); }
.ob-choice:focus-visible,
.ob-pill:focus-visible,
.ob-skip:focus-visible,
.ob-continue:focus-visible,
.ob-back:focus-visible,
.kyb-link:focus-visible,
.kyb-check input:focus-visible + i {
  outline: 1.5px solid var(--focus-ring);
  outline-offset: 2px;
}

/* yes / no, side by side */
.kyb-yesno { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-8); }
.kyb-yesno .ob-choice { margin-bottom: 0; justify-content: space-between; }

/* pills: onboarding.css's .ob-pill as drawn (12px subtle, --r-control, a
   hairline), selected in ink. */
.ob-pill { display: inline-flex; align-items: center; }
.ob-pill[aria-pressed="true"],
.ob-pill[aria-checked="true"] { color: var(--text-strong); background: var(--bg-hover); border-color: var(--text-strong); }
.ob-pill[hidden] { display: none; }
.ob-pill:disabled { opacity: 0.45; cursor: not-allowed; }
.kyb-pill-x { display: inline-grid; margin-left: var(--space-6); color: var(--text-subtle); }

/* checkbox */
.kyb-check {
  display: flex; align-items: flex-start; gap: var(--space-10);
  font-size: var(--text-13);
  color: var(--text-default);
  cursor: pointer;
  margin-bottom: var(--space-14);
  position: relative;
}
.kyb-check input { position: absolute; opacity: 0; width: 16px; height: 16px; margin: 0; }
.kyb-check i {
  width: 16px; height: 16px; flex: none; margin-top: 1px;
  border: 1.5px solid var(--border-strong);
  border-radius: var(--r-tag);
  display: grid; place-items: center;
  color: var(--surface);
  transition: background var(--dur-2) var(--ease-snappy), border-color var(--dur-2) var(--ease-snappy);
}
/* The icon set's check, drawn by js/kyb/fields.js, scales in the way
   .oc-state's does. */
.kyb-check i svg { opacity: 0; transform: scale(0.6); transition: opacity var(--dur-2) var(--ease-out-soft), transform var(--dur-2) var(--ease-out-soft); }
.kyb-check input:checked + i { background: var(--text-strong); border-color: var(--text-strong); }
.kyb-check input:checked + i svg { opacity: 1; transform: scale(1); }

/* combobox listbox (countryPicker) */
.kyb-combo { position: relative; }
.kyb-fieldset:has(.kyb-listbox:not([hidden])) { position: relative; z-index: 30; }
/* The field around the combo is itself a stacking context (the step's entrance gives it a transform), so the list's own
   z-index only counts inside it, and the suggestion pills after it, also transformed, painted over the options they sit under.
   Lift the field above them. */
.kyb-fieldset .ob-field:has(.kyb-listbox:not([hidden])) { position: relative; z-index: 25; }
/* Group blocks animate independently and therefore each forms a stacking
   context. Lift the whole block, not just its fieldset, above the next block. */
.kyb-group-block:has(.kyb-listbox:not([hidden])) { position: relative; z-index: 31; }
.kyb-listbox {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px);
  z-index: 20;
  max-height: min(232px, 38vh); overflow-y: auto;
  margin: 0; padding: var(--space-4);
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  box-shadow: var(--elev-2);
}
.kyb-listbox[hidden] { display: none; }
.kyb-listbox li {
  padding: var(--space-8) var(--space-10);
  font-size: var(--text-14);
  border-radius: var(--r-control);
  cursor: pointer;
  display: flex; justify-content: space-between; gap: var(--space-8);
}
.kyb-listbox li[aria-selected="true"] { background: var(--bg-hover); color: var(--text-strong); }
.kyb-listbox li .kyb-mono { color: var(--text-subtle); font-size: var(--text-12); }
.kyb-picked { display: flex; flex-wrap: wrap; gap: var(--space-6); margin-top: var(--space-10); }
.kyb-picked:empty { display: none; }
/* The token field (R12-16): the chosen chips and the input share one box that
   looks like every other input, and grows a row at a time. */
.kyb-tokenbox {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-6);
  min-height: 38px; padding: var(--space-4) var(--space-8);
  cursor: text;
}
.kyb-tokenbox:focus-within { border-color: var(--text-strong); box-shadow: 0 0 0 0.5px var(--text-strong); }
.kyb-tokenbox:has(input[aria-invalid="true"]) { border-color: var(--negative); box-shadow: none; }
.kyb-tokenbox .kyb-picked { display: contents; }
.kyb-token-input {
  flex: 1 1 140px; min-width: 120px;
  border: 0; background: transparent; outline: none;
  padding: 5px var(--space-4);
  font-size: var(--text-13); color: var(--text-default);
}
/* A single-choice search box: the clear button sits inside it once it has a
   value. The browser's own clear mark would be a second one. */
.kyb-searchbox { position: relative; }
.kyb-searchbox .ob-input { padding-right: 34px; }
.kyb-searchbox input[type="search"]::-webkit-search-cancel-button { display: none; }
.kyb-search-clear {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; display: grid; place-items: center;
  color: var(--text-subtle); border-radius: var(--r-tag);
}
.kyb-search-clear:hover { color: var(--text-strong); }
.kyb-search-clear[hidden] { display: none; }
.kyb-suggest { margin-top: var(--space-10); }
.kyb-suggest-k { font-size: var(--text-12); color: var(--text-subtle); margin-bottom: var(--space-6); }

/* roster rows (the step supplies them). No lines between rows: height and
   alignment separate them (DESIGN.md, "no lines"). The face is the shared
   .kyb-monogram. */
.kyb-roster { display: flex; flex-direction: column; list-style: none; padding: 0; margin: 0 0 var(--space-14); }
.kyb-roster-row {
  display: grid; grid-template-columns: 32px 1fr auto; gap: var(--space-10); align-items: center;
  min-height: 56px; padding: var(--space-8) 0;
}
.kyb-roster-av { width: 32px; height: 32px; font-size: var(--text-13); }
.kyb-roster-name { font-size: var(--text-14); color: var(--text-strong); }
.kyb-roster-meta { font-size: var(--text-12); color: var(--text-subtle); }
.kyb-roster-actions { display: flex; gap: var(--space-4); }

/* split rows (A uses it for revenue) */
.kyb-split { display: flex; flex-direction: column; gap: var(--space-8); margin-bottom: var(--space-14); }
.kyb-split-row { display: grid; grid-template-columns: 1fr 104px; gap: var(--space-10); align-items: center; }
.kyb-split-row label { font-size: var(--text-13); color: var(--text-default); }
.kyb-split-row .kyb-error { grid-column: 1 / -1; margin-top: -4px; }
.kyb-split-total { font-size: var(--text-12); color: var(--text-subtle); font-variant-numeric: tabular-nums; }
.kyb-note { margin-top: var(--space-14); padding: var(--space-12) var(--space-14); border: 1px solid var(--border-strong); border-radius: var(--r-card); font-size: var(--text-12); color: var(--text-subtle); }
.kyb-note p { margin: 0; }
.kyb-note p + p { margin-top: var(--space-6); color: var(--text-default); overflow-wrap: anywhere; }
.kyb-group-block + .kyb-group-block { margin-top: 22px; padding-top: var(--space-20); border-top: 1px solid var(--border-strong); }
.kyb-group-title { margin: 0 0 5px; color: var(--text-strong); font-size: var(--text-18); font-weight: var(--w-medium); line-height: 1.3; }
.kyb-group-block > .kyb-hint { margin: 0 0 var(--space-14); }

/* small text buttons inside a step */
/* the underlined text link fasset-biz draws for .ob-legal a and Resend */
.kyb-link {
  font-size: var(--text-13);
  color: var(--text-default);
  border-bottom: 1px solid var(--border-strong);
  transition: color var(--dur-1) var(--ease);
}
.kyb-link:hover { color: var(--text-strong); transition: none; }
.ob-legal { font-size: var(--text-12); color: var(--text-subtle); }
.ob-legal .kyb-link { font-size: var(--text-12); }
.ob-resend { color: var(--text-subtle); }
.ob-resend button { color: var(--text-default); }
.ob-resend button:disabled { opacity: 0.6; cursor: default; }

/* the operator disclosure (signin) */
.kyb-operator { margin-top: var(--space-24); }
.kyb-operator summary { font-size: var(--text-12); line-height: 24px; min-height: 24px; color: var(--text-subtle); cursor: pointer; }  /* 2.5.8: a block control, 24px tall */
.kyb-operator form { margin-top: var(--space-12); }
.kyb-operator .ob-btn-sm { margin-top: var(--space-4); }

/* The scroll cue (R12-14, js/kyb/scroll-cue.js): a 2px line pinned to the top
   of the form column on an --ink fill over a --border track, and a soft fade
   over the bottom edge while more is underneath. The section bar has
   segments and a label; this has neither, so the two are never confused. */
.kyb-scroll-line {
  position: sticky; top: 0; z-index: 6;
  --p: 0;
  flex: none; height: 2px; margin-bottom: -2px;
  background: var(--border);
}
.kyb-scroll-line[hidden] { display: none; }
.kyb-scroll-line > i {
  display: block; height: 100%;
  background: var(--ink);
  transform-origin: left center;
  transform: scaleX(var(--p));
  transition: transform var(--dur-1) var(--ease);
}
.ob-foot::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 32px;
  background: linear-gradient(to bottom, transparent, var(--bg));
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease);
}
.ob-form.has-more .ob-foot::before { opacity: 1; }
/* On a phone Continue stays reachable: the actions row sticks to the bottom
   edge, and the fade sits just above it (the footer is not sticky there). */
@media (max-width: 920px) {
  .ob-actions {
    position: sticky; bottom: 0; z-index: 5;
    margin-top: 18px; padding: var(--space-10) 0 var(--space-2);
    background: var(--bg);
  }
  .ob-actions::before {
    content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 32px;
    background: linear-gradient(to bottom, transparent, var(--bg));
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--dur-2) var(--ease);
  }
  .ob-form.has-more .ob-actions::before { opacity: 1; }
  .ob-foot::before { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .kyb-scroll-line > i, .ob-foot::before, .ob-actions::before { transition: none; }
}

/* Takes no part in the page and is not announced, but keeps its space. */
.kyb-ghost { visibility: hidden; }

/* ---------- actions ---------- */

.ob-actions { margin-top: 18px; }
/* Under Continue: one quiet, centred line (R12-2). */
.kyb-below-actions { margin-top: var(--space-14); text-align: center; }
.kyb-below-actions[hidden] { display: none; }
.kyb-below-actions .ob-legal { margin: 0; font-size: var(--text-13); color: var(--text-subtle); }
.kyb-below-actions .kyb-link { font-size: var(--text-13); }
/* The partners' documents, opened from the sentence (R12-1). */
.kyb-terms-partners { list-style: none; margin: 0 0 var(--space-14) 26px; padding: 0; display: flex; flex-direction: column; gap: var(--space-4); font-size: var(--text-13); }
.kyb-terms-partners[hidden] { display: none; }
.ob-actions[hidden] { display: none; }
/* Busy: the same grey as a disabled Continue, so a button that is working
   never looks pressable (R12-5). It keeps its words (the step's busy label
   replaces them after 300ms) and leads with a small ring, the one spinner
   Continue is allowed (R12-9): onboarding.css's ob-spin, sized to the label. */
.ob-continue.busy,
.ob-continue.busy:disabled {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  background: var(--surface-2);
  color: var(--text-faint);
  cursor: progress;
  pointer-events: none;
}
/* fasset-biz draws Back in --text-faint, under AA. */
.ob-back { color: var(--text-subtle); }
/* fasset-biz draws the footer faint at 70%, which is under AA. */
.ob-foot { color: var(--text-subtle); opacity: 1; }

.kyb-save-error {
  margin-top: var(--space-14);
  font-size: var(--text-13);
  color: var(--negative-text);
}

/* A failed draft save reads as the inline notice it is, beside Continue, and
   is absent otherwise (R12-9). */
.kyb-draft-status { margin-top: var(--space-12); margin-bottom: 0; color: var(--negative-text); }
.kyb-draft-status .kyb-link { margin-inline-start: var(--space-8); }

/* ---------- notices ---------- */

/* The DS .callout (app.css): a hairline note at --r-card, 12px subtle text,
   the lead line in --text-default. `.warn` is a warning tint with no border.
   Groups use this for any note inside a step instead of a tinted box. */
.kyb-notice {
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: var(--space-12) var(--space-14);
  margin-bottom: 22px;
  font-size: var(--text-12);
  color: var(--text-subtle);
  line-height: 1.55;
}
.kyb-notice[hidden] { display: none; }
.kyb-notice:not(.stack) p + p { margin-top: var(--space-4); }
.kyb-notice-h { color: var(--text-default); font-weight: var(--w-medium); }
/* A notice's heading is focused by script so a screen reader starts at it; it is
   not a control, so it draws no focus ring. */
.kyb-notice-h:focus { outline: none; }
/* The support reference (js/support-reference.js) wherever a failed state
   shows one: under the sentence, wrapping rather than forcing width, and
   present only when the service sent a reference. */
.support-reference {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-8);
  margin-top: var(--space-8);
  overflow-wrap: anywhere;
  color: var(--text-subtle);
  font-size: var(--text-13);
}
/* The contact inside a support block: the page's own face (the reference beside
   it may be mono, the contact is words) and its own line, so the block reads as
   contact, then reference, rather than a row. */
.support-reference .support-contact { flex: 0 0 100%; font-family: var(--font); }
.support-reference button { font-family: var(--font); }
.kyb-notice-ref[hidden] { display: none; }
/* A notice's actions: one row, the primary first, the same 8px the flow's
   other button rows use, 14px under the words above it. */
.avail-error { color: var(--negative-text); }
.avail-error[hidden] { display: none; }
.kyb-notice-ref .support-reference { margin-top: 0; font-size: inherit; }
.kyb-save-error .support-reference,
.kyb-docrow-feedback .support-reference { margin-top: var(--space-6); }
.kyb-notice.warn { background: var(--warning-tint); border-color: transparent; }
.kyb-notice:focus { outline: none; }
.kyb-notice:focus-visible { outline: 1.5px solid var(--focus-ring); outline-offset: 2px; }

/* ---------- the stage ---------- */

/* The camera. One transition on .pv-frame (onboarding.css), one recipe per
   name. The fasset-biz data-scene recipes are never triggered here. */
.ob[data-cam="far"] .pv-frame { transform: translateX(110px) translateY(10px) scale(0.94); filter: blur(9px) saturate(0.9); opacity: 0.55; }
.ob[data-cam="intro"] .pv-frame { transform: translateX(74px) translateY(6px) scale(0.96); filter: blur(0px); opacity: 0.92; }
.ob[data-cam="approach"] .pv-frame { transform: translateX(74px) scale(0.96); filter: blur(5px); opacity: 0.7; }
.ob[data-cam="near"] .pv-frame { transform: translateX(30px) scale(0.99); filter: blur(0px); opacity: 1; }
.ob[data-cam="resting"] .pv-frame { transform: none; filter: blur(0px); opacity: 1; }
/* pan-left shows the avatar's corner. fasset-biz pans 210px, which crops the
   chip; a slight pull back keeps both the chip (the company name) and the
   avatar on the stage. */
.ob[data-cam="pan-left"] .pv-frame { transform: translateX(-150px) scale(0.9); filter: blur(0px); opacity: 1; }
.ob[data-cam="aside"] .pv-frame { transform: translateX(40px) translateY(26px) scale(0.96); filter: blur(6px); opacity: 0; }
.ob[data-cam="hero"] .pv-frame { transform: translateX(-40px) scale(1.015); filter: blur(0px); opacity: 1; }
.ob[data-cam="sdk"] .pv-frame { transform: translateX(40px) scale(0.98); filter: blur(6px); opacity: 0; }
/* A step that does not list the frame layer takes it off the stage whatever
   the camera says. */
.ob[data-noframe] .pv-frame { opacity: 0; }
.ob[data-dim] .pv-frame { opacity: 0.55; }

/* lock, fact and orbit are shown by the stage rather than by a scene name */
.pv-lockwrap.on { opacity: 1; }
.pv-lock-label { color: var(--text-subtle); }
/* The lock does not pulse while it waits: the label says it is working and
   the ring stays still (onboarding.css loops ob-pulse forever). */
.pv-lock.busy .lk-ring { animation: none; }
.pv-fact.on { opacity: 1; transform: translateY(0); }
.pv-fact { z-index: 3; }
.pv-fact-k { color: var(--text-subtle); }
.pv-shapes.on { opacity: 1; transform: scale(1); }

.kyb-layers { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.pv-layer {
  position: absolute; inset: 0;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 500ms var(--ease-out-soft), transform 500ms var(--ease-out-soft);
  pointer-events: none;
}
.pv-layer.on { opacity: 1; transform: none; }
.ob-preview.kyb-live .pv-layer.on { pointer-events: auto; }

/* the greeting slot in the frame's main column */
.pvf-greet { min-height: 15px; display: flex; align-items: center; }
.pvf-greet-text { font-size: var(--text-16); color: var(--text-strong); letter-spacing: -0.01em; }
/* The frame's avatar is a monogram: 26px, so 11px type, --w-medium, subtle. */
.pvf-avatar-initials { font-size: 11px; font-weight: var(--w-medium); color: var(--text-subtle); letter-spacing: 0.02em; }
.pvf-avatar { position: relative; overflow: visible; }
.pvf-avatar-check {
  position: absolute; right: -5px; bottom: -5px;
  box-shadow: 0 0 0 1.5px var(--surface);
}

/* the people on the application, beside the chip in the frame */
.pvf-team { display: flex; align-items: center; margin-left: var(--space-6); }
.pvf-team:empty { display: none; }
/* Faces overlap with the neutral cut-out (app.css .seg), never a ring. */
.pvf-mate {
  position: relative;
  width: 24px; height: 24px;
  margin-left: -6px;
  font-size: 11px;
  box-shadow: 0 0 0 1.5px var(--surface);
}
.pvf-mate:first-child { margin-left: 0; }
/* Done carries no dot here: the next face covered it, and the roster already
   says who is done. One fewer green on review and done. */

/* On the people steps the roster sits on the product frame, in its main
   column, so the frame stays in view while the list builds. It starts where
   the main column's content starts (frame left 11%, 208px sidebar, 26px
   padding, plus the ~30px the approach camera moves the frame) and below
   the greeting, and the column's skeleton steps aside so nothing is printed
   over a bar. */
.ob[data-step="directors"] .kyb-rs,
.ob[data-step="invites"] .kyb-rs {
  left: calc(11% + 264px); top: calc(16% + 104px);
  width: min(330px, 52%);
  transform: none;
}
.ob[data-step="directors"][data-layers~="roster"] .pvf-main > :not(.pvf-greet),
.ob[data-step="invites"][data-layers~="roster"] .pvf-main > :not(.pvf-greet) { opacity: 0; }
.pvf-main > * { transition: opacity var(--dur-3) var(--ease); }

/* One caret at a time: while the company card echoes the typing, the frame
   chip's caret stays off, so the step has one green moment, not two. */
.ob[data-layers~="card"] .pvf-caret { display: none; }

/* The customers orbit holds still. onboarding.css spins the ring (and
   counter-spins each node) for 60s on the browser's linear curve, forever;
   the law has no ambient loops and no default easing. Nodes still arrive
   and leave on their own transitions. */
.ob .wk-ring,
.ob .node-spin { animation: none; }

/* the theme button sits outside the aside, where fasset-biz draws it */
.ob > .pv-theme { position: fixed; right: 18px; bottom: 16px; }

/* One pass of the skeleton's highlight (app.css .skeleton::after, same
   gradient, same --ease), run once by motion.shimmer() when a bar completes.
   Preview only, which is why it may run past the form column's 380ms. */
.kyb-shimmer { position: relative; overflow: hidden; }
.kyb-shimmer::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--bg) 60%, transparent), transparent);
  transform: translateX(-100%);
  pointer-events: none;
}
.kyb-shimmer.kyb-shimmer-go::after { animation: kyb-shimmer 700ms var(--ease) 1; }

/* ---------- small screens ---------- */

@media (max-width: 920px) {
  .ob > .pv-theme { display: none; }
  .ob { --ob-gutter: var(--space-32); --ob-pad-top: 64px; --ob-pad-bottom: 48px; }
  .ob-foot { position: static; padding: 0 var(--space-16) var(--space-16); }
}

/* ---------- reduced motion: opacity only ---------- */

@media (prefers-reduced-motion: reduce) {
  .ob-step > *,
  .ob-step.in > *,
  .ob-step.out > * { transform: none !important; }
  .pv-layer, .pv-layer.on { transform: none; transition: opacity var(--dur-3) var(--ease); }
  /* onboarding.css has `opacity 200ms ease` here, the browser's default curve. */
  .ob .pv-frame { transition: opacity var(--dur-3) var(--ease); }
  .pv-stage > .pv-lockwrap { transition: opacity var(--dur-3) var(--ease); }
  .pv-fact, .pv-fact.on { transform: none; }
  .pv-fact-body, .pv-fact-body.swap { transform: none; filter: none; }
  .pv-lock.busy .lk-ring { animation: none; }
  .ob-continue.busy::before { animation: none; }
  .kyb-skeleton::after { animation: none; }
  .ob-choice .oc-state::after, .kyb-check i svg, .ob-choice .kyb-oc-box svg { transition: none; }
}

/* ---------- done (the endings moved from the old page) ---------- */

.kyb-go {
  display: flex; align-items: center; justify-content: center;
  width: 100%;
  margin-top: var(--space-4);
  text-decoration: none;
}
.kyb-status { margin-top: var(--space-4); font-size: var(--text-14); color: var(--text-default); }
.kyb-status > .stack-actions { margin-top: var(--rhythm-body-actions); }
/* .kyb-hint carries its own top margin for hints under a control; in this row it is a flex item and that margin pushed the text below the button's centre line. */
.kyb-status ul { margin: var(--space-6) 0 0; padding-left: 18px; }
.kyb-due { margin-top: 18px; font-size: var(--text-14); }
/* The account-ending panel sits under the page heading at the heading gap, and
   its own words, reference and actions are a stack, so nothing in it carries a
   margin of its own. */
.kyb-outcome { margin-top: var(--rhythm-group); }
.kyb-outcome:focus, .kyb-approved:focus { outline: none; }
.kyb-outcome:focus-visible, .kyb-approved:focus-visible { outline: 1.5px solid var(--focus-ring); outline-offset: var(--space-8); border-radius: var(--r-control); }
.kyb-fix-list { margin: 0 0 var(--space-14); padding-left: 18px; font-size: var(--text-14); }

/* The lock lives beside the frame (onboarding.html), over the frame's own box,
   so the camera's blur on the frame never reaches it. What it covers should read
   as gone while a message sits on it ("Sent for review"): the page's own
   background at 96%, in either theme, not a see-through wash. */
:root { --lock-scrim: color-mix(in srgb, var(--bg) 96%, transparent); }
.pv-stage > .pv-lockwrap {
  inset: auto;
  top: 16%; left: 11%; right: 0;
  height: 62%; min-height: 420px;
  border-radius: var(--r-surface) 0 0 var(--r-surface);
  background: var(--lock-scrim);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transform-origin: 30% 40%;
  transition: opacity 400ms var(--ease-out-soft), transform 700ms var(--ease-out-soft);
  z-index: 3;
}
/* It follows the camera's position and scale, never its blur. */
.ob[data-cam="far"] .pv-stage > .pv-lockwrap { transform: translateX(110px) translateY(10px) scale(0.94); }
.ob[data-cam="intro"] .pv-stage > .pv-lockwrap { transform: translateX(74px) translateY(6px) scale(0.96); }
.ob[data-cam="approach"] .pv-stage > .pv-lockwrap { transform: translateX(74px) scale(0.96); }
.ob[data-cam="near"] .pv-stage > .pv-lockwrap { transform: translateX(30px) scale(0.99); }
.ob[data-cam="hero"] .pv-stage > .pv-lockwrap { transform: translateX(-40px) scale(1.015); }

/* ---------- law over reference ----------

   onboarding.css is fasset-biz's file byte for byte and is never edited here.
   These are the places it departs from DESIGN.md (DS-RULES.md section 9),
   corrected on top of it. */

/* Hovers: 120ms on --ease with the snap-in. The base rule carries the
   transition, so leaving fades; :hover drops it, so arriving is instant
   (app.css .btn). fasset-biz fades both ways over 160ms. */
.ob-back { transition: color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.ob-input { transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease); }
.ob-choice { transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.oc-ico { transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease); }
.ob-pill { transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.ob-btn-sm { transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.ob-btn-ghost,
.ob-resend button { transition: color var(--dur-1) var(--ease); }
.ob-radio { transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease); }
.ob-skip { transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.ob-continue { transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.pv-theme { transition: color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease); }
.ob-back:hover,
.ob-input:hover,
.ob-choice:hover,
.ob-pill:hover,
.ob-btn-sm:hover,
.ob-btn-ghost:hover,
.ob-resend button:hover,
.ob-radio:hover,
.ob-skip:hover,
.ob-continue:not(:disabled):hover,
.pv-theme:hover { transition: none; }

/* Press is a 1% settle everywhere (fasset-biz presses some to 0.97 and the
   theme button to 0.94). */
.ob-back:active,
.ob-pill:active,
.ob-btn-sm:active,
.pv-theme:active { transform: scale(0.99); }

/* No spinner on a busy card (fasset-biz .ob-choice.busy spins linearly). The
   Continue button is the one place a ring is drawn, see .ob-continue.busy. */
.ob-choice.busy .oc-state::before { animation: none; border-color: var(--border-strong); }

/* The lock tile sits on the 16px step, not fasset-biz's 22px. */
.pv-lock,
.pv-lock .lk-ring { border-radius: var(--r-surface); }

/* The fact card's line on the type scale, at the body weight (fasset-biz:
   17px at a literal 400). */
.pv-fact-body { font-size: var(--text-18); font-weight: var(--w-body); }

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

   One of each small component, mirroring the fasset-ds css/app.css specimen
   exactly, for every group to use. js/kyb/dom.js builds them: tag(),
   statusDot(), monogram(), okMark(), iconSvg(). Nothing here is rotated,
   capsule shaped, bordered in colour or glowing, and none of it is
   uppercase. A group that needs a variant adds a modifier to these in its own
   file; it does not draw a new chip. */

/* Metadata chip: role, zone, jurisdiction, rail label ("Director", "Owner",
   "Free zone", "Trades here too"). app.css .tag, one size everywhere: 22px
   tall, 6px corners. There is no small variant; app.css .doc-tag belongs to
   hosted documents, and two sizes on one card read as two different things.
   A `sm` class left in markup is inert. */
.kyb-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-6);
  font-size: var(--text-12);
  font-weight: var(--w-medium);
  line-height: 1.5;
  letter-spacing: 0;
  color: var(--text-subtle);
  border-radius: var(--r-control);
  padding: var(--space-2) var(--space-8);
  background: color-mix(in srgb, var(--text-subtle) 10%, transparent);
  white-space: nowrap;
}
.kyb-tag[hidden] { display: none; }

/* Status: a 6px dot carries the hue, the word stays neutral (app.css
   .status; named .kyb-state because .kyb-status is the done step's block).
   Only an error escalates the word. A live or working state is a
   status plus a skeleton, never a breathing dot. */
.kyb-state {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--text-12);
  color: var(--text-subtle);
  white-space: nowrap;
}
.kyb-state .dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--st-neutral);
  flex-shrink: 0;
}
.kyb-state-positive .dot { background: var(--st-positive); }
.kyb-state-warning .dot { background: var(--st-warning); }
.kyb-state-error .dot { background: var(--st-error); }
.kyb-state-error { color: var(--negative-text); }

/* A dot and a word with no container, for a rail or a secondary place.
   app.css .rail. */
.kyb-rail {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--text-12);
  font-weight: var(--w-body);
  letter-spacing: 0;
  color: var(--text-subtle);
  white-space: nowrap;
}
.kyb-rail i { width: 6px; height: 6px; border-radius: 50%; background: var(--text-faint); flex: none; }

/* Face: initials in a circle. app.css .monogram. Size it with width, height
   and font-size (monogram() does, 0.42 of the diameter on the scale). */
.kyb-monogram {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  font-size: var(--text-13);
  border-radius: 50%;
  background: var(--bg-selected);
  color: var(--text-subtle);
  font-weight: var(--w-medium);
  letter-spacing: 0.02em;
  flex-shrink: 0;
  user-select: none;
}

/* Done mark: an accent check in an accent-tint disc, fasset-biz's
   .ob-done-mark in miniature. The screen's one saturated moment. */
.kyb-ok {
  display: inline-grid;
  place-items: center;
  width: 16px; height: 16px;
  flex: none;
  border-radius: 50%;
  background: var(--accent-tint);
  color: var(--accent);
}
.kyb-ok.lg { width: 24px; height: 24px; }

/* Key and value: a founding year, a registration number, an amount.
   app.css .def-row. "Established 2000" is a def row or a subtle sub-line,
   never a stamp. */
.kyb-def { display: flex; flex-direction: column; }
.kyb-def-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-16);
  padding: 7px 0;
}
.kyb-def-label { color: var(--text-subtle); font-size: var(--text-12); flex-shrink: 0; }
.kyb-def-value {
  color: var(--text-default);
  font-size: var(--text-13);
  text-align: right;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  min-width: 0;
  overflow-wrap: anywhere;
}
.kyb-def-value.strong { color: var(--text-strong); font-weight: var(--w-medium); }

/* Waiting: the skeleton, the system's loading vocabulary for content (app.css
   .skeleton). Use it wherever a spinner, a breathing dot, bouncing dots or a
   scan line were drawn. The one exception is the Continue button's ring. */
.kyb-skeleton {
  position: relative;
  overflow: hidden;
  background: var(--surface-2);
  border-radius: var(--r-control);
}
.kyb-skeleton.bar { height: 8px; border-radius: var(--r-tag); }
.kyb-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--bg) 60%, transparent), transparent);
  animation: kyb-shimmer 1.4s var(--ease) infinite;
}

/* The first thing #kyb-steps ever shows, before boot's own draft/status/
   requirements reads (or a reload's resume read) return: a form shape rather
   than an empty area with only the greyed Continue for company, so the eye
   has something to read as "a form is coming" rather than "nothing loaded".
   Bars are sized off the real thing they stand in for (.ob-field's 12px
   label + 7px gap, .ob-input's ~38px height, .ob-continue's 38px/999px
   pill), so the swap to the real step doesn't jump. flow.js removes this
   element outright the moment a step actually renders. */
.kyb-loading-field { margin-bottom: var(--space-14); }
/* The bars are <i> elements, which are inline and take no width or height, so
   until they are blocks only the button below ever drew: one pill, not a form. */
.kyb-loading-field .bar { display: block; }
.kyb-loading-field .bar.label { width: 35%; height: 12px; margin-bottom: 7px; }
.kyb-loading-field .bar.field { width: 100%; height: 38px; border-radius: var(--r-control); }
.kyb-loading-button { display: block; width: 100%; height: 38px; border-radius: 999px; margin-top: var(--space-20); }

/* Icons drawn by iconSvg() inherit the text colour, like the DS set. */
.kyb-tag .icon,
.kyb-state .icon,
.kyb-rail .icon { flex: none; color: inherit; }

/* The human check's host: the width of the form's column under the anchor, hidden until a person has to act; or, with no anchor, pinned to the corner. */
.human-check { display: block; width: 100%; margin-top: var(--space-12); }
.human-check[hidden] { display: none; }
.human-check.human-check-floating { position: fixed; left: var(--space-16); bottom: var(--space-16); width: auto; margin-top: 0; z-index: 2147483000; }

/* A wrapper that takes no box of its own, so what a React component draws inside it lays out as if it were not there. (Not .panel-wrap: that is
   the open drawer's container, which closePanel and the tests look for.) */
.ds-contents { display: contents; }
