/* ============================================================
   styles.css — Global styles for KYC Dashboard
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&display=swap');
/* Inter / Inter Tight back the DriveWealth skin. Loaded for every brand because
   the skin is chosen at runtime, after the stylesheet has already been fetched. */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Inter+Tight:wght@500;600;700&display=swap');
/* Switzer backs the Genius Technologies skin. It is a Fontshare face, not a
   Google one, hence the separate origin. Loaded for every brand for the same
   reason as Inter: the skin is chosen at runtime, after CSS has been fetched. */
@import url('https://api.fontshare.com/v2/css?f[]=switzer@400,500,600,700&display=swap');

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

/* These names are unchanged so every existing var(--primary) etc. keeps
   working. They now resolve through brand.css, which is the one place to
   change the identity. The rendered values are identical to before. */
:root {
  --primary: var(--brand-product-accent);
  --primary-light: var(--brand-product-accent-tint);
  --primary-dark: var(--brand-product-accent-deep);
  /* Ink on the accent. White for every brand that has not said otherwise. */
  --on-primary: var(--brand-on-primary);
  --text: var(--brand-text);
  --text-secondary: var(--brand-text-secondary);
  --text-muted: var(--brand-text-muted);
  --border: var(--brand-line);
  --border-light: var(--brand-line-soft);
  --bg: var(--brand-surface-sunken);
  --white: var(--brand-surface);
  --success: var(--brand-success);
  --warning: var(--brand-warning);
  --danger: var(--brand-danger);
  --radius-sm: var(--brand-radius-sm);
  --radius: var(--brand-radius);
  --radius-lg: var(--brand-radius-lg);
  --radius-xl: var(--brand-radius-xl);
  --shadow-sm: var(--brand-shadow-sm);
  --shadow: var(--brand-shadow);
  --shadow-lg: var(--brand-shadow-lg);
}

html, body { width: 100%; height: 100%; overflow: hidden; font-family: var(--brand-font); background: var(--bg); color: var(--text); font-size: 14px; font-weight: 500;  line-height: 1.5; }

/* Headings take the display face. Reglio maps --brand-font-display back to
   --brand-font, so this is a no-op there and only separates the two for a brand
   that pairs different families. */
h1, h2, h3, h4, h5, h6 { font-family: var(--brand-font-display); }
button { font-family: inherit; color: inherit; cursor: pointer; border: none; background: none; }
input, select, textarea { font-family: inherit; font-size: 14px; }
a { text-decoration: none; color: inherit; }

/* ── Authentication ─────────────────────────────────────── */
.auth-shell {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--bg);
}
/* The only surface in this file that named Reglio's palette outright: two
   Reglio-blue/green washes over a near-white gradient, all hardcoded. Every
   tenant wore it, so a white-labelled applicant met their own mark on another
   company's colours — and a tenant whose brand is dark met a white card on the
   loading and completion screens.

   Tokenised rather than deleted: the wash is the shape of the design, the
   colours belong to the brand. The --brand-wash-* defaults in brand.css are
   the exact values that were hardcoded here, so every existing brand renders
   as it did; a workspace with its own wash (a dark one, say) sets them. */
.auth-shell-branded {
  background:
    radial-gradient(circle at 22% 18%, var(--brand-wash-shell-a), transparent 28%),
    radial-gradient(circle at 78% 76%, var(--brand-wash-shell-b), transparent 26%),
    linear-gradient(135deg, var(--brand-wash-shell-from), var(--brand-wash-shell-to));
}
.auth-panel {
  width: min(420px, 100%);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 34px;
  text-align: center;
}
.auth-panel-wide {
  width: min(520px, 100%);
  padding: 42px;
}
.auth-logo {
  height: 84px;
  width: auto;
  /* Wide wordmarks (DriveWealth) overflow the card at a fixed height; contain
     keeps the aspect ratio and the cap keeps it inside the panel. */
  max-width: 100%;
  object-fit: contain;
  margin-bottom: 10px;
}
.auth-logo-large {
  height: 104px;
  margin-bottom: 4px;
}
/* A workspace-supplied mark on the auth and hosted-completion panels.

   The fixed height above is drawn for the square Reglio artwork. Combined with
   object-fit:contain it letterboxes anything wider: a wordmark keeps its ratio
   but is laid inside an 84px-tall box, so it renders small with a band of empty
   space above and below it — on the completion screen, which is the last thing
   an applicant sees and often the only place they meet the brand at all.

   Releasing the height and capping it instead lets a wide mark size against the
   panel's width and a square one against the cap, so both fill the space they
   are given. Scoped to the attribute, so every registry brand keeps the fixed
   height it was tuned against. */
:root[data-tenant-logo] .auth-logo {
  height: auto;
  max-height: 84px;
}
:root[data-tenant-logo] .auth-logo-large {
  height: auto;
  max-height: 104px;
}
/* Holds a logo's place while a brand is still unknown.

   Occupies the same box .auth-logo does, so a real mark appears without
   shifting the panel under it. Deliberately unbranded: before /me answers, a
   TENANT mark shown here would be a guess about whose workspace this is, and
   the product's own mark would be a claim about it. pendingBrandLogo and
   hostedBrandMark both render this until the identity is resolved; it is also
   the shared source of the pulse below. */
.auth-logo-pending {
  height: 84px;
  width: 168px;
  max-width: 60%;
  margin-bottom: 10px;
  border-radius: 10px;
  background: var(--border);
  opacity: 0.3;
}

/* The auth panel's own shape while it waits, instead of a sentence.

   The card keeps the height it has when filled, so nothing jumps when the
   dashboard replaces it. role="presentation" on each line keeps them out of
   the accessibility tree — the panel itself carries the status label. */
.auth-skeleton-line {
  border-radius: 6px;
  background: var(--border);
  opacity: 0.3;
  margin-inline: auto;
}
.auth-skeleton-title {
  height: 20px;
  width: 180px;
  max-width: 70%;
  margin-bottom: 12px;
}
.auth-skeleton-subtitle {
  height: 13px;
  width: 260px;
  max-width: 88%;
  margin-bottom: 22px;
}

@media (prefers-reduced-motion: no-preference) {
  .auth-logo-pending,
  .auth-skeleton-line { animation: auth-logo-pulse 1.5s ease-in-out infinite; }
}
@keyframes auth-logo-pulse { 0%, 100% { opacity: 0.22; } 50% { opacity: 0.4; } }
.auth-title {
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 6px;
}
.auth-subtitle {
  font-size: 14px;
  color: var(--text-secondary);
  margin-bottom: 22px;
}
.auth-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}

/* ── Executive dashboard band ───────────────────────────── */
.reglio-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 420px);
  gap: 18px;
  margin-bottom: 22px;
  padding: 24px;
  /* Was a hardcoded Reglio-blue border over a blue/white/green wash. The
     greeting band is the first thing a tenant's staff see each morning, so it
     is the last place that should be another company's colours. Defaults are
     the old values (brand.css), so existing brands are unchanged. */
  border: 1px solid var(--brand-wash-hero-line);
  border-radius: var(--radius-lg);
  background:
    linear-gradient(135deg, var(--brand-wash-hero-from), var(--brand-wash-hero-mid) 46%, var(--brand-wash-hero-to));
  box-shadow: var(--shadow-sm);
}
.reglio-eyebrow {
  font-size: 11px;
  font-weight: 700;
  color: var(--primary);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 8px;
}
.reglio-hero-title {
  font-size: clamp(26px, 4vw, 38px);
  line-height: 1.05;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 9px;
}
.reglio-hero-copy {
  max-width: 680px;
  color: var(--text-secondary);
  font-size: 14px;
}
.reglio-hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 18px;
}
.reglio-hero-metrics {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
.reglio-hero-metric {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 14px 16px;
  background: var(--brand-wash-hero-card);
  border: 1px solid var(--brand-wash-hero-card-line);
  border-radius: var(--radius);
}
.reglio-hero-metric span {
  font-size: 12px;
  color: var(--text-secondary);
}
.reglio-hero-metric strong {
  font-size: 15px;
  color: var(--text);
}
.reglio-hero-metric strong.success { color: var(--success); }
.reglio-hero-metric strong.warning { color: var(--warning); }
.reglio-hero-metric strong.danger { color: var(--danger); }

/* ── Verification experience ────────────────────────────── */
.verification-shell {
  width: min(100%, 840px);
  margin-inline: auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.verification-intro {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  padding: 22px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--white);
  box-shadow: var(--shadow-sm);
}
.verification-intro h1 {
  font-size: clamp(28px, 4vw, 40px);
  line-height: 1.05;
  margin-bottom: 10px;
}
.verification-intro p {
  max-width: 720px;
  color: var(--text-secondary);
}
.verification-guidance {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}
.verification-guidance div {
  padding: 12px 14px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  background: var(--bg);
}
.verification-guidance strong,
.verification-guidance span {
  display: block;
}
.verification-guidance strong {
  font-size: 13px;
  margin-bottom: 2px;
}
.verification-guidance span {
  font-size: 12px;
  color: var(--text-secondary);
}
.verification-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
  align-items: start;
}
.verification-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.verification-widget-card {
  display: flex;
  width: 100%;
  min-width: 0;
  height: min(720px, calc(100vh - 220px));
  height: min(720px, calc(100dvh - 220px));
  min-height: 520px;
  max-height: 720px;
  padding: 14px;
  overflow: visible;
}
.verification-side {
  padding: 20px;
  position: static;
}

