/* Two-step binding (design v9, FE9-SEL-A2c). The board AdminLogin's
   先绑定两步验证 card leads here, and the page is that card's three steps
   inside the staff console: the page head, a danger banner only while
   something failed, a skeleton while the state is read, then either the
   bound state (a gray-25 row with the shield and 已开启) or the steps.
   The steps are numbered like the card (gray-100 circles, 24px here) on a
   1px rail: step 2 is a 232px tile, dashed with 生成二维码 until the QR code
   exists, then the code itself beside its manual entry (account, setup
   key behind 显示 and 复制, type, the setup link, the line that says never
   to send the key or the link to anyone, and the expiry); step 3
   is the six-box code field of the sign-in card and 确认绑定. One violet
   button shows at a time. Phones drop the rail and stack the QR code over
   its manual entry. */

.tf-head { margin-bottom: 24px; }
.tf-setup, .tf-bound, .tf-alert, .tf-loading { max-width: 720px; }

/* The banner: the shared danger banner (.banner.banner-danger gives the
   tint, border and ink) laid out as its circle mark, the message, and its
   one action (重试 when the state could not be read, 返回登录 when a
   binding ended unknown). Empty, it takes no room. */
.tf-alert { display: grid; grid-template-columns: 16px minmax(0, 1fr) auto; align-items: start; gap: 8px; margin: 0 0 24px; padding: 12px; }
.tf-alert:has(> #twofactor-status:empty) { display: none; }
.tf-alert-icon { flex: none; width: 16px; height: 16px; margin-top: 3px; background: currentColor; clip-path: path("M15.33 8A7.33 7.33 0 1 1 0.67 8A7.33 7.33 0 1 1 15.33 8ZM14 8A6 6 0 1 0 2 8A6 6 0 1 0 14 8ZM8.67 5.33L8.67 8L7.33 8L7.33 5.33ZM8.67 5.33A0.67 0.67 0 1 1 7.33 5.33A0.67 0.67 0 1 1 8.67 5.33ZM8.67 8A0.67 0.67 0 1 1 7.33 8A0.67 0.67 0 1 1 8.67 8ZM8 10L8.01 10L8.01 11.33L8 11.33ZM8.67 10.67A0.67 0.67 0 1 1 7.33 10.67A0.67 0.67 0 1 1 8.67 10.67ZM8.67 10.67A0.67 0.67 0 1 1 7.34 10.67A0.67 0.67 0 1 1 8.67 10.67Z"); }
#twofactor-status { min-width: 0; margin: 0; font-size: 14px; line-height: 22px; text-wrap: pretty; }
.tf-alert > .btn { margin: -5px 0; }

/* Reading the state: bars in the shape of the steps, shown only after
   200ms so a quick read never flashes them. */
.tf-loading { display: flex; flex-direction: column; gap: 12px; padding: 4px 0 8px; opacity: 0; animation: tf-appear 1ms linear 200ms forwards; }
.tf-loading > .skel { height: 16px; }
.tf-loading > .skel:nth-child(1) { width: 32%; }
.tf-loading > .skel:nth-child(2) { width: 64%; }
.tf-loading > .skel:nth-child(3) { width: 232px; height: 232px; margin-top: 8px; border-radius: var(--r-12); }
.tf-loading > .skel:nth-child(4) { width: 48%; }
@keyframes tf-appear { to { opacity: 1; } }

/* Bound: the shield in a success tile, the sentence, and 已开启. */
.tf-bound { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: start; gap: 16px; margin: 0; padding: 20px; border: 0; border-radius: var(--r-8); background: var(--gray-25); }
.tf-bound-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: var(--r-8); background: var(--success-tint); color: var(--success-fg); }
.tf-bound-icon > .glyph { width: 16px; height: 16px; }
.tf-bound-text > h2 { margin: 0; color: var(--text-1); font: 600 16px/24px var(--font-sans); }
.tf-bound-text > p { margin: 4px 0 0; color: var(--text-2); font-size: 14px; line-height: 22px; text-wrap: pretty; }
.tf-badge { display: inline-flex; align-items: center; gap: 6px; height: 22px; margin-top: 1px; padding: 0 8px; border-radius: var(--r-6); background: var(--success-tint); color: var(--success-fg); font: 500 12px/16px var(--font-sans); white-space: nowrap; }
.tf-dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--success-dot); }

/* The steps: a 24px number on a 1px rail, the title 16/24, one line of
   help 14/22, then the step's content. */
