/*
 * togetu.com — single shared stylesheet (landing + legal + contact + FAQ pages).
 * Layout follows the Figma "Design Preview 2.0" system (pill navbar, the
 * slanted FORGET band, stacked cards, the green crowd section, giant-wordmark
 * footer).
 * Brand rules that bind every change here:
 *   - Solid brand colors only, NO gradients (logo manual).
 *   - The logotype gets NO added outlines/shadows; approved colors only.
 *   - PiePie: headlines/labels ONLY, never paragraphs. Raleway: all body text.
 *   - Hard shadows on buttons/stickers: solid black, zero blur.
 * Fonts are self-hosted: PiePie per Fontspring webfont EULA (woff2/woff only),
 * Raleway under OFL. Do not swap these files for desktop formats.
 */

/* ── Fonts ── */
@font-face {
  font-family: 'PiePie';
  src: url('../fonts/piepie-regular-webfont.woff2') format('woff2'),
       url('../fonts/piepie-regular-webfont.woff') format('woff');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Raleway';
  /* the woff2 is the same variable font at 40% of the size (fontTools); the
     ttf stays for browsers without woff2 */
  src: url('../fonts/Raleway-VariableFont_wght.woff2') format('woff2'),
       url('../fonts/Raleway-VariableFont_wght.ttf') format('truetype');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ── Brand tokens ── */
:root {
  --indigo: #9172F7;
  --green:  #B0FFAF;
  --blue:   #3A39FF;
  --orange: #FF5B23;
  --mauve:  #DCB8FF;
  --black:  #000000;
  --white:  #FFFFFF;
  --pie: 'PiePie', 'Arial Black', sans-serif;
  --body: 'Raleway', 'Helvetica Neue', Arial, sans-serif;
  /* the slanted cuts (the FORGET band's edges, the footer's end): the rise
     is a share of the width, so the angle is the same on every screen */
  --slant: 8vw;
}

/* ── Reset-ish ── */
* { margin: 0; padding: 0; box-sizing: border-box; }
/* html paints the canvas behind safe areas + overscroll on iOS, and the
   phone's top strip AND bottom bar wear this color on every section (they
   paint themselves with the page color, never with the content beneath):
   keep it brand purple. One color serves both ends; the page ending black
   under a purple bar is the accepted trade. */
html {
  scroll-behavior: smooth;
  background: var(--indigo);
  overflow-x: hidden;
  overflow-x: clip;
}
body {
  font-family: var(--body);
  font-size: 1.06rem;
  line-height: 1.55;
  color: var(--black);
  background: var(--indigo); /* same as html: the top strip and overscroll stay purple */
  overflow-x: hidden;
  overflow-x: clip;
}
/* the curtain (js/curtain.js): while html.loading is on, the page is hidden
   and the purple canvas is all that shows; it lifts once the fonts and the
   hero mascot are in, so the first paint is already in PiePie and Raleway */
html.loading body { visibility: hidden; }
/* with the curtain script present the mascot stills never paint: they are a
   different pose than the animations, and js/anim.js decides per box whether
   the animation replaces them or (on failure, or with motion reduced) the
   still comes back, by inline style, which outranks this rule */
html.loading .art[data-anim] img, html.lifted .art[data-anim] img { display: none; }
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

/* the landing pages speak in bold Raleway throughout; utility pages
   (legal, contact) keep the regular weight for long reading */
.landing { font-weight: 700; }
h1, h2 {
  font-family: var(--pie);
  text-transform: uppercase;
  line-height: 0.98;
  letter-spacing: 0.01em;
  overflow-wrap: break-word;
}
/* every PiePie element: PiePie has ONE weight, and the landing's bold body
   would otherwise make the browser synthesize a fake bold that distorts it */
h1, h2, .wordmark, .menu-btn, .menu-title, .menu-x, .menu-links a,
.btn, .footer-mark, .deal-card, .loot-card, .bubble, .faq summary { font-weight: normal; }
/* one span per line in big display headlines (the hero fitter creates them,
   other headlines write them by hand): wrapping is structurally impossible,
   and the air between lines lives here rather than in line-height, so the
   block's outer edges stay put */
.hl { display: block; white-space: nowrap; }
.hl + .hl { margin-top: 0.12em; }

/* ── Navbar (Figma: floating white pill) ── */
/* The pill floats free - NO painted band behind it (owner rule): the top of
   the screen shows whatever content genuinely scrolls past. The fixed
   header starts 12px BELOW the viewport's top edge and never touches it:
   iOS Safari copies the background of a fixed element touching that edge
   onto the phone's top strip (a transparent one falls back to the body's
   background), which is how the strip would wear one fixed color over every
   section if the header were anchored at the edge. The pill sits BEHIND the
   page content (hero text and mascot scroll over it); the menu overlay
   still stacks above all. On the landing the whole header waits OFF-SCREEN
   to the left, fades into place near the end of the black FORGET band and
   fades back out on the way up (js/menu.js) - it is moved aside, never merely hidden,
   because a fixed element hidden at the top edge via opacity, visibility,
   or color still tints the strip. The fade is the bar's only motion. */
.site-header {
  position: fixed;
  top: 12px; left: 12px; right: 12px;
  z-index: 1; /* below .hero .sec-inner (2): content passes over the pill */
  transition: opacity 0.6s ease; /* entering: the move-aside jumps, then the fade */
}
/* off-screen AND transparent. Leaving: the fade runs in place and only then
   the move-aside jumps (delayed transform). 40px clears the pill's shadow so
   nothing peeks in at the left edge */
.site-header--hidden {
  transform: translateX(calc(-100% - 40px));
  opacity: 0;
  transition: opacity 0.6s ease, transform 0s linear 0.6s;
}
.navpill {
  max-width: 1360px;
  margin: 0 auto;
  background: var(--white);
  border-radius: 999px;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.14);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 10px 8px 24px;
}
.wordmark {
  font-family: var(--pie);
  font-size: 2rem;
  line-height: 1;
  text-decoration: none;
  text-transform: uppercase;
  color: var(--black);
}
.nav-links, .nav-lang { display: none; } /* the desktop bar's parts: the phone shows the wordmark and the menu button only */
.nav-right { display: flex; align-items: center; gap: 10px; }

/* menu opener: a labeled pill, not an icon (owner call: icons read as noise) */
.menu-btn {
  font-family: var(--pie);
  font-size: 1.05rem;
  text-transform: uppercase;
  background: var(--black);
  color: var(--white);
  border: none;
  border-radius: 999px;
  padding: 10px 14px;
  cursor: pointer;
}

@media (min-width: 900px) {
  /* a taller pill with bigger type, and the same 32px on both sides so the
     first link and the language switch sit at equal distances from the edges */
  .navpill { display: grid; grid-template-columns: 1fr auto 1fr; padding: 14px 32px; }
  .nav-links {
    display: flex;
    gap: 36px;
    font-weight: 700;
    font-size: 1.1rem;
  }
  .nav-links a { text-decoration: none; }
  .navpill .wordmark { justify-self: center; font-size: 2.4rem; }
  .nav-right { justify-self: end; }
  /* the page you are on is underlined; the homepage has no link of its own */
  .nav-links a[aria-current="page"] { text-decoration: underline; text-decoration-thickness: 3px; text-underline-offset: 6px; }
  /* the language switch replaces the menu: every link lives in the bar, so
     there is no menu on desktop */
  .nav-lang { display: inline-block; font-weight: 700; font-size: 1.1rem; text-decoration: none; }
  .menu-btn { display: none; }
}