/* ── Document selection — shown in place of the capture card until answered ── */
.verification-capture-column {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
.doc-selection-card {
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}
.doc-selection-head h2 {
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 6px;
}
.doc-selection-head p {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-secondary);
  margin: 0;
  max-width: 60ch;
}
.doc-selection-field {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.doc-selection-field > label,
.doc-selection-legend {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.doc-selection-select {
  font: inherit;
  font-size: 14px;
  color: var(--text);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 11px 13px;
  width: 100%;
  max-width: 420px;
}
.doc-selection-select:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
.doc-selection-options {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 420px;
}
.doc-selection-option {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}
.doc-selection-option:hover {
  border-color: var(--primary);
}
.doc-selection-option input {
  margin: 2px 0 0;
  accent-color: var(--primary);
  flex-shrink: 0;
}
.doc-selection-option span {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.doc-selection-option strong {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.doc-selection-option em {
  font-style: normal;
  font-size: 12.5px;
  color: var(--text-muted);
}
.doc-selection-option:has(input:checked) {
  border-color: var(--primary);
  background: var(--primary-light);
}
.doc-selection-continue {
  align-self: flex-start;
  min-width: 180px;
}
.doc-selection-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 13px;
  color: var(--text-secondary);
}
.doc-selection-sep {
  color: var(--text-muted);
  margin-inline: 5px;
}
.doc-selection-change {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--primary);
  background: none;
  border: none;
  padding: 2px 4px;
  cursor: pointer;
}
.doc-selection-change:hover:not(:disabled) {
  text-decoration: underline;
}
.doc-selection-change:disabled {
  opacity: 0.45;
  cursor: default;
}

.verification-widget-card facephi-sdk-provider,
.verification-widget-card facephi-selphi-widget,
.verification-widget-card facephi-selphid-widget {
  display: block;
  width: 100%;
  max-width: 100%;
  min-width: 0;
}

.verification-widget-card facephi-sdk-provider {
  height: 100%;
  min-height: 0;
}

/* While a capture widget is mounted but has drawn nothing yet — the first
   mount, and the document → selfie switch while the phone changes cameras —
   the card showed as a blank white panel. A spinner fills that gap. It only
   appears while the widget is empty, so it never sits on top of anything the
   widget draws, including its "Initializing…" screen and permission prompts.
   The provider's own screens (its licence loader, error and finish views) live
   in its shadow root where :empty cannot see them; verification.js marks the
   provider with data-sdk-screen while one is up, and the spinner stands down. */
.verification-widget-card { position: relative; }
.verification-widget-card:has(facephi-sdk-provider:not([data-sdk-screen]) facephi-selphi-widget:empty, facephi-sdk-provider:not([data-sdk-screen]) facephi-selphid-widget:empty)::before,
.verification-widget-card:has(facephi-sdk-provider:not([data-sdk-screen]) facephi-selphi-widget:empty, facephi-sdk-provider:not([data-sdk-screen]) facephi-selphid-widget:empty)::after {
  position: absolute;
  left: 50%;
  pointer-events: none;
}
.verification-widget-card:has(facephi-sdk-provider:not([data-sdk-screen]) facephi-selphi-widget:empty, facephi-sdk-provider:not([data-sdk-screen]) facephi-selphid-widget:empty)::before {
  content: "";
  top: calc(50% - 34px);
  width: 32px;
  height: 32px;
  margin-left: -16px;
  border: 3px solid color-mix(in srgb, var(--primary) 18%, transparent);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: verification-spin 0.9s linear infinite;
}
.verification-widget-card:has(facephi-sdk-provider:not([data-sdk-screen]) facephi-selphi-widget:empty, facephi-sdk-provider:not([data-sdk-screen]) facephi-selphid-widget:empty)::after {
  content: attr(data-starting-label);
  top: calc(50% + 12px);
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text-secondary);
}
@keyframes verification-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .verification-widget-card:has(facephi-sdk-provider:not([data-sdk-screen]) facephi-selphi-widget:empty, facephi-sdk-provider:not([data-sdk-screen]) facephi-selphid-widget:empty)::before { animation-duration: 2.4s; }
}

.verification-widget-card facephi-selphi-widget,
.verification-widget-card facephi-selphid-widget {
  height: 100%;
  min-height: 0;
}

/* ── FacePhi capture surface, branded ──────────────────────────────
   SelphID and Selphi register with Stencil's *scoped* encapsulation, not shadow
   DOM, so their markup is in the light DOM and inherits from here. Setting the
   font on the host is enough to carry the brand face into their captions and
   buttons — deliberately no selectors against their internal class names, which
   are undocumented and would break on an SDK bump.

   facephi-sdk-provider and facephi-logo ARE shadow DOM. Nothing below reaches
   inside them; rebranding those needs a themed asset bundle from FacePhi. */
.verification-widget-card facephi-selphi-widget,
.verification-widget-card facephi-selphid-widget,
.handoff-provider-host facephi-selphi-widget,
.handoff-provider-host facephi-selphid-widget {
  font-family: var(--brand-font);
  color: var(--text);
}
.verification-side-title {
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 12px;
}
.verification-status {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--bg);
  margin-bottom: 16px;
}
.verification-status strong,
.verification-status span {
  display: block;
}
.verification-status strong {
  font-size: 13px;
  margin-bottom: 2px;
}
.verification-status span {
  font-size: 12px;
  color: var(--text-secondary);
}
.status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: 0 0 auto;
  margin-top: 4px;
  background: var(--text-muted);
}
/* flows.js uses .status-dot for a different thing — a labelled pill badge —
   and its rule, further down this file, gives the class 3px 10px of padding
   and a ::before bullet. Same specificity, later in the file, so it won: the
   verification status dot rendered as a stretched 30×16 oval with a second dot
   inside it. Scoped here so neither component has to be renamed. */
.verification-status .status-dot {
  display: block;
  padding: 0;
}
.verification-status .status-dot::before { content: none; }
/* The halo is derived from the dot's own colour rather than a fixed rgba, so it
   follows the active brand instead of staying Reglio blue/green/red. */
/* Ready is the applicant's cue to act: the brand colour, held still. Loading
   and processing use the same colour but pulse, so waiting and ready differ. */
.verification-status.ready .status-dot { background: var(--primary); box-shadow: 0 0 0 5px color-mix(in srgb, var(--primary) 12%, transparent); }
.verification-status.processing .status-dot { background: var(--primary); box-shadow: 0 0 0 5px color-mix(in srgb, var(--primary) 12%, transparent); }
.verification-status.success .status-dot { background: var(--success); box-shadow: 0 0 0 5px color-mix(in srgb, var(--success) 12%, transparent); }
.verification-status.error .status-dot { background: var(--danger); box-shadow: 0 0 0 5px color-mix(in srgb, var(--danger) 12%, transparent); }
.verification-steps {
  display: grid;
  gap: 8px;
}
.verification-step {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--text-secondary);
  font-size: 12px;
}
.verification-step::before {
  content: "";
  width: 18px;
  height: 18px;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--white);
}
.verification-step.done {
  color: var(--text);
  font-weight: 600;
}
.verification-step.done::before {
  border-color: var(--success);
  background: var(--success);
  box-shadow: inset 0 0 0 5px var(--white);
}

/* ── The terminal panel ─────────────────────────────────────
   Complete, rejected, review required, paused, submitting — every state the
   applicant's journey can end on renders into this one element, and it had no
   rule anywhere in any stylesheet.

   <strong> and <span> are inline by default, so the heading and the
   explanation ran together as one sentence, hard against the top-left corner
   of a flex card 460–720px tall, at inherited size. That is the screen an
   applicant is left on when verification fails. It is also the screen they are
   left on when it succeeds.

   Centred inside whatever card it inherits rather than given a size of its
   own, so it holds at every height the breakpoints below produce, in portrait
   and in landscape. Every value resolves through a token, so a tenant with a
   dark brand gets a dark panel with no rule of its own. */
.verification-result-panel {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  text-align: center;
  padding: 32px 24px;
  border-radius: var(--radius);
  background: var(--bg);
}
.verification-result-icon {
  width: 52px;
  height: 52px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: var(--text-muted);
  background: color-mix(in srgb, currentColor 12%, transparent);
}
.verification-result-panel.is-success .verification-result-icon { color: var(--success); }
.verification-result-panel.is-rejected .verification-result-icon { color: var(--danger); }
.verification-result-panel.is-review .verification-result-icon { color: var(--warning); }
.verification-result-panel > strong {
  display: block;
  font-family: var(--brand-font-display);
  /* Scales with the viewport rather than the card, so the heading stays the
     same size whether this panel is the whole screen or a column beside the
     checklist. */
  font-size: clamp(19px, 3.4vw, 23px);
  font-weight: 700;
  line-height: 1.25;
  color: var(--text);
}
/* The message, and only the message — the icon above is also a span, and a
   bare descendant selector here would out-specify its own rules and flatten
   it back to a block. */
.verification-result-panel > span:not(.verification-result-icon) {
  display: block;
  /* A measure, not a width: long explanations stay readable and short ones do
     not stretch to the card's full 720px. */
  max-width: 44ch;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-secondary);
}
.verification-result-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin-top: 6px;
}
/* Below the phone breakpoint the two controls stack and go full width, so
   neither becomes a narrow target beside the other. */
@media (max-width: 420px) {
  .verification-result-panel {
    padding: 24px 16px;
  }
  .verification-result-actions {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }
  .verification-result-actions .btn {
    width: 100%;
    justify-content: center;
  }
}

/* A loading status had no dot of its own, so it rendered in the same neutral
   grey as an idle one — the applicant could not tell waiting from ready. */
.verification-status.loading .status-dot {
  background: var(--primary);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--primary) 12%, transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .verification-status.loading .status-dot,
  .verification-status.processing .status-dot {
    animation: verification-pulse 1.4s ease-in-out infinite;
  }
}
@keyframes verification-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

@media (max-width: 900px) {
  .reglio-hero,
  .verification-intro,
  .verification-grid {
    grid-template-columns: 1fr;
  }
  .verification-side {
    position: static;
  }
}

@media (max-width: 720px) {
  .verification-guidance {
    grid-template-columns: 1fr;
  }

  .verification-widget-card {
    height: min(680px, calc(100vh - 180px - env(safe-area-inset-bottom, 0px)));
    height: min(680px, calc(100dvh - 180px - env(safe-area-inset-bottom, 0px)));
    min-height: 460px;
    padding: 12px;
  }
}

@media (max-height: 560px) and (orientation: landscape) {
  .verification-widget-card {
    height: min(520px, calc(100vh - 120px));
    height: min(520px, calc(100dvh - 120px));
    min-height: min(300px, calc(100vh - 120px));
    min-height: min(300px, calc(100dvh - 120px));
  }
}

@media (max-width: 560px) {
  .reglio-hero,
  .verification-intro,
  .verification-widget-card,
  .verification-side {
    padding: 16px;
  }
  .reglio-hero-actions .btn {
    width: 100%;
    justify-content: center;
  }
  .auth-actions .btn {
    width: 100%;
    justify-content: center;
  }
}

@media (max-width: 420px) {
  .verification-widget-card {
    height: min(620px, calc(100vh - 160px - env(safe-area-inset-bottom, 0px)));
    height: min(620px, calc(100dvh - 160px - env(safe-area-inset-bottom, 0px)));
    min-height: 440px;
    padding: 10px;
  }
}

