/* ═══════════════════════════════════════════════════════════════
   THE CAMPAIGN'S STYLESHEET

   Replace everything below. This file and campaign/main.js are the mechanic;
   nothing else in the repo is.

   What ships here is the demo: a line of copy and a button that opens the
   registration dialog, so there is something working to replace rather than
   an empty file.

   ── The rules that still apply to you ────────────────────────
   1. NO HEX LITERALS, no rgb()/rgba(), and no --orange-N / --navy-N. Ask for
      the semantic tokens in css/tokens.css: --accent, --accent-on-light,
      --surface, --text, --text-muted, --danger, --r-md, --ease-out, --font.
      `python tools/tokens.py --check` fails the build on any of them, and it
      exists because two landings ago three stylesheets each invented their
      own "same" green.

   2. Prefix your classes `cmp-`. The shell owns `tw-`.

   3. Small text obeys the AA table: never #00a75c or #ff4500 on white for
      body copy — use --green-on-light and --accent-on-light. The 500s stay
      hero colours for large type and filled shapes.

   4. Any input you add is at least 16px, or iOS Safari zooms the page on
      focus.

   ── Choosing a layout ────────────────────────────────────────
   Four modes; the shell supports all four and assumes none.

   A. SCROLLING PAGE — the default. Do nothing. The header, your <main> and
      the footer are ordinary blocks. Add `position: sticky; top: 0` to
      .tw-hdr here if you want the bar to follow.

   B. FIXED STAGE, NEVER SCROLLS — uncomment the css/stage.css <link> in
      index.html. body becomes a flex column, the two bars keep their height
      and #tw-main takes the rest. Size things against the stage container
      (cqh/cqw) INSIDE your own elements; the shell stays px.

   B2. THE FOLD — uncomment css/fold.css instead. #tw-main is a definite box,
      92svh minus the header, the page scrolls and the footer starts below the
      fold with only its top edge showing. Same cqh/cqw container (tw-main) as
      mode B. Link one of the two, never both.

   C. ARTBOARD UNIT — for a hero that is a pixel-exact Figma comp. Define
      the unit here and multiply Figma's own numbers by it:

        #tw-main {
          --u: min(calc(100vw / 375), calc(100svh / 666));
        }
        @media (min-width: 900px) {
          #tw-main { --u: min(calc(100vw / 1920), calc(100svh / 1080)); }
        }
        .cmp-thing { width: calc(240 * var(--u)); }

      Use it only for the mechanic. Text the visitor reads stays in real px:
      viewport units break browser zoom.
   ═══════════════════════════════════════════════════════════════ */


/* ══ campaign palette ══════════════════════════════════════════
   DEVIATION (owner, 2026-10-17; docs/BRIEF.md): the Halloween design uses
   colours css/tokens.css does not have. They are defined ONCE, here, sampled
   from Figma 21:533, and every rule below reaches them by name. No hex or
   rgb() anywhere else in campaign/ -- campaign/cards.css keeps its own
   card-only block the same way. */
:root {
  --cmp-bg:        #131e3b;   /* page ground below the hero */
  --cmp-blue:      #006aff;   /* step digits, step titles, today card */
  --cmp-blue-hi:   #007bff;   /* carousel arrows, date pill */
  --cmp-blue-deep: #012b67;   /* today card, bottom of its gradient */
  --cmp-blue-line: #09449d;   /* step dividers */
  --cmp-cta-hi:    #ff8301;   /* GET BONUS gradient, top; bottom is --accent */
  --cmp-night:     #000100;   /* hero ground under the art, bg 21:600 */
  --cmp-step-text: #a8c4f0;   /* "How does it work?" body copy, 21:541 */
  --cmp-ring-from: #1e40af;   /* language button ring, 21:645 gradient start */
  --cmp-ring-to:   #3b82f6;   /* language button ring, 21:645 gradient end */
  --cmp-ring-brd:  #1e2a44;   /* language button inner border, 21:646 */
}