/* ── Full-screen menu (indigo takeover) ── */
.menu {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--indigo);
  display: flex;
  flex-direction: column;
  padding: 12px 12px 44px; /* same offsets as .site-header: the header pill
                              appears to stay put while the page falls away */
  overflow-y: auto;
  overflow-x: hidden; /* links fly in from beyond the left edge */
}
.menu[hidden] { display: none; }
.menu .menu-bar { margin-bottom: 44px; width: 100%; }
.menu-title {
  font-family: var(--pie);
  font-size: 2rem;
  line-height: 1;
  text-transform: uppercase;
  color: var(--black);
}
/* close affordance = the app's bare PiePie "X" (see sleep_screen.dart) */
.menu-x {
  font-family: var(--pie);
  font-size: 2rem; /* matches .menu-title */
  line-height: 1;
  text-transform: uppercase;
  background: none;
  border: none;
  color: var(--black);
  cursor: pointer;
  padding: 5px 14px;
}
.menu-links { display: flex; flex-direction: column; gap: 28px; padding: 0 24px; }
.menu-links a {
  font-family: var(--pie);
  font-size: clamp(1.9rem, 8.5vw, 3rem);
  line-height: 1;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--black);
  /* the cascade replays on every open: the menu is display:none while
     closed, so the animation restarts when it is shown */
  animation: fly-in-l 0.35s cubic-bezier(0.2, 0.8, 0.3, 1) both;
}
.menu-links a:nth-child(2) { animation-delay: 0.05s; }
.menu-links a:nth-child(3) { animation-delay: 0.1s; }
.menu-links a:nth-child(4) { animation-delay: 0.15s; }
.menu-links a:nth-child(5) { animation-delay: 0.2s; }
.menu-links a:nth-child(6) { animation-delay: 0.25s; }
@media (prefers-reduced-motion: reduce) { .menu-links a { animation: none; } }

body.menu-open { overflow: hidden; }

/* ── Shared section scaffolding ── */
/* a white floor under every section: section edges land on fractional
   pixels, and while the glitch animates, the slanted sections become their
   own layers, whose edges no longer meet the next section pixel-exactly.
   Without the floor the body's purple shows through that hairline. */
main { background: var(--white); }
.sec { padding: 96px 24px; }
.sec-inner { max-width: 1200px; margin: 0 auto; }
/* a .tilt section slides up over the one above it and cuts its own top
   edge on a slant (low on the left, high on the right); the corner it
   gives up shows the section above. The rise is a share of the width, so
   the angle is the same on every screen, and the top padding grows by the
   rise so the content stays clear of the cut. */
.tilt {
  margin-top: calc(-1 * var(--slant));
  padding-top: calc(96px + var(--slant));
  clip-path: polygon(0 var(--slant), 100% 0, 100% 100%, 0 100%);
}
.art { align-self: center; width: min(64vw, 320px); }
@media (prefers-reduced-motion: reduce) {
  .art img { animation: none; }
}

/* ── Hero ── */
/* keep a sliver of plain indigo padding above the mascot's layout box: a
   transformed layer starting exactly at y=0 can turn Safari's top glass
   white. The navbar never overlaps the hero (it waits off-screen), so the
   top padding is breathing room only, not a reserve. The poster owns the
   first screen: purple down to the bottom edge AND under the browser's
   bottom bar. iOS ends the viewport above the strip around the home
   indicator, so the hero overshoots one viewport by 60px and the bottom
   padding carries the same 60px: the button stays put, and whatever the
   glass bar shows beneath it is purple. */
.hero {
  background: var(--indigo);
  padding: 16px 24px 152px;
  min-height: calc(100vh + 60px);
  overflow-x: clip; /* headline lines fly in from beyond the screen edges */
  display: flex;
  flex-direction: column;
}
/* hero is centered on every size (the whole app is centered; owner call) */
.hero .sec-inner {
  flex: 1; /* fills the first screen so the slack below can be distributed */
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 28px;
  align-items: center;
  text-align: center;
  position: relative;
  z-index: 2; /* above the always-behind navbar pill */
}
/* the screen's slack goes above the actions: mascot and headline stay together
   at the top, the two buttons at the bottom - never a lump under the buttons */
.hero .actions { margin-top: auto; }
/* baseline is deliberately conservative (fits the longest line everywhere
   even with JS absent); the fitter in anim.js then sizes UP to the measured
   maximum for the actual device. Side padding = the never-hug-edges floor. */
.hero h1 { font-size: clamp(2rem, 8.5vw, 4.5rem); padding: 0 4px; }
/* key words wear slot orange (the scroll-word, the white section's triad),
   the app's key-word treatment */
.ow { color: var(--orange); }
.brand { color: var(--indigo); } /* the brand's own purple on the promise's closing words */

/* the scale lives on OUR wrapper: lottie-web writes an inline transform
   onto its own svg, which silently overrides any stylesheet rule there.
   Origin top => growth goes DOWN, keeping the document's top edge clean;
   the negative bottom margin pulls the headline over the canvas dead space */
.hero .art {
  width: min(80vw, 380px);
  /* top pulls the mascot up, so the two buttons fit on the first screen; the
     headline follows after the column gap (the canvas has dead margins, so the
     gap reads smaller than the number) */
  margin: -32px 0 0;
  /* the approved size: big but fully contained - the drawing stays inside
     the screen on every phone; grows in place, no repositioning */
  transform: scale(1.4);
  transform-origin: center;
}
/* one wide bar, not a floating pill: the page's two big actions (the
   poster's and the green section's). The label is ONE line on every phone:
   it sits a touch above body size and shrinks with the bar where the bar is
   too narrow for it. 14 is the longest label ("ZAPIŠ SE NA ČEKAČKU!",
   about 13.6em in PiePie) plus a margin, so a longer label means measuring
   again; 54px is the bar's side padding and frame; 48px the section's. */
.hero .btn, .join .btn { /* two-part selectors: the generic .btn rule below sets the size back to inherit otherwise */
  width: 100%;
  max-width: 480px;
  padding: 15px 24px;
  white-space: nowrap;
  font-size: min(calc(1em + 3px), calc((min(100vw - 48px, 480px) - 54px) / 14));
}
/* ── Desktop hero: two columns on one screen, the mascot on the left,
   headline and the two buttons on the right. The headline lines shrink to
   their text (the fitter measures them against the column: js/anim.js)
   and the column is wide so the headline can run big. ── */
@media (min-width: 900px) {
  .hero { min-height: 100vh; padding: 0 48px; }
  .hero .sec-inner {
    max-width: none; /* the poster uses the whole window; the mascot gets the room on the right */
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.7fr); /* the headline's size follows this column's width */
    column-gap: 48px;
    align-content: center;
    align-items: center;
    text-align: left;
  }
  .hero h1 { grid-column: 2; grid-row: 1; margin: 0; padding: 0; }
  .hero h1 .hl { width: max-content; }
  .hero .actions { grid-column: 2; grid-row: 2; margin-top: 36px; justify-content: flex-start; }
  .hero .art {
    grid-column: 1;
    grid-row: 1 / span 2;
    justify-self: center;
    width: min(34vw, 520px); /* the 1.4 scale grows it to ~700px */
    margin: 0;
    transform: translate(-56px, -148px) scale(1.4); /* translate BEFORE scale: these are screen pixels, left and up of its cell */
  }
}

/* ── Hero entrance + the scroll-word's twitch ──
   Armed by js/anim.js: .hero--arm hides the lines and the button the
   moment the script runs; .hero--in plays them once the display font is in
   (bounded wait). Without JS neither class exists and nothing is hidden.
   Reading order, with a real pause: lines fly in alternating from left and
   right, the button rises a beat later, and the twitch starts only once the
   button is in; the run is over in a second and a half. */
