/* ==========================================================================
   Topbet — Interactive Vault Landing Page
   Design reference: Figma "Topbet-LP" (Dynamic 1 / 2 / 3)
   ========================================================================== */

:root {
  /* Brand ---------------------------------------------------------------- */
  --tb-red:        #D21502;   /* Topbet primary, taken from the supplied logo */
  --tb-red-hot:    #FF3B21;   /* neon / glow / hover                          */
  --tb-red-deep:   #9B0E00;   /* the design's button underline + text shadow  */
  --tb-green:      #1EA84B;   /* access granted — success, not brand red     */
  --tb-green-deep: #0F7A34;

  --tb-gold:       #F5C147;
  --tb-gold-soft:  #FFE29A;

  /* Surfaces, from the Figma frames -------------------------------------- */
  --tb-ink:        #131416;   /* page ground                                  */
  --tb-card:       rgba(10, 10, 11, .5);   /* offer + timer cards             */
  --tb-card-solid: rgba(10, 10, 11, .8);   /* the denser registration card    */
  --tb-field:      #26272A;   /* inputs, inactive tab                         */
  --tb-mist:       #FFFFFF;
  --tb-mist-dim:   #888888;
  --tb-tab-off:    #727272;

  --tb-card-glow:  0 2px 50px 0 rgba(210, 21, 2, .2);
  --tb-card-glow-lg: 0 4px 100px 0 rgba(210, 21, 2, .2);
  --tb-text-glow:  0 0 25px var(--tb-red), 1px 1px 0 #9A9A9A;

  /* Type ------------------------------------------------------------------
     Fira Sans Condensed for display and controls, Montserrat for form text,
     both per the design. Loaded from Google Fonts in the markup; the stacks
     fall back to a condensed system face so a blocked CDN degrades rather
     than reflowing the whole page. */
  --tb-display: "Fira Sans Condensed", "Roboto Condensed", "Arial Narrow",
                system-ui, sans-serif;
  --tb-body:    "Montserrat", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Anchor point + rendered video size, published by app.js each frame. */
  --ov-x: 50%;
  --ov-y: 46%;
  --ov-w: 100vw;
  --ov-h: 100vh;

  /* --plate-size is the vault's scanner plate at its on-screen size, measured
     from the footage and published by app.js. The visible ring matches it
     exactly. The hit area is floored at 56px separately, because the plate can
     render smaller than a fingertip — especially on the landscape cut, where
     the vault sits much further back in frame. */
  --plate-size: 64px;
  --scan-size: max(var(--plate-size), 56px);

  --ease: cubic-bezier(.22,.61,.36,1);
  --shell-max: 460px;
}

*, *::before, *::after { box-sizing: border-box; }

/* Any `display` declaration outranks the UA's `[hidden] { display: none }`,
   so a hidden flex row stays on screen. Several rows here are flex, and the
   tab switch relies on `hidden`, so restore it explicitly. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--tb-ink);
  color: var(--tb-mist);
  font: 400 16px/1.5 var(--tb-body);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* ==========================================================================
   Stage — the video layer
   ========================================================================== */

.stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--tb-ink);
  z-index: 0;

  /* iOS treats a long press on selectable content as "select text", which
     raises the magnifier loupe and the blue selection handles right on top of
     the hold-to-scan interaction. The screen-reader label inside the button is
     enough to trigger it. Suppressing selection and the callout menu across
     the stage stops that; the copy in .shell stays selectable. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* Long-pressing media otherwise offers "Save Image" / "Share". */
.stage img,
.stage video {
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  pointer-events: none;
}

/* Hidden banner placeholders for Templater generation (vault uses video). */
.templater-backgrounds,
.templater-backgrounds img {
  display: none !important;
  visibility: hidden;
  width: 0;
  height: 0;
  overflow: hidden;
  pointer-events: none;
}

/* A plain full-bleed clipping box. Do NOT give this the footage's aspect
   ratio: aspect-ratio loses to min-width/min-height, the box collapses to the
   viewport, and object-fit then crops by an amount that varies with screen
   size — so any overlay positioned in % of it drifts off its mark.

   app.js sizes the video itself (--vid-*) and publishes overlay anchors
   (--ov-*, --ring-*) from the same geometry. */
.stage__frame {
  position: absolute;
  inset: 0;
}

/* Sized and placed by app.js so the vault lands on its target, rather than
   letting object-fit centre the video's frame. The box already carries the
   footage's aspect ratio, so `fill` scales without distorting. */
.clip {
  position: absolute;
  left: var(--vid-x, 0);
  top: var(--vid-y, 0);
  width: var(--vid-w, 100%);
  height: var(--vid-h, 100%);
  object-fit: fill;
  opacity: 0;
  /* Duration is published by app.js as CROSSFADE_MS so the blend and the
     delayed play() cannot drift apart. */
  transition: opacity var(--clip-fade, 420ms) var(--ease);
  pointer-events: none;
}

.clip.is-active { opacity: 1; }

/* Mobile portrait: nudge the video layers themselves a touch to the left —
   #clipIdle (the background already on screen) and #clipOpen (played on tap) —
   leaving the still and the overlays (ring/scanner) where they are. */
@media (max-width: 899px) and (orientation: portrait) {
  #clipIdle, #clipOpen { transform: translateX(-18px); }

  /* Lower the registration block (amount + form card) a touch on phones so
     it isn't hugging the top edge under the header. */
  .panel--register { margin-top: 48px; }

  /* Keep the "Get a Code" button on the SAME line as the number input on
     phone portrait (the reference nests it inside the field, right of the
     digits), instead of letting the field row wrap it onto its own line.
     The compact pill + shrinking input reproduce that inline look. */
  .field-row--phone { flex-wrap: nowrap; }
  /* Drop the digits part of the country dial (flag + caret remain) so the
     number input gets the freed space — NESTJS hides the code on mobile. */
  .field-row--phone .dial { padding-inline: 12px; gap: 8px; }
  .field-row--phone .dial__code { display: none; }
  .field-row--phone .dial__flag { width: 26px; height: 20px; flex: none; }
  .field-row--phone .input-wrap { flex: 1 1 90px; min-width: 78px; }
  .field-row--phone .get-code {
    height: 38px;
    padding: 0 11px;
    font-size: 11px;
    border-radius: 7px;
  }
}

/* The pinned final still sits beneath the clips. */
.clip--still { opacity: 0; z-index: -1; }
.clip--still.is-active { opacity: 1; }

.stage__vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(120% 70% at 50% 32%, transparent 40%, rgba(10,10,12,.55) 100%),
    linear-gradient(to bottom, rgba(10,10,12,.65) 0%, transparent 22%,
                    transparent 46%, rgba(10,10,12,.92) 88%);
}

/* ==========================================================================
   Vault FX — live glow over the baked neon ring

   Anchored to the footage by the same cover geometry as the scanner, so it
   holds position at any viewport. This is what carries the idle motion when
   the idle state is a still image rather than a clip: a CSS pulse loops
   perfectly, costs no bandwidth, and can respond to the user.
   ========================================================================== */