/* ══ page ═════════════════════════════════════════════════════
   Layout A, a scrolling page: hero, "How does it work?", the shell footer.
   Three bands of our own. campaign/cards.css uses the same queries, word
   for word, so the hero and the cards never disagree again (they did: the
   cards turned desktop at 768 while the hero stayed a column up to 1023,
   and the iPad got desktop cards over the fisherman):

     COLUMN   the phone comp (21:587): title, cards, the fisherman under
              them. Phones, and any window under 1024 that is not a short
              landscape one:
                (max-width: 1023.98px) and (orientation: portrait),
                (max-width: 1023.98px) and (min-height: 500.02px),
                (max-width: 559.98px)
     TABLET   COLUMN from 600px: the same comp at 1.25x, with the desktop
              card (k = 1), so 768-834 is not a phone strip in the middle
              of a dark screen.
     WIDE     the desktop comp (21:533): title and fisherman left, cards
              right. From 1024, and on a phone held sideways, where the
              column would put GET BONUS under a 390px screen:
                (min-width: 1024px),
                (min-width: 560px) and (max-height: 500px) and (orientation: landscape)

   WIDE is Figma's 1440 frame scaled by the window, s = width / 1440, up to
   1440 (above it the whole page is zoomed, see "wide screens" at the end):
   the hero keeps the frame's 1440:652 shape, so the painted art fills it at
   every width instead of leaving a black band above it, and positions are
   Figma's px times s, written min(N/1440 * 100vw, Npx). Text stays real px.
   The shell's 700px still moves its own two bars. Safe areas (a phone's
   notch and home bar, viewport-fit=cover) pad the header, hero and footer. */

body { background: var(--cmp-bg); }

#tw-main { background: var(--cmp-bg); color: var(--text); }


/* ══ header over the hero ═════════════════════════════════════
   Figma draws no bar: the Halloween logo and a round flag button stand on
   the art. The shell's markup is untouched; only its box is restyled here.
   Absolute, so <main> starts at the top of the page under it. 21:616: the
   row is 16px in from the left and the flag ends 39px from the right. */

.tw-hdr {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: auto;
  padding: calc(6px + env(safe-area-inset-top)) max(37px, calc(16px + env(safe-area-inset-right))) 0 max(16px, env(safe-area-inset-left));
  background: none;
  border: 0;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* The plain wordmark (Figma 35:832, 220.917x44 = 5.02:1). On a phone it is
   the footer's size, 132x26; the link is a 44px-tall flex box so the tap
   target stays 44 while the mark is smaller. */
.tw-hdr__logo { display: flex; align-items: center; min-height: 44px; }
.tw-hdr__logo img { display: block; width: 132px; height: auto; aspect-ratio: 220.917 / 44; }

/* The sign-up card shows the same file; the shell's boxes (101x18, 247x44)
   are not 5.02:1, so only the height is fixed. */
.tw-promo__logo { width: auto; height: 18px; }
@media (min-width: 560px) { .tw-promo__logo { width: auto; height: 44px; } }

/* The language trigger as Figma draws it (21:618): the current flag in a
   blue ring, 40px on a phone. The button itself stays 44px for the tap
   target; the ring (::before) and the flag (::after) are drawn inside it,
   2px in. The globe, the code and the caret are hidden; the button keeps
   its aria-label. */
html[lang="uk"] { --cmp-flag: url("../assets/img/icons/flag-ua.svg"); }
html[lang="ru"] { --cmp-flag: url("../assets/img/icons/flag-ru.svg"); }
html[lang="en"] { --cmp-flag: url("../assets/img/icons/flag-gb.svg"); }

.tw-lang {
  --ring-in: 2px;
  position: relative;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
}

.tw-lang > * { display: none; }

.tw-lang::before,
.tw-lang::after {
  content: "";
  position: absolute;
  border-radius: 50%;
}

.tw-lang::before {
  inset: var(--ring-in);
  background: linear-gradient(119deg, var(--cmp-ring-from) 6%, var(--cmp-ring-to) 100%);
}

.tw-lang::after {
  inset: calc(var(--ring-in) + 2px);
  border: 1.4px solid var(--cmp-ring-brd);
  background: var(--cmp-flag) center / cover no-repeat;
}


/* ══ hero ═════════════════════════════════════════════════════
   Layers, back to front: the frame (forest, fisherman, cobweb -- and on
   WIDE the title -- one raster), the fades in ::after, then the title
   and the cards. */

.cmp-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 700 is the mobile frame (21:588). The plate hangs from the bottom edge,
     so a taller carousel pushes the fisherman down with it rather than
     under the cards. */
  min-height: 700px;
  padding: 62px max(16px, env(safe-area-inset-right)) 0 max(16px, env(safe-area-inset-left));
  background: var(--cmp-night);
}

