/* ---------------------------------------------------------------------------
   eKYC prototype UI.
   Palette is token-driven and defined three times on purpose: bare :root is the
   complete light set, the media query covers viewers on "system", and the
   [data-theme] block lets an explicit choice win. Never declare a colour only
   inside one of those blocks.
   --------------------------------------------------------------------------- */

:root {
  --bg:          #eef2f4;
  --surface:     #ffffff;
  --sunken:      #f4f7f8;
  --line:        #dde5e9;
  --line-soft:   #e9eff2;

  --ink:         #0f171c;
  --ink-2:       #44535e;
  --muted:       #78878f;

  --brand:       #0d8f85;
  --brand-ink:   #0a6f68;
  --brand-soft:  #e3f2f0;

  --ok:          #0d8f85;
  --warn:        #a8620a;
  --warn-soft:   #fbf0e2;
  --danger:      #b3253f;
  --danger-soft: #fbeaee;

  --stage:       #c9dedd;   /* camera backdrop */
  --stage-ink:   #0f171c;
  --guide:       rgba(255,255,255,.92);

  --shadow-sm:   0 1px 2px rgba(15,32,39,.06);
  --shadow-lg:   0 2px 4px rgba(15,32,39,.05), 0 18px 40px -20px rgba(15,32,39,.28);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:          #0a1014;
    --surface:     #121a1f;
    --sunken:      #18232a;
    --line:        #24323a;
    --line-soft:   #1d2930;

    --ink:         #e6eef2;
    --ink-2:       #adbcc5;
    --muted:       #7e8f99;

    --brand:       #2fc7b8;
    --brand-ink:   #5ad9cb;
    --brand-soft:  #102b2a;

    --ok:          #2fc7b8;
    --warn:        #e0a352;
    --warn-soft:   #2a2114;
    --danger:      #f27a90;
    --danger-soft: #2b1519;

    --stage:       #1b2b2e;
    --stage-ink:   #e6eef2;
    --guide:       rgba(255,255,255,.42);

    --shadow-sm:   0 1px 2px rgba(0,0,0,.4);
    --shadow-lg:   0 2px 4px rgba(0,0,0,.3), 0 18px 40px -20px rgba(0,0,0,.8);
  }
}