.hero--arm h1 .hl, .hero--arm .btn { opacity: 0; }
.hero--in h1 .hl { animation: fly-in-l 0.35s cubic-bezier(0.2, 0.8, 0.3, 1) both; }
.hero--in h1 .hl:nth-child(2) { animation-name: fly-in-r; animation-delay: 0.1s; }
.hero--in h1 .hl:nth-child(3) { animation-delay: 0.2s; }
.hero--in .btn { animation: rise-in 0.3s ease-out 1s both; }
/* shared entrances: the headline lines and the menu links */
@keyframes fly-in-l { from { opacity: 0; transform: translateX(-100vw); } to { opacity: 1; transform: none; } }
@keyframes fly-in-r { from { opacity: 0; transform: translateX(100vw); } to { opacity: 1; transform: none; } }
@keyframes rise-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
/* the scroll-word is the antagonist: once in, it twitches - nervous bursts
   between stills: jolts, skews, a brand-blue / black split (black = the
   brand's hard-shadow color) and blackouts flashing at the peaks (hard
   offsets, no blur). Discrete steps make it a glitch, not a wobble. */
.hero h1 .ow { display: inline-block; }
.hero--in h1 .ow { animation: twitch 2.8s steps(1, end) 1.5s infinite; }
@keyframes twitch {
  0%, 100% { transform: none; text-shadow: none; }
  2%  { transform: translate(-4px, 2px) rotate(-2deg) skewX(-6deg); text-shadow: 5px 0 var(--blue), -5px 0 var(--black); }
  4%  { transform: translate(4px, -2px) skewX(8deg); text-shadow: none; }
  6%  { transform: translate(-2px, 3px) rotate(1.5deg); text-shadow: -6px 0 var(--blue), 6px 0 var(--black); }
  8%  { transform: translate(3px, 1px) skewX(-10deg); color: var(--black); text-shadow: -4px 0 var(--blue); }
  10% { transform: translate(-3px, -2px) rotate(-1deg); color: var(--orange); text-shadow: 4px 0 var(--black); }
  12% { transform: translate(1px, 0); text-shadow: none; }
  14% { transform: none; }
  44% { transform: translate(2px, -3px) rotate(1.2deg) skewX(6deg); text-shadow: -4px 0 var(--blue), 4px 0 var(--black); }
  46% { transform: translate(-3px, 2px); text-shadow: 3px 0 var(--black); }
  48% { transform: none; text-shadow: none; }
  74% { transform: translate(-3px, 1px) skewX(-8deg); color: var(--black); text-shadow: 5px 0 var(--blue); }
  76% { transform: translate(3px, -2px) rotate(-1.5deg); color: var(--orange); text-shadow: -5px 0 var(--blue), 5px 0 var(--black); }
  78% { transform: translate(-1px, 2px); text-shadow: none; }
  80% { transform: none; }
}

/* ── The page's two actions: the trailer and the waitlist ──
   The same two buttons everywhere: stacked bars on phones, side by side on
   desktops (wrapping under each other when the column is too narrow). The
   waitlist button is the one the store link replaces once the app is out. */
.actions { display: flex; flex-direction: column; align-items: center; gap: 16px; width: 100%; }
@media (min-width: 900px) {
  .actions { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 20px; }
  .hero .btn, .join .btn { width: auto; max-width: none; padding: 18px 40px; font-size: 1.5rem; }
}

/* ── The trailer action's wiggle: a shake the moment the button is there,
   then again every few seconds, so the eye finds it. It animates `rotate`,
   not `transform`, so it never fights the hero's rise-in (transform) or the
   press (translate); the hero button lists both animations because one
   `animation` declaration replaces the other, and starts the shake as the
   rise-in ends. ── */
.wiggle { animation: wiggle 2.4s ease-in-out infinite; }
.hero--in .btn.wiggle { animation: rise-in 0.3s ease-out 1s both, wiggle 2.4s ease-in-out 1.3s infinite; }
@keyframes wiggle {
  0%, 20%, 100% { rotate: 0deg; }
  4% { rotate: -5deg; }
  8% { rotate: 5deg; }
  12% { rotate: -3deg; }
  16% { rotate: 2deg; }
}
@media (prefers-reduced-motion: reduce) { .wiggle { animation: none; } }

/* ── The trailer player (js/trailer.js): the whole screen, black, the video
   fitted inside, and a close button that is always there whatever the video
   is doing - the player's only way out besides Esc and the video's end. ── */
.trailer {
  position: fixed;
  inset: 0;
  z-index: 200; /* above the menu (100) */
  background: var(--black);
  display: flex;
  align-items: center;
  justify-content: center;
}
.trailer video { width: 100%; height: 100%; object-fit: contain; }
.trailer .menu-x { position: absolute; top: max(12px, env(safe-area-inset-top)); right: 12px; z-index: 1; color: var(--white); }
.trailer-msg { position: absolute; padding: 0 24px; color: var(--white); text-align: center; font-weight: 700; }

/* ── The FORGET machine (black slanted band) ──
   FORGET (white) over one negative word at a time in slot orange, 15px
   from the screen edges. FORGET is pinned at the top of a fixed-height box
   and the word hangs right under it, growing downward: js/anim.js sizes
   each word to the biggest font at which the pair still fits the box
   (wrapping at spaces only) and cycles the words in data-words, hard and
   fast. The fixed height and the top pin are what keep FORGET and the
   band still while the words change size. Without the script the first
   word simply stays at the fallback size. */
.forget {
  background: var(--black);
  color: var(--white);
  /* far deeper than .sec: with the box, the band runs taller than a phone
     screen. The top gets more than the bottom on purpose: the pair hangs
     from the top of the box, so the extra puts a one-line word at the
     band's middle and a three-liner only a little under it. */
  padding: calc(32vh + min(18vw, 120px) + var(--slant)) 15px calc(32vh - min(18vw, 120px) + 20px + var(--slant));
  /* the bottom edge slants too, parallel to the top: the black keeps its
     bottom on the right and gains the slant's rise on the left (the extra
     bottom padding above is that rise) */
  clip-path: polygon(0 var(--slant), 100% 0, 100% calc(100% - var(--slant)), 0 100%);
}
.forget h2 {
  font-size: clamp(2.4rem, 13vw, 5.2rem); /* FORGET only; the word's size is the fitter's */
  height: min(70vw, 760px);
  display: grid; /* not flex: a too-wide word must overflow to the RIGHT, or the fitter cannot see it */
  grid-template-rows: auto 1fr; /* FORGET takes its line; the word owns everything below */
  row-gap: 12px;
  text-align: center;
}
.forget-word {
  align-self: start; /* right under FORGET whatever its height, so FORGET never moves */
  font-size: clamp(2rem, 12vw, 7rem); /* no-script fallback: fits the first word on a phone */
  overflow-wrap: normal; /* never break inside a word: it overflows instead, and the fitter shrinks it */
}
.forget-word.is-swapping { animation: word-swap 0.3s steps(1, end); }
/* the swap: hard jolts, a blue / white split, a flash to blue, a blank
   frame in the middle (that is when the text changes), then a burst back.
   Distances are in em so the jolt scales with the word. */
@keyframes word-swap {
  0%   { transform: none; opacity: 1; text-shadow: none; }
  12%  { transform: translate(-0.2em, 0.08em) skewX(-14deg); text-shadow: 0.12em 0 var(--blue), -0.12em 0 var(--white); }
  24%  { transform: translate(0.18em, -0.08em) skewX(12deg); color: var(--blue); text-shadow: -0.09em 0 var(--white); }
  36%  { transform: translate(-0.1em, 0) scaleY(0.6); color: var(--orange); opacity: 0.5; text-shadow: 0.09em 0 var(--blue); }
  48%  { transform: none; opacity: 0; text-shadow: none; }
  56%  { opacity: 0; }
  68%  { transform: translate(0.15em, 0.05em) skewX(-12deg) scaleY(1.2); opacity: 1; color: var(--white); text-shadow: -0.12em 0 var(--blue); }
  80%  { transform: translate(-0.15em, -0.05em) skewX(10deg); color: var(--orange); text-shadow: 0.12em 0 var(--blue), -0.12em 0 var(--white); }
  92%  { transform: translate(0.05em, 0); text-shadow: -0.06em 0 var(--blue); }
  100% { transform: none; opacity: 1; text-shadow: none; }
}