.vaultfx {
  position: absolute;
  left: var(--ring-x);
  top: var(--ring-y);
  width: var(--ring-d);
  height: var(--ring-d);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  z-index: 2;
  opacity: 0;

  border: max(2px, calc(var(--ring-d) * 0.012)) solid rgba(255, 92, 64, .9);
  box-shadow:
    0 0 calc(var(--ring-d) * .10) calc(var(--ring-d) * .012) rgba(255, 59, 33, .75),
    inset 0 0 calc(var(--ring-d) * .07) rgba(255, 59, 33, .55);

  /* Adds light to the ring already in the frame rather than covering it. */
  mix-blend-mode: plus-lighter;
}

/* Only breathes while the vault is shut and waiting — and only when the idle
   state is a still. If the idle clip carries its own pulse, a CSS pulse on top
   would beat against it. */
.idle-still[data-state="idle"] .vaultfx,
.idle-still[data-state="scanning"] .vaultfx {
  animation: ringPulse 2.8s ease-in-out infinite;
}

@keyframes ringPulse {
  0%, 100% { opacity: .22; filter: brightness(.85); }
  50%      { opacity: .85; filter: brightness(1.35); }
}

/* Holding accelerates the pulse — the vault reacting to the user is the one
   thing a pre-rendered loop cannot do. */
.idle-still[data-state="scanning"] .vaultfx { animation-duration: .55s; }

/* Once it opens, the footage takes over. */
[data-state="unlocking"] .vaultfx,
[data-state="revealing"] .vaultfx,
[data-state="register"]  .vaultfx { animation: none; opacity: 0; }

/* ==========================================================================
   Scanner — hold-to-unlock target
   ========================================================================== */

.scanner {
  position: absolute;
  left: var(--ov-x);
  top: var(--ov-y);
  transform: translate(-50%, -50%);
  width: var(--scan-size);
  height: var(--scan-size);
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 16px;
  background: radial-gradient(circle, rgba(210,21,2,.22) 0%, transparent 70%);
  color: var(--tb-red-hot);
  cursor: pointer;
  touch-action: none;          /* stop the hold turning into a scroll        */
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none; /* no "copy / look up" menu on long press     */
  -webkit-user-select: none;   /* no magnifier loupe, no selection handles   */
  user-select: none;
  z-index: 3;
  transition: opacity .35s var(--ease), transform .35s var(--ease);
}

.scanner:focus-visible {
  outline: 2px solid var(--tb-gold);
  outline-offset: 6px;
}

/* Matches the plate, not the hit area, so the outline sits exactly on the
   brackets in the footage even when the target is padded out for fingers. */
.scanner__ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--plate-size);
  height: var(--plate-size);
  transform: translate(-50%, -50%);
}

.scanner__track,
.scanner__progress {
  fill: none;
  stroke-width: 3;
  stroke-linecap: round;
}

.scanner__track { stroke: rgba(255,255,255,.16); }

.scanner__progress {
  stroke: var(--tb-red-hot);
  /* Rounded-rect perimeter: 2*(88-20) + 2*(88-20) + 2*PI*10 ≈ 335 */
  stroke-dasharray: 335;
  stroke-dashoffset: 335;
  filter: drop-shadow(0 0 6px var(--tb-red-hot));
}

.scanner__pad {
  position: relative;
  width: calc(var(--plate-size) * 0.5);
  color: #fff;
  opacity: .9;
  filter: drop-shadow(0 0 8px var(--tb-red-hot));
  overflow: hidden;
}
/* The glyph's drawn mass sits low in its own viewBox, so it reads as offset
   even though the box is perfectly centred on the plate. Nudge it optically. */
.scanner__pad svg {
  width: 100%;
  height: auto;
  display: block;
  transform: translateY(-7%);
}

/* Scan line sweeping across the fingerprint. Runs continuously while idle,
   then speeds up under the finger so the hold feels like it is doing work. */
.scanner__pad::after {
  content: "";
  position: absolute;
  left: -12%;
  right: -12%;
  height: 22%;
  top: -22%;
  background: linear-gradient(to bottom,
              transparent, rgba(255,120,90,.95), transparent);
  filter: blur(1px);
  animation: scanSweep 2.8s ease-in-out infinite;
}

@keyframes scanSweep {
  0%   { top: -22%; opacity: 0; }
  12%  { opacity: 1; }
  50%  { top: 100%; opacity: 1; }
  62%  { opacity: 0; }
  100% { top: 100%; opacity: 0; }
}

[data-state="scanning"] .scanner__pad::after { animation-duration: .55s; }

/* Ripple radiating out from the plate on each beat. */
.scanner::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--plate-size);
  height: var(--plate-size);
  margin: calc(var(--plate-size) / -2) 0 0 calc(var(--plate-size) / -2);
  border-radius: 16px;
  border: 1.5px solid rgba(255, 92, 64, .55);
  animation: ripple 2.8s ease-out infinite;
  pointer-events: none;
}

@keyframes ripple {
  0%   { transform: scale(.82); opacity: 0; }
  25%  { opacity: .75; }
  100% { transform: scale(1.55); opacity: 0; }
}

[data-state="scanning"] .scanner::after { animation-duration: .55s; }

[data-state="unlocking"] .scanner::after,
[data-state="revealing"] .scanner::after { animation: none; opacity: 0; }

/* Idle attract-loop. Suppressed while the user is actually holding. */
[data-state="idle"] .scanner { animation: breathe 2.6s var(--ease) infinite; }

@keyframes breathe {
  0%, 100% { transform: translate(-50%, -50%) scale(1);    opacity: .85; }
  50%      { transform: translate(-50%, -50%) scale(1.07); opacity: 1;   }
}

[data-state="scanning"] .scanner { animation: none; }

/* Once unlocking begins the target has done its job. */
[data-state="unlocking"] .scanner,
[data-state="revealing"] .scanner,
[data-state="register"]  .scanner {
  opacity: 0;
  pointer-events: none;
}

/* ==========================================================================
   Shell / layout
   ========================================================================== */

.shell {
  position: relative;
  z-index: 2;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  padding: 0 16px max(16px, env(safe-area-inset-bottom));
}

/* The overlay sits above the stage, so every layout container must be
   pointer-transparent or it swallows clicks meant for the scanner — the
   scanner lives inside .stage and cannot escape that stacking context.
   Only genuinely interactive blocks opt back in. */
.shell,
.topbar,
.content,
.footer,
.granted { pointer-events: none; }

/* Only interactive leaves opt back in — NOT the panels. A panel spans the
   whole column, so making it clickable puts an invisible sheet over the
   vault's scanner plate, which is CTA #1. That is exactly what happened:
   every mobile size reported the scanner as unreachable, with .panel--intro
   as the topmost element at its centre.

   The cards stay transparent to pointers too, so a tap that lands on a card
   overlapping the plate still reaches the vault. */
.panel--register,
.card--form,
.cta-frame,
.cta,
.tab,
.input,
.dial,
.select,
.form,
.brand,
.language-select,
.language-select button,
.footer a { pointer-events: auto; }

/* Header — logo left, language + age right, over a top scrim.

   Out of flow, as the design draws it: the offer card starts at y=75 and rides
   over the bottom of the 96px-tall bar rather than being pushed below it. */
.topbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 32px 16px;
  background: linear-gradient(to bottom, rgba(19,20,22,.9), rgba(19,20,22,0));
}