:root[data-theme="dark"] {
  --bg:          #0a1014;
  --surface:     #121a1f;
  --sunken:      #18232a;
  --line:        #24323a;
  --line-soft:   #1d2930;

  --ink:         #e6eef2;
  --ink-2:       #adbcc5;
  --muted:       #7e8f99;

  --brand:       #2fc7b8;
  --brand-ink:   #5ad9cb;
  --brand-soft:  #102b2a;

  --ok:          #2fc7b8;
  --warn:        #e0a352;
  --warn-soft:   #2a2114;
  --danger:      #f27a90;
  --danger-soft: #2b1519;

  --stage:       #1b2b2e;
  --stage-ink:   #e6eef2;
  --guide:       rgba(255,255,255,.42);

  --shadow-sm:   0 1px 2px rgba(0,0,0,.4);
  --shadow-lg:   0 2px 4px rgba(0,0,0,.3), 0 18px 40px -20px rgba(0,0,0,.8);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 20px;
  background: var(--bg);
  color: var(--ink);
  font: 400 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.card {
  position: relative;
  width: 100%;
  max-width: 440px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

.card-body { padding: 22px 24px 26px; display: flex; flex-direction: column; gap: 18px; }

/* ---- step rail: encodes where you are in a two-step flow ---- */
.rail {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 24px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--sunken);
}

.rail-step {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: .01em;
}

.rail-step .pip {
  width: 21px; height: 21px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11px;
  font-weight: 700;
  background: var(--line);
  color: var(--muted);
  transition: background .25s, color .25s;
}

.rail-step.active { color: var(--ink); }
.rail-step.active .pip { background: var(--brand); color: #fff; }
.rail-step.done .pip { background: var(--brand-soft); color: var(--brand-ink); }
.rail-line { flex: 1; height: 1px; background: var(--line); }

h1 { margin: 0; font-size: 20px; font-weight: 650; letter-spacing: -.01em; }
.lead { margin: 0; color: var(--ink-2); font-size: 14px; min-height: 2.6em; }

/* ---- step 1: intro guidance ---- */
.pane[hidden] { display: none; }

.tips { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }

.tip {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13.5px;
  color: var(--ink-2);
}

.tip-mark {
  flex: none;
  width: 20px; height: 20px;
  border-radius: 50%;
  display: grid; place-items: center;
}
.tip-mark svg { width: 11px; height: 11px; }
.tip.good .tip-mark { background: var(--brand-soft); color: var(--brand-ink); }
.tip.bad  .tip-mark { background: var(--danger-soft); color: var(--danger); }

/* ---- step 1: live scanner ---- */
.scan-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

.side-pill {
  flex: none;
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: .04em;
  padding: 4px 10px;
  border-radius: 20px;
  background: var(--brand-soft);
  color: var(--brand-ink);
}

.scanner {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 14px;
  overflow: hidden;
  background: #0b1114;
}

#cam { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* Dim everything outside the guide so the rectangle reads as a target. */
.mask { position: absolute; inset: 0; }
.mask .hole {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 84%;
  aspect-ratio: 1.586;
  border-radius: 10px;
  box-shadow: 0 0 0 9999px rgba(6, 14, 18, .55);
}

.frame {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 84%;
  aspect-ratio: 1.586;
  border-radius: 10px;
  border: 2px solid rgba(255,255,255,.55);
  transition: border-color .25s;
}

.frame.ready { border-color: var(--brand); }

/* Corner brackets: they read as "align here" far better than a plain rectangle. */
.frame .c { position: absolute; width: 22px; height: 22px; border: 3px solid #fff; transition: border-color .25s; }
.frame.ready .c { border-color: var(--brand); }
.frame .tl { top: -2px; left: -2px; border-right: 0; border-bottom: 0; border-radius: 10px 0 0 0; }
.frame .tr { top: -2px; right: -2px; border-left: 0; border-bottom: 0; border-radius: 0 10px 0 0; }
.frame .bl { bottom: -2px; left: -2px; border-right: 0; border-top: 0; border-radius: 0 0 0 10px; }
.frame .br { bottom: -2px; right: -2px; border-left: 0; border-top: 0; border-radius: 0 0 10px 0; }

/* Sits below the viewport, not over it - a pill floating on the card covers exactly the
   text the user is being asked to frame. */
.coach {
  margin: 0;
  align-self: center;
  text-align: center;
  padding: 8px 15px;
  border-radius: 20px;
  background: var(--sunken);
  border: 1px solid var(--line);
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 600;
  transition: background .25s, color .25s, border-color .25s;
}

.coach.ready { background: var(--brand); border-color: var(--brand); color: #fff; }

/* The frame we just took, held briefly so the capture is visibly acknowledged. */
.flash {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s;
}
.flash.show { opacity: 1; }

.shutter-row { display: grid; place-items: center; }

.shutter {
  width: 62px; height: 62px;
  padding: 0;
  border-radius: 50%;
  background: transparent;
  border: 3px solid var(--line);
  display: grid; place-items: center;
}

.shutter-dot {
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--brand);
  transition: transform .1s;
}

.shutter:active .shutter-dot { transform: scale(.9); }

.hint { margin: 0; text-align: center; font-size: 12.5px; color: var(--muted); }

/* ---- buttons ---- */
button {
  width: 100%;
  padding: 13px;
  border: 0;
  border-radius: 11px;
  background: var(--brand);
  color: #fff;
  font: inherit;
  font-weight: 650;
  cursor: pointer;
  transition: opacity .2s, transform .06s;
}

button:hover:not(:disabled) { opacity: .92; }
button:active:not(:disabled) { transform: translateY(1px); }
button:disabled { opacity: .4; cursor: not-allowed; }
button:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

button.ghost { background: transparent; border: 1px solid var(--line); color: var(--ink); }

/* A third rank below .ghost, for an escape hatch that must be findable but must never
   compete with the action the applicant is actually here to finish. */
button.quiet {
  background: transparent;
  border: 0;
  padding: 10px;
  color: var(--muted);
  font-weight: 500;
  font-size: 14px;
  text-decoration: underline;
  text-underline-offset: 3px;
}
button.quiet:hover:not(:disabled) { color: var(--ink-2); opacity: 1; }

/* ---- step 2: circular camera stage ---- */
.stage-wrap { display: grid; place-items: center; padding: 6px 0 2px; }

.stage {
  position: relative;
  width: min(320px, 78vw);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
}

.stage-media {
  position: absolute;
  inset: 26px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--stage);
}

#video, #overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: scaleX(-1);          /* mirrored so tilting feels natural */
}

#video { object-fit: cover; }

.ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.ring circle { fill: none; stroke-linecap: round; }
.ring .track { stroke: var(--line); stroke-width: 3; opacity: .8; }

.ring .seg {
  stroke: var(--line);
  stroke-width: 5;
  transition: stroke .4s ease;
}
.ring .seg.done { stroke: var(--brand); }
.ring .seg.active { stroke: var(--brand); opacity: .45; }

/* One continuous arc that fills once while neutral pose is measured. The three
   challenge segments are hidden until it hands over, so the ring only ever shows
   one kind of progress at a time. */
.ring .cal {
  stroke: var(--brand);
  stroke-width: 5;
  opacity: 0;
  transition: stroke-dashoffset .1s linear, opacity .3s;
}

.stage.calibrating .cal { opacity: 1; }
.stage.calibrating .seg { opacity: 0; }

/* Dashed guide sits inside the ring, echoing the "put your face here" affordance. */
.guide {
  position: absolute;
  inset: 26px;
  border-radius: 50%;
  border: 3px dashed var(--guide);
  pointer-events: none;
}

/* ---- status badge ---- */
.badge {
  width: 54px; height: 54px;
  border-radius: 50%;
  display: grid; place-items: center;
  margin: 0 auto;
  background: var(--sunken);
  border: 2px solid var(--line);
  transition: background .3s, border-color .3s, transform .3s;
}

.badge svg { width: 26px; height: 26px; stroke-width: 2.6; fill: none; stroke: var(--muted); }
.badge.ok { background: var(--brand-soft); border-color: var(--brand); transform: scale(1.06); }
.badge.ok svg { stroke: var(--brand-ink); }
.badge.bad { background: var(--danger-soft); border-color: var(--danger); }
.badge.bad svg { stroke: var(--danger); }

/* ---- movement demonstration ----
   A looping pictogram of the pose being asked for. It is keyed off data-move, so the
   challenge code sets one attribute and the right animation runs. Hidden entirely while
   no movement is being requested, so the calibration screen stays calm. */
.demo {
  height: 72px;
  display: grid;
  place-items: center;
  margin-top: 2px;
  opacity: 0;
  transition: opacity .3s;
}
.demo[data-move=""] { opacity: 0; }
.demo:not([data-move=""]) { opacity: 1; }

/* Rendered well above its 64x48 viewBox: at icon size the glyph was decoration, not an
   instruction. Scaling the viewBox thickens the strokes with it. */
.demo svg { width: 104px; height: 78px; overflow: visible; }

.demo-face  { fill: var(--brand-soft); stroke: var(--brand-ink); stroke-width: 2; }
.demo-eye   { fill: var(--brand-ink); stroke: none; }
.demo-mouth { fill: none; stroke: var(--brand-ink); stroke-width: 1.6; stroke-linecap: round; }

.demo-arrow {
  fill: none; stroke: var(--brand-ink); stroke-width: 3;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0;
}

/* The head glyph pivots about its own centre; transform-box keeps that honest inside SVG. */
.demo-head { transform-origin: 32px 24px; transform-box: view-box; }

@keyframes demo-up    { 0%,100% { transform: translateY(0)   scaleY(1); }
                        45%     { transform: translateY(-5px) scaleY(.88); } }
@keyframes demo-down  { 0%,100% { transform: translateY(0)   scaleY(1); }
                        45%     { transform: translateY(5px)  scaleY(.88); } }
@keyframes demo-left  { 0%,100% { transform: translateX(0)    rotate(0); }
                        45%     { transform: translateX(-6px) rotate(-16deg); } }
@keyframes demo-right { 0%,100% { transform: translateX(0)    rotate(0); }
                        45%     { transform: translateX(6px)  rotate(16deg); } }
@keyframes demo-hint  { 0%,100% { opacity: 0; } 45% { opacity: 1; } }

.demo[data-move="up"]    .demo-head { animation: demo-up    1.8s ease-in-out infinite; }
.demo[data-move="down"]  .demo-head { animation: demo-down  1.8s ease-in-out infinite; }
.demo[data-move="left"]  .demo-head { animation: demo-left  1.8s ease-in-out infinite; }
.demo[data-move="right"] .demo-head { animation: demo-right 1.8s ease-in-out infinite; }

/* Only the arrow on the side being asked for. Two arrows would read as "either way". */
.demo[data-move="left"]  .demo-arrow-a { animation: demo-hint 1.8s ease-in-out infinite; }
.demo[data-move="right"] .demo-arrow-b { animation: demo-hint 1.8s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .demo-head, .demo-arrow { animation: none !important; }
  .demo[data-move="left"]  .demo-head { transform: translateX(-6px) rotate(-16deg); }
  .demo[data-move="right"] .demo-head { transform: translateX(6px)  rotate(16deg); }
  .demo[data-move="up"]    .demo-head { transform: translateY(-5px); }
  .demo[data-move="down"]  .demo-head { transform: translateY(5px); }
  .demo[data-move="left"]  .demo-arrow-a,
  .demo[data-move="right"] .demo-arrow-b { opacity: 1; }
}

.checks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;          /* five items must not force the card to scroll sideways */
  gap: 8px 14px;
  font-size: 12.5px;
  color: var(--muted);
}

