/* ============ fonts (self-hosted, spec §3) ============ */
@font-face { font-family: "Causten"; font-style: normal; font-weight: 400; font-display: swap; src: url("../assets/fonts/causten-regular.woff2") format("woff2"); }
@font-face { font-family: "Causten"; font-style: normal; font-weight: 500; font-display: swap; src: url("../assets/fonts/causten-medium.woff2") format("woff2"); }
@font-face { font-family: "Causten"; font-style: normal; font-weight: 700; font-display: swap; src: url("../assets/fonts/causten-bold.woff2") format("woff2"); }

/* ============ tokens (spec §3–4) ============ */
:root {
  --navy: #0F182A; --white: #FFFFFF;
  --ink: #0F172A; --ink-2: #374151;
  --paper: #F8FAFC; --paper-2: #94A3B8;
  --line-light: #E5E7EB; --line-dark: rgba(248, 250, 252, 0.12);
  --accent: #0EA5EA; --accent-ink: #FFFFFF;   /* Tomi, 2026-09-11: white, his call. 1.68:1 on the teal
                                                 end of the CTA gradient, 2.77:1 on the blue, against
                                                 4.5:1. Navy measured 6.41-10.54 — `--accent-ink: var(--navy)`
                                                 is the one-line revert. Logged in detect-exceptions.md. */
  --accent-2: #00E0C6;                           /* Tomi, 2026-09-11: the CTA runs accent -> this */
  --accent-grad: linear-gradient(100deg, var(--accent-2) 0%, var(--accent) 100%);   /* teal left, blue right */
  --spectrum: linear-gradient(90deg, #0EA5EA 0%, #00E0C6 50%, #668CFF 100%);
  --r-control: 8px; --r-surface: 16px;
  --font: "Causten", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --container: 1200px; --gutter: 24px;
  --section-pad: clamp(4.5rem, 3.5rem + 4vw, 7.5rem);   /* was clamp(6rem, 5rem + 6vw, 11rem): 166 → 114 px at 1440, 103 → 72 px at 390 */
  --pull01: 55svh;   /* phones: how far section 01 slides up into the end of the hero pin. 58 -> 55 on 2026-09-11 (Tomi: a little lower); 3svh = 25 px is all the mobile scroll budget has left. */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 240ms; --dur-base: 600ms;
  --fs-display: clamp(2.75rem, 1.2rem + 4.1vw, 4.5rem);
  --fs-h2: clamp(2rem, 1.2rem + 2.6vw, 3.25rem);
  --fs-lead: clamp(1.125rem, 1rem + 0.4vw, 1.375rem);
  --fs-body: 1.0625rem;
  --fs-label: 0.8125rem;
  --fs-numeral: clamp(3rem, 2rem + 3vw, 4.5rem);
  --fs-verb: clamp(2.75rem, 0.5rem + 9vw, 11rem);
}

/* ============ reset ============ */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body, h1, h2, h3, p, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, canvas { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Lenis */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

/* ============ base type ============ */
body {
  font-family: var(--font); font-size: var(--fs-body); line-height: 1.6;
  color: var(--paper); background: var(--navy);
  -webkit-font-smoothing: antialiased;
}
.display { font-size: var(--fs-display); font-weight: 700; line-height: 1.02; letter-spacing: -0.025em; text-wrap: balance; }
.h2 { font-size: var(--fs-h2); font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; text-wrap: balance; max-width: 22ch; }
.lead { font-size: var(--fs-lead); line-height: 1.5; max-width: 60ch; }
.body { font-size: var(--fs-body); line-height: 1.6; max-width: 62ch; }
.label { font-size: var(--fs-label); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; }
.numeral { font-size: var(--fs-numeral); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.muted { color: var(--paper-2); }
.h2 + .lead, .h2 + .body, .label + .h2 { margin-top: 1.25rem; }
.body + .body { margin-top: 1rem; }

/* ============ layout ============ */
.container { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gutter); }
.section { padding-block: var(--section-pad); }
.skip { position: absolute; left: -999px; top: 16px; z-index: 100; display: inline-flex; align-items: center; min-height: 44px; padding: 8px 16px; background: var(--white); color: var(--ink); border-radius: var(--r-control); }
.skip:focus { left: 16px; }

/* ============ buttons ============ */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  /* The page's only <button class="btn"> is the form's submit; every other .btn is an <a>. Without
     this a UA default (2px outset ButtonBorder) rings the gradient on that one button alone. */
  border: 0;
  min-height: 48px; padding: 0 22px; border-radius: var(--r-control);
  /* The one gradient outside the wave, the preload bar and the closing hairline: Tomi asked for the
     CTA to run from --accent into --accent-2 (2026-09-11), teal on the left, blue on the right, with
     white text across all of it. Flat --accent is the fallback for anything that cannot paint a
     gradient. See the --accent-ink note above for what that costs in contrast. */
  background: var(--accent); background: var(--accent-grad);
  color: var(--accent-ink); font-weight: 700; text-decoration: none;
  text-align: center; transition: filter var(--dur-fast);
}
.btn:hover { filter: brightness(0.92); }
.btn--lg { min-height: 56px; padding: 12px 28px; font-size: var(--fs-body); line-height: 1.3; }
.nav__cta { white-space: nowrap; }

/* ============ preload bar ============ */
.loadbar {
  position: fixed; top: 0; left: 0; z-index: 30; width: 100%; height: 2px;
  background: var(--spectrum); transform-origin: left; transform: scaleX(0);
  transition: transform 200ms linear, opacity 400ms;
}
.loadbar.is-done { opacity: 0; }
html:not(.js) .loadbar { display: none; }

/* ============ nav ============ */
/* The bar sits on a white island: invisible against the white hero, and it surfaces on its own as
   the wave carries the page into navy. Because the island is white in both states, the nav keeps
   one set of colours — no dark variant, no inverted logo — and it no longer has to borrow contrast
   from whatever happens to be scrolling behind it. */
/* the gutter moves out here: the island is the container's box now, and it needs the page's own
   margin to sit inside rather than running edge to edge on a phone */
.nav { position: fixed; inset: 0 0 auto 0; z-index: 20; height: 72px; display: flex; align-items: center; padding-inline: var(--gutter); color: var(--ink); }
.nav__inner {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--gutter);
  background: var(--white); border-radius: var(--r-surface); padding: 6px 6px 6px var(--gutter);
}
.nav__logo { display: inline-flex; align-items: center; min-height: 44px; }
.nav__logo img { height: 28px; width: auto; display: block; }
.nav__right { display: flex; align-items: center; gap: 28px; }
.lang { display: flex; gap: 16px; }
.lang a { font-size: var(--fs-label); font-weight: 500; letter-spacing: 0.08em; text-decoration: none; padding: 12px 4px; color: var(--ink-2); }
.nav .lang a { color: var(--ink-2); }
[data-lang="hu"] .lang-hu, [data-lang="en"] .lang-en, [data-lang="ro"] .lang-ro { color: var(--ink); text-decoration: underline; text-underline-offset: 6px; }
@media (max-width: 767px) {
  .nav__cta { display: none; }
  /* no button on the right to sit inside the padding, so the island is symmetrical and needs its
     own height — the language links are inline, their padding does not give the island any */
  .nav__inner { padding: 10px 20px; }
}