/* ── The promise (white; the white then runs on into the step cards) ── */
.easy { background: var(--white); padding-top: 236px; padding-bottom: 156px; } /* a long white breath around the promise */
.easy .sec-inner { text-align: center; }
/* baseline that fits the longest line on any phone, down to 320px wide;
   js/anim.js then grows the lines toward FORGET's size for the actual screen.
   The divisor is that line's width in ems plus a margin: DOBRODRUŽSTVÍ is
   about 10.7em in PiePie, so a longer line means measuring again. */
/* seven short stacked lines read as one block at the headlines' tight spacing; this one
   breathes (owner's call), and the Czech accents get room above the capitals */
.easy h2 { font-size: clamp(1.4rem, calc((100vw - 48px) / 11), 4.5rem); line-height: 1.15; }
/* the promise flies in line by line like the hero headline, once it scrolls
   into view: every line is its own box (the markup's spans, split by <br>);
   js/anim.js numbers each box's line (--line), flips odd lines to come from
   the right, then arms (.easy--arm) and plays (.easy--in). Without JS neither
   class exists and nothing is hidden. A box never wraps inside itself: a
   two-word line too wide for the screen then overflows, which is what the
   fitter measures, instead of quietly breaking the owner's lines. */
.easy h2 span { display: inline-block; white-space: nowrap; }
.easy--arm h2 span { opacity: 0; }
.easy--in h2 span { animation: fly-in-l 0.35s cubic-bezier(0.2, 0.8, 0.3, 1) both; animation-delay: calc(var(--line, 0) * 0.1s); }
.easy--in h2 span.from-r { animation-name: fly-in-r; }

/* ── Pushable button (brand CTA) ── */
.btn {
  display: inline-block;
  font-family: var(--pie);
  font-size: inherit; /* the label is body-text size: the bar is loud, the label is not */
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
  border: 3px solid var(--black);
  border-radius: 999px;
  padding: 15px 40px;
  /* reward green (the app's OXP color): the one CTA color on every ground */
  background: var(--green);
  color: var(--black);
  box-shadow: 0 6px 0 var(--black);
  cursor: pointer;
}
/* press / lift use `translate`, not `transform`: the hero button's entrance
   animation keeps its final `transform` applied and would swallow the press */
.btn:active { translate: 0 6px; box-shadow: none; }
@media (hover: hover) {
  .btn:hover { translate: 0 -2px; box-shadow: 0 8px 0 var(--black); }
}

/* ── Step cards (normal scrolling; the sticky-stack effect fed the cards'
   colors into the phone's browser chrome for whole screen-heights — owner
   chose solid slot colors over the stacking animation) ── */
.deck {
  padding: 24px 12px 96px;
  background: var(--white);
  overflow-x: hidden;
  overflow-x: clip;
  display: grid;
  gap: 24px;
}
.card {
  width: 100%; /* explicit, or the auto margins would shrink the card to its content */
  max-width: 1200px;
  margin: 0 auto;
  border-radius: 28px;
  padding: 44px 28px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-height: 480px;
  overflow: hidden; /* clips the fanned deck's corners at the rounded edges */
}
/* slot color/foreground pairs mirror the app (lib/core/slot_colors.dart):
   Blue→white, Orange→black, Mauve→brand blue (never black) */
.card--slot-blue   { background: var(--blue);   color: var(--white); }
.card--slot-orange { background: var(--orange); color: var(--black); }
.card--slot-mauve  { background: var(--mauve);  color: var(--blue); }
.card--slot-black  { background: var(--black);  color: var(--white); }
.card h2 { font-size: clamp(2.2rem, 9vw, 4rem); }
.br-desk { display: none; } /* a line break the desktop's wide headings take and the phone's do not */
.card p { max-width: 30em; }
/* the hungry card's mascot fills the card's width */
/* scaled like the hero mascot: the canvas has dead margins, so width alone
   barely moves the drawing; the margins make room for the grown drawing */
.card--slot-mauve .art { width: 100%; max-width: 440px; transform: scale(1.3); transform-origin: center; margin: 32px 0 16px; }

/* ── Challenge deck (inside the orange step card) ──
   real challenge titles on colored sticker cards in a clean stack: the top
   card reads; the four behind it step down a few pixels each, a little
   smaller every step, so a thin edge of each peeks out beneath. The deck's
   single hard shadow is a STATIC box under the bottom slot, never on a
   card: a shadow that travels with the bottom card jumps at every deal. The
   rest wait at the bottom slot's position, fully opaque, hidden behind the
   bottom card, so a card entering the pile never fades in. Every card has the SAME fixed
   size, or the edges would not line up: the title up in the top left, the
   app's description right under it, the rest of the card left empty. The
   title is one <span> that js/anim.js sizes to fit: as big as the cap
   allows, never a word wider than the card, never more than three lines.
   The span matters: the card lays its content out as a flex column, and
   a bare title with a colored word inside would split into separate boxes.
   js/anim.js slides the top card away every few seconds and returns it to
   the back, so every title comes round. Without the script the cards simply
   stack, last one on top, at the cap size. */
.deal {
  position: relative;
  isolation: isolate; /* the slot z-indexes stay inside the deck */
  width: 100%; /* the card's width on the phone; the desktop pins its own */
  height: 297px; /* exactly the visible pile: card, four steps, shadow */
  margin: 28px 0 -10px; /* on the phone: a breath under the heading, the paragraph pulled closer */
}
/* the static shadow: a transparent box exactly where the bottom slot sits */
.deal::after {
  content: '';
  position: absolute;
  left: 0; top: 0;
  width: 100%;
  height: 255px;
  border-radius: 18px;
  transform: translateY(32px) scale(0.88);
  box-shadow: 0 6px 0 var(--black);
  z-index: 0;
}
.deal-card {
  position: absolute;
  left: 0; top: 0;
  width: 100%;
  height: 255px; /* the phone's card; the desktop's is 300 (see the 900px block) */
  padding: 28px 30px;
  display: flex;
  flex-direction: column;
  align-items: stretch; /* the title keeps the card's width, so the fitter can see it overflow */
  justify-content: flex-start;
  gap: 16px; /* the description hangs right under the title; the leftover stays at the bottom */
  overflow: hidden; /* a description the fitter cannot make fit is cut, never spilt under the pile */
  border: 3px solid var(--black);
  border-radius: 18px;
  font-family: var(--pie);
  font-size: 38px; /* the fitter's cap; it only ever goes down from here */
  line-height: 1.05;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  text-align: left;
  transition: transform 0.45s cubic-bezier(0.2, 0.8, 0.3, 1), opacity 0.35s;
  /* kept on its own layer at all times: a card that gets a layer only while
     it moves is re-rendered when the move ends, and that re-render lands a
     pixel or two off - the hop at the top of the pile after every deal */
  will-change: transform, opacity;
  /* two colorways alternate down the pile (DOM order, which the dealing
     never changes), so the peeking edges differ too: slot blue with white
     text, reward green with black text, black borders on both */
  background: var(--blue);
  color: var(--white);
}
.deal-card span { display: block; width: 100%; }
.deal-card .desc {
  font-family: var(--body);
  font-weight: 700;
  font-size: 15px;
  line-height: 1.4;
  text-transform: none;
  letter-spacing: 0;
}
.deal-card:nth-child(even) { background: var(--green); color: var(--black); }
.slot-0 { transform: none; z-index: 5; }
.slot-1 { transform: translateY(8px) scale(0.97); z-index: 4; }
.slot-2 { transform: translateY(16px) scale(0.94); z-index: 3; }
.slot-3 { transform: translateY(24px) scale(0.91); z-index: 2; }
.slot-4 { transform: translateY(32px) scale(0.88); z-index: 1; }
.slot-off { transform: translateY(32px) scale(0.88); z-index: -1; }
/* cards leave to the right and to the left by turns */
.is-leaving { transform: translateX(70%) rotate(8deg); opacity: 0; z-index: 6; }
.is-leaving-left { transform: translateX(-70%) rotate(-8deg); opacity: 0; z-index: 6; }
.no-move { transition: none; }