/* The design's box is 138.667x32. Both axes are set rather than letting the
   width follow the asset's own ratio (which is slightly wider, and rendered
   145px): an <img> scales an SVG into its box with preserveAspectRatio, so
   the mark is letterboxed, never stretched. */
.brand img { display: block; width: 138.667px; height: 32px; }

/* Language switcher sits alone in the top-right corner. */
.topbar__right {
  display: flex;
  align-items: center;
}

.language-select {
  display: grid;
  grid-template-columns: auto;
  position: relative;
  gap: 4px;
  z-index: 5;
  cursor: pointer;
}

.active-lang {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 6px 8px;
  border: 0;
  border-radius: 8px;
  background: var(--tb-red);
  color: #fff;
  filter: drop-shadow(0 2px 0 var(--tb-red-deep));
  cursor: pointer;
}

.active-lang span {
  font: 600 18px/1 var(--tb-display);
  text-transform: uppercase;
  color: #fff;
}

.active-lang__arrow {
  display: block;
  width: 16px;
  height: 16px;
  object-fit: contain;
  flex: none;
  transition: transform .2s ease;
}

.active-lang__arrow.rotate {
  transform: rotate(180deg);
}

.lang-list {
  display: grid;
  gap: 4px;
  padding: 4px 0;
  grid-template-columns: auto;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  border-radius: 8px;
  border: 1px solid var(--tb-red);
  background: #222;
  transition: max-height .3s ease, opacity .3s ease;
}

.lang-list.show {
  max-height: 500px;
  height: fit-content;
  opacity: 1;
}

.lang-list button {
  text-transform: uppercase;
  padding: 5px;
  font: 600 18px/1 var(--tb-display);
  color: #fff;
  background: transparent;
  border: none;
  cursor: pointer;
}

.language-select button:hover {
  border-radius: 6px;
  background: var(--tb-red);
  color: #fff;
}

.content {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 16px;
  width: 100%;
  max-width: var(--shell-max);
  margin: 0 auto;
  /* 75px is where the design puts the offer block's wrapper, which then adds
     its own 10px — landing the card at y=85 under the out-of-flow header. */
  padding: 85px 0 8px;
}

.panel { animation: rise .55s var(--ease) both; }

@keyframes rise {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
  width: 100%;
  padding: 24px 12px;
  border-radius: 8px;
  background: var(--tb-card);
  box-shadow: var(--tb-card-glow);
  text-align: center;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.card--offer {
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: center;
}

/* The countdown card: separate from the offer card on mobile, folded into it
   on desktop. The CTA is a sibling of the timer block, not a child, because
   desktop separates them by the card's own 54px rhythm. */
.card--lock {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  padding: 12px;
}

/* The design carries the logo in the topbar on mobile and at the head of the
   card on desktop — never both. */
.card__logo { display: none; height: 32px; width: auto; }

/* The design trims every display line to its cap height (Figma's
   `text-box-trim: trim-both` / `text-box-edge: cap alphabetic`). Without it
   the ascender and descender slack adds up — the idle card measured 641px
   against the design's 576. Browsers that do not support `text-box` simply
   get the untrimmed box and a slightly taller card. */
.form__title {
  margin: 0;
  width: 100%;
}

.form__head-content-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  width: 100%;
}

.form__head-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  width: 100%;
  margin-top: 0;
  text-align: center;
}

.form__head-content-wrap .offer1,
.form__head-content-wrap .offer2,
.form__head-content-wrap .offer3,
.form__head-content .offer1,
.form__head-content .offer2,
.form__head-content .offer3 {
  margin: 0;
  text-align: center;
  text-box: trim-both cap alphabetic;
}

/* Intro */
.panel--intro .form__head-content-wrap .offer1,
.panel--intro .form__head-content-wrap .offer2 {
  font: 800 clamp(26px, 8.6vw, 37px)/1.05 var(--tb-display);
  text-transform: uppercase;
  text-shadow: var(--tb-text-glow);
  text-wrap: balance;
  color: #fff;
}

/* Intro — больший отступ между offer1 и offer2 */
.panel--intro .form__head-content-wrap,
.panel--intro .form__head-content {
  gap: 0;
}

.panel--intro .form__head-content-wrap .offer1 + .offer2 {
  margin-top: 40px;
}

.panel--intro .form__head-content-wrap .offer3 {
  margin-top: 24px;
  font: 700 clamp(17px, 5.9vw, 22px)/1.2 var(--tb-display);
  text-transform: uppercase;
  color: #fff;
}

/* Reveal */
.panel--reveal .form__head-content-wrap .offer1 {
  font: 800 clamp(20px, 6.4vw, 24px)/1 var(--tb-display);
  text-transform: uppercase;
}

.panel--reveal .form__head-content-wrap .offer2 {
  font: 800 clamp(36px, 13vw, 58px)/1 var(--tb-display);
  text-transform: uppercase;
  text-shadow: var(--tb-text-glow);
}

.panel--reveal .form__head-content-wrap .offer3 {
  font: 700 clamp(17px, 5.9vw, 22px)/1.2 var(--tb-display);
  text-transform: uppercase;
}

/* Register claim block — same roles as TEMPLATER offer1/2/3 */
.card--claim .form__head-content-wrap .offer1 {
  font: 800 clamp(38px, 13.5vw, 64px)/1 var(--tb-display);
  text-transform: uppercase;
  text-shadow: 0 0 36.2px var(--tb-red), 1px 1px 0 #9A9A9A;
}

.card--claim .form__head-content-wrap .offer2 {
  font: 700 clamp(21px, 7.5vw, 28px)/1 var(--tb-display);
  text-transform: uppercase;
  color: var(--tb-red);
}

.card--claim .form__head-content-wrap .offer3 {
  font: 500 18px/1.3 var(--tb-display);
}

.card--offer .form__title,
.card--claim .form__title {
  width: 100%;
}

/* ==========================================================================
   Intro
   ========================================================================== */

/* Card at the top, action low: the vault's scanner plate sits mid-screen and
   is itself CTA #1, so the button must not land on top of it. */
.panel--intro {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 8vh;
}

.intro__action {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: 100%;
}

/* ==========================================================================
   Access granted
   ========================================================================== */

.granted {
  position: fixed;
  left: 50%;
  top: 15%;
  transform: translate(-50%, 0);
  display: none;
  align-items: center;
  gap: 9px;
  padding: 10px 20px 10px 14px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.14);
  background: linear-gradient(135deg, var(--tb-green) 0%, var(--tb-green-deep) 100%);
  color: #fff;
  font: 600 14px/1 var(--tb-display);
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 0 8px 40px rgba(22,163,74,.45);
  z-index: 4;
}

/* Confirms the scan, then clears out before the gold reveal lands so it is not
   still sitting there over the payoff.

   These keyframes must carry the translate(-50%) that centres the badge. The
   shared `rise` animation ends on `transform: none`, which wipes it out and
   leaves the badge starting at the centre and running off to the right. */
[data-state="unlocking"] .granted {
  display: flex;
  animation: grantedIn .4s var(--ease) both,
             grantedOut .5s var(--ease) 1.7s both;
}