/* ============ 1 · hero ============ */
.hero { position: relative; min-height: 100vh; min-height: 100svh; isolation: isolate; background: var(--white); color: var(--ink); overflow: hidden; }
.hero__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
html:not(.js) .hero__canvas { display: none; }
.hero__inner { position: relative; min-height: 100vh; min-height: 100svh; display: grid; grid-template-columns: repeat(12, 1fr); column-gap: var(--gutter); align-items: center; padding-block: 96px; }
.hero__text { grid-column: 1 / 11; max-width: 1000px; }
/* the coin: desktop only (Tomi, 2026-09-10: hidden on phones, and never downloaded there) */
.hero__coin { display: none; }
@media (min-width: 900px) {
  .hero__text { grid-column: 1 / 9; }   /* cols 1-8 keep the H1 at 3 lines in HU/EN/RO at 1280-1920 (measured) */
  .hero__coin { display: block; grid-column: 9 / 13; align-self: center; position: relative; aspect-ratio: 1;
    width: calc(100% + 6vw); max-width: 560px; margin-right: -6vw; }   /* bleeds into the page margin */
  .hero__coinPoster, .hero__coinCanvas { position: absolute; inset: 0; width: 100%; height: 100%; }
  .hero__coinPoster { object-fit: contain; transition: opacity 400ms; }
  .hero__coin.is-live .hero__coinPoster { opacity: 0; }
  .hero__coinCanvas { cursor: grab; touch-action: pan-y; }
  .hero__coinCanvas.is-drag { cursor: grabbing; }
  /* The lead runs the full text column and is justified, so both edges line up with the H1 block.
     Measured at 1440: it needs 640px to fall to 4 lines and the column gives 760, which also keeps
     the word gaps tight — at 565px (the H1 last-line width) it was 5 lines with visible rivers.
     Phones keep the ragged right: justifying a column that narrow opens gaps again. */
  .hero__lead { text-align: justify; }
  /* Contact shadow. The coin spans FILL (0.8) of its square box, so its bottom edge sits at 90 %;
     the ellipse sits just under that. It is a pseudo-element on the host, so it fades with the
     coin as the wave takes it, shows under the poster too, and never exists on phones. */
  .hero__coin::before {
    content: ''; position: absolute; left: 50%; top: 96%; width: 56%; height: 8%;
    transform: translate(-50%, -50%); pointer-events: none;
    background: radial-gradient(closest-side, rgba(15, 24, 42, 0.20), rgba(15, 24, 42, 0));
  }
}
.hero__title { color: var(--ink); }
.hero__title, .hero__lead, .hero__cta, .hero__cue { will-change: opacity, filter, transform; }
.hero__lead { margin-top: 1.75rem; color: var(--ink-2); }
.hero__cta { margin-top: 2.25rem; }
.hero__lead .em { font-weight: 700; color: var(--ink); white-space: nowrap; }   /* the justified measure split it across lines */
/* The service name inside the hero CTA. The colour is a no-op while --accent-ink is white, and is
   what restores the emphasis if the ink ever goes back to navy; the underline is what carries it now
   (Tomi, 2026-09-11). .btn kills text-decoration, so it is set here, on the span itself. */