.cmp-hero__art { position: absolute; z-index: 0; pointer-events: none; }

.cmp-hero__art img { width: 100%; height: auto; max-width: none; }

/* The 375x700 frame (21:600), hung from the hero's bottom so a taller
   carousel pushes the fisherman down rather than under the cards. Phones up
   to 430 scale it with the width (the top it loses is empty sky); past 430
   it stops growing -- at 560 the fisherman rose under the cards -- and its
   side edges fade into the ground instead of showing as a strip. */
.cmp-hero__art {
  --plate: clamp(375px, 100%, 430px);
  left: calc(50% - var(--plate) / 2);
  bottom: 0;
  width: var(--plate);
}
.cmp-hero__art img { aspect-ratio: 1500 / 2800; }
@media (min-width: 431px) and (max-width: 1023.98px) and (orientation: portrait),
       (min-width: 431px) and (max-width: 1023.98px) and (min-height: 500.02px),
       (min-width: 431px) and (max-width: 559.98px) {
  .cmp-hero__art { mask-image: linear-gradient(to right, transparent, #000 18%, #000 82%, transparent); }
}

/* 21:604 (navy from the top, 443px), 21:603 (black, last 55px of the
   plate) and 21:605 (navy, last 85px): the art fades into --cmp-bg. */
.cmp-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(to top, var(--cmp-bg), transparent 85px),
    linear-gradient(to top, var(--cmp-night), transparent 55px),
    linear-gradient(to bottom, var(--cmp-bg), transparent 427px);
}

.cmp-hero__title,
.cmp-hero > .cmp-cards { position: relative; z-index: 2; }

/* 21:610: 332x98, and the card's top (144) tucks 16px under it (160). */
.cmp-hero__title { width: 332px; max-width: 100%; margin-bottom: -16px; }

.cmp-hero__title-img { width: 100%; height: auto; aspect-ratio: 768 / 227; }

.cmp-hero > .cmp-cards { width: 100%; }


/* ══ How does it work? ════════════════════════════════════════
   21:591 on mobile, 21:537 on desktop. The digits are decoration; the step
   titles say the number in words. Line heights are Figma's "normal" for
   Roboto (1.17); the self-hosted cut's own normal is 1.2, which made every
   step 3-5px taller than the comp. */

.cmp-how { padding: 32px max(0px, env(safe-area-inset-right)) 32px max(0px, env(safe-area-inset-left)); }

.cmp-how__title {
  margin-bottom: 32px;
  padding: 0 16px;
  color: var(--accent);
  font-size: 22px;
  font-weight: var(--fw-xbold);
  line-height: 1.18;
  text-align: center;
  text-transform: uppercase;
}

.cmp-how__steps { display: grid; gap: 24px; }

.cmp-step {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 16px;
  padding-bottom: 24px;
  border-bottom: 2px solid var(--cmp-blue-line);
  line-height: 1.172;
}

.cmp-step:last-child { padding-bottom: 0; border-bottom: 0; }

/* 21:595 is 122 tall with 24 + 2 under it: the digit's box is 96. */
.cmp-step__num {
  flex: none;
  color: var(--cmp-blue);
  font-size: 84px;
  font-weight: var(--fw-black);
  line-height: 96px;
}

.cmp-step__txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 10px; }