.checks li { display: flex; align-items: center; gap: 6px; transition: color .3s; }
.checks .dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--line);
  transition: background .3s, border-color .3s;
}
.checks li.done { color: var(--brand-ink); font-weight: 600; }
.checks li.done .dot { background: var(--brand); border-color: var(--brand); }

/* ---- result readout ---- */
.readout {
  display: none;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  border-radius: 12px;
  background: var(--sunken);
  border: 1px solid var(--line-soft);
}
.readout.show { display: flex; }

.readout-row { display: flex; justify-content: space-between; gap: 14px; font-size: 13.5px; }
.readout-row dt { color: var(--muted); }
.readout-row dd {
  margin: 0;
  color: var(--ink);
  font-weight: 600;
  text-align: right;
  font-variant-numeric: tabular-nums;
  word-break: break-word;
}

.debug {
  margin: 0;
  font: 400 11.5px/1.5 ui-monospace, "Cascadia Mono", Consolas, monospace;
  color: var(--muted);
  text-align: center;
  word-break: break-all;
}

.err {
  margin: 0;
  padding: 11px 13px;
  border-radius: 10px;
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 13px;
}

.err a { display: inline-block; margin-top: 6px; color: var(--brand-ink); font-weight: 600; word-break: break-all; }

/* ---- busy overlay: two real phases, never a decorative spinner ---- */
.busy {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 28px;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(3px);
  text-align: center;
}