/* ── Hosted capture on a phone ──────────────────────────────
   Measured at 390×844 before this block: the camera started 504px down the
   page, below an intro and three guidance cards; the status line that narrates
   every step ("Switching to the front camera…", "Ready for selfie") sat at
   ~1180px, under the camera, where nobody capturing would ever see it; and the
   selection screen's Continue was at ~1140px. Every step meant scrolling a page
   whose middle is a live camera view, which is exactly where a thumb-scroll is
   least reliable.

   On a phone the page is reordered around the camera instead: a compact
   intro, the status line directly above the camera, and a camera card sized
   so status + camera fill one screen. bringCaptureIntoView (verification.js)
   scrolls there on each step change. Desktop is untouched. */
@media (max-width: 720px) {
  .verification-shell { gap: 12px; }
  .verification-intro {
    gap: 12px;
    padding: 16px;
  }
  .verification-intro h1 {
    font-size: 24px;
    line-height: 1.15;
    margin-bottom: 4px;
  }
  .verification-intro p { font-size: 13px; }
  .verification-guidance { gap: 6px; }
  .verification-guidance div { padding: 9px 12px; }
  /* Its job is done once the camera is on; now it only pushes the camera down. */
  .verification-shell.is-capturing .verification-guidance { display: none; }

  .verification-grid { gap: 10px; }
  /* The sidebar becomes a status bar above the camera. Its title and the
     checklist repeat what the status line and the camera already show. */
  .verification-side {
    order: -1;
    padding: 0;
    border: 0;
    background: transparent;
    box-shadow: none;
  }
  .verification-side-title,
  .verification-steps { display: none; }
  .verification-status {
    margin-bottom: 0;
    padding: 10px 12px;
    background: var(--white);
  }
  /* Before the camera there is nothing to narrate; "SDK loading" above the
     document questions only reads as the page being stuck. */
  .verification-shell:not(.is-capturing) .verification-status { display: none; }
  .verification-capture-column { gap: 8px; }

  /* Status (60–80px: the camera-change message wraps to two lines) + the
     selection summary + gaps come to ~170px; the camera takes the rest of the
     screen, less a margin either side. Sized so status AND camera fit together
     on the selfie step too — that is the step whose status line matters most,
     and bringCaptureIntoView only keeps the status in view when both fit. dvh,
     so the browser's own bars are already subtracted. */
  .verification-widget-card {
    height: calc(100vh - 190px - env(safe-area-inset-bottom, 0px));
    height: calc(100dvh - 190px - env(safe-area-inset-bottom, 0px));
    min-height: 380px;
    max-height: 720px;
    padding: 8px;
  }

  /* The document questions are long enough to scroll; the button that ends
     them should not have to be found. Sticky inside its own card, so it rides
     the bottom of the screen while the questions are in view and settles at
     the card's end. */
  .doc-selection-card {
    padding: 18px 16px;
    gap: 18px;
  }
  .doc-selection-continue {
    position: sticky;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    z-index: 2;
    width: 100%;
    min-width: 0;
    justify-content: center;
    box-shadow: 0 0 0 10px var(--white), 0 6px 22px 8px color-mix(in srgb, var(--text) 16%, transparent);
  }
}

/* A phone on its side has no room for both; the camera wins, and
   bringCaptureIntoView lands on the camera alone. */
@media (max-width: 960px) and (max-height: 500px) and (orientation: landscape) {
  .verification-widget-card {
    height: calc(100vh - 16px);
    height: calc(100dvh - 16px);
    min-height: 260px;
  }
}

/* Already on a phone: offering to continue on a phone is noise, and on the
   hosted page it would put a QR code of this very page in front of the
   camera. Touch-only, so a narrow desktop window keeps the offer. */
@media (max-width: 720px) and (hover: none) and (pointer: coarse) {
  .verification-side .handoff-slot { display: none; }
}

/* ── Layout ─────────────────────────────────────────────── */
#app { display: flex; flex-direction: column; height: 100vh; height: 100dvh; }

.header {
  position: fixed; top: 0; left: 0; right: 0; height: 64px;
  background: var(--white); border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 32px; z-index: 1000;
}

.main-content {
  margin-top: 64px;
  height: calc(100vh - 64px);
  height: calc(100dvh - 64px);
  overflow-y: auto; overflow-x: hidden;
  /* The page is the only thing that scrolls here, so its momentum must not
     chain out to the document behind it. */
  overscroll-behavior-y: contain;
  padding: 36px 48px;
  padding-bottom: calc(36px + env(safe-area-inset-bottom, 0px));
}

/* ── Header ─────────────────────────────────────────────── */
.header-logo img { height: 80px; max-width: 300px; width: auto; object-fit: contain; }
/* DriveWealth is a wide wordmark. The shared height above is sized for the
   square Reglio mark, which leaves the DriveWealth box overflowing a 64px
   header and running into the nav. Brand-scoped so Reglio is unaffected. */
[data-brand="drivewealth"] .header-logo img { height: 34px; max-width: 200px; }
/* Genius Technologies stacks an icon beside two lines of text, so it is nearer
   16:9 than DriveWealth's long wordmark. It needs more height to stay legible
   and far less width to fit. */
[data-brand="geniustrader"] .header-logo img { height: 46px; max-width: 190px; }
/* A workspace-supplied logo has no hand-tuned rule of its own. It arrives from
   the database and may be a wide wordmark or a square mark, while the shared
   default above is sized for the square Reglio artwork — it forces an 80px
   height into a 64px header, which a wide logo then fills edge to edge.

   Capping both axes and forcing neither lets the artwork's own ratio decide
   which bound binds first, so nothing is stretched and nothing outgrows the
   header. Scoped to the attribute, so every registry brand keeps its own rule
   and a tenant that supplies no logo is untouched. */
:root[data-tenant-logo] .header-logo img {
  height: auto;
  max-height: 40px;
  max-width: 200px;
  width: auto;
  object-fit: contain;
}

.header-nav { display: flex; gap: 4px; }
.nav-btn {
  padding: 8px 16px; border-radius: var(--radius-sm); font-size: 14px;
  font-weight: 500; color: var(--text-secondary); transition: all 0.15s;
  border-bottom: 2px solid transparent;
}
.nav-btn:hover { color: var(--text); background: var(--bg); }
.nav-btn.active { color: var(--primary); border-bottom-color: var(--primary); border-radius: var(--radius-sm) var(--radius-sm) 0 0; }

.header-right { display: flex; align-items: center; gap: 12px; }

.workspace-switcher select {
  height: 36px;
  max-width: 180px;
  min-width: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
  padding: 0 28px 0 10px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.icon-btn {
  width: 36px; height: 36px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-secondary); transition: all 0.15s;
}
.icon-btn:hover { background: var(--bg); color: var(--text); }

.profile-btn {
  display: flex; align-items: center; gap: 10px; padding: 6px 12px;
  border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--bg); transition: all 0.15s;
}
.profile-btn:hover { border-color: #D1D5DB; }
.profile-avatar {
  width: 28px; height: 28px; border-radius: 50%; background: var(--primary);
  display: flex; align-items: center; justify-content: center;
  color: white; font-size: 11px; font-weight: 700;
}
.profile-name { font-size: 13px; font-weight: 500; }
.profile-org  { font-size: 11px; color: var(--text-secondary); }

.dropdown {
  position: absolute; top: calc(100% + 8px); right: 0;
  background: var(--white); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); min-width: 180px; z-index: 1001; overflow: hidden;
}
.dropdown-item {
  width: 100%; padding: 10px 16px; text-align: left; font-size: 13px;
  color: var(--text); display: block; transition: background 0.1s;
}
.dropdown-item:hover { background: var(--bg); }
.dropdown-item.active { font-weight: 600; background: var(--bg); }

/* ── Page headers ───────────────────────────────────────── */
.page-title { font-size: 25px; font-weight: 500; color: var(--text); margin-bottom: 4px; letter-spacing: -0.03em; }
.page-subtitle { font-size: 14px; color: var(--text-secondary); margin-bottom: 36px; }

/* ── Cards ──────────────────────────────────────────────── */
.card {
  background: var(--white); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.card-pad { padding: 24px; }
.card-pad-lg { padding: 32px; }

/* ── Badges ─────────────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 10px; border-radius: var(--radius-sm);
  font-size: 12px; font-weight: 600; white-space: nowrap;
}

/* ── Buttons ────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 18px; border-radius: var(--radius-sm);
  font-size: 13px; font-weight: 600; transition: all 0.15s; white-space: nowrap;
}
.btn-primary { background: var(--primary); color: var(--on-primary); }
.btn-primary:hover { background: var(--primary-dark); }
.btn-outline { background: transparent; color: var(--primary); border: 1px solid var(--primary); }
.btn-outline:hover { background: var(--primary-light); }
.btn-ghost  { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); }
.btn-ghost:hover  { background: var(--bg); }
.btn-success { background: var(--success); color: white; }
.btn-danger  { background: var(--danger); color: white; }

/* ── Form controls ──────────────────────────────────────── */
.input, .select-input {
  padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--white); color: var(--text); font-size: 13px; transition: border 0.15s;
}
.input:focus, .select-input:focus { border-color: var(--primary); }
.select-input {
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2394A3B8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 28px;
}

/* ── Tabs ───────────────────────────────────────────────── */
.tabs { display: flex; border-bottom: 2px solid var(--border-light); gap: 24px; margin-bottom: 28px; }
.tab-btn {
  padding: 12px 4px; font-size: 14px; font-weight: 500; color: var(--text-secondary);
  border-bottom: 2px solid transparent; margin-bottom: -2px; transition: all 0.15s;
}
.tab-btn:hover { color: var(--text); }
.tab-btn.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; }

/* ── Table ──────────────────────────────────────────────── */
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; white-space: nowrap; }
thead tr { background: var(--bg); border-bottom: 1px solid var(--border); }
th { padding: 14px 20px; font-size: 11px; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.06em; text-align: left; }
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--text); }
td { padding: 16px 20px; border-bottom: 1px solid var(--border-light); font-size: 13px; }
tr.clickable { cursor: pointer; transition: background 0.1s; }
tr.clickable:hover td { background: var(--bg); }

