    /* ============================ OTP LOGIN (Telegram bot style) ============================ */
    .otp-modal{ text-align:center; }

    /* Registered so the conic tracer's angle can interpolate (Chrome/Safari/FF 128+).
       Browsers without @property simply show a static arc — still legible. */
    @property --otp-angle{ syntax:'<angle>'; initial-value:0deg; inherits:false; }
    @keyframes otpOrbit{ to{ --otp-angle:1turn; } }

    .otp-modal .otp-logo{
      position:relative;
      width:64px; height:64px; margin:6px auto 18px;
      display:grid; place-items:center;
      background:linear-gradient(135deg,var(--lime),var(--cyan));
      border-radius:18px; font-family:'Bricolage Grotesque', 'Native Emoji', sans-serif; font-weight:800;
      font-size:32px; color:#0a0b10;
      box-shadow:0 0 28px rgba(198,242,78,.45);
    }
    /* slow lime→cyan halo orbiting the logo */
    .otp-modal .otp-logo::before{
      content:""; position:absolute; inset:-7px; border-radius:23px;
      padding:2px;
      background:conic-gradient(from var(--otp-angle,0deg),
        transparent 0turn .55turn, rgba(198,242,78,.9) .8turn,
        rgba(78,224,242,.95) .94turn, transparent 1turn);
      -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
      -webkit-mask-composite:xor;
      mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
      mask-composite:exclude;
      opacity:.75; filter:blur(.4px);
      animation:otpOrbit 3.4s linear infinite;
    }
    .otp-modal h3{ font-family:'Bricolage Grotesque', 'Native Emoji', sans-serif; font-size:26px; margin-bottom:14px; }
    .otp-modal .otp-desc{ font-size:14.5px; line-height:1.6; margin:0 4px 22px; color:var(--muted); }
    .otp-modal .tg-link{
      color:var(--cyan); font-weight:700; text-decoration:underline;
      text-decoration-color:rgba(78,224,242,.4); text-underline-offset:3px;
      transition:color .2s;
    }
    .otp-modal .tg-link:hover{ color:var(--lime); text-decoration-color:var(--lime); }

    /* ---------------- digit cells ---------------- */
    .otp-stage{ position:relative; margin:6px 0 4px; }
    /* The six cells SHARE the row instead of each claiming a fixed width.
       They used to be 46px wide (40px under the old 420px breakpoint) with a
       fixed gap, so the row needed a hard 321px / 270px no matter how narrow
       the screen was. Inside this modal a 320px phone only offers a 224px
       content box, so the sixth digit was cut off the login screen — the one
       screen every single user has to get through. Sharing the row (flex:1
       with min-width:0) makes the cells shrink to whatever fits and the
       max-width keeps them from ballooning on a wide viewport, so one rule
       now covers every phone from 300px up. */
    .otp-boxes{
      display:flex; gap:clamp(5px, 1.6vw, 9px); justify-content:center; padding:4px 0;
      transition:opacity .35s ease, filter .35s ease, transform .35s ease;
    }
    .otp-cell{ position:relative; border-radius:15px; flex:1 1 0; min-width:0; max-width:46px; }
    /* the tracer: a lime→cyan comet that orbits the cell border */
    .otp-cell::before{
      content:""; position:absolute; inset:-3px; border-radius:16px;
      padding:2.5px;
      background:conic-gradient(from var(--otp-angle,0deg),
        transparent 0turn .58turn, var(--lime) .82turn,
        var(--cyan) .94turn, transparent .99turn);
      -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
      -webkit-mask-composite:xor;
      mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
      mask-composite:exclude;
      opacity:0; transition:opacity .22s ease;
      animation:otpOrbit 1.5s linear infinite paused;
    }
    /* soft under-glow for the active cell */
    .otp-cell::after{
      content:""; position:absolute; inset:-2px; border-radius:16px;
      box-shadow:0 0 22px rgba(198,242,78,.26), 0 0 44px rgba(78,224,242,.12);
      opacity:0; transition:opacity .25s ease;
      pointer-events:none;
    }
    .otp-boxes input{
      position:relative; z-index:1;
      /* fills whatever width .otp-cell was given; aspect-ratio keeps the
         original 46:58 proportion as it shrinks, so the cells stay on-shape
         instead of turning into squares on a narrow phone. */
      width:100%; min-width:0; aspect-ratio:46 / 58; height:auto; text-align:center;
      /* the responsive digit size lives in core/42-responsive-system.css — it
         needs a heavier selector than this one to beat the global 16px input
         rule in 31-responsive-global.css. This is the desktop value. */
      font-family:'Bricolage Grotesque', 'Native Emoji', sans-serif; font-size:26px; font-weight:800;
      border:2px solid var(--glass-line); border-radius:13px;
      background:rgba(0,0,0,.35); color:var(--text); outline:none;
      caret-color:var(--lime);
      transition:border-color .2s, box-shadow .2s, transform .15s, background .2s, color .2s;
      padding:0; margin:0;
    }
    .otp-boxes input:focus{ border-color:rgba(198,242,78,.5); }

    /* digit lands: pop, plus a lit corner accent that stays on the cell */
    .otp-cell.filled input{ border-color:rgba(78,224,242,.6); animation:otpPop .26s cubic-bezier(.2,1.4,.4,1); }
    .otp-cell.filled:not(:focus-within)::before{
      opacity:.45; animation:none;
      background:conic-gradient(from calc(150deg + var(--i,0) * 12deg),
        transparent 0turn .62turn, var(--lime) .84turn,
        var(--cyan) .96turn, transparent 1turn);
    }
    @keyframes otpPop{ 0%{ transform:scale(.78); } 55%{ transform:scale(1.08); } 100%{ transform:scale(1); } }

    /* focused cell: run the tracer + glow */
    .otp-cell:focus-within::before{ opacity:1; animation-play-state:running; }
    .otp-cell:focus-within::after{ opacity:1; }

    /* ---------------- verifying: every cell traces out of phase = a wave ---------------- */
    .otp-boxes.verifying .otp-cell::before{
      opacity:1;
      background:conic-gradient(from var(--otp-angle,0deg),
        transparent 0turn .55turn, var(--cyan) .8turn,
        var(--lime) .95turn, transparent 1turn);
      animation:otpOrbit 1.1s linear infinite;
      animation-delay:calc(var(--i,0) * -.14s);
    }
    .otp-boxes.verifying .otp-cell input{ color:var(--muted); }

    /* ---------------- error: red flash + shake (JS sweeps the digits out) ---------------- */
    .otp-modal .otp-boxes.error .otp-cell input{
      border-color:#ff5d6c; background:rgba(255,93,108,.08); color:#ff8c97;
    }
    .otp-boxes.error .otp-cell::before{ opacity:0; }
    .otp-boxes.error .otp-cell::after{
      opacity:1; box-shadow:0 0 20px rgba(255,93,108,.32);
    }
    .otp-boxes.shake{ animation:otpShake .45s cubic-bezier(.36,.07,.19,.97); }
    @keyframes otpShake{
      10%, 90%{ transform:translateX(-2px); }
      20%, 80%{ transform:translateX(5px); }
      30%, 50%, 70%{ transform:translateX(-9px); }
      40%, 60%{ transform:translateX(9px); }
    }

    /* ---------------- success: cells ripple to lime, then the check badge takes over ---------------- */
    .otp-boxes.success .otp-cell{
      animation:otpRise .55s cubic-bezier(.2,1.2,.3,1) both;
      animation-delay:calc(var(--i,0) * 70ms);
    }
    .otp-modal .otp-boxes.success .otp-cell input{
      border-color:var(--lime); color:var(--lime);
      background:rgba(198,242,78,.10);
      box-shadow:0 0 18px rgba(198,242,78,.25);
      transition-delay:calc(var(--i,0) * 70ms);
    }
    .otp-boxes.success .otp-cell::before{
      opacity:.9; animation:none;
      background:conic-gradient(from 0deg, var(--lime), var(--cyan), var(--lime));
      transition-delay:calc(var(--i,0) * 70ms);
    }
    @keyframes otpRise{ 0%{ transform:translateY(0); } 35%{ transform:translateY(-7px); } 100%{ transform:translateY(0); } }

    /* ---------------- park the tracers while the user is away ----------------
       Every orbit above animates `--otp-angle`, a registered custom property
       feeding a conic-gradient through a mask-composite — so each one is a real
       REPAINT of that element on every single frame, not a cheap composited
       transform.

       This modal is the one place on the site anyone leaves the page unattended:
       you open it, switch to Telegram to fetch the code, and come back a minute
       later. Nothing here is being looked at during that minute, and
       `document.hidden` does not cover it — another app being frontmost leaves a
       partly-visible Chrome window fully "visible" to the page. `win-blurred`
       (01-foundation.js) is what closes that gap.

       Only the infinite decorative loops are parked. The one-shot sequences —
       shake, rise, the check badge — must never be frozen half-drawn, because
       the user comes back to whatever state they were left in. */
    body.win-blurred .otp-modal .otp-logo::before,
    body.win-blurred .otp-cell::before,
    body.win-blurred .otp-boxes.verifying .otp-cell::before{
      animation-play-state:paused;
    }

    .otp-stage.done .otp-boxes{ opacity:.08; filter:blur(4px) saturate(.5); transform:scale(.94); }

    /* the check badge, drawn over the faded boxes */
    .otp-check{
      position:absolute; inset:0; display:grid; place-items:center;
      opacity:0; transform:scale(.45); pointer-events:none;
      transition:opacity .3s ease, transform .5s cubic-bezier(.18,1.4,.35,1);
    }
    .otp-stage.done .otp-check{ opacity:1; transform:scale(1); }
    .otp-check svg{ width:62px; height:62px; overflow:visible; display:block; }
    .otp-check .ck-circle{
      stroke:var(--lime); stroke-width:3.5; fill:rgba(198,242,78,.10);
      stroke-linecap:round;
      stroke-dasharray:176; stroke-dashoffset:176;
      transform:rotate(-90deg); transform-origin:center;
      filter:drop-shadow(0 0 14px rgba(198,242,78,.55));
    }
    .otp-check .ck-mark{
      stroke:var(--lime); stroke-width:5; fill:none;
      stroke-linecap:round; stroke-linejoin:round;
      stroke-dasharray:35; stroke-dashoffset:35;
    }
    .otp-stage.done .ck-circle{ animation:otpDraw .5s ease-out .05s forwards; }
    .otp-stage.done .ck-mark{ animation:otpDraw .32s cubic-bezier(.55,0,.35,1) .42s forwards; }
    @keyframes otpDraw{ to{ stroke-dashoffset:0; } }
    /* expanding ripple ring behind the badge */
    .otp-check-ring{
      position:absolute; width:62px; height:62px; border-radius:50%;
      border:2px solid rgba(198,242,78,.8); opacity:0;
    }
    .otp-stage.done .otp-check-ring{ animation:otpRipple 1s ease-out .25s; }
    @keyframes otpRipple{ 0%{ opacity:.9; transform:scale(.7); } 100%{ opacity:0; transform:scale(2.2); } }

    /* ---------------- status line (verifying / error / success) ---------------- */
    .otp-status{
      min-height:24px; margin:2px 0 10px;
      display:flex; align-items:center; justify-content:center; gap:8px;
      font-size:13.5px; font-weight:700; line-height:1.35;
      opacity:0; transform:translateY(-4px);
      transition:opacity .25s ease, transform .25s ease;
    }
    .otp-status[data-state="verifying"],
    .otp-status[data-state="error"],
    .otp-status[data-state="success"]{ opacity:1; transform:translateY(0); }
    .otp-status[data-state="verifying"]{ color:var(--cyan); }
    .otp-status[data-state="error"]{ color:#ff5d6c; }
    .otp-status[data-state="success"]{ color:var(--lime); }
    .otp-spinner{
      display:none; flex:0 0 auto;
      width:14px; height:14px; border-radius:50%;
      border:2px solid rgba(78,224,242,.25); border-top-color:var(--cyan);
      animation:otpSpin .7s linear infinite;
    }
    .otp-status[data-state="verifying"] .otp-spinner{ display:inline-block; }
    @keyframes otpSpin{ to{ transform:rotate(1turn); } }
    .otp-status-ico{
      display:none; flex:0 0 auto;
      width:15px; height:15px;
      stroke:currentColor; stroke-width:2; stroke-linecap:round; fill:none;
    }
    .otp-status[data-state="error"] .otp-status-ico{ display:inline-block; animation:otpPop .25s ease; }

    .otp-hint{ font-size:11.5px; color:var(--muted); margin-top:10px; opacity:.75; }

    /* light theme: lime text is unreadable on cream — darken the success/error ink */
    :root[data-theme="light"] .otp-modal .otp-boxes.success .otp-cell input{ color:#4d7c0f; }
    :root[data-theme="light"] .otp-modal .otp-boxes.error .otp-cell input{ color:#d43d4d; }
    :root[data-theme="light"] .otp-status[data-state="success"]{ color:#4d7c0f; }

    /* Small phones: the cells now shrink to fit on their own (see .otp-boxes
       above), so this step no longer sets a width, a height or a gap — it only
       tightens the corner radii and the tracer inset to match the smaller box.
       The old width/height/font-size overrides here are what pinned the row to
       270px and pushed the sixth digit off a 320px screen. */
    @media (max-width:420px){
      .otp-boxes input{ border-radius:11px; }
      .otp-cell{ border-radius:13px; }
      .otp-cell::before{ inset:-2.5px; border-radius:14px; padding:2px; }
      .otp-cell::after{ border-radius:14px; }
    }

    /* tiny toast popup */
    #toast{
      position:fixed; bottom:26px; left:50%;
      transform:translateX(-50%) translateY(30px);
      background:#fff; color:#0a0b10; font-weight:600;
      padding:13px 22px; border-radius:12px;
      box-shadow:0 12px 40px rgba(0,0,0,.4);
      opacity:0; transition:opacity .3s ease, transform .3s ease;
      z-index:200; font-size:14px; pointer-events:none;
      max-width:calc(100vw - 32px);
    }
    #toast.show{ opacity:1; transform:translateX(-50%) translateY(0); }

    /* undo bar — the replacement for confirm('… cannot be undone'), see
       bandlyUndo() in 02-landing.js. Same corner as #toast and above it: of
       the two, the one you can act on has to be the one you can reach. */
    .bd-undo{
      position:fixed; left:50%; bottom:26px; z-index:1200;
      display:flex; align-items:center; gap:11px; overflow:hidden;
      padding:11px 12px 11px 15px; border-radius:14px;
      max-width:min(92vw, 470px);
      background:var(--bg-soft); color:var(--text);
      border:1px solid var(--card-border-hi);
      box-shadow:var(--shadow-lg), 0 0 0 1px rgba(0,0,0,.22);
      transform:translate(-50%, 26px); opacity:0;
      transition:transform .3s cubic-bezier(.2,1.1,.3,1), opacity .28s ease;
    }
    .bd-undo.is-in{ transform:translate(-50%, 0); opacity:1; }
    .bd-undo-ic{ flex:none; font-size:15px; opacity:.7; }
    .bd-undo-tx{ min-width:0; display:flex; flex-direction:column; gap:1px; }
    .bd-undo-tx b{ font-size:13px; font-weight:750; letter-spacing:-.1px; }
    .bd-undo-tx em{
      font-style:normal; font-size:11.5px; color:var(--muted);
      white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
    }
    .bd-undo-btn{
      flex:none; margin-left:5px; cursor:pointer; font:inherit; border:0;
      padding:8px 16px; border-radius:10px;
      background:var(--lime); color:var(--on-lime);
      font-size:12.5px; font-weight:800; letter-spacing:-.1px;
      transition:transform .18s cubic-bezier(.3,1.4,.5,1), filter .18s;
    }
    .bd-undo-btn:hover{ filter:brightness(1.07); transform:translateY(-1px); }
    .bd-undo-btn:active{ transform:scale(.95); }
    /* The rail IS the countdown — its duration is set per call. */
    .bd-undo-rail{
      position:absolute; left:0; bottom:0; height:2px; width:100%; transform-origin:left;
      background:linear-gradient(90deg, var(--lime), var(--cyan));
      animation:bdUndoRail 7s linear forwards;
    }
    @keyframes bdUndoRail{ from{ transform:scaleX(1) } to{ transform:scaleX(0) } }
    .bd-undo.is-done .bd-undo-ic{ color:var(--lime); opacity:1; }
    @media (prefers-reduced-motion: reduce){
      .bd-undo, .bd-undo-btn{ transition:none !important; }
      .bd-undo{ opacity:1; transform:translate(-50%, 0); }
      .bd-undo-rail{ animation:none !important; opacity:.5; }
    }

    /* respect users who prefer less motion */
    @media (prefers-reduced-motion: reduce){
      .aurora span, .tile:hover::after, .topic:hover::after, .tile .ico{ animation:none !important; }
      [data-reveal]{ opacity:1 !important; filter:none !important; transform:none !important; }
      .otp-modal .otp-logo::before,
      .otp-cell::before,
      .otp-boxes.verifying .otp-cell::before,
      .otp-cell.filled input,
      .otp-boxes.shake,
      .otp-boxes.success .otp-cell,
      .otp-check-ring{ animation:none !important; }
      .otp-stage.done .ck-circle,
      .otp-stage.done .ck-mark{ animation:none !important; stroke-dashoffset:0; }
      .otp-check{ transition:opacity .2s ease; transform:scale(1); }
    }