.btn__em { color: var(--white); text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 4px; }
.hero__cue { position: absolute; left: var(--gutter); bottom: 32px; display: flex; align-items: center; gap: 12px; font-size: var(--fs-label); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.hero__cue i { width: 1px; height: 40px; background: var(--ink-2); transform-origin: top; animation: cue 1.6s var(--ease-out) infinite; }
@keyframes cue { 0% { transform: scaleY(0); opacity: 0; } 40% { opacity: 1; } 100% { transform: scaleY(1); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .hero__cue i { animation: none; } }

/* ============ 2 · the change ============ */
.change__text { grid-column: 1 / 6; }
.change__visual { grid-column: 7 / 13; margin: 0; align-self: center; }
.change__visual img { width: 100%; height: auto; border-radius: var(--r-surface); }
.change__punch { margin-top: 2rem; font-size: var(--fs-lead); font-weight: 700; line-height: 1.35; letter-spacing: -0.01em; max-width: 30ch; }

/* ============ 3 · the service ============ */
.svc__head { grid-column: 1 / 7; max-width: 720px; }
.svc__visual { grid-column: 7 / 13; align-self: center; position: relative; overflow: hidden; }
.svc__poster { display: block; width: 100%; height: auto; transition: opacity 400ms; }
/* The coins cross-fade the poster out once the first rendered frame lands, exactly like the
   hero coin. Until then — and forever on phones, with no JS, and under reduced motion — the
   poster IS the picture, so nothing about that path is a fallback. */
.svc__coinsCanvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.svc__visual.is-live .svc__poster { opacity: 0; }
.svc__question { margin-top: 3rem; color: var(--paper-2); }
.svc__body { margin-top: 2.5rem; align-items: start; }
.chat { grid-column: 1 / 7; max-width: 560px; padding: 28px; border: 1px solid var(--line-dark); border-radius: var(--r-surface); background: rgba(248, 250, 252, 0.04); }
/* Flush left at every width (Tomi, 2026-09-11). It used to be margin-left: 15%, which read as the
   user-side bubble of a chat; the question is the section’s own example, so it starts at the edge. */
.chat__q { margin-right: 15%; padding: 14px 18px; border-radius: var(--r-surface); background: rgba(248, 250, 252, 0.06); transition: box-shadow var(--dur-base); }
.chat__a { margin-top: 20px; }
.chat__ad { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--line-dark); }
.chat__adLabel { color: var(--paper-2); }
.chat__adRow { display: flex; gap: 14px; margin-top: 12px; }
.chat__mark { flex: 0 0 32px; width: 32px; height: 32px; border-radius: 50%; background: var(--accent); margin-top: 2px; }
.chat__adBrand { font-weight: 700; }
.chat__adText { margin-top: 2px; }
.chat__adLink { display: inline-block; margin-top: 8px; color: var(--accent); font-weight: 500; text-decoration: none; }
.chat__adLink:hover { text-decoration: underline; text-underline-offset: 4px; }
.chat__chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.chat__chips li { font-size: var(--fs-label); letter-spacing: 0.02em; padding: 6px 10px; border: 1px solid var(--line-dark); border-radius: var(--r-control); color: var(--paper-2); }
.chat__metric { margin-top: 18px; color: var(--paper-2); }
.chat__track { display: block; margin-top: 8px; height: 4px; border-radius: 2px; background: var(--line-dark); overflow: hidden; }
.chat__fill { display: block; height: 100%; width: 70%; background: var(--accent); }
.chat__thumb { position: relative; margin-top: 18px; padding: 12px; width: 160px; border: 1px solid var(--line-dark); border-radius: var(--r-control); }
.chat__thumbBar { display: block; height: 4px; width: 40%; border-radius: 2px; background: var(--line-dark); }
.chat__thumbH { display: block; margin-top: 10px; height: 8px; width: 80%; border-radius: 2px; background: rgba(248, 250, 252, 0.22); }
.chat__thumbBtn { display: block; margin-top: 10px; height: 12px; width: 48px; border-radius: 6px; background: var(--accent); }
.chat__thumb .label { margin-top: 10px; color: var(--paper-2); font-size: 0.6875rem; }
.svc__points { grid-column: 7 / 13; display: grid; gap: 40px; padding-left: 8px; }
.svc__point { display: grid; grid-template-columns: auto 1fr; column-gap: 20px; row-gap: 6px; align-items: baseline; transition: opacity var(--dur-base); }
.svc__point .numeral { grid-row: span 2; color: var(--paper-2); }
.svc__pointTitle { font-size: calc(var(--fs-h2) * 0.6); font-weight: 700; line-height: 1.15; letter-spacing: -0.015em; }
.svc__point .body { grid-column: 2; }

/* ============ 4 · the method ============ */
.method__intro { margin-top: 1.25rem; }
.method__track { margin-top: 4rem; }
.method__panel { max-width: var(--container); margin-inline: auto; padding: 3rem var(--gutter); }  /* stacked by default; Task 8 adds the horizontal layout for JS on ≥ 900px */
.verb { font-size: var(--fs-verb); font-weight: 700; line-height: 0.95; letter-spacing: -0.03em; margin-top: 0.5rem; }
.live { margin-top: 2.5rem; width: 420px; max-width: 100%; height: 160px; color: var(--paper-2); }
.chart__base { stroke: var(--line-dark); stroke-width: 1; }
.chart__line { fill: none; stroke: var(--accent); stroke-width: 2.5; stroke-linecap: round; }
.live--scale { display: flex; align-items: flex-end; gap: 12px; height: 120px; }
.live--scale span { width: 32px; background: var(--paper-2); border-radius: 3px 3px 0 0; transform-origin: bottom; }
.live--scale span:nth-child(1) { height: 18%; } .live--scale span:nth-child(2) { height: 32%; } .live--scale span:nth-child(3) { height: 48%; } .live--scale span:nth-child(4) { height: 70%; } .live--scale span:nth-child(5) { height: 100%; background: var(--accent); }
.method__outro { margin-top: 4rem; }
.method__goal { margin-top: 2rem; font-size: var(--fs-lead); font-weight: 700; line-height: 1.35; max-width: 34ch; }
.method__goal s { color: var(--paper-2); font-weight: 500; }
.method__goal em { font-style: normal; color: var(--accent); }

/* ============ 5 · cta + footer ============ */
.cta__inner { max-width: 720px; text-align: center; margin-inline: auto; }
.cta__inner .h2 { margin-inline: auto; }
.cta__inner .lead { margin-inline: auto; }
.cta__fallback { margin-top: 2.5rem; }

/* --- Closing CTA: the lead form -------------------------------------------------
   The form only works with JS (the endpoint answers JSON, so a native post would put raw
   JSON on the screen). CSS owns both final states via the unconditional .has-js class —
   NOT html.js, which is motion-gated and would hide the form from reduced-motion visitors. */
.cta__form { display: none; }
html.has-js .cta__form { display: grid; }
html.has-js .cta__form[hidden] { display: none; }
html.has-js .cta__fallback { display: none; }

.cta__form {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 28px 32px;
  max-width: 720px;
  margin: 48px auto 0;
  text-align: left;
}
.field--wide, .cta__submit, .cta__status { grid-column: 1 / -1; }

.field__label {
  display: block;
  font-size: var(--fs-label);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--paper-2);
  margin-bottom: 8px;
}
.field__input {
  width: 100%;
  padding: 10px 2px;
  font: inherit;
  font-size: var(--fs-body);
  color: var(--white);
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(248, 250, 252, 0.36);
  border-radius: 0;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.field__input:hover { border-bottom-color: rgba(248, 250, 252, 0.52); }
.field__input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-bottom-color: var(--accent);
}
.field__input[aria-invalid="true"] { border-bottom-color: var(--accent-2); }