/* Figma draws 22px here. --cmp-blue on --cmp-bg is 3.53:1, which is AA
   only as large text, and 24px is where medium-weight text becomes large.
   The line stays Figma's 22px one (26px), so the step keeps its height. */
.cmp-step__title { color: var(--cmp-blue); font-size: 24px; font-weight: var(--fw-medium); line-height: 26px; }

.cmp-step__text { color: var(--cmp-step-text); font-size: 16px; font-weight: var(--fw-regular); text-wrap: pretty; }


/* ══ footer ═══════════════════════════════════════════════════
   The shell's footer on the page's own ground (21:548 / 21:598), with
   Figma's spacing. The header, the footer and the card all use the plain wordmark through
   brand.logo; the footer keeps its own painted copy (21:550), the same
   vectors, so the shell's img stays hidden. */

.tw-ftr {
  gap: 32px;
  padding: 40px max(24px, calc(16px + env(safe-area-inset-right))) calc(32px + env(safe-area-inset-bottom)) max(24px, calc(16px + env(safe-area-inset-left)));
  background: var(--cmp-bg);
}

.tw-ftr__copy { padding-top: 32px; }

/* Figma's badges are 36 tall and 94 wide for Visa with the 1px stroke
   inside; the shell's 10px padding plus the border made each one 2px
   wider, the row 8px wider than 21:563. */
.tw-ftr__badge { padding-inline: 9px; }

.tw-ftr__logo {
  width: 132px;
  height: 26px;
  padding: 0;
  border-radius: 0;
  background: url("assets/page/logo-footer.svg") center / contain no-repeat;
  filter: none;
}

.tw-ftr__logo img { visibility: hidden; }


/* ══ TABLET: the column at 1.25x ══════════════════════════════
   Every number below is the phone's times 1.25; the card is the desktop
   card (cards.css, k = 1). */

@media (min-width: 600px) and (max-width: 1023.98px) and (orientation: portrait),
       (min-width: 600px) and (max-width: 1023.98px) and (min-height: 500.02px) {

  .tw-hdr__logo img { width: 160px; }

  .cmp-hero { min-height: 875px; padding-top: 77.5px; }

  .cmp-hero__art { --plate: clamp(469px, 100%, 537.5px); }

  .cmp-hero__title { width: 415px; margin-bottom: -20px; }

  /* Long lines on a wide screen: the steps keep a reading measure. */
  .cmp-how__steps { max-width: 720px; margin-inline: auto; }
}


/* ══ WIDE ═════════════════════════════════════════════════════ */

