/* CodeSlots — vanilla port of React Bits' <CodeSlots />, brief at
   docs/feedback/2026-09-18-codeslots-brief.md. Colors come from kit.css tokens only:
   slot surface = --ground, border = --hair, digit = --ink, active ring = --link /
   --butter-soft (matches .field input:focus), danger = --rose-ink. No hardcoded hex.
   Preferred slot width 44px (shrinks on narrow screens), gap 8px, height = width * 1.15, radius
   var(--r-in). */

.code-field.cs-mounted {
  position: relative;
  margin-top: 6px;
}
.code-field.cs-mounted > input[type='text'] {
  /* The label caption ("Code") stays a normal block above; the slot row below replaces
     the input's own box. This selector no longer applies once .cs-input is reparented
     into .cs-row (see .cs-row input.cs-input below) — kept only as a safety net for the
     no-JS/pre-mount instant. */
  margin-top: 0;
}

.cs-row {
  position: relative;
  display: flex;
  gap: 8px;
  /* R2-5: height is no longer pinned here -- .cs-slot now derives its own height from
     its (fluid) width via aspect-ratio, so the row's height follows it. The caret below
     still positions itself with percentages of this element's box, which now resolves
     to whatever height the slots actually rendered at. */
  /* R1-M5/M6: the row now spans the full field width and each slot flexes between a
     floor and ceiling, so the row's own edges line up with the label and the Verify
     button above/below it (no more orphaned-left dead space at 1440) and the card can
     shrink under the row at 360/320 instead of being pinned to a fixed max-content
     width (min-width:0 below lets this element actually shrink in its grid/flex
     ancestors). */
  width: 100%;
  min-width: 0;
  justify-content: space-between;
  cursor: text;
  -webkit-tap-highlight-color: transparent;
}

.cs-slot {
  position: relative;
  width: clamp(44px, calc((100% - 5 * 8px) / 6), 56px);
  min-width: 0;
  /* Allow flex shrink to reach ~32px at 320px without a minimum height transferring
     back through the aspect ratio and forcing the card wider than the viewport. */
  aspect-ratio: 1 / 1.15;
  min-height: 0;
  height: auto;
  flex: 0 1 auto;
  border-radius: min(var(--r-in), 25%);
  background: var(--ground);
  border: 1px solid var(--hair);
  overflow: hidden;
  display: grid;
  place-items: center;
  /* M5 (crossfade note): the active-slot tint and the error/danger tint both land on
     this background-color — crossfade it instead of jumping, roughly in step with the
     180ms caret glide. The active ring itself is a ::after pseudo-element (below) whose
     OPACITY crossfades instead — animating box-shadow directly is against house rule G3. */
  transition: background-color 120ms ease;
}

.cs-fill {
  position: absolute;
  /* R1-M4: the overshoot now lives on .cs-slot's own transform (below), not on the fill
     scaling past 1 inside its own clip box, so the fill can sit flush at inset:0 with no
     1px ground ring left showing between fill and slot border (the old "pill inside a
     pill"). */
  inset: 0;
  border-radius: min(var(--r-in), 25%);
  /* R1-M3: --butter is the site's own accent (the Verify button's fill) — using it here
     instead of a --link/--ground mix means a landed slot and the success wash read as
     the same accent as the button next to them, not a muddy gray-lavender. */
  background: var(--butter);
  transform: scale(0);
  transform-origin: center;
}

.cs-digit {
  position: relative;
  z-index: 1;
  /* R1-nit15/M1: Iowan Old Style has real lining tabular figures; the site's default
     serif stack (kit.css body) puts Georgia first, whose digits are old-style (4/7/9
     descend, 8 ascends) — uneven next to each other in a fixed-width row. lining-nums
     forces the lining variant even if a fallback font in the stack defaults to
     old-style. */
  font-family: 'Iowan Old Style', Palatino, Georgia, serif;
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--ink);
  opacity: 0;
}
/* R2-H1: index.html's own inline <style> has `.field span{font-weight:500;
   color:var(--ink-3)}` (specificity 0,1,1) which outranks the plain `.cs-digit` rule
   above (0,1,0) — digits were rendering in --ink-3 at weight 500 site-wide, and in the
   error state that made the digit only ~1.05:1 against the rose fill. `.cs-row .cs-digit`
   (0,2,0) beats it on the class count alone, regardless of source order. */
.cs-row .cs-digit {
  /* R1-M3: on the --butter fill, --ink-on-accent is the token pair already used for the
     Verify button's own label — same digit-on-accent contrast the rest of the page uses. */
  color: var(--ink-on-accent);
  font-weight: 700;
}
/* R2-H1: on the SOLID rose fill specifically, --ink itself is only ~2.2:1/1.5:1 — not
   --ground (the slot's own base color) reads 5.62:1 light / 9.45:1 dark against
   --rose-ink (contrast is symmetric with the --rose-ink-on-ground pair already verified
   AA in the main review). */
.cs-row[data-status='error'] .cs-digit {
  color: var(--ground);
}

