/*
InputOTP

Slots drawn over one transparent input. Each slot is a fixed square from
the control height, joined into groups (shared borders, rounded only at a
group's ends); a separator sits between groups. The active slot gets the
focus ring and border as box-shadow and border color, and an empty active
slot draws a blinking caret, so editing never changes a slot's size.

  .input-otp                  root (data-disabled, data-invalid, data-complete)
  .input-otp-input-clip       clips the input to the slots' box
  .input-otp-input            the real input, transparent, over the slots
  .input-otp-group            a run of joined slots
  .input-otp-slot             one character (data-active, data-filled, data-index)
  .input-otp-caret            the fake caret in an empty active slot
  .input-otp-separator        between groups
  .input-otp.is-sm | is-lg    slot size
*/

@layer components {

.input-otp {
  --input-otp-slot: var(--control-height);
  --input-otp-font: var(--text-ui-lg);

  position:    relative;
  display:     inline-flex;
  align-items: center;
  gap:         var(--space-2);
  max-width:   100%;
}

.input-otp.is-sm {
  --input-otp-slot: var(--control-height-sm);
  --input-otp-font: var(--text-ui);
}

.input-otp.is-lg {
  --input-otp-slot: var(--control-height-lg);
  --input-otp-font: var(--text-h4);
}

/* Dimmed by the effective disabled state: its own prop, a disabled Field
   (which disables the input) or a native <fieldset disabled>. */
.input-otp:is([data-disabled], :has(.input-otp-input:disabled)) {
  cursor:  not-allowed;
  opacity: var(--opacity-disabled);
}

/* The input's clip box. overflow: clip (not hidden, so it is no scroll
   container a focus could scroll) keeps the increase-width input from adding
   scrollable overflow: a code at a phone page's content edge never pushes the
   page sideways. */
.input-otp-input-clip {
  position: absolute;
  inset:    0;
  z-index:  2;
  overflow: clip;
}

/* The real input covers the slots, the active one included (it stacks above
   the active slot's raised ring), so taps, a long press and its paste menu,
   autofill and the OS keyboard all reach it. Its text and caret are transparent; the slots draw them. */
.input-otp-input {
  position:       absolute;
  inset:          0;
  z-index:        2;
  width:          100%;
  height:         100%;
  margin:         0;
  padding:        0;
  border:         0;
  border-radius:  0;
  outline:        none;
  background:     transparent;
  box-shadow:     none;
  color:          transparent;
  caret-color:    transparent;
  font-size:      var(--input-otp-font);
  letter-spacing: -0.5em;
  text-indent:    -9999px;
  font-family:    var(--font-mono);
  cursor:         text;
  appearance:     none;
}

.input-otp-input::selection {
  background: transparent;
}

.input-otp-input:autofill,
.input-otp-input:-webkit-autofill {
  -webkit-text-fill-color: transparent;
  -webkit-box-shadow:      0 0 0 1000px transparent inset;
  transition:              background-color 99999s;
}

/* Room for a password manager badge (input-otp's increase-width): the input
   reaches past the slots at the inline end and the overflow is clipped, so
   nothing moves. Under rtl the inline end is the left, and the clip follows. */
.input-otp[data-push-password-manager] .input-otp-input {
  width:     calc(100% + var(--input-otp-slot));
  clip-path: inset(0 var(--input-otp-slot) 0 0);
}

.input-otp[data-push-password-manager] .input-otp-input:dir(rtl) {
  clip-path: inset(0 0 0 var(--input-otp-slot));
}

.input-otp-input:disabled {
  cursor: not-allowed;
}

.input-otp-group-wrap {
  display:     flex;
  align-items: center;
  gap:         var(--space-2);
}

.input-otp-group {
  display:     flex;
  align-items: center;
}

.input-otp-slot {
  position:        relative;
  display:         flex;
  flex:            none;
  align-items:     center;
  justify-content: center;
  box-sizing:      border-box;
  width:           var(--input-otp-slot);
  height:          var(--input-otp-slot);
  border-block:    var(--control-border-width) solid var(--control-border);
  border-inline-end: var(--control-border-width) solid var(--control-border);
  background:      var(--control-bg);
  box-shadow:      var(--control-shadow);
  color:           var(--ink);
  font-family:     var(--font-ui);
  font-size:       var(--input-otp-font);
  font-weight:     var(--weight-ui);
  font-variant-numeric: tabular-nums;
  line-height:     1;
  transition:      border-color var(--duration-fast) var(--ease-out),
                   box-shadow var(--duration-fast) var(--ease-out);
}

.input-otp-slot:first-child {
  border-inline-start:        var(--control-border-width) solid var(--control-border);
  border-start-start-radius:  var(--radius-control);
  border-end-start-radius:    var(--radius-control);
}

.input-otp-slot:last-child {
  border-start-end-radius: var(--radius-control);
  border-end-end-radius:   var(--radius-control);
}

.input-otp-slot[data-active] {
  z-index:      1;
  border-color: var(--focus);
  box-shadow:   var(--focus-ring);
}

.input-otp-slot[data-placeholder] {
  color: var(--ink-dim);
}

.input-otp[data-invalid] .input-otp-slot {
  border-color: var(--danger);
}

.input-otp[data-invalid] .input-otp-slot[data-active] {
  box-shadow: var(--focus-ring-danger);
}

.input-otp-caret {
  position:       absolute;
  inset:          0;
  display:        flex;
  align-items:    center;
  justify-content: center;
  pointer-events: none;
}

.input-otp-caret::after {
  content:    "";
  width:      var(--control-border-width);
  height:     calc(var(--input-otp-font) * 1.1);
  background: var(--ink);
  animation:  input-otp-blink var(--caret-blink) step-end infinite;
}

@keyframes input-otp-blink {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0; }
}

.input-otp-separator {
  display:     inline-flex;
  flex:        none;
  align-items: center;
  color:       var(--ink-dim);
}

.input-otp-separator .icon {
  width:  var(--icon-size);
  height: var(--icon-size);
}

/* Without script the slots cannot follow typing: the real input shows
   itself in the control chrome (input-otp's noScriptCSSFallback). */
@media (scripting: none) {
  .input-otp-input-clip {
    display: contents;
  }

  .input-otp-input {
    position:       static;
    width:          calc(var(--input-otp-slot) * 6);
    height:         var(--input-otp-slot);
    padding:        0 var(--control-padding-x);
    border:         var(--control-border-width) solid var(--control-border);
    border-radius:  var(--radius-control);
    background:     var(--control-bg);
    color:          var(--ink);
    caret-color:    auto;
    letter-spacing: normal;
    text-indent:    0;
    clip-path:      none;
  }

  .input-otp-group-wrap {
    display: none;
  }
}

}