/* ── KPI cards row ──────────────────────────────────────── */
.kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 28px; }
.kpi-card { padding: 20px 24px; }
.kpi-label { font-size: 12px; color: var(--text-secondary); font-weight: 500; margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.04em; }
.kpi-value { font-size: 28px; font-weight: 700; color: var(--text); }

/* ── Filter bar ─────────────────────────────────────────── */
.filter-bar {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px;
  padding: 18px 22px; margin-bottom: 20px; justify-content: space-between;
}
.filter-group { display: flex; flex-direction: column; gap: 4px; }
.filter-label { font-size: 11px; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.04em; }
.filter-left  { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; }

/* ── Modal ──────────────────────────────────────────────── */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.4);
  display: flex; align-items: center; justify-content: center; z-index: 2000;
}
.modal {
  background: var(--white); border-radius: var(--radius-xl);
  padding: 36px; width: 680px; max-width: 90vw;
  max-height: 85vh;
  /* dvh, so a phone's collapsing URL bar cannot make the modal taller than the
     screen it is centred in. The vh line above stays as the fallback. */
  max-height: 85dvh;
  overflow-y: auto; box-shadow: 0 24px 80px rgba(0,0,0,0.2);
  /* Scrolling to the end of a modal must not start scrolling the page behind it. */
  overscroll-behavior: contain;
}
.modal-title { font-size: 20px; font-weight: 700; margin-bottom: 6px; }
.modal-subtitle { font-size: 13px; color: var(--text-secondary); margin-bottom: 26px; }
.modal-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.modal-card {
  padding: 18px 20px; background: var(--bg); border: 1.5px solid var(--border);
  border-radius: var(--radius); cursor: pointer; text-align: left; transition: all 0.15s;
}
.modal-card:hover { background: var(--primary-light); border-color: var(--primary); box-shadow: 0 4px 12px color-mix(in srgb, var(--primary) 12%, transparent); }
.modal-card-title { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
.modal-card-desc  { font-size: 12px; color: var(--text-secondary); line-height: 1.4; }

/* ── Session detail ─────────────────────────────────────── */
.detail-layout { display: grid; grid-template-columns: 360px 1fr; gap: 40px; align-items: start; }
.detail-sidebar { position: sticky; top: 0; }
.breadcrumb { display: flex; align-items: center; gap: 8px; font-size: 13px; margin-bottom: 28px; }
.breadcrumb-link { color: var(--text-secondary); cursor: pointer; }
.breadcrumb-link:hover { color: var(--text); }
.breadcrumb-sep { color: var(--text-muted); }
.breadcrumb-current { color: var(--primary); font-weight: 500; }
/* The explicit back control on session detail. Its arrow reuses
   .breadcrumb-arrow, so the RTL flip in Directional glyphs covers it too. */
.session-back-btn { display: inline-flex; align-items: center; gap: 8px; margin-bottom: 14px; padding: 6px 12px; font-size: 13px; }

/* Sessions: the stat strip above the filter bar.
   One low row rather than four cards — the table is what the page is for. */
.sessions-stat-strip {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 16px;
  padding: 4px; background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius);
}
.sessions-stat {
  flex: 1 1 140px; display: flex; flex-direction: column; gap: 2px;
  padding: 10px 14px; background: var(--white); border: 1px solid transparent;
  border-radius: calc(var(--radius) - 2px); text-align: start;
}
.sessions-stat-value { font-size: 20px; font-weight: 700; line-height: 1.1; color: var(--text); }
.sessions-stat-label {
  font-size: 11px; font-weight: 600; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em;
}
/* Only the pending-review stat is a control, so only it looks like one. */
.sessions-stat-action { cursor: pointer; font-family: inherit; transition: border-color 0.15s, box-shadow 0.15s; }
.sessions-stat-action:hover { border-color: var(--primary); }
.sessions-stat-action.is-active { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }
@media (max-width: 720px) {
  .sessions-stat { flex-basis: calc(50% - 8px); }
  .sessions-stat-value { font-size: 18px; }
}

/* Risk & Compliance: the capability strip and the setup state.
   A compliance screen has to look answerable even when the answer is none. */
.compliance-strip {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 8px;
  padding: 4px; background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius);
}
.compliance-stat {
  flex: 1 1 130px; display: flex; flex-direction: column; gap: 2px;
  padding: 10px 14px; background: var(--white);
  border-radius: calc(var(--radius) - 2px); text-align: start;
}
.compliance-stat-value { font-size: 20px; font-weight: 700; line-height: 1.1; color: var(--text); }
.compliance-stat-label {
  font-size: 11px; font-weight: 600; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.compliance-notice {
  display: flex; flex-direction: column; gap: 3px;
  padding: 12px 16px; margin-bottom: 18px;
  border-inline-start: 3px solid var(--warning);
  background: var(--bg); border-radius: var(--radius);
  font-size: 12.5px; color: var(--text-secondary); line-height: 1.55;
}
.compliance-notice strong { color: var(--text); font-size: 13px; font-weight: 600; }
.compliance-empty { padding: 32px 8px 26px; text-align: center; }
.compliance-empty-title { font-size: 15px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
.compliance-empty-body {
  font-size: 12.5px; color: var(--text-secondary); line-height: 1.6;
  max-width: 560px; margin: 0 auto;
}
.compliance-planned { border-top: 1px solid var(--border-light); padding-top: 18px; }
.compliance-planned-title {
  font-size: 11px; font-weight: 700; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 12px;
}
.compliance-planned-row {
  display: grid; grid-template-columns: minmax(150px, 220px) 1fr; gap: 16px;
  padding: 9px 0; border-bottom: 1px solid var(--border-light);
}
.compliance-planned-row:last-child { border-bottom: 0; }
.compliance-planned-name { font-size: 13px; font-weight: 600; color: var(--text); }
.compliance-planned-detail { font-size: 12.5px; color: var(--text-secondary); line-height: 1.55; }
@media (max-width: 720px) {
  .compliance-stat { flex-basis: calc(50% - 8px); }
  .compliance-planned-row { grid-template-columns: 1fr; gap: 2px; }
}

.session-avatar {
  width: 72px; height: 72px; border-radius: 50%; background: var(--primary);
  display: flex; align-items: center; justify-content: center;
  color: white; font-size: 24px; font-weight: 700; flex-shrink: 0;
}
.risk-circle {
  width: 160px; height: 160px; border-radius: 50%; border-width: 4px; border-style: solid;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  margin: 0 auto 16px;
}
.risk-circle-score { font-size: 42px; font-weight: 700; line-height: 1; }
.risk-circle-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; opacity: 0.7; margin-top: 4px; }

/* One heading treatment for the case page, which previously drew five
   different ones inline — 18px, 16px and three at 14px — and carried no heading
   element at all, so the page had no outline for a screen reader or a keyboard
   user to move through. */
.detail-case-title { margin-bottom: 2px; }
.detail-case-subtitle { font-size: 12px; color: var(--text-muted); margin-bottom: 18px; overflow-wrap: anywhere; }
.detail-section-title { font-size: 17px; font-weight: 700; color: var(--text); margin-bottom: 20px; }
.detail-subsection-title { font-size: 13px; font-weight: 700; color: var(--text); margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.05em; }

/* The review decision on the case page: the evidence and the buttons in one
   bordered box, so nothing a reviewer decides on is out of sight of the
   control that decides it. Logical properties throughout for RTL. */
.review-panel {
  padding: 18px 20px; margin-bottom: 26px;
  border: 1px solid var(--border); border-inline-start: 3px solid var(--warning);
  border-radius: var(--radius); background: var(--bg);
}
.review-panel .detail-section-title { margin-bottom: 6px; }
.review-panel-lead { font-size: 12.5px; color: var(--text-secondary); line-height: 1.55; margin: 0 0 14px; }
.review-panel-evidence { margin-bottom: 14px; }
.review-panel-images {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px; margin-bottom: 14px;
}
.review-panel-image { margin: 0; }
.review-panel-image-frame {
  aspect-ratio: 3 / 2; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: var(--white); border: 1px solid var(--border); border-radius: var(--radius);
}
.review-panel-image-frame img { width: 100%; height: 100%; object-fit: contain; }
.review-panel-image figcaption { font-size: 12px; color: var(--text-secondary); margin-top: 6px; }
.review-panel-muted { font-size: 12.5px; color: var(--text-muted); margin-bottom: 14px; }
.review-panel-notice {
  padding: 10px 14px; margin-bottom: 14px;
  border-inline-start: 3px solid var(--warning); border-radius: var(--radius);
  background: var(--white); font-size: 12.5px; color: var(--text-secondary); line-height: 1.55;
}
.review-panel-notice.is-danger { border-inline-start-color: var(--danger); color: var(--text); }
/* Louder than the advisory notices: this one changes what an approval means. */
.review-panel-liveness {
  border-inline-start-width: 4px; font-size: 13px;
  background: color-mix(in srgb, var(--danger) 8%, var(--white));
}
.review-panel-liveness strong { display: block; color: var(--danger); margin-bottom: 2px; }
.review-history { margin-top: 20px; }
.review-history-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.review-history-entry {
  padding: 10px 14px; border: 1px solid var(--border); border-radius: var(--radius);
  border-inline-start: 3px solid var(--text-muted); background: var(--white);
}
.review-history-entry.is-approve { border-inline-start-color: var(--success); }
.review-history-entry.is-reject { border-inline-start-color: var(--danger); }
.review-history-entry.is-legacy { border-inline-start-color: var(--warning); }
.review-history-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 12px; }
.review-history-kind { font-size: 13px; font-weight: 600; color: var(--text); }
.review-history-meta { font-size: 12px; color: var(--text-muted); }
.review-history-reason { font-size: 12.5px; color: var(--text-secondary); margin-top: 4px; }
.review-history-note { font-size: 13px; color: var(--text); margin-top: 6px; white-space: pre-wrap; overflow-wrap: anywhere; }
.image-access { margin-top: 20px; }
.image-access-list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--border); border-radius: var(--radius); }
.image-access-entry {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 12px;
  padding: 8px 14px; border-top: 1px solid var(--border);
}
.image-access-entry:first-child { border-top: 0; }
.image-access-line { font-size: 12.5px; color: var(--text); overflow-wrap: anywhere; }
.review-panel-label { display: block; font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: 6px; }
.review-panel-note { width: 100%; min-height: 88px; resize: vertical; margin-bottom: 12px; }
.review-panel-reason { width: 100%; max-width: 520px; margin-bottom: 14px; }
.review-panel-noted { color: var(--success); font-size: 12.5px; margin-bottom: 10px; }
.review-panel-hint { font-size: 12px; color: var(--text-muted); margin: 10px 0 0; }
.review-panel-error { color: var(--danger); font-size: 12.5px; margin-bottom: 10px; }
.review-panel-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.review-panel-outcome {
  padding: 12px 16px; margin-bottom: 20px;
  border-inline-start: 3px solid var(--success); border-radius: var(--radius);
  background: var(--bg); font-size: 13px; font-weight: 600; color: var(--text);
}