/* ── Reward cards (inside the mauve step card) ──
   copies of the app's gift-card REVEAL card (giftcard_detail_view.dart):
   85% of the screen wide and 70% tall, radius 17, a 3px border and a hard
   4px shadow in the reward's text color, and a soft glow in the rarity
   color; inside, the rarity pill, the name in PiePie, a Lottie preview
   where the app shows one, and the app's EQUIP button (flipped to white on
   a very dark card). The four-person unlock is the app's SPECIAL card: slot
   orange, black border and shadow, "the Four" as pushable 3D text over four
   figures. Per-card grounds ride on --bg in the markup, a flat color or the
   catalog's gradient (the app's gradient angle is CSS's angle minus 90).
   Shown as a PREVIEW: each card is laid out at the app's full
   size (334 x 596, the card on a 393 x 852 phone) and then scaled to two
   fifths and fanned like cards held in a hand: all three share ONE pivot
   half a card below their bottom edge, so the bases sit almost on top of
   each other and the tops spread; the Four is in front. The numbers below
   are tied together: the pivot sits 298px (150% of 596) under the box, so
   `bottom` places the scaled bottom edge 8px above the row's floor. */
.loot {
  position: relative;
  isolation: isolate; /* the fan's z-indexes stay inside it, under the fixed bar */
  height: 260px; /* the fan's footprint at the preview scale */
  margin-top: 8px;
}
.loot-card {
  --fg: #FFFFFF;
  --glow: transparent;
  position: absolute;
  left: 50%;
  bottom: 187px;
  width: 334px;
  height: 596px;
  margin-left: -167px; /* centered by margin, not transform: the transform is the fan */
  transform-origin: 50% 150%; /* the fan's pivot, half a card below the cards */
  transform: scale(0.4);
  padding: 24px;
  border-radius: 17px;
  background: var(--bg);
  color: var(--fg);
  border: 3px solid var(--fg);
  box-shadow: 0 0 40px 10px var(--glow), 4px 4px 0 var(--fg);
  display: flex;
  flex-direction: column;
  align-items: center;
  font-family: var(--pie);
  text-align: center;
}
/* the fan: the cards are the only divs in the row (the icon sheet is an svg);
   the first card (the Four) is in front, each card behind the one before it */