.tf-setup { margin: 0; padding: 0; border: 0; }
.tf-steps { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.tf-step { position: relative; display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: 16px; padding-bottom: 32px; }
.tf-step:last-child { padding-bottom: 0; }
.tf-step:not(:last-child)::before { content: ""; position: absolute; top: 32px; bottom: 8px; left: 12px; width: 1px; background: var(--line); }
.tf-n { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: var(--gray-100); color: var(--text-2); font: 500 12px/16px var(--font-sans); font-variant-numeric: tabular-nums; }
.tf-body { min-width: 0; }
.tf-body > h3 { margin: 0; color: var(--text-1); font: 600 16px/24px var(--font-sans); }
.tf-body > p { margin: 4px 0 0; color: var(--text-2); font-size: 14px; line-height: 22px; text-wrap: pretty; }

/* Step 2: the 232px tile. Empty, it is dashed with the QR mark and the
   page's one violet button; filled, it is white with the code painted in
   a 200px canvas (the canvas draws its own quiet zone). */
.tf-qr { display: grid; flex: none; place-items: center; box-sizing: border-box; width: 232px; height: 232px; border: 1px solid var(--line); border-radius: var(--r-12); background: var(--gray-0); }
.tf-qr-empty { align-content: center; gap: 16px; margin-top: 16px; border-style: dashed; border-color: var(--gray-300); background: var(--gray-25); }
.tf-qr-glyph { width: 40px; height: 40px; border-radius: var(--r-8); background: var(--gray-100); }
.tf-qr-glyph::after { content: ""; display: block; width: 20px; height: 20px; margin: 10px; background: var(--gray-700); clip-path: path(evenodd, "M1.5 1.5H8.5V8.5H1.5ZM3 3V7H7V3ZM4.25 4.25H5.75V5.75H4.25ZM11.5 1.5H18.5V8.5H11.5ZM13 3V7H17V3ZM14.25 4.25H15.75V5.75H14.25ZM1.5 11.5H8.5V18.5H1.5ZM3 13V17H7V13ZM4.25 14.25H5.75V15.75H4.25ZM11.5 11.5H13.5V13.5H11.5ZM16.5 11.5H18.5V13.5H16.5ZM14 14H16V16H14ZM11.5 16.5H13.5V18.5H11.5ZM16.5 16.5H18.5V18.5H16.5Z"); }
#twofactor-qr { display: block; width: 200px; height: 200px; image-rendering: pixelated; }
.tf-challenge { display: grid; grid-template-columns: 232px minmax(0, 1fr); align-items: start; gap: 24px; margin-top: 16px; }
.tf-qr-note { grid-column: 1; margin: 0; padding: 12px; border-radius: var(--r-8); background: var(--warning-tint); color: var(--warning-fg); font-size: 13px; line-height: 20px; }
.tf-manual { min-width: 0; }
.tf-manual > dl { display: flex; flex-direction: column; gap: 12px; margin: 0; }
.tf-manual > dl > div { display: grid; grid-template-columns: 72px minmax(0, 1fr); align-items: start; gap: 12px; }
.tf-manual dt { color: var(--text-3); font-size: 13px; line-height: 24px; white-space: nowrap; }
.tf-manual dd { min-width: 0; margin: 0; color: var(--text-1); font-size: 14px; line-height: 24px; }
.tf-mono { font: 400 13px/24px var(--font-mono); overflow-wrap: anywhere; }
.tf-key { display: grid; grid-template-columns: minmax(0, 21ch) auto; justify-content: start; align-items: start; gap: 4px 12px; }
#twofactor-setup-code { letter-spacing: .04em; }
.tf-key-actions { display: inline-flex; gap: 4px; }
.tf-key-actions > .btn { gap: 4px; }
.tf-key-actions > .btn > .glyph { width: 14px; height: 14px; }
#twofactor-reveal[aria-pressed="false"] > .glyph-pub-eye-off, #twofactor-reveal[aria-pressed="true"] > .glyph-pub-eye { display: none; }
.tf-link { margin: 16px 0 0; }
.tf-link > .btn { font-size: 13px; line-height: 20px; }
.tf-link > .btn > .glyph { width: 14px; height: 14px; }
/* A copy answers with a check and 已复制 for 1.6s (the button's ::before
   is its phone hit area, so the check is the ::after, moved first). */
:is(.tf-key-actions, .tf-link) > .btn.is-copied { color: var(--success-fg); }
:is(.tf-key-actions, .tf-link) > .btn.is-copied > .glyph { display: none; }
:is(.tf-key-actions, .tf-link) > .btn.is-copied::after { content: ""; order: -1; flex: none; width: 12px; height: 12px; margin: 0 1px; background: currentColor; clip-path: path("M1.4 6.4L2.9 4.9L4.9 6.9L9.1 2.7L10.6 4.2L4.9 9.9Z"); }
/* Under the link, 13/20 in the secondary ink: the key and the link work
   as the authenticator, so never send them; then the expiry, muted. */
.tf-share { margin: 8px 0 0; color: var(--text-2); font-size: 13px; line-height: 20px; text-wrap: pretty; }
.tf-expiry { margin: 8px 0 0; color: var(--text-3); font-size: 13px; line-height: 20px; text-wrap: pretty; }
.tf-share + .tf-expiry { margin-top: 4px; }
.tf-expiry time { color: var(--text-2); font-variant-numeric: tabular-nums; }

/* Step 3: the code is one field over six boxes, 40 by 48 with 8 between
   them and 8 more after the third, as on the sign-in card: the field's
   own text is transparent and each digit is written into its box. The
   box the next digit goes to wears the focus ring; a refused code turns
   all six red. */
#twofactor-form { display: flex; flex-direction: column; margin: 20px 0 0; }
#twofactor-form > label { margin: 0 0 6px; color: var(--text-1); font: 500 13px/20px var(--font-sans); }
.tf-otp { position: relative; display: block; width: 288px; max-width: 100%; height: 48px; }
#twofactor-code { position: absolute; top: 0; left: 0; z-index: 1; width: 100%; height: 100%; min-height: 0; margin: 0; padding: 0 0 0 14px; border: 0; border-radius: 0; background: transparent; box-shadow: none; color: transparent; -webkit-text-fill-color: transparent; caret-color: transparent; font: 500 20px/48px var(--font-mono); letter-spacing: 0; text-align: left; }
#twofactor-code::selection { background: transparent; color: transparent; }
#twofactor-code:focus, #twofactor-code:focus-visible { outline: none; box-shadow: none; }
.tf-boxes { position: absolute; inset: 0; display: flex; gap: 8px; pointer-events: none; }
.tf-boxes > span { display: grid; flex: none; place-items: center; width: 40px; height: 48px; border: 1px solid var(--gray-200); border-radius: var(--r-8); background: var(--gray-0); box-shadow: var(--shadow-control); color: var(--text-1); font: 500 20px/28px var(--font-mono); transition: border-color var(--t-hover) var(--ease), box-shadow var(--t-hover) var(--ease); }
.tf-boxes > span:nth-child(4) { margin-left: 8px; }
.tf-otp:hover .tf-boxes > span { border-color: var(--gray-300); }
#twofactor-code:focus:not([aria-invalid="true"]) + .tf-boxes > .is-next { border-color: var(--violet-600); box-shadow: var(--ring-input); }
#twofactor-code[aria-invalid="true"] + .tf-boxes > span { border-color: var(--danger-solid); box-shadow: var(--ring-danger); }
.tf-code-hint { display: flex; align-items: flex-start; gap: 4px; margin: 8px 0 0; color: var(--danger-fg); font-size: 12px; line-height: 16px; overflow-wrap: anywhere; }
.tf-code-hint::before { content: ""; flex: none; width: 14px; height: 14px; margin-top: 1px; background: currentColor; clip-path: path("M13.42 7A6.42 6.42 0 1 1 0.58 7A6.42 6.42 0 1 1 13.42 7ZM12.25 7A5.25 5.25 0 1 0 1.75 7A5.25 5.25 0 1 0 12.25 7ZM7.58 4.67L7.58 7L6.42 7L6.42 4.67ZM7.58 4.67A0.58 0.58 0 1 1 6.42 4.67A0.58 0.58 0 1 1 7.58 4.67ZM7.58 7A0.58 0.58 0 1 1 6.42 7A0.58 0.58 0 1 1 7.58 7ZM7 8.75L7.01 8.75L7.01 9.92L7 9.92ZM7.58 9.33A0.58 0.58 0 1 1 6.42 9.33A0.58 0.58 0 1 1 7.58 9.33Z"); }
.tf-code-hint:not([hidden]) + .tf-help { display: none; }
.tf-help { margin: 8px 0 0; color: var(--text-3); font-size: 13px; line-height: 20px; text-wrap: pretty; }
.tf-submit { margin: 20px 0 0; }

/* Phones: no rail; the number sits beside the title and the content takes
   the whole width, so the 232px tile and the six 44 by 52 boxes fit 326px.
   The manual entry puts each label over its value, the banner's action
   under its message. */
@media (max-width: 767px) {
  .tf-head { margin-bottom: 20px; }
  .tf-step { display: block; padding-bottom: 28px; }
  .tf-step:not(:last-child)::before { display: none; }
  .tf-n { position: absolute; top: 0; left: 0; }
  .tf-body > h3 { min-height: 24px; padding-left: 36px; }
  .tf-bound { grid-template-columns: 40px minmax(0, 1fr); padding: 16px; }
  .tf-badge { grid-column: 2; justify-self: start; margin-top: -8px; }
  .tf-challenge { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .tf-qr-note { grid-column: auto; }
  .tf-alert { grid-template-columns: 16px minmax(0, 1fr); }
  .tf-alert > .btn { grid-column: 2; justify-self: start; margin: 0; }
  .tf-manual > dl > div { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .tf-manual dt { font-size: 12px; line-height: 16px; }
  .tf-key { grid-template-columns: minmax(0, 1fr); }
  .tf-key-actions { margin-left: -8px; }
  .tf-otp { width: 300px; height: 52px; }
  #twofactor-code { font-size: 16px; line-height: 52px; }
  .tf-boxes { gap: 6px; }
  .tf-boxes > span { width: 44px; height: 52px; }
  .tf-boxes > span:nth-child(4) { margin-left: 6px; }
  #twofactor-confirm { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .tf-loading { animation-duration: 1ms; }
  .tf-boxes > span { transition: none; }
}