@media (min-width: 1024px),
       (min-width: 560px) and (max-height: 500px) and (orientation: landscape) {

  /* The 1440 frame's 80px gutter, scaled; 1280 of content above it. */
  :root {
    --cmp-pad: max(min(5.556vw, 80px), calc((100% - 1280px) / 2));
  }

  /* Same grid as the hero below it. The logo centres over the carousel
     box (not the column): 21:643 sits at 828..1094 and the active card at
     823..1098. */
  .tw-hdr {
    max-width: 1440px;
    margin-inline: auto;
    padding: calc(min(2.708vw, 39px) + env(safe-area-inset-top))
             max(min(5.556vw, 80px), env(safe-area-inset-right)) 0
             max(min(5.556vw, 80px), env(safe-area-inset-left));
    display: grid;
    grid-template-columns: minmax(0, 503fr) minmax(0, 777fr);
    gap: 0;                    /* the shell's 10px flex gap would shift column 2 */
    align-items: start;
  }

  .tw-hdr__logo {
    --logo-w: clamp(125px, 15.34vw, 221px);
    grid-column: 2;
    justify-self: start;
    /* Figma 35:832: 62px down at 1440 (the header pads 39). */
    margin-top: calc(min(4.306vw, 62px) - min(2.708vw, 39px));
    /* The box is min(754px, column 2); written in vw, not %, because
       WebKit's zoom (above 1440) applies twice to a % inside calc() here. */
    margin-left: calc((min(754px, (100vw - 2 * min(5.556vw, 80px)) * 777 / 1280) - var(--logo-w)) / 2);
  }
  .tw-hdr__logo img { width: var(--logo-w); }

  /* 21:644: 62px down, 40px in from the carousel box's right edge, which
     is the column's edge until the column outgrows the 754px box (1402+).
     A 44px ring here, as Figma draws it (44.7). */
  .tw-hdr__right {
    position: absolute;
    top: calc(min(4.306vw, 62px) + env(safe-area-inset-top));
    right: calc(max(min(5.556vw, 80px), env(safe-area-inset-right))
                + max(0px, (100% - 2 * min(5.556vw, 80px)) * 777 / 1280 - 754px)
                + min(2.778vw, 40px));
  }
  .tw-lang { --ring-in: 0px; }

  /* 21:534: 1440x652. The cards' column starts where the left arrow does
     (583 - 80 = 503 of 1280); the active card's top is at 137 and the
     title's foot 23px above the hero's. The min-height is the frame's
     shape (652/1440 of the width); a taller carousel still grows it.
     Not aspect-ratio: with overflow hidden that would fix the height and
     clip the cards on a short screen. */
  .cmp-hero {
    display: grid;
    grid-template-columns: minmax(0, 503fr) minmax(0, 777fr);
    align-items: start;
    min-height: min(45.28vw, 652px);
    padding: min(9.514vw, 137px)
             max(var(--cmp-pad), env(safe-area-inset-right))
             min(1.597vw, 23px)
             max(var(--cmp-pad), env(safe-area-inset-left));
  }

  /* The whole 1440x652 frame (21:658), full width and hung from the
     bottom: the hero has its shape, so it fills it (a window's scrollbar
     leaves a few px of the frame's own black top row above it). */
  .cmp-hero__art { left: 0; bottom: 0; width: 100%; }
  .cmp-hero__art img { aspect-ratio: 5760 / 2608; }

  /* The frame carries its own fade into the page (its last row is
     --cmp-bg); a fade on top would dim the title painted into it. */
  .cmp-hero::after { display: none; }

  /* 21:666: 530x296 at x 99 of 1440, i.e. 19px into a 503px column --
     41.4% of the 1280 container, so it scales with it. */
  .cmp-hero__title {
    grid-column: 1;
    grid-row: 1;
    align-self: end;
    width: 105.4%;
    max-width: none;
    margin: 0 0 0 3.8%;
  }

  .cmp-hero__title-img { aspect-ratio: 1376 / 768; }

  /* The lettering is painted into the desktop frame: the <h1> keeps its
     place in the grid and its alt for screen readers, and paints nothing. */
  .cmp-hero__title { opacity: 0; }

  .cmp-hero > .cmp-cards { grid-column: 2; grid-row: 1; }
}

/* A phone held sideways: 390px of screen. The cards start 56px down (the
   logo moves to the left column, above the painted title), so GET BONUS
   ends near y 343 and stays on screen even with Safari's bars showing.
   The carousel is taller than the frame here, so the frame hangs from the
   top and the ground below it is the page's navy, which its last row
   already fades into (hung from the bottom it left a black band on top). */
@media (min-width: 560px) and (max-height: 500px) and (orientation: landscape) and (max-width: 1023.98px) {
  .cmp-hero { padding-top: 56px; padding-right: max(16px, env(safe-area-inset-right)); background: var(--cmp-bg); }
  .cmp-hero__art { top: 0; bottom: auto; }
  .cmp-how__steps { max-width: 720px; margin-inline: auto; }
  .tw-hdr__logo { --logo-w: 120px; grid-column: 1; margin-left: 0; margin-top: 0; }
}

/* ══ How it works, wide ════════════════════════════════════════
   21:537: 1280 wide, title 28px, 40px down to the steps; the footer
   starts right under them (21:548 y 851 = 21:537 bottom; its own 48px top
   padding is the whole gap). Three 403px steps 35.5px apart, centred on
   each other, with a 118px divider in the middle of each gap. */