@keyframes grantedIn {
  from { opacity: 0; transform: translate(-50%, 14px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

@keyframes grantedOut {
  to { opacity: 0; transform: translate(-50%, -22px); }
}

.granted__tick { width: 19px; height: 19px; }

/* ==========================================================================
   Reveal
   ========================================================================== */

.panel--reveal {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 4vh;
}

.reveal__head {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 0 8px;
}

/* Reveal: the icon is tucked LEFT of the offer1 kicker (a tab-like pin) while
   offer2/offer3 keep stacking beneath it. This is done purely with a
   `::before` on `.offer1` — no HTML change — so the JS text substitution and
   the dev language swap (which rewrite `.offer1`'s textContent and swap the
   `.form__head-content` blocks) keep working untouched. The standalone head
   row is hidden; its little star icon is recreated here. */
.panel--reveal .card--offer {
  display: flex;
  flex-direction: column;
}

.panel--reveal .reveal__head { display: none; }

.panel--reveal .form__head-content-wrap .offer1 {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.panel--reveal .form__head-content-wrap .offer1::before {
  content: "";
  flex: none;
  width: 34px; height: 34px;
  border: .425px solid var(--tb-red);
  border-radius: 17px;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23D21502'%3E%3Cpath d='M12 2.6l1.9 5.1 5.1 1.9-5.1 1.9L12 16.6l-1.9-5.1-5.1-1.9 5.1-1.9z'/%3E%3Cpath d='M18.6 15.2l.9 2.3 2.3.9-2.3.9-.9 2.3-.9-2.3-2.3-.9 2.3-.9.9-2.3z'/%3E%3Cpath d='M5.2 14.4l.7 1.8 1.8.7-1.8.7-.7 1.8-.7-1.8-1.8-.7 1.8-.7.7-1.8z'/%3E%3C/svg%3E")
    center / 17px 17px no-repeat,
    rgba(210,21,2,.07);
}

.reveal__icon {
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  flex: none;
  border: .425px solid var(--tb-red);
  border-radius: 17px;
  background: rgba(210,21,2,.07);
  color: var(--tb-red);
}

.reveal__icon svg { width: 17px; height: 17px; }

/* Timer block ------------------------------------------------------------
   On mobile the design gives this no fill of its own — it is plain content
   inside the lower card (node 1:1298), which is why there is no background
   here. Desktop turns it into the boxed panel the design draws there. */

.lockbox {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 0 8px;
}

.lock__line {
  margin: 0;
  font: 400 16px/1.35 var(--tb-body);
  text-wrap: balance;
}

.lock__timer {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font: 800 32px/1 var(--tb-display);
  font-variant-numeric: tabular-nums;
}

.lock__clock { width: 18px; height: 18px; color: var(--tb-red); }

.lockbox.is-expired .lock__timer { color: var(--tb-mist-dim); }

/* ==========================================================================
   Buttons

   The design frames the button: a 1px brand-red outline with 5px of padding
   around a solid fill, which reads as a machined bezel rather than a plain
   pill.
   ========================================================================== */

.cta-frame {
  width: 100%;
  max-width: 264px;
  padding: 5px;
  border: 1px solid var(--tb-red);
  border-radius: 4px;
}

.cta-frame--flat {
  max-width: none;
  padding: 0;
  border: 0;
}

.cta {
  display: grid;
  place-items: center;
  width: 100%;
  height: 46px;
  padding: 6px 5px;
  border: 0;
  border-radius: 4px;
  background: var(--tb-red);
  color: #fff;
  font: 600 16px/1 var(--tb-display);
  text-transform: uppercase;
  text-shadow: 0 .5px 0 var(--tb-red-deep);
  cursor: pointer;
  transition: filter .18s var(--ease), transform .18s var(--ease);
}

.cta-frame--flat .cta {
  height: 40px;
  border-radius: 8px;
  text-shadow: 0 1px 0 var(--tb-red-deep);
}

.cta:hover  { filter: brightness(1.1); }
.cta:active { transform: translateY(1px); }
.cta:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

/* ==========================================================================
   Registration
   ========================================================================== */

.panel--register {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

/* The claim block is bare on mobile — the design floats it above the card
   rather than inside one (node 1:1341). Desktop pulls it in. */
.card--claim {
  display: flex;
  flex-direction: column;
  gap: 17.376px;          /* the design's own value, odd but deliberate */
  padding: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.card--form {
  width: 100%;
  max-width: 340px;
  padding: 24px 16px 48px;
  border-radius: 16px;
  background: var(--tb-card-solid);
  box-shadow: var(--tb-card-glow-lg);
  text-align: left;
}

/* Tabs ------------------------------------------------------------------- */

.tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

.tab {
  flex: 1 0 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  height: 40px;
  padding: 14px 12px;
  border: 0;
  border-radius: 8px;
  background: var(--tb-field);
  color: var(--tb-tab-off);
  font: 600 13px/1 var(--tb-display);
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease);
}

.tab svg { width: 16px; height: 16px; flex: none; }
/* phone icon is slightly smaller than the email one, matching NESTJS */
#tabPhone svg { width: 14px; height: 14px; }

.tab.is-active { background: var(--tb-red); color: #fff; }

.tab:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* Fields ----------------------------------------------------------------- */

.form { display: flex; flex-direction: column; gap: 8px; }

.field-row { display: flex; gap: 8px; align-items: stretch; }

/* Promo-code column is hidden visually but still populated from the URL /
   landing data — mirrors TEMPLATER NESTJS (.container-inputs > .input-wrap). */
.field-row:has(#fPromo) { display: none; }

.input {
  flex: 1 0 0;
  min-width: 0;
  height: 40px;
  padding: 7px 12px;
  border: 0;
  border-radius: 8px;
  background: var(--tb-field);
  color: #fff;
  font: 400 16px/1 var(--tb-body);   /* 16px stops iOS zooming on focus */
}

.input::placeholder { color: var(--tb-mist-dim); }

.input:focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--tb-red);
}

/* Country dial button */
.dial {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 40px;
  padding: 7px 12px;
  border: 0;
  border-radius: 8px;
  background: var(--tb-field);
  cursor: pointer;
  flex: none;
}

.dial__flag {
  display: block;
  width: 26px; height: 20px;
  border-radius: 4px;
  overflow: hidden;
  background: #fff;
  flex: none;
}

.dial__flag svg,
.dial__flag img { display: block; width: 100%; height: 100%; object-fit: cover; }

.dial__code {
  font: 600 14px/1 var(--tb-display);
  color: #fff;
}

.dial__caret { width: 10px; height: 10px; color: var(--tb-red); flex: none; }

/* Bonus select */
.select {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 0 0;
  height: 40px;
  padding: 6px 12px;
  border-radius: 8px;
  background: var(--tb-field);
  cursor: pointer;
}

.select.is-open .select__caret { transform: rotate(180deg); }

.select__icon { width: 24px; height: 24px; flex: none; display: block; object-fit: contain; color: var(--tb-mist-dim); }
.select__caret { width: 10px; height: 10px; flex: none; color: var(--tb-red); transition: transform .2s var(--ease); }

.select__value {
  flex: 1 0 0;
  min-width: 0;
  color: var(--tb-mist-dim);
  font: 400 14px/1 var(--tb-body);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.select__options {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 20;
  margin: 0;
  padding: 4px;
  list-style: none;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,.08);
  background: var(--tb-field);
  box-shadow: 0 8px 24px rgba(0,0,0,.4);
  display: none;
}
.select.is-open .select__options { display: block; }

.select__options li {
  padding: 8px 10px;
  border-radius: 6px;
  color: #fff;
  font: 400 14px/1.2 var(--tb-body);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.select__options li:hover,
.select__options li:focus-visible { background: var(--tb-red); color: #fff; }
.select__options li.is-selected { color: var(--tb-red); }
.select__options li.is-selected:hover { color: #fff; }

.select__control {
  flex: 1 0 0;
  min-width: 0;
  border: 0;
  background: none;
  color: var(--tb-mist-dim);
  font: 400 14px/1 var(--tb-body);
  appearance: none;
  -webkit-appearance: none;
}

.select__control:focus { outline: none; }
.select:focus-within { box-shadow: 0 0 0 2px var(--tb-red); }

.err {
  margin: 0;
  font: 400 12px/1.2 var(--tb-body);
  color: var(--tb-red);
}
.err:empty { display: none; }

.input-wrap > .err {
  position: absolute;
  top: 0;
  left: 8px;
  z-index: 10;
  transform: translateY(-58%);
  white-space: nowrap;
  pointer-events: none;
}

.form .cta-frame { margin-top: 16px; }

.form__note {
  margin: 0;
  text-align: center;
  font: 500 12px/1.4 var(--tb-body);
  color: #fff;
}

.form__note a { color: var(--tb-red); text-decoration: none; }
.form__note a:hover { text-decoration: none; }

.input-wrap--promo .input { padding-left: 40px; }

.user-is-exist {
  display: none;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 30px;
  margin: 0 0 10px;
  padding: 4px 10px;
  border: 1px solid var(--tb-red);
  border-radius: 10px;
  background: #131416;
  color: var(--tb-red);
  font: 400 14px/1.2 var(--tb-body);
  text-align: center;
}

.form__confirm {
  margin: 20px 0 0;
  padding: 0;
  width: 100%;
}

.form__agree {
  position: relative;
  display: block;
}

.form__confirm p {
  margin: 0;
  text-align: center;
}

.form__confirm p,
.form__confirm p span,
.form__confirm p a {
  font: 400 12px/1.35 var(--tb-body);
  letter-spacing: normal;
  -webkit-text-size-adjust: 100%;
}

.form__confirm p span {
  color: #888;
}

.form__confirm p a {
  color: var(--tb-red);
  text-decoration: none;
}

.check-bocks-error {
  position: absolute;
  top: 0;
  right: 8px;
  z-index: 10;
  transform: translateY(-60%);
  margin: 0;
  padding: 1px 6px;
  border-bottom: 1px solid #393630;
  border-radius: 10px;
  background: #020202;
  color: var(--tb-red);
  font: 400 12px/1.2 var(--tb-body);
  opacity: 0;
  pointer-events: none;
}

.check-bocks-error:not(:empty) {
  opacity: 1;
}

.otp__edit {
  flex: 0 0 auto;
  margin-left: auto;
  padding: 0;
  border: none;
  background: none;
  color: var(--tb-red);
  font: 400 12px/1.4 var(--tb-body);
  text-decoration: underline;
  text-transform: none;
  cursor: pointer;
}

.modal__wrap {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 10000;
  align-items: center;
  justify-content: center;
  padding: 0 20px;
  background: rgba(0, 0, 0, 0.5);
}

.modal__wrap.active {
  display: flex;
}

.modal__wrap .modal {
  max-width: 500px;
  padding: 40px 24px;
  border-radius: 8px;
  background: #020202;
  text-align: center;
}

.modal__text {
  margin: 0;
  font: 700 16px/1.3 var(--tb-display);
  color: #fff;
}

.disabled-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.8);
}

.disabled-modal.active {
  display: flex;
}

.disabled-modal .modal-content {
  max-width: 476px;
  width: calc(100% - 20px);
  padding: 32px;
  border: 1px solid var(--tb-red);
  border-radius: 6px;
  background: rgba(2, 2, 4, 1);
  text-align: center;
}

.disabled-modal .title-content h2,
.disabled-modal .title-content span,
.disabled-modal .title-content p {
  color: #fff;
}

.disabled-modal .title-content a {
  display: inline-flex;
  justify-content: center;
  margin: 24px 0;
  padding: 10px 16px;
  border-radius: 6px;
  background: #fff;
  color: #050409;
  text-decoration: none;
}

/* ==========================================================================
   Phone verification — "Get a Code" / SMS entry / verified check

   The phone row grows a third child (the button) once the number looks
   valid, and disappears altogether once a code has been requested — the
   OTP block below takes its place. Submit stays dimmed until the number is
   verified; email skips all of this, per the design.
   ========================================================================== */

.otp-hint {
  margin: 0 0 8px;
  text-align: center;
  font: 400 12.5px/1.35 var(--tb-body);
  color: var(--tb-green);
}
.otp-hint:empty { display: none; }

.field-row--phone { flex-wrap: wrap; }

.input-wrap {
  position: relative;
  display: flex;
  flex: 1 0 0;
  min-width: 120px;
}

.input-wrap .input { padding-right: 34px; }

.input-wrap .input-icon {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  object-fit: contain;
  pointer-events: none;
}

.input-wrap:has(.input-icon) .input {
  padding-left: 44px;
}

.input-check {
  position: absolute;
  top: 50%;
  right: 10px;
  width: 18px;
  height: 18px;
  transform: translateY(-50%);
  color: var(--tb-green);
  pointer-events: none;
}

.get-code {
  flex: none;
  height: 40px;
  padding: 0 14px;
  border: 0;
  border-radius: 8px;
  background: var(--tb-red);
  color: #fff;
  font: 600 13px/1 var(--tb-display);
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: filter .18s var(--ease);
}
.get-code:hover  { filter: brightness(1.1); }
.get-code:active { transform: translateY(1px); }
.get-code[hidden] { display: none; }

/* Submit is a plain flat CTA already; dim it while phone is unverified so the
   design's "disabled until valid" read comes through without a second style. */
.cta:disabled {
  opacity: .55;
  cursor: not-allowed;
  filter: none;
}
.cta:disabled:hover { filter: none; }

/* SMS code entry --------------------------------------------------------- */

.otp {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 0;
  margin-bottom: 0;
  padding-bottom: 8px;
}

.otp__hint {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 8px;
  font: 400 14px/1.4 var(--tb-body);
  text-transform: none;
}

.otp__hint-text {
  color: var(--tb-mist-dim);
}

.otp__phone { color: var(--tb-green); font-weight: 600; }

.otp__inputs {
  display: flex;
  justify-content: center;
  gap: 8px;
  min-width: 0;
  width: 100%;
}

.otp__input {
  flex: 0 0 48px;
  width: 48px;
  height: 48px;
  min-width: 48px;
  max-width: 48px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 8px;
  background: var(--tb-field);
  color: #fff;
  text-align: center;
  font: 700 20px/1 var(--tb-display);
  outline: none;
  caret-color: var(--tb-red);
}

.otp__input::placeholder {
  color: rgba(255, 255, 255, 0.25);
}

.otp__input:focus {
  outline: none;
  border-color: var(--tb-red);
  color: #fff;
}

.otp__input.error-state {
  border-color: var(--tb-red);
  color: var(--tb-red);
}

.otp__err:empty { display: none; }

.otp__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}

.otp__timer {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font: 400 13px/1 var(--tb-body);
  color: var(--tb-mist-dim);
}

.otp__timer span:last-child {
  color: var(--tb-green);
  font-variant-numeric: tabular-nums;
}

.otp__clock { width: 15px; height: 15px; flex: none; color: var(--tb-mist-dim); }

.otp__resend {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--tb-red);
  font: 400 13px/1 var(--tb-body);
  text-decoration: underline;
  cursor: pointer;
}

.otp__resend:disabled {
  color: var(--tb-mist-dim);
  text-decoration: none;
  cursor: default;
  opacity: .6;
}

/* ==========================================================================
   Registration success modal
   ========================================================================== */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  padding: 24px 16px;
  background: rgba(10,10,11,.82);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.modal-backdrop[hidden] { display: none; }

.modal-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 380px;
  padding: 32px 24px;
  border-radius: 16px;
  background: var(--tb-card-solid);
  box-shadow: var(--tb-card-glow-lg);
  text-align: center;
}

.modal-card__logo { width: 110px; height: auto; }

.modal-card__title {
  margin: 8px 0 0;
  font: 800 22px/1.2 var(--tb-display);
  text-transform: uppercase;
  text-shadow: 0 0 20px var(--tb-red), 1px 1px 0 #9A9A9A;
}

.modal-card__ok {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 0;
  color: var(--tb-green);
  font: 600 14px/1 var(--tb-display);
  line-height: 1;
}

.modal-card__ok-icon {
  flex: none;
  display: block;
  width: 15px;
  height: 15px;
}

.modal-card__ok #successOk {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  color: var(--tb-green);
}

.modal-card__desc {
  margin: 4px 0 8px;
  color: var(--tb-mist-dim);
  font: 400 14px/1.45 var(--tb-body);
}

.creds {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
}

.cred {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
}

.cred__label {
  font: 600 11px/1 var(--tb-body);
  letter-spacing: .04em;
  color: var(--tb-mist-dim);
  text-transform: uppercase;
}

.cred__field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  height: 42px;
  padding: 0 12px;
  border-radius: 8px;
  background: var(--tb-field);
  border: 1px solid transparent;
  transition:
    border-color 0.15s ease,
    box-shadow 0.15s ease,
    background-color 0.15s ease;
}

.cred__field.is-copied {
  border-color: var(--tb-green);
  box-shadow: 0 0 0 1px rgba(30, 168, 75, 0.35);
  background: rgba(30, 168, 75, 0.08);
}

.cred__value {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  overflow: hidden;
  color: #fff;
  font: 400 14px/1 var(--tb-body);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cred__field.is-copied .cred__value {
  color: var(--tb-green);
}

.cred__copy {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--tb-mist-dim);
  cursor: pointer;
}

.cred__copy svg {
  display: block;
  flex: none;
  width: 16px;
  height: 16px;
}
.cred__copy:hover, .cred__copy.is-copied { color: var(--tb-green); }

.modal-card .cta-frame { margin-top: 4px; }
.modal-card .cta { text-decoration: none; }

.modal-card__note {
  margin: 0;
  color: var(--tb-mist-dim);
  font: 400 11.5px/1.4 var(--tb-body);
}

/* ==========================================================================
   Footer
   ========================================================================== */

.footer {
  width: 100%;
  max-width: var(--shell-max);
  margin: 0 auto;
  padding-top: 14px;
  text-align: center;
}

.footer p {
  margin: 0;
  font: 400 11.5px/1.55 var(--tb-body);
  color: #6D737D;
}

.footer a {
  color: #8A9099;
  text-decoration: none;
}

/* reCAPTCHA v3 is invisible but still drops its badge in the corner. The
   registration keeps sending the token — we just don't show the widget,
   exactly like TEMPLATER API NESTJS (css/components/assets.css). */
.rc-anchor-container,
.grecaptcha-badge {
  opacity: 0 !important;
}

/* ==========================================================================
   Tap to begin (autoplay blocked)
   ========================================================================== */

.tapstart {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  width: 100%;
  border: 0;
  background: rgba(19,20,22,.88);
  color: #fff;
  font: 700 17px/1 var(--tb-display);
  letter-spacing: .04em;
  text-transform: uppercase;
  cursor: pointer;
}

.tapstart[hidden] { display: none; }

/* ==========================================================================
   State visibility
   ========================================================================== */

[data-state="unlocking"] .panel--intro,
[data-state="revealing"] .panel--intro,
[data-state="register"]  .panel--intro { display: none; }

[data-state="register"] .panel--reveal { display: none; }

/* ==========================================================================
   Reduced motion — skip the cinematics entirely
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .clip { display: none; }
  .clip--still { display: block; opacity: 1; }
}

/* ==========================================================================
   Desktop — vault left, card right

   Everything here is transcribed from the Figma desktop frames (1920x1080):
   Dynamic 1 node 1:1150, Dynamic 2 node 1:1212, Dynamic 3 node 1:485. Sizes
   are written as the design's own pixel values multiplied by --fu, so the
   whole composition scales as one piece instead of each element drifting on
   its own clamp.

   app.js places the vault so its right edge clears the card's left edge —
   it measures the card rather than trusting a constant here, so this
   breakpoint and the video stay in step on their own.
   ========================================================================== */

@media (min-width: 900px) and (orientation: landscape) {
  :root {
    /* One pixel of the 1920x1080 Figma frame, in viewport terms.
       - the vw term reproduces the design exactly on a 16:9 screen
       - the vh term keeps the tall reveal card (718 design px) inside short
         landscape windows instead of letting it overflow
       - the 1.25px ceiling stops the whole thing ballooning past QHD */
    --fu: min(0.0520833vw, 0.0925926vh, 1.25px);

    /* Card 820 design px wide. Symmetric side gutters (4.7917vw = 92/1920)
       like the reference topbet-vault-lp build: this sits the form and the
       language control further right and keeps the background less zoomed. */
    --shell-max: calc(820 * var(--fu));
    --desktop-shell-left: 4.7917vw;
  }

  .shell {
    padding-inline: var(--desktop-shell-left);
  }

  /* The design centres each card on the viewport, not on the space left over
     below a header. Lifting the bar and the legal line out of the flow puts
     the card's midpoint back on the 540 line the Figma draws it at. */
  .topbar, .footer {
    position: absolute;
    left: var(--desktop-shell-left);
    right: var(--desktop-shell-left);
    width: auto;
    margin: 0;
    padding-inline: 0;
  }

  .topbar {
    top: calc(76 * var(--fu));               /* chip top edge, y=76 */
    padding-block: 0;
    background: none;                        /* the design has no desktop scrim */
  }

  .footer { bottom: 16px; }

  /* Logo moves into the card, so the bar carries only the language control —
     which the design right-aligns with the card's right edge. */
  .topbar .brand { display: none; }
  .topbar { justify-content: flex-end; }

  .lang__btn {
    gap: calc(8 * var(--fu));
    padding: calc(8 * var(--fu)) calc(12 * var(--fu));
    border-radius: calc(8 * var(--fu)) calc(8 * var(--fu)) 0 0;
    font-size: calc(24 * var(--fu));
    filter: none;
  }
  .lang__caret { width: calc(16 * var(--fu)); height: calc(16 * var(--fu)); }

  /* Open state is drawn as one continuous chip: the trigger keeps square
     bottom corners and the list hangs off it with no gap. */
  .lang__menu {
    top: 100%;
    min-width: calc(74 * var(--fu));
    padding: calc(10 * var(--fu)) calc(12 * var(--fu));
    border-radius: 0 0 calc(8 * var(--fu)) calc(8 * var(--fu));
    background: var(--tb-ink);
    box-shadow: none;
  }
  .lang__option {
    padding: calc(4 * var(--fu));
    border-radius: calc(4 * var(--fu));
    font-size: calc(24 * var(--fu));
  }

  .content {
    margin: 0 0 0 auto;
    justify-content: center;
    padding: 0;
  }

  /* The design collapses each state into ONE card on desktop — logo, copy and
     CTA together — with the vault beside it rather than behind it. The panel
     becomes that card and its inner blocks go transparent. */
  .panel--intro,
  .panel--reveal,
  .panel--register {
    align-items: center;
    justify-content: flex-start;
    flex: 0 0 auto;
    gap: calc(54 * var(--fu));
    padding: calc(48 * var(--fu)) calc(24 * var(--fu));
    border-radius: calc(16 * var(--fu));
    background: var(--tb-card);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
  }

  .panel--intro   { box-shadow: 0 4px calc(200 * var(--fu)) 0 rgba(210,21,2,.8); }
  .panel--reveal  { box-shadow: 0 4px calc(250 * var(--fu)) 0 var(--tb-red); }

  /* Registration is a narrower card in the design — 548 wide against 820 —
     and denser. Right edge stays put so the card does not jump between
     states; only the left edge moves in. */
  .panel--register {
    width: calc(548 * var(--fu));
    margin-left: auto;
    padding-bottom: calc(54 * var(--fu));
    background: var(--tb-card-solid);
    box-shadow: 0 4px calc(200 * var(--fu)) 0 rgba(210,21,2,.8);
  }

  .panel--intro .card,
  .panel--reveal .card,
  .panel--register .card {
    width: 100%;
    padding: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* Safe: app.js keeps the vault clear of this card, so it cannot cover the
     scanner plate. */
  .panel--intro,
  .panel--reveal,
  .panel--register { pointer-events: auto; }

  /* Idle card — node 1:1150 --------------------------------------------- */

  .card__logo {
    display: block;
    width: calc(266 * var(--fu));
    height: calc(61 * var(--fu));
  }

  .card--offer { gap: calc(24 * var(--fu)); }

  .form__head-content-wrap,
  .form__head-content {
    gap: calc(24 * var(--fu));
  }

  .panel--intro .form__head-content-wrap,
  .panel--intro .form__head-content {
    gap: 0;
  }

  .panel--intro .form__head-content-wrap .offer1 + .offer2 {
    margin-top: calc(48 * var(--fu));
  }

  .panel--intro .form__head-content-wrap .offer1,
  .panel--intro .form__head-content-wrap .offer2 {
    font-size: calc(90 * var(--fu));
    line-height: normal;
    text-shadow: 0 0 calc(50 * var(--fu)) var(--tb-red), 2px 2px 0 #9A9A9A;
  }

  .panel--intro .form__head-content-wrap .offer3 {
    margin-top: calc(24 * var(--fu));
    font-size: calc(44 * var(--fu));
    line-height: normal;
    color: var(--tb-red);
  }

  .panel--reveal .form__head-content-wrap .offer3 { display: none; }

  .intro__action { width: auto; }

  /* Reveal card — node 1:1212 -------------------------------------------- */

  .panel--reveal .card--offer { gap: calc(34 * var(--fu)); }

  .reveal__head {
    margin-bottom: calc(12 * var(--fu));
  }

  .reveal__icon {
    width: calc(80 * var(--fu));
    height: calc(80 * var(--fu));
    border-width: 1px;
    border-radius: 50%;
  }
  .reveal__icon svg { width: calc(40 * var(--fu)); height: calc(40 * var(--fu)); }

  .panel--reveal .form__head-content-wrap .offer1::before {
    width: calc(80 * var(--fu));
    height: calc(80 * var(--fu));
    border-width: 1px;
    border-radius: 50%;
    background-size: calc(40 * var(--fu)) calc(40 * var(--fu));
  }

  .panel--reveal .form__head-content-wrap .offer1 {
    font-size: calc(74 * var(--fu));
    line-height: normal;
  }

  .panel--reveal .form__head-content-wrap .offer2 {
    font-size: calc(120 * var(--fu));
    line-height: normal;
    text-shadow: 0 0 calc(50 * var(--fu)) var(--tb-red), 2px 2px 0 #9A9A9A;
  }

  /* Desktop boxes the countdown; mobile leaves it bare. */
  .card--lock { display: contents; }

  .lockbox {
    width: calc(572 * var(--fu));
    gap: calc(8 * var(--fu));
    padding: calc(20 * var(--fu)) calc(32 * var(--fu));
    border: 1px solid rgba(255,255,255,.07);
    border-radius: calc(20 * var(--fu));
    background: #161312;
  }

  .lock__line  { font-size: calc(18 * var(--fu)); font-family: var(--tb-body); }
  .lock__timer { gap: calc(8 * var(--fu)); font-size: calc(32 * var(--fu)); }
  .lock__clock { width: calc(18 * var(--fu)); height: calc(18 * var(--fu)); }

  /* Buttons — 528x64 inside a 10px frame, on both idle and reveal --------- */

  /* Shrink to the button rather than the card: the frame is a bezel around a
     528px button, not a full-width bar. */
  .cta-frame {
    width: auto;
    max-width: none;
    padding: calc(10 * var(--fu));
    border-radius: calc(8 * var(--fu));
  }

  .cta {
    width: calc(528 * var(--fu));
    height: calc(64 * var(--fu));
    border-radius: calc(8 * var(--fu));
    font-size: calc(26 * var(--fu));
    text-shadow: 0 1px 0 var(--tb-red-deep);
  }

  /* Registration card — node 1:485 --------------------------------------- */

  .panel--register .card--claim {
    gap: calc(24 * var(--fu));
    text-align: center;
  }

  .card--claim .form__head-content {
    gap: calc(24 * var(--fu));
  }

  .card--claim .form__head-content-wrap .offer1 {
    font-size: calc(90 * var(--fu));
    line-height: normal;
    text-shadow: 0 0 calc(50 * var(--fu)) var(--tb-red), 2px 2px 0 #9A9A9A;
  }

  .card--claim .form__head-content-wrap .offer2 {
    font-size: calc(44 * var(--fu));
    line-height: normal;
  }

  .card--claim .form__head-content-wrap .offer3 {
    font-size: calc(28 * var(--fu));
    line-height: normal;
  }

  .panel--register .card--form {
    max-width: none;
    padding: 0 calc(8 * var(--fu));
    text-align: left;
  }

  .tabs { gap: calc(8 * var(--fu)); margin-bottom: calc(16 * var(--fu)); }

  .tab {
    gap: calc(10 * var(--fu));
    height: calc(48 * var(--fu));
    padding: calc(14 * var(--fu)) calc(12 * var(--fu));
    border-radius: calc(8 * var(--fu));
    font-size: calc(16 * var(--fu));
  }
  .tab svg { width: calc(24 * var(--fu)); height: calc(24 * var(--fu)); }
  #tabPhone svg { width: calc(22 * var(--fu)); height: calc(22 * var(--fu)); }

  .form { gap: calc(8 * var(--fu)); }
  .field-row { gap: calc(8 * var(--fu)); }

  .input, .dial, .select {
    height: calc(46 * var(--fu));
    border-radius: calc(8 * var(--fu));
  }
  .input  { padding: calc(14 * var(--fu)) calc(12 * var(--fu)); font-size: calc(14 * var(--fu)); }
  .dial   { padding: calc(7 * var(--fu)) calc(12 * var(--fu));  gap: calc(10 * var(--fu)); }
  .select { padding: calc(6 * var(--fu)) calc(12 * var(--fu));  gap: calc(10 * var(--fu)); }

  .dial__flag  { width: calc(26 * var(--fu)); height: calc(20 * var(--fu));
                 border-radius: calc(4 * var(--fu)); }
  .dial__caret,
  .select__caret { width: calc(10 * var(--fu)); height: calc(10 * var(--fu)); }
  .select__icon  { width: calc(24 * var(--fu)); height: calc(24 * var(--fu)); }
  .select__value,
  .select__control { font-size: calc(14 * var(--fu)); }

  .select__options {
    top: calc(100% + 4 * var(--fu));
    padding: calc(4 * var(--fu));
    border-radius: calc(8 * var(--fu));
    border-width: 1px;
  }
  .select__options li {
    padding: calc(8 * var(--fu)) calc(10 * var(--fu));
    border-radius: calc(6 * var(--fu));
    font-size: calc(14 * var(--fu));
  }

  /* The registration CTA is full-bleed and unframed, unlike the other two. */
  .form .cta-frame { margin-top: calc(16 * var(--fu)); padding: 0; border: 0; }
  .form .cta {
    width: 100%;
    height: auto;
    padding: calc(12 * var(--fu)) calc(10 * var(--fu));
    font-size: calc(20 * var(--fu));
  }

  .form__note { margin-top: calc(24 * var(--fu)); font-size: calc(14 * var(--fu)); }

  .form__confirm {
    margin-top: calc(20 * var(--fu));
    width: 100%;
  }

  .form__confirm p,
  .form__confirm p span,
  .form__confirm p a {
    font-size: calc(11 * var(--fu));
    line-height: 1.35;
  }

  /* Phone verification, scaled with the rest of the form — no separate
     Figma desktop measurements were available for this part, so these
     follow the same --fu ratio as the sibling controls above. */
  .otp-hint { margin-bottom: calc(8 * var(--fu)); font-size: calc(14 * var(--fu)); }

  .otp {
    gap: calc(16 * var(--fu));
    padding-bottom: calc(8 * var(--fu));
  }

  .otp__hint,
  .otp__timer,
  .otp__resend {
    font-size: calc(14 * var(--fu));
  }

  .otp__edit {
    font-size: calc(12 * var(--fu));
  }

  .otp__inputs {
    gap: calc(8 * var(--fu));
  }

  .otp__input {
    flex: 0 0 calc(48 * var(--fu));
    width: calc(48 * var(--fu));
    height: calc(48 * var(--fu));
    min-width: calc(48 * var(--fu));
    max-width: calc(48 * var(--fu));
    font-size: calc(20 * var(--fu));
    border-radius: calc(8 * var(--fu));
  }

  .input-wrap .input { padding-right: calc(34 * var(--fu)); }

  .input-wrap .input-icon {
    width: calc(24 * var(--fu));
    height: calc(24 * var(--fu));
    left: calc(10 * var(--fu));
    object-fit: contain;
  }

  .input-wrap:has(.input-icon) .input,
  .email-input-wrap .input {
    padding-left: calc(44 * var(--fu));
  }
  .input-check { right: calc(10 * var(--fu)); width: calc(20 * var(--fu)); height: calc(20 * var(--fu)); }

  /* Phone field on desktop keeps the flag selector and the number input as
     TWO separate fields with a visible gap between them (as on mobile), while
     the "Get a Code" button nests on the right inside the number field —
     TEMPLATER NESTJS style. */
  .field-row--phone {
    position: relative;
    flex-wrap: nowrap;
    gap: calc(8 * var(--fu));
  }

  .field-row--phone .dial {
    position: relative;
    flex: none;
  }

  .field-row--phone .dial__code { display: none; }

  .field-row--phone .input-wrap {
    position: relative;
    flex: 1 1 auto;
  }

  .field-row--phone .input-wrap .input {
    padding-right: calc(92 * var(--fu));
  }

  .get-code {
    position: absolute;
    top: 50%;
    right: calc(4 * var(--fu));
    transform: translateY(-50%);
    z-index: 2;
    height: calc(34 * var(--fu));
    padding: 0 calc(12 * var(--fu));
    border-radius: calc(8 * var(--fu));
    font-size: calc(14 * var(--fu));
  }

  .otp__clock { width: calc(16 * var(--fu)); height: calc(16 * var(--fu)); }

  .modal-card {
    gap: calc(20 * var(--fu));
    max-width: calc(600 * var(--fu));
    padding: calc(56 * var(--fu)) calc(32 * var(--fu));
    border-radius: calc(16 * var(--fu));
  }
  .modal-card__logo { width: calc(140 * var(--fu)); }
  .modal-card__title { font-size: calc(36 * var(--fu)); }
  .modal-card__ok {
    gap: calc(8 * var(--fu));
    font-size: calc(16 * var(--fu));
    line-height: 1;
  }

  .modal-card__ok-icon {
    width: calc(17 * var(--fu));
    height: calc(17 * var(--fu));
  }

  .modal-card__ok #successOk {
    line-height: 1;
  }
  .modal-card__desc { max-width: calc(460 * var(--fu)); font-size: calc(16 * var(--fu)); }
  .creds {
    flex-direction: row;
    gap: calc(20 * var(--fu));
    margin-top: calc(12 * var(--fu));
  }
  .cred {
    flex: 1 1 0;
    min-width: 0;
    gap: calc(8 * var(--fu));
  }
  .cred__label { font-size: calc(13 * var(--fu)); }
  .cred__field { height: calc(52 * var(--fu)); padding: 0 calc(14 * var(--fu)); border-radius: calc(8 * var(--fu)); }
  .cred__value { font-size: calc(16 * var(--fu)); }
  .cred__copy {
    width: calc(24 * var(--fu));
    height: calc(24 * var(--fu));
  }
  .cred__copy svg {
    width: calc(17 * var(--fu));
    height: calc(17 * var(--fu));
  }
  .modal-card .cta-frame { margin-top: calc(12 * var(--fu)); }
  .modal-card .cta-frame--flat .cta { height: calc(56 * var(--fu)); border-radius: calc(8 * var(--fu)); font-size: calc(22 * var(--fu)); }
  .modal-card__note { font-size: calc(13 * var(--fu)); }

  .footer { max-width: none; }

  /* The vault sits left, so weight the scrim to the right where the copy is. */
  .stage__vignette {
    background:
      radial-gradient(70% 90% at 30% 50%, transparent 30%, rgba(19,20,22,.45) 100%),
      linear-gradient(to right, rgba(19,20,22,.35) 0%, transparent 22%,
                      rgba(19,20,22,.82) 62%, rgba(19,20,22,.94) 100%);
  }
}