.field__err {
  margin: 8px 0 0;
  font-size: var(--fs-label);
  color: var(--accent-2);
}

.field--check {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 0 12px;
}
.field__check {
  width: 20px; height: 20px;
  margin: 12px 0 0;
  accent-color: var(--accent);
  /* 44px touch target without a 44px box: the padding sits on the label instead. */
}
.field__consent {
  font-size: var(--fs-label);
  line-height: 1.6;
  color: var(--paper-2);
  padding: 12px 0;
}
.field__consent a { color: var(--white); text-underline-offset: 3px; }
.field--check .field__err { grid-column: 2; }

/* The honeypot. Off-screen, never display:none — a bot that reads computed styles
   skips hidden fields, and skipping it is exactly what we are trying to detect. */
.field--trap {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.cta__submit { margin-top: 8px; }
.cta__status { margin: 0; font-size: var(--fs-label); min-height: 1.5em; }
.cta__status[data-state="error"] { color: var(--accent-2); }
.cta__done { margin-top: 48px; }

@media (max-width: 720px) {
  .cta__form { grid-template-columns: minmax(0, 1fr); gap: 24px; margin-top: 36px; }
}

.hairline { display: block; width: 120px; height: 1px; margin: 0 auto 2.5rem; background: var(--spectrum); }
.footer { padding-block: 4rem; border-top: 1px solid var(--line-dark); color: var(--paper-2); }
.footer__inner { display: flex; flex-wrap: wrap; align-items: center; gap: 24px 40px; }
.footer__logo { height: 24px; width: auto; opacity: 0.9; }
.footer .lang { margin-left: auto; }
.footer .lang a { color: var(--paper-2); }
.footer a { text-decoration: none; }
.footer p.label > a { display: inline-block; padding: 6px 0; }
.footer a:hover { text-decoration: underline; text-underline-offset: 4px; }

/* ============ responsive ============ */
@media (max-width: 899px) {
  :root { --gutter: 20px; }
  .grid, .hero__inner { grid-template-columns: 1fr; }
  .hero__text, .change__text, .change__visual, .chat, .svc__points, .svc__head, .svc__visual { grid-column: auto; }
  /* Phones read the intro as: label, H2, lead, the coins, then the question (Tomi, 2026-09-11).
     The question lives inside .svc__head, so the head goes display: contents and its children
     become grid items in their own right - then order moves the question past the coins. row-gap
     goes to 0 with it: the type already carries its own margins, and a 20 px gap on top of them
     loosened every line in the block. */
  .svc__head { display: contents; }
  .svc__intro { row-gap: 0; }
  .svc__visual { margin-top: 3rem; }
  .svc__question { order: 1; }
  .change__visual { margin-top: 2.5rem; }
  .svc__points { padding-left: 0; }
  .chat { max-width: none; }
}

/* ---- motion initial states (JS on only) ---- */
/* The logo starts oversized and shrinks to its real size over the first 160 px of scroll
   (sections.js, initNav). The start state lives here so there is no jump when JS boots, and it is
   a transform, not a height, so nothing reflows and the island keeps its 60 px. Scale 2 is the
   ceiling: 56 px tall, centred on the same line, leaves 2 px inside the island’s 60. Scaling from the
   LEFT edge is the point: big or small, the logo stands on the same 144 px as the H1 below it.
   No-JS and reduced motion never see the big state - they get the logo at its resting size. */
@media (min-width: 900px) {
  html.js .nav__logo img { transform: scale(2); transform-origin: left center; }
}
html.js .change__punch { opacity: 0; transform: translateY(16px); }

/* ---- 3 · the service: annotation states ---- */
html.js .chat__q, html.js .chat__ad { opacity: 0; transform: translateY(16px); }
html.js .chat__a .w { opacity: 0.12; }
html.js .svc__point { opacity: 0.35; }
html.js .chat__chips, html.js .chat__metric, html.js .chat__thumb { opacity: 0; transform: translateY(8px); transition: opacity var(--dur-base), transform var(--dur-base) var(--ease-out); }
html.js .chat__fill { transform: scaleX(0); transform-origin: left; transition: transform var(--dur-base) var(--ease-out); }
.svc[data-active="1"] .chat__q { box-shadow: 0 0 0 2px var(--accent); }
.svc[data-active="1"] .svc__point:nth-child(1),
.svc[data-active="2"] .svc__point:nth-child(2),
.svc[data-active="3"] .svc__point:nth-child(3),
.svc[data-active="4"] .svc__point:nth-child(4) { opacity: 1; }
.svc[data-active="2"] .chat__chips, .svc[data-active="3"] .chat__chips, .svc[data-active="4"] .chat__chips,
.svc[data-active="3"] .chat__metric, .svc[data-active="4"] .chat__metric,
.svc[data-active="4"] .chat__thumb { opacity: 1; transform: none; }
.svc[data-active="3"] .chat__fill, .svc[data-active="4"] .chat__fill { transform: scaleX(1); }
html:not(.js) .svc[data-active="0"] .chat__chips, html:not(.js) .svc[data-active="0"] .chat__metric, html:not(.js) .svc[data-active="0"] .chat__thumb { opacity: 1; transform: none; }

/* ---- 4 · the method: one word at a time ----
   No-JS / reduced motion keep the stacked panels with every live element in its final state.
   With JS the live elements are driven by GSAP timelines (sections.js, PRISMA.methodLive). */
.fx { position: absolute; width: 0; height: 0; overflow: hidden; }
.chart__dot { fill: var(--accent); }
/* 02 Mérj — a measuring chart: axes with ticks, and each point the line reaches projected onto both.
   The tip's own projection is the only accent one besides the line and its dot. */
.chart__axis { stroke: var(--paper-2); stroke-width: 1; opacity: 0.55; }
.chart__ticks { fill: none; stroke: var(--paper-2); stroke-width: 1; opacity: 0.45; }
.chart__proj { stroke: var(--paper-2); stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.5; }
.chart__mark { stroke: var(--paper); stroke-width: 1.5; }
.chart__ring { fill: var(--navy); stroke: var(--paper); stroke-width: 1.5; }
.chart__pt--tip .chart__proj { stroke: var(--accent); opacity: 0.8; }
.chart__pt--tip .chart__mark { stroke: var(--accent); }

/* 01 Tesztelj — five answers write in; only one carries a sponsored slot: a small, deliberate share
   of the traffic. The questions, the slot rule and its label are the frame and never animate. */
.live--share svg { display: block; width: 100%; height: auto; }
.live--share text { font-family: inherit; fill: var(--paper-2); }
.live--share .share__q rect { fill: var(--paper-2); opacity: 0.4; }
.live--share .share__p { stroke: var(--line-dark); stroke-width: 1; }
.live--share .share__slot { stroke: var(--line-dark); stroke-width: 1; }
.live--share .share__lab { font-size: 10.5px; font-weight: 500; letter-spacing: 0.06em; }
.live--share .share__mark { fill: var(--accent); }
.live--share .share__brand { fill: var(--paper); }
.live--share .share__copy { stroke: var(--paper-2); stroke-width: 1; opacity: 0.55; }
.live--share .share__link { fill: var(--accent); }
/* 03 Tanulj — three real ways of asking are braced into one intent, and the ad is built on it.
   HTML, not SVG, so the questions ellipsis instead of overflowing in EN / RO. */
.live--intent { display: flex; flex-direction: column; justify-content: flex-end; color: var(--paper); }
.live--intent .intent { display: grid; grid-template-columns: minmax(0, 1fr) 40px minmax(0, 0.82fr); align-items: center; width: 100%; }
.live--intent .intent__qs { list-style: none; margin: 0; padding: 0; }
.live--intent .intent__qs li { padding: 15px 14px 15px 0; border-bottom: 1px solid var(--line-dark); }
.live--intent .intent__qs li:first-child { border-top: 1px solid var(--line-dark); }
.live--intent .intent__q { display: block; font-size: 0.8125rem; line-height: 1.2; opacity: 0.62;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* the brace: three stubs, a spine from row one's centre to row three's centre, one arm out */
.live--intent .intent__brace { position: relative; align-self: stretch; }
.live--intent .intent__stub { position: absolute; left: 0; width: 12px; height: 1px; background: var(--paper-2);
  opacity: 0.55; transform-origin: left center; }
.live--intent .intent__stub:nth-child(1) { top: 16.667%; }
.live--intent .intent__stub:nth-child(2) { top: 50%; }
.live--intent .intent__stub:nth-child(3) { top: 83.333%; }
.live--intent .intent__spine { position: absolute; left: 12px; top: 16.667%; bottom: 16.667%; width: 1px;
  background: var(--paper-2); opacity: 0.55; transform-origin: center; }
.live--intent .intent__out { position: absolute; left: 12px; top: 50%; width: 28px; height: 1px;
  background: var(--paper-2); opacity: 0.55; transform-origin: left center; }
.live--intent .intent__ad { align-self: center; padding-top: 12px; border-top: 1px solid var(--line-dark); }
.live--intent .intent__label { font-size: 0.6875rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--paper-2); }
.live--intent .intent__adRow { display: flex; align-items: flex-start; gap: 10px; margin-top: 10px; }
.live--intent .intent__mark { flex: 0 0 22px; width: 22px; height: 22px; border-radius: 50%; background: var(--accent); }
.live--intent .intent__adCol { flex: 1 1 auto; min-width: 0; }
.live--intent .intent__brand { font-size: 0.8125rem; font-weight: 700; line-height: 1.2; }
.live--intent .intent__rule { display: block; margin-top: 7px; height: 3px; border-radius: 2px;
  background: var(--line-dark); transform-origin: left center; }
.live--intent .intent__rule--b { width: 64%; }
.live--intent .intent__link { margin-top: 9px; font-size: 0.6875rem; color: var(--accent); }
/* 04 Optimalizálj — an even split drains off the two browsing questions onto the one ready to buy.
   The bars start at x = 208, not 182: EN "Which software should I choose…" is 191 units at 13px. */
.live--spend svg { display: block; width: 100%; height: auto; }
.live--spend .spend__axis { stroke: var(--line-dark); stroke-width: 1; }
.live--spend .spend__q { font-family: inherit; font-size: 13px; fill: var(--paper-2); opacity: 0.32; }
.live--spend .spend__bar { fill: var(--paper-2); opacity: 0.28; }
/* the resting picture is the decision already made: the intent that converts holds the budget */
.live--spend .spend__row--win .spend__q { fill: var(--paper); opacity: 1; }
.live--spend .spend__bar--win { fill: var(--accent); opacity: 1; }
html.js .method__panel { overflow-x: clip; }
@media (min-width: 900px) {
  /* the stage: one viewport, the five panels stacked on top of each other, one visible at a time */
  html.js .method__track { position: relative; height: 100vh; height: 100svh; margin-top: 0; }
  html.js .method__panel {
    position: absolute; inset: 0; max-width: var(--container); margin-inline: auto; padding: 0 var(--gutter);
    display: grid; grid-template-columns: repeat(12, 1fr); column-gap: var(--gutter); row-gap: 1.25rem;
    align-content: center; visibility: hidden; overflow: visible;
  }
  html.js .method__panel > .label, html.js .method__panel > .verb { grid-column: 1 / -1; }
  html.js .method__panel > .live { grid-column: 7 / 13; margin-top: 1.5rem; }
  /* The stage is a whole viewport, so the composition has to earn it: the verb runs the full
     container (the 11rem cap left "Tesztelj" at 49% of it), the live element fills its own six
     columns instead of a hard 420px. The box keeps the spec's 420:160 as a ratio rather than fixed
     pixels: every word gets the same height, so the verb does not jump as the words swap (letting
     the box size itself measured 90px of jitter). Phones and no-JS are untouched. */
  html.js .method__panel > .verb { font-size: clamp(4rem, 1rem + 13vw, 13.5rem);   /* the cap is set by the longest verb: RO "Optimizează" is 1102 px of the 1152 px container at 13.5rem */ }
  html.js .method__panel > .live { width: 100%; height: auto; aspect-ratio: 426 / 160; }
  html.js .method__panel > .live--chart svg { width: 100%; height: auto; }
  /* in a box this size the ink has to use it: the five bars grew tall but stayed a 207px cluster
     inside 564px */
  html.js .method__panel > .live--scale { gap: 16px; }
  html.js .method__panel > .live--scale span { width: auto; flex: 1; }
  /* every live element rests on the box's bottom edge, so the five share one baseline and the eye
     does not jump as the words swap (centred, the ink bottoms measured 610/699/665/628/699) */
  html.js .method__panel > .live:not(.live--scale) { display: flex; flex-direction: column; justify-content: flex-end; }
}

/* ---- soft reveals for non-pinned content ---- */
html.js [data-reveal] { opacity: 0; transform: translateY(24px); }

/* ---- hero: the flat navy that fades on under the receding wave ----
   The scrub now ends while the wave still shows colour at the top, and no frame before 180
   has a bottom edge that is exactly --navy (they are 7-14 units off, which reads as a seam
   line where the canvas meets the page). This veil is opaque --navy at the bottom, so the
   hero's bottom edge is the page's own colour while the wave still plays above it. */
/* ---- the wave lives on a fixed layer behind the page ----
   Pinning the hero could never let section 01 share the screen with the wave (measured: a ~96 px
   dead gap between the pin releasing and the section arriving). With the canvas fixed, the scrub
   simply keeps running past the pin and section 01 scrolls up into the navy under the crest. */
.stage { position: fixed; inset: 0; z-index: 0; background: var(--white); }
html:not(.js) .stage { display: none; }
html.js .hero { background: transparent; }
main, .footer { position: relative; z-index: 1; }

.hero__veil {
  position: absolute; inset: 0; z-index: 1; opacity: 0; pointer-events: none;
  background: linear-gradient(to top,
    var(--navy) 0%, var(--navy) 42%,
    rgba(15, 24, 42, 0.72) 68%,
    rgba(15, 24, 42, 0) 94%);
}
html:not(.js) .hero__veil { display: none; }
.hero__inner { z-index: 2; }

/* section 01 entrance: text first, rows after (JS only; no-JS keeps them visible) */
html.js .change__text > .label,
html.js .change__text > .h2,
html.js .change__text > .body { opacity: 0; transform: translateY(20px); }
html.js .change__visual { opacity: 0; transform: translateY(24px) scale(0.98); }
@media (max-width: 767px) { html.js .change { margin-top: calc(-1 * var(--pull01)); } }

/* phones: section 03 stays stacked, but tighter (part of the 30 % scroll cut) */
@media (max-width: 899px) {
  .method__panel { padding-block: 1.5rem; }   /* 1.75 → 1.5 on 2026-09-14: the new 03 diagrams are ~136 px taller than the bars they replaced */
  .live { margin-top: 1.5rem; }
  .live--intent .intent__qs li { padding-block: 10px; }
  .live:not(.live--scale) { height: auto; }   /* 160 px boxes around 30-135 px of content; the bars keep their 120 px */
}

/* phones: the last 170 px of the 30 % cut. 02 is visually unchanged apart from tighter gaps. */
@media (max-width: 899px) {
  .svc__points { gap: 28px; }                              /* was 40px */
  .chat { padding: 22px; }                                 /* was 28px */
  .svc__question { margin-top: 2rem; }                     /* was 3rem */
  .method__track, .method__outro { margin-top: 2.5rem; }   /* were 4rem */
}

/* Phones: the veil is a navy band that TRAVELS WITH THE CREST - transparent above it, opaque from
   just below it down. Section 01 rises through the wave band right after it is revealed, so white
   body text was landing on fully saturated cyan (measured: peak channel spread 220); a flat navy
   veil fixed that but flattened the wave while it still had a third of the screen to climb (Tomi,
   2026-09-11: the navy fades in too early). The crest band is only 60-80 px tall and everything
   below it is already dark (measured at 390: min channel 17-22), so painting only below the crest
   keeps the wave vivid to the top and still hands 01 a flat navy ground. --veil-y is crestY, set
   per frame in hero.js. The ramp starts 120 px BELOW the crest and takes 140 px: on the band itself
   it read as a haze over the wave (Tomi, 2026-09-11), and by 120 px down the frame is already navy,
   so the ramp has nothing left to darken and is invisible. */
@media (max-width: 767px) {
  .hero__veil {
    background: linear-gradient(to bottom,
      rgba(15, 24, 42, 0) calc(var(--veil-y, 100%) + 120px),
      var(--navy) calc(var(--veil-y, 100%) + 260px));
  }
}