.busy.show { display: flex; }

.busy-title { font-size: 16px; font-weight: 650; }
.busy-note { font-size: 13px; color: var(--muted); max-width: 30ch; }

.meter {
  width: 190px;
  height: 6px;
  border-radius: 99px;
  background: var(--line);
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  width: 0%;
  border-radius: 99px;
  background: var(--brand);
  transition: width .2s ease;
}

/* Upload gives a real percentage. Server-side work does not, so it gets a sweep
   that is honestly indeterminate rather than a fake countdown. */
.meter.indeterminate .meter-fill {
  width: 40%;
  animation: sweep 1.15s ease-in-out infinite;
}

@keyframes sweep {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(360%); }
}

.busy-pct { font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted); }

.spinner {
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 3px solid var(--line);
  border-top-color: var(--brand);
  animation: spin .8s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
  .meter.indeterminate .meter-fill { animation: none; width: 100%; opacity: .5; }
  * { transition-duration: .01ms !important; }
}

/* Dev shortcut banner. Loud on purpose: a dev session must never be mistaken for a real
   one in a screenshot, and its presence in production should be immediately obvious. */
/* ---- /dev result panel ----
   Deliberately looks like tooling, not product: the warn-coloured label matches the DEV
   MODE bar on step 1, so a screenshot of it can never pass for what an applicant sees. */
.devresult {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 6px;
  padding: 14px 16px 16px;
  border: 1px dashed var(--warn);
  border-radius: 12px;
  background: var(--warn-soft);
  text-align: left;
}
.devresult[hidden] { display: none; }

.devresult-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--warn);
}

.devresult-verdict { display: flex; align-items: baseline; gap: 10px; }
.devresult-pill {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #fff;
  background: var(--muted);
}
.devresult-pill.match    { background: var(--brand); }
.devresult-pill.review   { background: var(--warn); }
.devresult-pill.no-match { background: var(--danger); }
.devresult-score { font-size: 22px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

.devresult-fields {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 14px;
  font-size: 13.5px;
}
.devresult-fields dt { color: var(--muted); }
.devresult-fields dd { margin: 0; color: var(--ink); font-weight: 600; overflow-wrap: anywhere; }
.devresult-fields dd.missing { color: var(--danger); font-weight: 500; font-style: italic; }

.devbar {
  padding: 7px 14px;
  background: var(--warn);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .05em;
  text-align: center;
}

/* Shown when the applicant has no active session - see the load check in index.js. */
.blocked { display: flex; flex-direction: column; gap: 14px; text-align: center; padding: 8px 0; }
.blocked h1 { text-align: center; }