/* Active slot (the first empty slot, i.e. the one the caret sits in): a soft tint at
   8%, matching the brief's accentColor active-slot recipe and the page's own
   .field input:focus ring color family (--link / --butter-soft). The ring itself is a
   ::after pseudo-element (below) so its crossfade animates opacity, not box-shadow. */
.cs-row .cs-slot.cs-active {
  background: color-mix(in srgb, var(--butter-soft) 8%, var(--ground));
}
.cs-slot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 2px var(--link);
  opacity: 0;
  transition: opacity 120ms ease;
  pointer-events: none;
}
.cs-slot.cs-active::after {
  opacity: 1;
}

.cs-caret {
  position: absolute;
  /* R1-nit15: percentages of .cs-row's own height (which equals .cs-slot's height,
     52px) instead of hand-pinned 12px/28px — if the slot height ever changes, the
     caret's proportions (54% tall, vertically centered) track it automatically. */
  top: 23%;
  height: 54%;
  left: 0;
  width: 1.5px;
  background: var(--link);
  transform: translateX(-50%);
  animation: cs-blink 1s step-end infinite;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .cs-caret {
    animation: none;
  }
}
@keyframes cs-blink {
  0%,
  50% {
    opacity: 1;
  }
  50.01%,
  100% {
    opacity: 0;
  }
}

/* Specificity note: index.html's own inline <style> defines .field input and
   .field input:focus (border/box-shadow/height) AFTER this stylesheet loads, and
   .field input:focus alone outweighs a plain .cs-input rule. Match .code-field.cs-mounted
   input.cs-input (and its own :focus) so this overlay input never shows the old field
   chrome — the visible focus ring is drawn by .cs-slot.cs-active instead. */
.code-field.cs-mounted input.cs-input,
.code-field.cs-mounted input.cs-input:focus {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  box-shadow: none;
  background: transparent;
  color: transparent;
  caret-color: transparent;
  letter-spacing: normal;
  text-align: left;
  z-index: 2;
}
.cs-input::selection {
  background: transparent;
}
/* B1: belt-and-suspenders — code-slots.js also removes the placeholder attribute on
   mount, but this keeps a stray placeholder from ever rendering through the overlay. */
.cs-input::placeholder {
  color: transparent;
}
/* B3: the active-slot ring (.cs-slot.cs-active box-shadow, below) is this control's own
   visible focus indicator, so the input's own outline (including the site's global
   !important :focus-visible ring at shell.css:69) is suppressed here specifically. */
.code-field.cs-mounted input.cs-input:focus-visible {
  outline: none !important;
  /* R1-M2: shell.css's global :focus-visible rule (shell.css:68) also puts a 4px
     ground-colored box-shadow ring on every input — invisible-looking here since it's
     the same color as the page ground, but it draws a soft halo around the WHOLE row.
     Killed locally on this overlay input only; the global rule itself is untouched. */
  box-shadow: none !important;
}

/* Keep the transparent typing surface at the 44px primary-control minimum on the
   narrowest layouts. It remains absolutely positioned, so enlarging its hit box
   cannot change the slot row's measured position or its visual geometry. */
@media (max-width: 360px) {
  .code-field.cs-mounted input.cs-input {
    top: 50%;
    bottom: auto;
    height: max(100%, 44px);
    transform: translateY(-50%);
  }
}

/* status=error: slot surfaces + fills tint danger. B4/R2-H1: the digit color for this
   state is set above (--ground on the solid rose fill, 5.62:1/9.45:1) — B4's original
   fix (leaving it --ink) only reached ~2.2:1/1.5:1 on the solid fill. */
.cs-row[data-status='error'] .cs-slot {
  background: color-mix(in srgb, var(--rose-ink) 20%, var(--ground));
}
.cs-row[data-status='error'] .cs-fill {
  background: var(--rose-ink);
}

/* status=success: wash overlay + checkmark. H2: the wash must paint ABOVE the digits
   (z-index) — otherwise the sinking/fading digits show through on top of it — and the
   check (drawn last) sits above the wash. */
.cs-wash {
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: min(var(--r-in), 25%);
  /* R1-M3: same reasoning as .cs-fill — match the button/fill accent instead of a
     --link/--ground mix. */
  background: var(--butter);
  clip-path: inset(0 50% round min(var(--r-in), 25%));
  pointer-events: none;
  opacity: 0;
}
.cs-row[data-status='success'] .cs-wash {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  /* Reduced motion keeps the brief's 150ms opacity fades, with no spatial movement. */
  .cs-wash {
    clip-path: none;
  }
}

.cs-check {
  position: absolute;
  inset: 0;
  z-index: 4;
  margin: auto;
  width: 26px;
  height: 26px;
  color: var(--ink-on-accent);
  opacity: 0;
  transform: translateY(8px) scale(0.85);
  pointer-events: none;
}
.cs-check.cs-check-in {
  opacity: 1;
}

/* Disabled row (opts.disabled — not currently wired from index.html). */
.cs-row[data-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
}

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