.loot-card:nth-of-type(1) { transform: scale(0.4) rotate(-15deg); z-index: 3; }
.loot-card:nth-of-type(2) { z-index: 2; }
.loot-card:nth-of-type(3) { transform: scale(0.4) rotate(15deg); z-index: 1; }
/* the glow colors are the app's: blueAccent, mauve */
.loot-card--rare { --glow: #448AFF; }
.loot-card--epic { --glow: #DCB8FF; }
.pill { padding: 4px 12px; border-radius: 8px; font-size: 12px; color: var(--white); text-transform: uppercase; }
.pill--rare { background: var(--blue); }
.pill--epic { background: var(--mauve); }
.loot-name { margin-top: 20px; font-size: 32px; line-height: 1.05; text-transform: uppercase; }
.loot-card .art { width: 100%; flex: 1 1 0; min-height: 0; margin-top: 24px; align-self: stretch; }
/* the special card: black frame, the pill pinned at the top, the graphic
   centered; the label is white 3D text standing 6px above its dark shadow */
.loot-card--special { --fg: #000000; justify-content: center; }
.loot-card--special .pill { position: absolute; top: 24px; left: 50%; transform: translateX(-50%); }
/* the site's one addition to the app's card: an UNLOCKED label at the
   height where the neighbouring cards carry their name, in the label's white */
.loot-card--special .loot-name { position: absolute; top: 66px; left: 24px; right: 24px; margin: 0; color: var(--white); }
.four-label { font-size: 45px; line-height: 1.3; color: var(--white); text-shadow: 0 6px 0 #2E2E2E; }
.four-icons { display: flex; justify-content: center; margin-top: 8px; }
.pic { width: 40px; height: 40px; fill: var(--white); filter: drop-shadow(3px 3px 0 rgba(0, 0, 0, 0.4)); }
.equip {
  margin-top: auto;
  width: 100%;
  flex: 0 0 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  border: 2px solid var(--black);
  background: var(--black);
  color: var(--white);
  font-size: 16px;
}
/* on a very dark card the app flips the button: white, black text, white border */
.equip--inverse { background: var(--white); color: var(--black); border-color: var(--white); }


/* ── Green "join" section: the app's courtyard crowd inviting the visitor,
   the download button under it. The crowd box has height only once
   js/anim.js has taken it (.crowd--on): without the script the section is
   headline + button, never a green hole. ── */
.join { background: var(--green); color: var(--black); }
.join .sec-inner { display: flex; flex-direction: column; align-items: center; gap: 24px; text-align: center; }
.join h2 { font-size: clamp(2.4rem, 10vw, 4.5rem); }
.crowd {
  position: relative;
  isolation: isolate; /* the bubble's z-index stays inside the crowd, under the fixed bar */
  width: 100%;
  max-width: 520px;
  height: 0;
}
.crowd--on { height: min(130vw, 600px); }
.face { position: absolute; object-fit: contain; pointer-events: none; will-change: transform, opacity; }
/* the app's SlotSpeechBubble on a green ground: black pill, green PiePie
   text and border, green hard shadow; the script deals each line a tilt
   (--tilt) and a spot, and pops it in with a small overshoot */
.bubble {
  position: absolute;
  z-index: 1; /* above the faces, which the script appends after it */
  padding: 13px 18px 11px;
  border: 2.5px solid var(--green);
  border-radius: 16px;
  background: var(--black);
  color: var(--green);
  box-shadow: 3px 4px 0 var(--green);
  font-family: var(--pie);
  font-size: 26px;
  line-height: 1.05;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transform: rotate(var(--tilt, 0deg)) scale(0.7);
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.18s;
}
.bubble--far { font-size: 19px; } /* an overheard murmur from the back: smaller, a touch dimmer */
.bubble--on { opacity: 1; transform: rotate(var(--tilt, 0deg)) scale(1); }
.bubble--far.bubble--on { opacity: 0.9; }
.btn--join { background: var(--indigo); color: var(--white); } /* the two buttons on a green ground: purple fill, white label, black frame */

/* ── FAQ page (faq.html) ──
   every question is a card in the site's own language: white, the hard
   black frame and shadow of the buttons and reward cards, a chevron that
   turns. An open card goes brand green so the answer stands on color, and a
   press pushes the card down onto its shadow like a button. ── */
.legal .art { width: min(77vw, 384px); margin: 0 auto 32px; } /* the thinking mascot is the page's headline: a fifth larger than the card mascots */
.faq {
  border: 3px solid var(--black);
  border-radius: 20px;
  background: var(--white);
  box-shadow: 4px 4px 0 var(--black);
  margin-bottom: 18px;
  padding: 0 20px;
  transition: background 0.2s;
}
.faq[open] { background: var(--green); }
.faq summary {
  list-style: none;
  cursor: pointer;
  font-family: var(--pie);
  font-size: 1.25rem;
  line-height: 1.1;
  text-transform: uppercase;
  padding: 18px 40px 18px 0;
  position: relative;
  /* no touch highlight and no focus ring on tap or click (iOS draws its
     blue ring from finger-down until the entry opens); keyboard focus keeps it */
  -webkit-tap-highlight-color: transparent;
}
.faq summary:focus:not(:focus-visible) { outline: none; }
.faq summary:active { translate: 4px 4px; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::marker { content: none; }
/* the chevron: a bordered square corner, turned; points down closed, up open */
.faq summary::after {
  content: '';
  position: absolute;
  right: 4px; top: 50%;
  width: 12px; height: 12px;
  border-right: 3px solid var(--black);
  border-bottom: 3px solid var(--black);
  transform: translateY(-70%) rotate(45deg);
  transition: transform 0.2s;
}
.faq[open] summary::after { transform: translateY(-30%) rotate(225deg); }
.faq p { padding: 0 0 20px; max-width: 44em; font-weight: 700; } /* the landing page's bold Raleway, not the legal pages' reading weight */
/* the maker's disco: three stacked portraits that js/anim.js strobes through
   the app's soul-card shuffle. Without the script the first one stands. */
.disco { position: relative; width: 170px; height: 170px; margin: 4px auto 20px; }
.disco-frame {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 0.2s;
}
.disco:not(.disco--on) .disco-frame:first-child, .disco-frame.is-on { opacity: 1; }
/* the maker's entry is centered; credits stack "role:" over the name, the role muted */
.faq--maker p, .faq--maker .credits { text-align: center; }
.credits { padding: 0 0 20px; font-weight: 700; }
.credits div + div { margin-top: 10px; }
.credits dt, .credits dd { display: block; margin: 0; }
.credits dt { opacity: 0.6; }
.credits dt::after { content: ':'; }
/* a solid heart in the text color, drawn, never an emoji */
.heart { display: inline-block; width: 0.9em; height: 0.9em; vertical-align: -0.1em; fill: currentColor; }
.heart + .heart { margin-left: 0.15em; }
.hearts { display: block; text-align: center; margin-top: 14px; } /* the three hearts close the entry on their own centered line */

/* ── Footer (giant white wordmark on black). The bookend: the black ends
   on the same slant the page's black began with, into the purple canvas
   that the phone's bar continues. ── */
.site-footer {
  background: var(--black);
  color: var(--white);
  padding: 64px 0 calc(28px + var(--slant)); /* the slant's rise, so the fineprint stays clear of the cut */
  text-align: center;
  overflow: hidden;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--slant)), 0 100%);
}
.footer-mark {
  display: block;
  text-decoration: none; /* a link home, but it must read as the wordmark, not as a link */
  font-family: var(--pie);
  font-size: clamp(4.6rem, 18.5vw, 16rem);
  line-height: 0.85;
  text-transform: uppercase;
  white-space: nowrap;
}
.site-footer nav {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  justify-content: center;
  font-weight: 700;
  margin: 36px 0 20px;
}
.site-footer nav a { text-decoration: none; }
/* The struck-through networks: a statement that TOGETU has no social media,
   never links. All four icons and their slashes are ONE image, so the row
   cannot wrap on any phone and the artwork lives only here. */
.no-socials {
  height: 30px;
  margin: 0 0 20px;
  background: center / contain no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-3 -3 156 30' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cdefs%3E%3Cg id='s'%3E%3Cpath stroke='black' stroke-width='4.5' d='M1 23L23 1'/%3E%3Cpath d='M1 23L23 1'/%3E%3C/g%3E%3C/defs%3E%3Cpath d='M4 8a4 4 0 0 1 4 -4h8a4 4 0 0 1 4 4v8a4 4 0 0 1 -4 4h-8a4 4 0 0 1 -4 -4z'/%3E%3Cpath d='M9 12a3 3 0 1 0 6 0a3 3 0 1 0 -6 0'/%3E%3Cpath d='M16.5 7.5l0 .01'/%3E%3Cuse href='%23s'/%3E%3Cg transform='translate(42)'%3E%3Cpath d='M21 7.917v4.034a9.948 9.948 0 0 1 -5 -1.951v4.5a6.5 6.5 0 1 1 -8 -6.326v4.326a2.5 2.5 0 1 0 4 2v-11.5h4.083a6.005 6.005 0 0 0 4.917 4.917z'/%3E%3Cuse href='%23s'/%3E%3C/g%3E%3Cg transform='translate(84)'%3E%3Cpath d='M7 10v4h3v7h4v-7h3l1 -4h-4v-2a1 1 0 0 1 1 -1h3v-4h-3a5 5 0 0 0 -5 5v2h-3'/%3E%3Cuse href='%23s'/%3E%3C/g%3E%3Cg transform='translate(126)'%3E%3Cpath d='M22 4.01c-1 .49 -1.98 .689 -3 .99c-1.121 -1.265 -2.783 -1.335 -4.38 -.737s-2.643 2.06 -2.62 3.737v1c-3.245 .083 -6.135 -1.395 -8 -4c0 0 -4.182 7.433 4 11c-1.872 1.247 -3.739 2.088 -6 2c3.308 1.803 6.913 2.423 10.034 1.517c3.58 -1.04 6.522 -3.723 7.651 -7.742a13.84 13.84 0 0 0 .497 -3.753c0 -.249 1.51 -2.772 1.818 -4.013z'/%3E%3Cuse href='%23s'/%3E%3C/g%3E%3C/svg%3E");
}
.site-footer .fineprint { font-size: 0.85rem; opacity: 0.65; }

/* ── Desktop: every card is two columns, text left and its art right. The
   heading and its text own the two middle rows; the empty first and last
   rows are flexible and absorb the art's height, so the pair stays a tight
   block centered against the art (otherwise the grid hands the art's
   height to the text rows and the heading drifts away from its text). ── */
@media (min-width: 900px) {
  .card {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    grid-template-rows: 1fr auto auto 1fr;
    align-items: center;
    column-gap: 48px;
    row-gap: 0;
    padding: 56px 64px;
    min-height: 640px; /* every card as tall as the mascot cards, so the deck and the fan get air too */
  }
  .card > * { grid-column: 1; }
  .card h2 { grid-row: 2; margin-bottom: 28px; }
  .card p { grid-row: 3; }
  .card .art, .card .deal, .card .loot { grid-column: 2; grid-row: 1 / span 4; justify-self: center; align-self: center; }
  /* the orange and black (level-up) cards are mirrored: art left, text
     right, the text still in the wider column and still left-aligned, so
     the sides alternate down the deck (blue, orange, chat, black, mauve) */
  .card--slot-orange, .card--slot-black { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); }
  .card--slot-orange > *, .card--slot-black > * { grid-column: 2; }
  .card--slot-orange .deal, .card--slot-black .loot { grid-column: 1; }
  /* the challenge deck at a fixed width and taller cards here (the phone's
     fills its card and runs 15% shorter); the pile and its shadow follow */
  .card--slot-orange .deal { width: 400px; height: 342px; margin: 0; }
  .br-desk { display: inline; }
  .deal::after, .deal-card { height: 300px; }
  /* the reward fan, enlarged as a whole (zoom scales the fan's fixed
     geometry along with it; the width keeps the zoomed box inside its column) */
  .card .loot { width: 300px; zoom: 1.45; }
}

/* ── Desktop: the courtyard box is a touch wider and much taller than wide.
   The crowd's math packs the huddle into a roughly square cluster that sits
   LOW in its box and clamps every face inside the box's edges: in a box as
   wide as it is tall the back rows have nowhere to go and pile up along the
   top edge. The extra height gives the tail its room above the cluster,
   as the app's tall slot does. ── */
@media (min-width: 900px) {
  .join .crowd { max-width: 560px; }
  .join .crowd--on { height: 800px; }
}

/* ── The chat card. A phone mock of the app's chat (TOGETU's side of a
 round-1 chat: his real script lines and polls) and of the quiz screen,
 four screens cycling (js: chat screens). Every number inside .app is the
 app's own logical pixel (lib/widgets/chat/*, quiz_page.dart), zoomed as
 a whole, so a change in the app is copied here 1:1 (the date tiles are
 the one stated exception). Shared by both screens; the desktop block
 below sets the phone's size and grid place. ── */
.card--chat { background: var(--indigo); color: var(--white); row-gap: 20px; }
/* on the phone the order is heading, phone, paragraph, packed tight (the
   desktop grid places them itself); the text sits left, the phone centered */
.card--chat p { order: 1; }
.card--chat .phone { align-self: center; }
.phone {
  position: relative;
  width: 278px; /* the zoomed screen (265.2 × 571.2) plus the 6px bezel */
  height: 584px;
  --frame: var(--black); /* the hardware: the bezel and the island share it */
  background: var(--frame);
  border-radius: 52px;
}
/* every screen stays on its own compositor layer for good (will-change):
   promoting it only for the fade re-rasterizes the content and can nudge
   it by a fraction of a pixel at every change */
.phone .screen {
  position: absolute;
  inset: 6px;
  border-radius: 46px;
  overflow: hidden;
  background: var(--sbg);
  opacity: 0;
  transition: opacity 0.5s;
  will-change: opacity;
}
.phone .screen.on { opacity: 1; }
/* one palette per slot, the app's slot rules (lib/utils/color_helpers.dart):
   background, ink, border, the bubble gradient's end, the bubble shadow,
   the vote badge. Every element inside the phone reads from these */
.screen--orange { --sbg: var(--orange); --sfg: var(--black); --sborder: var(--black); --sgrad: rgba(250, 115, 79, 0.93); --sshadow: rgba(0, 0, 0, 0.3); --sbadge: var(--green); }
.screen--mauve  { --sbg: var(--mauve);  --sfg: var(--blue);  --sborder: var(--blue);  --sgrad: rgba(205, 168, 240, 0.93); --sshadow: rgba(0, 0, 0, 0.3); --sbadge: var(--orange); }
.screen--blue   { --sbg: var(--blue);   --sfg: var(--white); --sborder: var(--white); --sgrad: rgba(89, 88, 255, 0.93);  --sshadow: rgba(0, 0, 0, 0.35); --sbadge: var(--orange); }
/* 390 × 840 logical px, zoomed to 0.68 (0.85 on desktop). A Mac has two device pixels per
   CSS pixel where the phone has three, so the smaller the mock the more
   PiePie's tiny counters fill in; the antialiased hint (Mac only, ignored
   on iOS) draws the heavy face thinner for the same reason */
.app {
  zoom: 0.68;
  position: relative;
  width: 390px;
  height: 840px;
  display: flex;
  flex-direction: column;
  color: var(--sfg);
  -webkit-font-smoothing: antialiased;
}
.island { position: absolute; top: 11px; left: 50%; width: 126px; height: 37px; margin-left: -63px; border-radius: 20px; background: var(--frame); }
.app-top { flex: none; height: 59px; }
/* the title bar: the group's name centered, the back arrow layered on top */
.chat-bar {
  flex: none;
  position: relative;
  height: 55px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 54px;
  font-family: var(--pie);
  font-size: 25px;
  line-height: 1.05;
  text-align: center;
}
.chat-bar .back { position: absolute; left: 10px; top: 50%; width: 34px; height: 34px; margin-top: -17px; transform: rotate(180deg); fill: currentColor; }
/* the thread hugs the input bar like a chat scrolled to its end; what
   does not fit is cut at the top */
.chat-list { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: flex-end; overflow: hidden; padding-bottom: 20px; }
/* one TOGETU block: name label + bubbles on the right, the mascot beside */
.tg { display: flex; align-items: flex-start; padding: 25px 0 0 25px; }
.tg-col { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-end; }
.tg-name { padding: 9px 8px 1px 7px; font-family: var(--pie); font-size: 17px; }
.tg-face { flex: none; width: 60px; margin: 0 5px; }
.tg-bubble {
  max-width: 100%;
  min-width: 40px;
  margin-top: 3px;
  padding: 6px 8px;
  font-family: var(--body);
  font-weight: 600;
  font-size: 15px;
  line-height: 1.25;
  background: linear-gradient(var(--sbg), var(--sgrad));
  border: 1.4px solid var(--sborder);
  border-radius: 14px;
  box-shadow: 1px 0 4px var(--sshadow);
}
.tg-name + .tg-bubble { margin-top: 0; }
/* chained bubbles share a tight corner on the mascot's side */
.tg-bubble + .tg-bubble { border-top-right-radius: 4px; }
.tg-bubble:has(+ .tg-bubble) { border-bottom-right-radius: 4px; }
/* a member's message: the sticker on the left (the app's cutout in the
   slot's outline color), name and bubble beside it; the bubble is TOGETU's
   with the roles mirrored */
.usr { display: flex; align-items: flex-start; padding: 25px 25px 0 0; }
.usr-face { flex: none; width: 55px; height: 55px; margin: 0 10px 0 6px; object-fit: contain; }
.usr-col { flex: 1; min-width: 0; padding-right: 5px; display: flex; flex-direction: column; align-items: flex-start; }
/* polls: a centered title, then either two pills around a VS or a grid
   of date tiles; the vote badge hangs off each option's top-right corner */
.poll { padding-bottom: 40px; }
.poll-title { padding: 70px 15px 16px; text-align: center; font-family: var(--pie); font-size: 21px; line-height: 1.1; }
.poll--grid .poll-title { padding-left: 10px; padding-right: 10px; }
/* .opt, not .pill: the reward cards' RARE/EPIC tag already owns .pill
   (white capitals) and would paint the options with it */
.opt-row { display: flex; justify-content: center; padding: 9px 9px 0; margin-bottom: 13px; }
.opt-row:last-child { margin-bottom: 0; }
.opt {
  position: relative;
  max-width: 80%;
  padding: 10px 15px;
  background: var(--sbg);
  border: 1.4px solid var(--sborder);
  border-radius: 20px;
  font-family: var(--body);
  font-weight: 600;
  font-size: 15px;
  text-align: center;
}
/* the viewer's own vote: brand purple, white, a thicker border, the
   text one size up (the app's voted state) */
.opt.is-mine, .tile-in.is-mine { background: var(--indigo); color: var(--white); border-width: 2px; }
.opt.is-mine { font-size: 16px; }
.votes {
  position: absolute;
  top: -9px;
  right: -9px;
  width: 25px;
  height: 25px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--sbadge);
  border: 2px solid var(--sborder);
  color: var(--sfg);
  font-family: var(--pie);
  font-size: 12px;
  font-weight: normal;
  line-height: 1;
}
.vs { margin-bottom: 13px; text-align: center; font-family: var(--pie); font-size: 14px; opacity: 0.55; }
/* three equal tiles inside the 25px margins; minmax(0, …) so a tile can
   never widen its column past the screen. One badge for every option:
   the tiles wear the pill's badge, hanging off the corner the same way */