@media (min-width: 1024px) {

  .cmp-how { padding: 0 max(var(--cmp-pad), env(safe-area-inset-right)) 0 max(var(--cmp-pad), env(safe-area-inset-left)); }

  .cmp-how__title { margin-bottom: 40px; padding: 0; font-size: 28px; }

  .cmp-how__steps {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: center;
    gap: 0 35.5px;
  }

  .cmp-step {
    gap: 14px;
    margin: 0;
    padding: 0;
    border-bottom: 0;
  }

  .cmp-step + .cmp-step::before {
    content: "";
    position: absolute;
    left: -18.75px;
    top: 50%;
    width: 2px;
    height: 118px;
    margin-top: -59px;
    background: var(--cmp-blue-line);
  }

  .cmp-step__num { font-size: 104px; line-height: 1.172; }
  .cmp-step__txt { gap: 14px; }
  .cmp-step__title { line-height: 1.172; }
  .cmp-step__text { font-size: 18px; }
}

/* 1280+: the steps share one row height and the digit stays centred in it,
   but the text hangs from the row's top. Centred, a step with fewer wrapped
   lines (step 1) started its title up to 31px below the others. */
@media (min-width: 1280px) {
  .cmp-how__steps { align-items: stretch; }
  .cmp-step__txt { align-self: flex-start; }
}

/* 1024-1279: three 280-ish columns cannot hold a 104px digit beside the
   text (the Russian step 2 ran to 11 lines). The digit goes above, the
   steps hang from one top line, the dividers run their full height. */
@media (min-width: 1024px) and (max-width: 1279.98px) {
  .cmp-how__steps { align-items: start; }
  .cmp-step { flex-direction: column; align-items: flex-start; gap: 4px; }
  .cmp-step__num { font-size: 72px; line-height: 1; }
  .cmp-step + .cmp-step::before { top: 0; height: 100%; margin-top: 0; }
}

/* The footer turns into its row at the shell's own breakpoint, not ours.
   21:548: 48 / 120 / 40. The logo and the copyright share a 26px line at
   the top (footer-bottom, items-center), the payments column is
   left-aligned (21:561, items-start) beside it. These restate the shell's
   wide values because the mobile rules above outrank its media query by
   coming later. */
@media (min-width: 700px) {
  .tw-ftr {
    align-items: flex-start;
    gap: 40px;
    padding: 48px max(clamp(24px, 8.33vw, 120px), env(safe-area-inset-right)) calc(40px + env(safe-area-inset-bottom)) max(clamp(24px, 8.33vw, 120px), env(safe-area-inset-left));
  }
  .tw-ftr__copy { padding-top: 0; line-height: 26px; text-align: right; }
  .tw-ftr__pay { align-items: flex-start; }
  .tw-ftr__label { text-align: left; }
}

/* One line from the desktop layout up: the shell gives the copy a zero
   flex basis, so it only got what the logo and payments left over and wrapped
   (right-aligned) when that was short. Auto basis + nowrap makes it claim its
   own width, and the row shrinks the gap around it instead. */
@media (min-width: 1024px) {
  .tw-ftr__copy { flex: 1 0 auto; white-space: nowrap; }
}


/* ══ wide screens ═════════════════════════════════════════════
   Figma "Desktop - 1920" (27:1072) is the 1440 frame (21:533) times
   1920/1440, nothing moved: above 1440 the whole page grows with the
   window, and the owner wants the same on 2560x1440. CSS zoom on <html>
   scales layout and text together, so browser zoom still works on top.
   tan(atan2(a, b)) is a / b as a plain number, the portable way to divide
   two lengths in CSS; it applies before the first paint (it used to be set
   by main.js after it, a 0.14 layout shift at 1920). A browser without CSS
   trigonometry ignores the line and main.js sets the same value. */
@media (min-width: 1440.02px) {
  html { zoom: tan(atan2(100vw, 1440px)); }
}