.detail-row { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 9px 0; border-bottom: 1px solid var(--border-light); font-size: 13px; }
.detail-row:last-child { border-bottom: none; }
.detail-key { color: var(--text-secondary); font-weight: 500; flex: 0 0 auto; }
/* text-align:end rather than right so the value follows the writing direction,
   and overflow-wrap because session ids and audit references are single
   unbreakable tokens — in a two-up grid on a tablet they pushed straight out of
   the card rather than wrapping inside it. */
.detail-val { font-weight: 500; text-align: end; min-width: 0; overflow-wrap: anywhere; }

/* Below this the key and value have too little room to share a line. */
@media (max-width: 860px) {
  .detail-row { flex-direction: column; align-items: stretch; gap: 2px; }
  .detail-val { text-align: start; }
}

.signal-card { padding: 16px 18px; border-radius: var(--radius); margin-bottom: 12px; }
.signal-card.high   { background: #FEE2E2; border: 1px solid #FCA5A5; }
.signal-card.medium { background: #FEF3C7; border: 1px solid #FCD34D; }
.signal-title { font-weight: 600; font-size: 13px; margin-bottom: 4px; }
.signal-desc  { font-size: 12px; opacity: 0.8; }

.audit-line { position: absolute; left: 18px; top: 20px; bottom: 0; width: 2px; background: var(--border); }
.audit-dot  { width: 36px; height: 36px; border-radius: 50%; background: var(--success); display: flex; align-items: center; justify-content: center; color: white; flex-shrink: 0; border: 3px solid white; box-shadow: 0 0 0 1px var(--border); position: relative; z-index: 1; }

/* ── Flow canvas ─────────────────────────────────────────── */
#flow-canvas {
  background: #EAECF0;
  overflow: hidden;
  cursor: grab;
}
#flow-canvas:active { cursor: grabbing; }
#canvas-inner {
  position: absolute;
  width: 8000px;
  height: 8000px;
  transform-origin: 0 0;
}
.flow-node {
  position: absolute; width: 300px; background: var(--white);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm); cursor: grab; user-select: none;
}
.flow-node:active { cursor: grabbing; }
.flow-node.selected { border: 2px solid var(--primary); }
.flow-node-header { padding: 16px 18px; border-bottom: 1px solid var(--border-light); display: flex; align-items: center; justify-content: space-between; }
.flow-node-body   { padding: 14px 18px; }
.flow-node-icon   { width: 30px; height: 30px; border-radius: 6px; background: var(--bg); display: flex; align-items: center; justify-content: center; font-size: 14px; }
.flow-node-name   { font-size: 14px; font-weight: 600; color: var(--text); }
.flow-node-port   { position: absolute; width: 10px; height: 10px; border-radius: 50%; background: var(--text); border: 2px solid white; box-shadow: 0 0 0 1px var(--border); }
.flow-node-port.left  { left: -5px; top: 50%; transform: translateY(-50%); }
.flow-node-port.right { right: -5px; top: 50%; transform: translateY(-50%); }

.node-field { margin-bottom: 12px; }
.node-field:last-child { margin-bottom: 0; }
.node-field-label { font-size: 10px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 3px; }
.node-field-value { font-size: 12px; color: var(--text-secondary); }

.canvas-add-btn {
  position: absolute; bottom: 24px; right: 24px; width: 48px; height: 48px;
  border-radius: 50%; background: var(--primary); color: var(--on-primary);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 40%, transparent); transition: all 0.15s; z-index: 10;
}
.canvas-add-btn:hover { transform: scale(1.08); box-shadow: 0 6px 18px color-mix(in srgb, var(--primary) 50%, transparent); }

.config-panel {
  position: fixed; right: 0; top: 64px; bottom: 0; width: 360px;
  background: var(--white); border-left: 1px solid var(--border);
  box-shadow: -4px 0 20px rgba(0,0,0,0.06); padding: 28px; overflow-y: auto; z-index: 100;
}
.config-panel-title { font-size: 17px; font-weight: 700; margin-bottom: 24px; display: flex; justify-content: space-between; align-items: center; }
.close-btn { width: 28px; height: 28px; border-radius: 50%; background: var(--bg); display: flex; align-items: center; justify-content: center; font-size: 16px; color: var(--text-secondary); transition: all 0.15s; }
.close-btn:hover { background: var(--border); }

.field-label { font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: 6px; display: block; }
.toggle-group { display: flex; gap: 8px; }
.toggle-btn { flex: 1; padding: 8px; border-radius: var(--radius-sm); font-size: 13px; font-weight: 500; border: 1px solid var(--border); background: var(--bg); color: var(--text-secondary); transition: all 0.15s; }
.toggle-btn.active { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }

/* ── Wizard ─────────────────────────────────────────────── */
.wizard-wrap { max-width: 520px; margin: 0 auto; padding: 40px 0; }
.wizard-dots { display: flex; justify-content: center; gap: 8px; margin-bottom: 40px; }
.wizard-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border); transition: background 0.2s; }
.wizard-dot.active { background: var(--primary); }
.wizard-title { font-size: 22px; font-weight: 700; margin-bottom: 6px; }
.wizard-subtitle { font-size: 13px; color: var(--text-secondary); margin-bottom: 28px; }
.wizard-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 24px; }
.wizard-option {
  padding: 18px; border: 2px solid var(--border); border-radius: var(--radius);
  cursor: pointer; text-align: center; transition: all 0.15s; background: transparent;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.wizard-option:hover { border-color: var(--primary); background: var(--primary-light); }
.wizard-option.selected { border-color: var(--primary); background: var(--primary-light); }
.wizard-option-emoji { font-size: 24px; }
.wizard-option-label { font-size: 13px; font-weight: 600; }
.wizard-option-full { width: 100%; text-align: left; padding: 18px 20px; }
.wizard-option-full-title { font-size: 14px; font-weight: 600; margin-bottom: 3px; }
.wizard-option-full-sub { font-size: 12px; color: var(--text-secondary); }
.chip {
  padding: 5px 12px; border-radius: 999px; font-size: 12px; font-weight: 500;
  border: 1px solid var(--border); background: transparent; cursor: pointer; transition: all 0.15s;
}
.chip.selected { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.chip:hover:not(.selected) { border-color: var(--primary); color: var(--primary); }

/* ── Flows list ─────────────────────────────────────────── */
.flow-card {
  display: flex; align-items: center; justify-content: space-between;
  padding: 22px 26px; cursor: pointer; transition: all 0.15s; position: relative;
}
.flow-card:hover { box-shadow: var(--shadow); }
.flow-card-arrow { color: #CBD5E1; transition: color 0.15s; font-size: 18px; }
.flow-card:hover .flow-card-arrow { color: var(--primary); }
.status-dot { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; }
.status-dot::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.status-active { color: #059669; background: #D1FAE5; }
.status-draft  { color: #6B7280; background: #F1F5F9; }

/* ── Notifications / System ─────────────────────────────── */
.notif-item { padding: 20px 26px; border-bottom: 1px solid var(--border-light); display: flex; align-items: flex-start; gap: 14px; }
.notif-item.unread { background: var(--bg); }
.notif-dot { width: 10px; height: 10px; border-radius: 50%; margin-top: 4px; flex-shrink: 0; }
.notif-unread-badge { width: 8px; height: 8px; border-radius: 50%; background: var(--primary); flex-shrink: 0; margin-top: 6px; }

.section-title { font-size: 25px; font-weight: 500; margin-bottom: 20px; letter-spacing: -0.03em; }
.api-key-row { display: flex; justify-content: space-between; align-items: center; padding: 16px; background: var(--bg); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 10px; }
.integration-card { padding: 20px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); display: flex; justify-content: space-between; align-items: center; }
.toggle-switch { width: 42px; height: 22px; border-radius: 11px; position: relative; cursor: pointer; transition: background 0.2s; }
.toggle-switch-knob { width: 18px; height: 18px; border-radius: 50%; background: white; position: absolute; top: 2px; transition: left 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.15); }

/* ── Table, stacked ─────────────────────────────────────────
   A wide table's mobile strategy here was a horizontal scrollbar: eleven
   columns at min-width 600px inside a 366px phone, with the leading column a
   36-character session id. What the reader actually needed — status and risk —
   sat off the right edge behind a scroll with nothing to indicate it existed.

   Below 720px each row becomes a labelled block instead. The header row is
   dropped and every cell carries its column name from data-label, so no column
   is lost; they are stacked rather than hidden. Add .table-stack to a
   .table-wrap and give each td a data-label. The cell marked data-primary is
   the row's heading and keeps its own emphasis without a label. */
@media (max-width: 720px) {
  .table-stack table { min-width: 0; white-space: normal; }
  .table-stack thead { display: none; }
  .table-stack tbody,
  .table-stack tr,
  .table-stack td { display: block; width: 100%; }
  .table-stack tr {
    padding: 14px 2px;
    border-bottom: 1px solid var(--border);
  }
  .table-stack tr:last-child { border-bottom: 0; }
  .table-stack td {
    border: 0;
    padding: 3px 0;
    font-size: 13px;
    display: grid;
    grid-template-columns: minmax(88px, 34%) minmax(0, 1fr);
    gap: 10px;
    align-items: center;
  }
  .table-stack td::before {
    content: attr(data-label);
    font-size: 10px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }
  /* The row's own heading: no label, full width, and it leads. */
  .table-stack td[data-primary] { display: block; padding-bottom: 6px; }
  .table-stack td[data-primary]::before { content: none; }
  /* An empty cell is noise once it has a label beside it. */
  .table-stack td[data-empty="1"] { display: none; }
  /* A badge is a chip, not a bar: it must not stretch across the value column. */
  .table-stack td .badge,
  .table-stack td .status-dot { justify-self: start; }
}

/* ── Responsive row list ────────────────────────────────────
   Shared by the AML results and the review queue. Set --data-cols on the
   wrapper; head and rows both read it, so they cannot drift apart. */
.data-list-head,
.data-list-row {
  display: grid;
  grid-template-columns: var(--data-cols, 1fr);
  gap: 16px;
}
.data-list-head {
  padding: 12px 0;
  border-bottom: 2px solid var(--border);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.data-list-row {
  padding: 14px 0;
  border-bottom: 1px solid var(--border-light);
  align-items: center;
}
.data-list-row:last-child { border-bottom: 0; }
.data-list-row.is-selected { background: var(--bg); }
.data-list-subject { font-weight: 600; font-size: 13px; margin-bottom: 2px; }
.data-list-meta { font-size: 11px; color: var(--text-muted); }
.data-list-meta + .data-list-meta { margin-top: 3px; }
.data-list-cell { font-size: 12px; color: var(--text-secondary); min-width: 0; }
/* Ids and references are single unbreakable tokens; without this they push the
   whole grid wider than its container instead of wrapping. */
.data-list-meta.force-ltr { overflow-wrap: anywhere; }

@media (max-width: 720px) {
  /* One column. The head is dropped and its labels reappear per cell, so a row
     reads as a small record rather than as a row with its headings off-screen. */
  .data-list-head { display: none; }
  .data-list-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
    padding: 16px 0;
    align-items: start;
  }
  .data-list-cell[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 10px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-bottom: 3px;
  }
  /* The action is the one cell that reads better full width at the end. */
  .data-list-row .data-list-action { justify-self: stretch; }
  .data-list-row .data-list-action .btn { width: 100%; justify-content: center; }
}

/* ── Focus ──────────────────────────────────────────────────
   One ring for everything that takes focus, from the tokens brand.css declared
   for it. :focus-visible, so pointer users never see it and keyboard users
   always do. Anything that needs a different ring overrides the token, not the
   rule — that is the whole point of having the token. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: var(--brand-focus-ring);
  outline-offset: var(--brand-focus-offset);
  border-radius: var(--radius-sm);
}
/* The ring is drawn outside the box, so a focused control at the edge of a
   clipping container would otherwise lose half of it. */
.table-wrap, .tabs, .filter-bar { scroll-padding: 4px; }

/* ── Utilities ──────────────────────────────────────────── */
.flex { display: flex; } .flex-col { flex-direction: column; }
.items-center { align-items: center; } .justify-between { justify-content: space-between; }
.gap-2 { gap: 8px; } .gap-3 { gap: 12px; } .gap-4 { gap: 16px; }
.mb-1 { margin-bottom: 4px; } .mb-2 { margin-bottom: 8px; } .mb-3 { margin-bottom: 16px; } .mb-4 { margin-bottom: 24px; }
.mt-2 { margin-top: 8px; } .mt-3 { margin-top: 16px; }
.text-sm { font-size: 12px; } .text-muted { color: var(--text-secondary); } .font-bold { font-weight: 700; } .font-semibold { font-weight: 600; }
.w-full { width: 100%; } .relative { position: relative; }
.hidden { display: none !important; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; }

/* ── Animations ─────────────────────────────────────────── */
/* fadeIn animation removed — Router.render() handles page transitions via opacity */

/* ── Responsive ─────────────────────────────────────────── */
html, body { overflow-x: hidden; }
* { min-width: 0; }

/* ── Mobile nav drawer ──────────────────────────────────── */
.mobile-nav-toggle { display: none; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: var(--radius-sm); }
.mobile-nav-toggle:hover { background: var(--bg); }

.mobile-nav-drawer {
  position: fixed; left: 0; right: 0; top: 64px; bottom: 0;
  background: var(--white); z-index: 999;
  padding: 8px 12px 32px;
  border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 2px;
  transform: translateY(-108%);
  transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1);
  overflow-y: auto;
  padding-bottom: calc(32px + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 8px 32px rgba(0,0,0,0.12);
}
.mobile-nav-drawer.open { transform: translateY(0); }
/* A closed drawer is off-screen but still in the DOM: without this it keeps its
   place in the tab order and a keyboard user falls into six invisible links. */
.mobile-nav-drawer:not(.open) { visibility: hidden; }
.mobile-nav-drawer { overscroll-behavior: contain; }
.mobile-nav-item {
  padding: 14px 16px; border-radius: var(--radius);
  font-size: 16px; font-weight: 500; color: var(--text-secondary);
  cursor: pointer; min-height: 52px; display: flex; align-items: center;
  transition: background 0.1s, color 0.1s; border-left: 3px solid transparent;
}
.mobile-nav-item:hover  { background: var(--bg); color: var(--text); }
.mobile-nav-item.active { background: var(--primary-light); color: var(--primary); font-weight: 600; border-left-color: var(--primary); }
.mobile-nav-item:active { opacity: 0.8; }

/* ── Dashboard grids ────────────────────────────────────── */
.dash-kpi-grid  { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; margin-bottom: 22px; align-items: start; }
.dash-main-grid { display: grid; grid-template-columns: clamp(220px,28%,280px) 1fr; gap: 18px; align-items: start; max-width: 1600px; }

/* ── Profile avatar-only (≤ 1083px) ────────────────────── */
@media (max-width: 1083px) {
  .profile-name, .profile-org { display: none; }
  .profile-btn { gap: 0; }
  .profile-btn > svg { display: none; }
}

/* ── Tablet (≤ 980px) ───────────────────────────────────── */
@media (max-width: 980px) {
  .header { padding: 0 20px; }
  .header-nav { display: none; }
  .mobile-nav-toggle { display: flex !important; }
  .main-content { padding: 28px 24px; }

  .dash-kpi-grid  { grid-template-columns: repeat(3, 1fr); }
  .dash-main-grid { grid-template-columns: 1fr; }

  .detail-layout { grid-template-columns: 1fr; gap: 24px; }
  .detail-sidebar { position: static; }
  .grid-2 { grid-template-columns: 1fr 1fr; }
  .grid-3 { grid-template-columns: 1fr 1fr; }
  .kpi-row { grid-template-columns: repeat(3, 1fr); }
}

/* ── Small tablet / large phone (≤ 768px) ───────────────── */
@media (max-width: 768px) {
  .main-content { padding: 20px 16px; }
}

/* ── Phone (≤ 475px) ────────────────────────────────────── */
@media (max-width: 475px) {
  .header { padding: 0 14px; height: 56px; gap: 8px; }
  /* The switcher is the only header control sized in absolutes, so it is the one
     that has to give. Measured at 320px: 180px of switcher in a 320px row pushed
     the profile button 58px past the edge, where overflow-x:hidden clipped it —
     Sign Out was unreachable rather than merely cramped. */
  .workspace-switcher { min-width: 0; flex: 0 1 auto; }
  .workspace-switcher select { max-width: min(116px, 32vw); font-size: 12px; padding: 0 22px 0 8px; }
  .header-right { gap: 6px; min-width: 0; }
  .header-logo img { height: 60px; }
  /* A 200px wordmark plus the language switch, bell, profile and hamburger does
     not fit a 360px phone: the row overflows and pushes the menu button off the
     edge. Cap the mark on phones so the controls stay reachable. */
  [data-brand="drivewealth"] .header-logo img { max-width: min(140px, 42vw); }
  [data-brand="geniustrader"] .header-logo img { height: 38px; max-width: min(130px, 40vw); }
  /* The header is 56px here, and the same controls still have to fit beside
     the mark, so both bounds tighten together. */
  /* min(…, 100%) as well as the fixed cap: .header-logo is allowed to shrink
     below, but an image capped only in pixels did not shrink with it, so a wide
     workspace wordmark ran under the workspace switcher on a 390px phone. */
  :root[data-tenant-logo] .header-logo img { max-height: 32px; max-width: min(150px, 40vw, 100%); }
  .header-logo { min-width: 0; }
  :root[data-tenant-logo] .header-logo { display: flex; align-items: center; flex: 0 1 auto; }
  .header-right { flex: none; }
  .main-content {
    margin-top: 56px;
    height: calc(100vh - 56px);
    height: calc(100dvh - 56px);
    padding: 14px 12px;
    padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  }
  .mobile-nav-drawer { top: 56px; }

  #org-dropdown-wrap { display: none; }
  .profile-name, .profile-org { display: none; }
  .profile-btn { padding: 4px 6px; gap: 0; border: none; background: none; box-shadow: none; }
  .profile-btn > svg { display: none; }

  .page-title  { font-size: 20px; }
  .page-subtitle { font-size: 13px; margin-bottom: 14px; }

  /* Dashboard grids */
  .dash-kpi-grid  { grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
  .dash-main-grid { grid-template-columns: 1fr; gap: 12px; }

  /* KPI cards */
  .dash-kpi-card  { padding: 14px 16px !important; }
  .dash-kpi-label { font-size: 10px !important; }
  .dash-kpi-value { font-size: 22px !important; }
  .dash-kpi-btn   { width: 100% !important; height: 32px !important; padding: 0 10px !important;
                    align-self: stretch !important; font-size: 11px !important; justify-content: center !important; }

  .card-pad   { padding: 14px; }
  .card-pad-lg { padding: 16px; }

  .kpi-row { grid-template-columns: 1fr 1fr; gap: 10px; }
  .kpi-value { font-size: 22px; }

  .grid-2 { grid-template-columns: 1fr; gap: 12px; }
  .grid-3 { grid-template-columns: 1fr; gap: 12px; }

  .tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; flex-wrap: nowrap; gap: 0; padding-bottom: 2px; }
  .tab-btn { white-space: nowrap; flex-shrink: 0; padding: 10px 14px; }

  .btn { padding: 8px 14px; font-size: 13px; }

  .filter-bar { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px; }
  .filter-left { flex-wrap: wrap; gap: 8px; }
  .filter-group { flex: 1 1 calc(50% - 4px); min-width: 0; }
  .select-input, .input { width: 100%; }

  .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  table { min-width: 600px; }
  th, td { padding: 10px 12px; }

  .modal { padding: 20px 16px; border-radius: var(--radius-lg); max-height: 92vh; }
  .modal-grid { grid-template-columns: 1fr; }

  .detail-layout { gap: 14px; }
  .breadcrumb { font-size: 12px; margin-bottom: 14px; }
  .session-avatar { width: 48px; height: 48px; font-size: 16px; }

  .wizard-wrap { padding: 20px 0; }

  .section-title { font-size: 20px; }
}

/* ── Small phone (≤ 390px) ──────────────────────────────── */
@media (max-width: 390px) {
  .workspace-switcher select { max-width: min(104px, 30vw); }
  .main-content { padding: 12px 10px; }
  .header { padding: 0 10px; }
  .page-title { font-size: 18px; }

  .dash-kpi-grid  { grid-template-columns: 1fr 1fr; gap: 8px; }
  .dash-kpi-value { font-size: 20px !important; }
  .dash-kpi-label { font-size: 9px !important; letter-spacing: 0.03em !important; }
  .dash-kpi-card  { padding: 12px !important; }

  .kpi-value { font-size: 20px; }
  .badge { font-size: 11px; padding: 3px 7px; }
  .btn { font-size: 12px; padding: 7px 12px; }
  .card-pad { padding: 12px; }
  .card-pad-lg { padding: 14px; }
  .section-title { font-size: 18px; }
}

/* ── Touch targets ──────────────────────────────────────────
   44px is the floor, applied to everything that is actually a control rather
   than to the handful that were remembered first. The list below is every
   interactive class in this stylesheet; a new one should be added to it.

   font-size:16px on fields is not a taste choice — iOS Safari zooms the page
   on focus for anything smaller, and never zooms back out. */
@media (hover: none) and (pointer: coarse) {
  .nav-btn, .tab-btn, .dropdown-item, .mobile-nav-item,
  .btn, .chip, .toggle-btn, .sessions-stat-action,
  .doc-selection-change, .breadcrumb-link, .session-back-btn { min-height: 44px; }
  .btn, .chip, .toggle-btn { display: inline-flex; align-items: center; justify-content: center; }
  .icon-btn, .close-btn { width: 44px; height: 44px; }
  /* The switch itself stays 42×22 — it is the label beside it that has to be
     grabbable, and the pair share one hit target. */
  .toggle-switch { flex: 0 0 auto; }
  /* Both header controls are sized in absolutes and neither was covered. */
  .workspace-switcher select { height: 44px; }
  .profile-btn { min-height: 44px; }
  .lang-switch-option { min-height: 38px; }

  /* .input and .select-input set 13px as a class, which outranks a bare element
     selector no matter which media query it sits in — so this rule has never
     reached the fields it was written for, and every filter and search box on a
     phone still triggered iOS Safari's zoom-on-focus. Matching the specificity
     is the whole fix. Safari zooms in and never zooms back out, which is why
     16px is a floor rather than a preference. */
  input, select, textarea,
  .input, .select-input, .doc-selection-select { font-size: 16px; min-height: 44px; }
  .lang-switch-option { min-height: 44px; }

  /* Controls whose size is written into a style attribute.

     The dashboard's chart navigation and the sessions pagination set their own
     width and height inline — 22px on the calendar's month arrows, 32px on a
     page number — and an inline style cannot be reached from a stylesheet any
     other way. Scoped to the four containers that hold them rather than applied
     to every button, so this cannot quietly resize something else later. The
     surrounding phone breakpoints in this file already use !important for the
     same reason. */
  .sessions-pagination button,
  .filter-mobile-wrap > button,
  .chart-mobile-header button,
  .chart-window-nav button,
  .chart-desktop-controls button {
    min-height: 44px !important;
    min-width: 44px !important;
  }
  /* Removes the ~300ms double-tap-to-zoom delay on every control. */
  a, button, .btn, .chip, [role="button"], [onclick] { touch-action: manipulation; }
}

/* ── Sessions filter — mobile icon vs desktop selects ───── */
.filter-mobile-wrap { display: none; }
/* Sessions filter */
@media (max-width: 521px) {
  .filter-desktop { display: none !important; }
  .filter-mobile-wrap { display: block; }
  .filter-bar { flex-wrap: nowrap; padding: 10px 12px; gap: 8px; }
}

/* ── Dashboard chart responsive ─────────────────────────── */
.chart-desktop-header { display:flex; justify-content:space-between; align-items:flex-start; gap:10px; flex-wrap:wrap; padding:18px 20px 0; }
.chart-desktop-controls { display:flex; align-items:center; gap:6px; flex-wrap:wrap; justify-content:flex-end; }
.chart-mobile-header { display:none; }
.chart-series-label { display:inline; }
.chart-mobile-row { display:flex; align-items:center; gap:8px; width:100%; }

@media (max-width: 520px) {
  .chart-desktop-header { display:none !important; }
  .chart-mobile-header  { display:flex !important; flex-direction:column; gap:10px; padding:14px 14px 0; }
  .chart-series-label   { display:none !important; }
  .chart-pad { padding: 8px 0 8px !important; }
}

/* ── Very narrow (≤ 240px) ──────────────────────────────── */
@media (max-width: 240px) {
  /* Hide bell and profile from header — they appear in the drawer */
  .header-bell-wrap, #profile-dropdown-wrap { display: none !important; }
  .header-logo img { height: 48px; max-width: 120px; }
  [data-brand="drivewealth"] .header-logo img { max-width: 120px; }
  [data-brand="geniustrader"] .header-logo img { height: 34px; max-width: 110px; }
  :root[data-tenant-logo] .header-logo img { max-height: 28px; max-width: 110px; }
  .header { padding: 0 10px; }
  /* Show the bell + profile rows inside the mobile drawer */
  .drawer-narrow-only { display: block !important; }
}

/* ── Sessions mobile padding + title spacing ─────────────── */
@media (max-width: 630px) {
  .sessions-page-title { padding-top: 15px !important; }
}

/* ── Dark mode: high risk card — left border only, no full surround ── */
[data-theme="dark"] .dash-kpi-grid .card[style*="7F1D1D"] {
  border-color: var(--border) !important;
  border-left-color: #F87171 !important;
  color: #F87171;
}
[data-theme="dark"] .dash-kpi-grid .card[style*="7F1D1D"] .dash-kpi-value {
  color: #F87171 !important;
}



/* ── Chart max height on large screens ───────────────────── */
#dash-chart-svg { max-height: 340px; }
.dash-kpi-grid  { max-width: 1600px; }

/* ============================================================
   Arabic / RTL
   ------------------------------------------------------------
   i18n.js puts lang="ar" dir="rtl" class="lang-ar" on <html>.
   Flex/grid layouts mirror on their own; the rules below only
   fix the places where the stylesheet names a physical side.
   ============================================================ */

/* IBM Plex Sans Arabic carries the Arabic glyphs DM Sans lacks.
   DM Sans stays in the stack so Latin runs (IDs, emails, codes)
   keep the same shapes they have in English. */
html.lang-ar body {
  font-family: var(--brand-font-arabic);
  line-height: 1.6;
}
/* Arabic ascenders/descenders need a little more room than Latin. */
html.lang-ar .kpi-value,
html.lang-ar .dash-kpi-value,
html.lang-ar .page-title,
html.lang-ar .section-title,
html.lang-ar .modal-title,
html.lang-ar .auth-title { line-height: 1.35; }

/* Session IDs, emails, API keys and other machine values stay LTR
   even inside an RTL paragraph. */
[dir="rtl"] .force-ltr {
  direction: ltr;
  unicode-bidi: isolate;
  text-align: right;
}

/* ── Form controls ──────────────────────────────────────── */
[dir="rtl"] .workspace-switcher select { padding: 0 10px 0 28px; }
[dir="rtl"] .select-input {
  background-position: left 10px center;
  padding-left: 28px;
  padding-right: 12px;
}

/* ── Menus, tables, detail rows ─────────────────────────── */
[dir="rtl"] .dropdown { right: auto; left: 0; }
[dir="rtl"] .dropdown-item { text-align: right; }
[dir="rtl"] th { text-align: right; }
[dir="rtl"] .modal-card,
[dir="rtl"] .wizard-option-full { text-align: right; }

/* ── Session detail audit rail ──────────────────────────── */
[dir="rtl"] .audit-line { left: auto; right: 18px; }

/* ── Mobile drawer active marker ────────────────────────── */
[dir="rtl"] .mobile-nav-item {
  border-left: 3px solid transparent;
  border-right: 3px solid transparent;
}
[dir="rtl"] .mobile-nav-item.active {
  border-left-color: transparent;
  border-right-color: var(--primary);
}

/* ── Flow config panel docks to the other edge ──────────── */
[dir="rtl"] .config-panel {
  right: auto;
  left: 0;
  border-left: none;
  border-right: 1px solid var(--border);
  box-shadow: 4px 0 20px rgba(0,0,0,0.06);
}

/* ── Flow canvas stays LTR ───────────────────────────────
   Node coordinates, connector paths and drag math are all
   absolute pixels measured from the left edge. Mirroring the
   canvas would desync the wiring, so it keeps its own
   direction; Arabic labels inside nodes still shape RTL. */
[dir="rtl"] #flow-canvas,
[dir="rtl"] #canvas-inner { direction: ltr; }
[dir="rtl"] #canvas-inner .flow-node-name,
[dir="rtl"] #canvas-inner .node-field-label,
[dir="rtl"] #canvas-inner .node-field-value { direction: rtl; text-align: right; }

/* ── Directional glyphs ─────────────────────────────────── */
[dir="rtl"] .flow-card-arrow,
[dir="rtl"] .breadcrumb-arrow { transform: scaleX(-1); }
[dir="rtl"] .rtl-flip { transform: scaleX(-1); }

/* ── Language toggle (header) ───────────────────────────── */
.lang-switch {
  display: inline-flex;
  align-items: center;
  position: relative;
  padding: 3px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg);
  isolation: isolate;
}
.lang-switch-thumb {
  position: absolute;
  top: 3px;
  bottom: 3px;
  inset-inline-start: 3px;
  width: calc(50% - 3px);
  border-radius: 999px;
  background: var(--primary);
  transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1);
  z-index: -1;
}
/* Default thumb position covers EN (the first option). */
.lang-switch[data-lang="ar"] .lang-switch-thumb { transform: translateX(100%); }
[dir="rtl"] .lang-switch[data-lang="ar"] .lang-switch-thumb { transform: translateX(-100%); }
.lang-switch-option {
  min-width: 34px;
  padding: 4px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
  background: none;
  border: none;
  cursor: pointer;
  transition: color 0.22s ease;
  /* Latin labels either way, so they never reorder with the page. */
  direction: ltr;
}
.lang-switch-option.active { color: #fff; }
.lang-switch-option:not(.active):hover { color: var(--text); }

@media (max-width: 475px) {
  .lang-switch-option { min-width: 30px; padding: 4px 6px; font-size: 10px; }
}

/* Below 340px the pill and the workspace switcher cannot share the row, and
   flex resolves that by shrinking the brand mark to nothing. The language is
   reachable from Settings; a header with no mark and no workspace name is not
   recoverable, so the pill is what gives. Declared after the base rule above
   because a media query adds no specificity and that rule would otherwise win. */
@media (max-width: 340px) {
  .lang-switch { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .lang-switch-thumb { transition: none; }
}

/* ── Phone handoff ──────────────────────────────────────── */
/* Desktop: the "continue on phone" offer inside the verification sidebar. */
/* The slot sits at the top of the sidebar, above the status, so the divider
   goes underneath it — a rule above the first element would look detached. */
.handoff-slot:not(:empty) {
  margin-block-end: 16px;
  padding-block-end: 16px;
  border-block-end: 1px solid var(--border);
}
/* Accented rather than ghost: this is an offer people should notice, not a
   footnote. Reads as an outlined primary so it does not compete with the
   solid primary CTA above it in the sidebar. */
.handoff-trigger {
  width: 100%;
  justify-content: center;
  gap: 8px;
  padding-block: 11px;
  font-weight: 600;
  color: var(--primary-dark);
  background: var(--primary-light);
  border: 1px solid var(--primary);
}
.handoff-trigger:hover {
  background: var(--primary);
  color: #fff;
}
.handoff-trigger svg { width: 16px; height: 16px; flex: none; }
.handoff-panel {
  display: grid;
  gap: 10px;
  justify-items: center;
  text-align: center;
}
.handoff-panel-title {
  font-weight: 700;
  font-size: 13px;
}
.handoff-qr {
  width: min(190px, 100%);
  padding: 10px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.handoff-qr .qr-svg {
  display: block;
  width: 100%;
  height: auto;
}
.handoff-qr-error,
.handoff-hint {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.45;
}
.handoff-expiry {
  font-size: 11px;
  color: var(--text-muted);
}
.handoff-error { color: var(--danger); }
.handoff-success { color: var(--success); }
.handoff-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}
.handoff-waiting {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-secondary);
}
.handoff-spinner {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  border: 2px solid var(--border);
  border-block-start-color: var(--primary);
  border-radius: 50%;
  animation: handoff-spin 0.9s linear infinite;
}
@keyframes handoff-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .handoff-spinner { animation: none; }
}

/* In-button spinner for an action that has been sent and not yet answered.

   Shares one rotation definition with .handoff-spinner rather than declaring a
   second identical keyframe, but draws itself from currentColor instead of the
   --border/--primary pair: this sits on a filled button, where those two tokens
   are the button's own background and would be invisible. currentColor also
   means it follows whatever brand is active without knowing which. */
.btn-spinner {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  display: inline-block;
  border: 2px solid currentColor;
  border-block-start-color: transparent;
  border-radius: 50%;
  opacity: 0.9;
  animation: handoff-spin 0.9s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .btn-spinner { animation: none; }
}

/* Phone: the standalone capture page. No header, no navigation. */
.handoff-mobile {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: var(--bg);
}
.handoff-mobile-card {
  width: min(460px, 100%);
  display: grid;
  gap: 14px;
  justify-items: center;
  text-align: center;
  padding: 26px 22px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.handoff-mobile-logo { height: 30px; width: auto; }
[data-brand="drivewealth"] .handoff-mobile-logo {
  height: 34px;
  max-width: min(220px, 80vw);
  object-fit: contain;
}
/* The phone handoff card is white, so this resolves to the transparent
   dark-ink mark, never the inverted one. */
[data-brand="geniustrader"] .handoff-mobile-logo {
  height: 52px;
  max-width: min(200px, 70vw);
  object-fit: contain;
}
/* The same treatment :root[data-tenant-logo] already gives the header mark,
   for the same reason: a workspace logo arrives from the database with no
   hand-tuned rule and may be a wide wordmark or a square badge, while the
   30px base above is sized for neither. Capping both axes and forcing neither
   lets the artwork's own ratio pick the bound that binds. The registry brands
   keep their own rules untouched. */
:root[data-tenant-logo] .handoff-mobile-logo {
  height: auto;
  width: auto;
  max-height: 52px;
  max-width: min(220px, 70vw);
  object-fit: contain;
}
.handoff-mobile-title { font-size: 19px; font-weight: 700; }
.handoff-mobile-text {
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.5;
}
.handoff-mobile-note { font-size: 11px; color: var(--text-muted); }
.handoff-mobile-cta { width: 100%; }
.handoff-mobile-status {
  display: grid;
  gap: 4px;
  width: 100%;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
}
.handoff-mobile-status strong { font-size: 14px; }
.handoff-mobile-status span { font-size: 12px; color: var(--text-secondary); }
.handoff-mobile-status { justify-items: center; }

/* The outcome mark: one icon per state, coloured from the brand's own
   success/danger/warning tokens. */
.handoff-mobile-icon {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  margin-block-end: 4px;
  color: var(--text-muted);
  background: color-mix(in srgb, currentColor 12%, transparent);
}
.handoff-mobile-icon.is-success { color: var(--success); }
.handoff-mobile-icon.is-error { color: var(--danger); }
.handoff-mobile-icon.is-review { color: var(--warning); }

/* What the intro is about to ask for, in order. */
.handoff-mobile-steps {
  width: 100%;
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: handoff-step;
  text-align: start;
}
.handoff-mobile-steps li {
  counter-increment: handoff-step;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  column-gap: 12px;
  align-items: start;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
}
.handoff-mobile-steps li::before {
  content: counter(handoff-step);
  grid-row: span 2;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 13px;
  font-weight: 700;
  color: var(--on-primary);
  background: var(--primary);
}
.handoff-mobile-steps strong { font-size: 13px; color: var(--text); }
.handoff-mobile-steps span { font-size: 12px; line-height: 1.45; color: var(--text-secondary); }

/* Waiting on the network: a spinner, so a pause does not read as a stop. */
.handoff-mobile-busy {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: 14px;
  color: var(--text);
}
.handoff-mobile-busy .handoff-spinner {
  width: 18px;
  height: 18px;
}

/* Where the applicant is: "Step 1 of 2" plus a two-segment meter. */
.handoff-mobile-progress {
  width: 100%;
  display: grid;
  gap: 6px;
}
.handoff-mobile-step {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}
.handoff-mobile-meter {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}
.handoff-mobile-meter span {
  height: 4px;
  border-radius: 999px;
  background: var(--border);
}
.handoff-mobile-meter span.is-done { background: var(--primary); }

/* The capture widget needs a real box to render into on a phone screen. */
.handoff-provider-host {
  width: 100%;
  height: min(560px, 68vh);
  display: flex;
}

/* Capture: the camera gets the screen.

   The card stops being a centred box and stretches to the viewport; the logo
   shrinks and the title gives way to the step line, and the camera takes every
   pixel that is left. The header above it plus the card and page padding come
   to ~140px (logo, step line, meter, hint), so the camera is 100dvh minus
   that — nothing to scroll (measured: page height = viewport at 844 and 664), and
   the SDK's own controls at the bottom of its view stay on screen. dvh rather
   than vh so the browser's toolbars are already accounted for. */
.handoff-mobile.is-capturing {
  align-items: stretch;
  padding: 8px 8px calc(8px + env(safe-area-inset-bottom, 0px));
}
.handoff-mobile.is-capturing .handoff-mobile-card {
  width: min(560px, 100%);
  align-content: start;
  gap: 8px;
  padding: 12px;
}
.handoff-mobile.is-capturing .handoff-mobile-logo {
  height: 22px;
  max-height: 22px;
}
.handoff-mobile.is-capturing .handoff-mobile-text { font-size: 13px; }
.handoff-mobile.is-capturing .handoff-provider-host {
  height: calc(100vh - 140px - env(safe-area-inset-bottom, 0px));
  height: calc(100dvh - 140px - env(safe-area-inset-bottom, 0px));
  min-height: 320px;
}
/* On its side there is no room for a header at all. */
@media (max-height: 500px) and (orientation: landscape) {
  .handoff-mobile.is-capturing .handoff-mobile-logo,
  .handoff-mobile.is-capturing .handoff-mobile-meter,
  .handoff-mobile.is-capturing .handoff-mobile-text { display: none; }
  .handoff-mobile.is-capturing .handoff-provider-host {
    height: calc(100vh - 56px);
    height: calc(100dvh - 56px);
    min-height: 220px;
  }
}
.handoff-provider-host facephi-sdk-provider,
.handoff-provider-host facephi-selphi-widget,
.handoff-provider-host facephi-selphid-widget {
  display: block;
  width: 100%;
  max-width: 100%;
  height: 100%;
}

/* ── Settings: one-time secrets and notices ───────────────────
   Built from theme tokens rather than a fixed amber palette, so they read on
   light and dark workspaces alike. The warning colour marks them; the text
   stays in the page's own ink so it keeps its contrast. */
.secret-reveal {
  margin-bottom: 22px;
  padding: 16px 18px;
  border: 1px solid color-mix(in srgb, var(--warning) 45%, transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--warning) 9%, var(--white));
}
.secret-reveal-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text);
}
.secret-reveal-title svg { flex: 0 0 auto; color: var(--warning); }
.secret-reveal-note {
  margin-bottom: 12px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--text-secondary);
}
.secret-reveal-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.secret-reveal-value {
  flex: 1;
  min-width: 0;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  user-select: all;
}
.secret-reveal-row .btn { flex: 0 0 auto; gap: 6px; font-size: 12.5px; }
.secret-reveal-meta { margin-top: 10px; font-size: 12px; color: var(--text-muted); }
@media (max-width: 560px) {
  .secret-reveal-row { flex-wrap: wrap; }
  .secret-reveal-value { flex-basis: 100%; }
  .secret-reveal-row .btn { flex: 1 1 0; }
}

.settings-callout {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 14px;
  border: 1px solid color-mix(in srgb, var(--warning) 35%, transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--warning) 7%, var(--white));
  font-size: 12px;
  line-height: 1.6;
  color: var(--text-secondary);
}
.settings-callout svg { flex: 0 0 auto; margin-top: 2px; color: var(--warning); }