.poll-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 5px; row-gap: 15px; padding: 0 25px; }
/* the tiles are flatter than the app's (44 tall, not 55) and read at
   the pills' 15px, not 13: the app's box looks empty at the Mac's scale */
.tile { position: relative; min-width: 0; height: 44px; }
.tile-in {
  height: 100%;
  display: grid;
  place-items: center;
  padding: 0 4px;
  background: var(--sbg);
  border: 1.4px solid var(--sborder);
  border-radius: 20px;
  font-family: var(--body);
  font-weight: 600;
  font-size: 15px;
  text-align: center;
}
.tile-in.is-mine { font-size: 16px; }
/* the input bar: the field in the slot's ink color with the sticker
   toggle inside, the envelope send beside it (the Material glyphs the
   app draws, at the app's sizes) */
.chat-input { flex: none; display: flex; align-items: center; padding: 0 8px 44px; } /* 10px above the phone's 34px home-bar safe area, as the app sits */
.chat-input .field { flex: 1; height: 44px; margin: 0 5px; display: flex; align-items: center; justify-content: flex-end; padding-right: 8px; background: var(--sfg); border-radius: 24px; }
.chat-input .field svg { width: 24px; height: 24px; fill: var(--sbg); }
.chat-input .send { flex: none; width: 60px; height: 55px; margin: 0 15px 0 10px; display: grid; place-items: center; }
.chat-input .send svg { width: 57px; height: 57px; fill: var(--sfg); }
/* the quiz screen: the round's question cards, the picked one lifted,
   the rest dimmed; the continue button and the quiet exit line below */
.app--quiz { padding-top: 45px; }
.quiz { flex: 1; display: flex; flex-direction: column; justify-content: space-evenly; align-items: center; }
.qcard { width: 82%; opacity: 0.45; }
.qcard:nth-child(odd) { transform: rotate(1.5deg); }
.qcard:nth-child(even) { transform: rotate(-1.5deg); }
.qcard.is-picked { opacity: 1; transform: rotate(1.5deg) scale(1.05); }
.qname { display: block; padding: 0 20px; margin-bottom: 2px; font-family: var(--pie); font-size: 16px; text-transform: uppercase; }
.qcard--right .qname { text-align: right; }
.qcard--all .qname { text-align: center; }
.qpill {
  padding: 13px 18px 11px;
  background: var(--sfg);
  color: var(--sbg);
  border: 2.5px solid var(--sbg);
  border-radius: 16px;
  box-shadow: 4px 6px 0 var(--sfg);
  font-family: var(--pie);
  font-size: 26px;
  line-height: 1.05;
  text-align: center;
}
/* the app's pushable button: a capsule in two pills, the darker base
   7px lower, the label PiePie white with a soft black shadow */
.qgo { position: relative; width: 80%; height: 67px; margin: 0 auto; }
.qgo::before { content: ''; position: absolute; inset: 7px 0 0; border-radius: 30px; background: hsl(254, 89%, 53%); }
.qgo span {
  position: absolute;
  inset: 0 0 7px;
  display: grid;
  place-items: center;
  padding: 0 10px;
  border-radius: 30px;
  background: hsl(254, 89%, 71%);
  color: var(--white);
  font-family: var(--pie);
  font-size: 21px;
  text-shadow: 2px 2px 2px var(--black);
  white-space: nowrap;
}
.qstop { margin: 14px 0 64px; text-align: center; font-family: var(--pie); font-size: 17px; opacity: 0.35; }

/* ── Desktop: the chat card in the deck's two-column grid, text left, the
   phone bigger on the right ── */
@media (min-width: 900px) {
  .card.card--chat { display: grid; row-gap: 0; }
  .card--chat .phone { grid-column: 2; grid-row: 1 / span 4; justify-self: center; align-self: center; transform: rotate(2deg); }
  .phone { width: 344px; height: 726px; }
  .app { zoom: 0.85; }
}

/* ── Contact form ── */
.contact-form { display: flex; flex-direction: column; gap: 18px; align-items: flex-start; }
.contact-form[hidden] { display: none; } /* the waitlist hides its form once joined; display:flex would beat the attribute */
.contact-form label { display: block; width: 100%; font-weight: 700; }
.contact-form input, .contact-form textarea, .contact-form select {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 12px 14px;
  font: inherit;
  font-weight: 400;
  border: 3px solid var(--black);
  border-radius: 14px;
  background: var(--white);
  color: var(--black);
}
.contact-form textarea { resize: vertical; }
.contact-form input:focus, .contact-form textarea:focus, .contact-form select:focus {
  outline: none;
  box-shadow: 0 4px 0 var(--black);
}
/* the waitlist's city picker, drawn like the email field above it: the browser's own select
   ignores the padding and the font, so it is drawn from scratch with its own chevron. A
   picked city turns it brand green: the empty placeholder option keeps the required select
   :invalid until then. */
.contact-form select {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 44px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%23000' stroke-width='2.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
}
.contact-form select:valid { background-color: var(--green); }
/* the waitlist's heading stands alone (no subtitle), so it takes the subtitle's air itself;
   both classes, or the later `.legal h1` rule wins and the heading hugs the form */
.legal.waitlist h1 { margin-bottom: 40px; }
/* the waitlist's consent: the box beside its sentence, not a full-width field */
.contact-form .consent { display: flex; gap: 12px; align-items: flex-start; font-weight: 400; }
.contact-form .consent input { width: 22px; height: 22px; flex: none; margin: 2px 0 0; padding: 0; accent-color: var(--black); }
/* honeypot lives off-screen, never display:none (bots skip hidden fields) */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-status { border: 3px solid var(--black); border-radius: 14px; padding: 12px 16px; font-weight: 700; }
.form-status--ok  { background: var(--green); }
.form-status--err { background: var(--mauve); color: var(--blue); }

/* ── Legal pages ── */
.legal-page { background: var(--white); color: var(--black); }
/* the canvas under these pages goes white too: with the purple html left
   underneath, a page whose height ends on a fraction of a pixel shows it as
   a hairline across the document's end (the contact form's line heights do) */
html:has(body.legal-page) { background: var(--white); }
.legal {
  max-width: 760px;
  margin: 0 auto;
  padding: 112px 24px 160px; /* top clears the fixed navbar; bottom is the white breath before the footer */
}
.legal h1 { font-size: clamp(2.2rem, 8vw, 3.6rem); margin-bottom: 8px; }
.legal .subtitle { margin-bottom: 40px; font-weight: 600; opacity: 0.7; }
.legal h2 {
  font-size: clamp(1.5rem, 5vw, 2.1rem);
  margin: 44px 0 16px;
}
.legal p { margin: 0 0 14px; }
/* hanging numbers: "1.1" / "a)" sit in a fixed gutter, text aligns cleanly */
.legal p.num, .legal p.sub {
  display: grid;
  grid-template-columns: 3.6em 1fr;
  gap: 0 6px;
}
.legal p.sub { padding-left: 3.6em; grid-template-columns: 2.4em 1fr; }
.legal p .no { font-weight: 700; }
/* docx tables (privacy policy). Three legal columns cannot fit a phone as
   columns, so the base view stacks each row into a bordered block whose
   cells restate their column header via data-label (set by the generator);
   from 900px it becomes a plain bordered table and the labels vanish. */
.legal table { width: 100%; border-collapse: collapse; margin: 8px 0 24px; }
.legal thead { display: none; }
.legal tbody, .legal tr, .legal td { display: block; }
.legal tr {
  border: 2px solid var(--black);
  border-radius: 16px;
  padding: 6px 16px;
  margin: 0 0 14px;
}
.legal td { padding: 12px 0; }
.legal td + td { border-top: 1px solid rgba(0, 0, 0, 0.25); }
.legal td[data-label]::before {
  content: attr(data-label);
  display: block;
  font-weight: 800;
  margin-bottom: 4px;
}
.legal td p { margin: 0 0 10px; }
.legal td p:last-child, .legal td:last-child { margin-bottom: 0; }
@media (min-width: 900px) {
  .legal thead { display: table-header-group; }
  .legal tbody { display: table-row-group; }
  .legal tr { display: table-row; border: 0; border-radius: 0; padding: 0; margin: 0; }
  .legal th, .legal td {
    display: table-cell;
    border: 2px solid var(--black);
    padding: 12px 14px;
    text-align: left;
    vertical-align: top;
  }
  .legal th { background: var(--black); color: var(--white); }
  .legal td[data-label]::before { content: none; }
}

/* ── Tablet (600 to 899px: iPads in portrait, phones on their side). The
   phone's stack, given room: the sizes below are the phone's until 600 and
   the desktop's from 900, so neither of those screens is touched here. ── */
@media (min-width: 600px) and (max-width: 899px) {
  .card { padding: 56px 48px; }
  .card p, .join p { font-size: 1.25rem; }
  .deal { max-width: 440px; margin-left: auto; margin-right: auto; } /* the deck stays a deck, centered, not a slab across the card */
  .card--chat .phone { width: 344px; height: 726px; } /* the desktop's phone */
  .card--chat .app { zoom: 0.85; }
  .card .loot { width: 300px; margin: 8px auto 0; zoom: 1.3; } /* the fan grown as a whole, centered */
}
