/* Not For Free — poster pass (NFF-3b). Mobile-first.
   Reference: Barbara Kruger + Swiss grid — protest content, surgical execution. */

:root {
  --color-bg: #111111;
  --color-fg: #f4f4f2;
  --color-accent: #E63329;
  --color-border: #333333;
  --font-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Unbounded", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.04 0'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23g)'/%3E%3C/svg%3E");

  /* Modular scale, base 8px */
  --space-1: 0.5rem;   /* 8px */
  --space-2: 1rem;     /* 16px */
  --space-3: 1.5rem;   /* 24px */
  --space-4: 2rem;     /* 32px */
  --space-6: 3rem;     /* 48px */
  --space-8: 4rem;     /* 64px */
  --space-12: 6rem;    /* 96px  - section gap, mobile */
  --space-20: 10rem;   /* 160px - section gap, desktop */

  --gutter: var(--space-3);
}

@media (min-width: 768px) {
  :root {
    --gutter: var(--space-6);
  }
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
}

html {
  background: var(--color-bg);
}

body {
  color: var(--color-fg);
  font-family: var(--font-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Seamless wall: one fixed, viewport-covering texture layer behind the
   entire page (footer included). A fixed-position pseudo-element rather
   than `background-attachment: fixed` on body — the property is buggy on
   iOS Safari, and `cover` sized directly against a body as tall as the
   whole page would zoom the photo into an unrecognizable blur on scroll.
   Layer order (top to bottom): dark wash for AAA contrast, grain for
   liveliness, photo. Perfectly static regardless of scroll position —
   the wall does not move, only what's painted on it does. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(0, 0, 0, 0.68), rgba(0, 0, 0, 0.68)),
    var(--grain),
    url("public/wall.webp");
  background-repeat: no-repeat, repeat, no-repeat;
  background-position: center, center, center;
  background-size: cover, 240px 240px, cover;
}

img, svg {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
}

button, input, select {
  font: inherit;
  color: inherit;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Vertical rhythm: one gap value between sections, applied as margin-top
   only, so consecutive sections never double up. Hero excluded (page start). */
.section-gap {
  margin-top: var(--space-12);
}

@media (min-width: 768px) {
  .section-gap {
    margin-top: var(--space-20);
  }
}

#hero, #manifesto, #slogans, #join, #spray, .site-footer {
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

/* Hero: one statement, nothing else on the first viewport. */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.hero-heading {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(4rem, 13vw, 13.75rem); /* 64px -> 220px */
  line-height: 0.95;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0;
  overflow-wrap: normal;
  word-break: keep-all;
  hyphens: none;
}

/* Cyrillic runs wider than Latin at the same size class — "БЕЗКОШТОВНО"
   needs both a smaller scale and a forced single line to stay clear of
   the viewport edge; EN keeps its default (space-)wrapping behavior. */
html[lang="uk"] .hero-heading {
  font-size: clamp(1.5rem, 7.2vw, 9rem); /* 24px -> 144px */
  white-space: nowrap;
}

.hero-teaser {
  font-size: 1.5rem; /* 24px */
  line-height: 1.3;
  margin: var(--space-6) 0 0;
}

/* Scroll cue: sprayed arrow, bottom center of the hero. Instant state
   switch on hover/focus — a nudge, not a bounce. */
.scroll-arrow {
  position: absolute;
  bottom: var(--space-6);
  left: 50%;
  transform: translateX(-50%);
  padding: var(--space-1);
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
}

.scroll-arrow svg {
  height: 48px;
  width: auto;
}

.scroll-arrow:hover,
.scroll-arrow:focus-visible {
  outline: none;
  color: rgba(255, 255, 255, 1);
  transform: translateX(-50%) translateY(2px);
}

/* Manifesto — starts on scroll, below the hero's first viewport. Padding
   (not the shared section-gap margin) so the text column's own top offset
   is independent of whatever sits above it. */
.manifesto {
  padding-top: var(--space-12); /* 96px mobile */
}

@media (min-width: 768px) {
  .manifesto {
    padding-top: var(--space-20); /* 160px desktop */
  }
}

.manifesto-inner {
  max-width: 72ch;
  margin-inline: auto;
}

.manifesto-line {
  font-size: 1.1875rem; /* 19px mobile */
  font-weight: 500;
  line-height: 1.6;
  color: #ffffff;
  margin: 0 0 var(--space-4);
}

@media (min-width: 768px) {
  .manifesto-line {
    font-size: 1.375rem; /* 22px desktop */
  }
}

/* inline-block so clip-path applies to one box even when the line wraps */
.manifesto-line-part {
  display: inline-block;
}

/* Kruger plaque: the closing manifesto lines, wherever they appear
   (hero teaser and the end of the full manifesto). Same red as buttons
   and the future user-sprayed slogan — it appears nowhere else. */
.plaque {
  background: var(--color-accent);
  color: #ffffff;
  font-family: var(--font-display);
  font-weight: 700;
  padding: 0.1em 0.4em;
}

/* Spray-plaque moment (manifesto closing lines only): the red swatch is
   sprayed in behind the line first, then the white text lands on it.
   One-way — the observer never removes .revealed. The span itself stays
   opaque; the reveal lives entirely in its pseudo-element and text color. */
.plaque--spray {
  position: relative;
  isolation: isolate;
  background: none;
  color: transparent;
}

.plaque--spray::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-accent);
  opacity: 0;
}

.to-reveal.plaque--spray {
  opacity: 1;
  transition: none;
}

/* Mobile: pre-drawn swatch pops in whole (one opacity switch, no
   clip-path animation — cheap), text follows 100ms later. */
.to-reveal.plaque--spray.revealed {
  animation: plaque-text-in 80ms linear 100ms both;
}

.to-reveal.plaque--spray.revealed::before {
  opacity: 1;
}

/* Desktop: rough ~45° diagonal sweep draws the swatch in, quantized
   steps for the sprayed-not-tweened feel; text lands right after. */
@media (min-width: 768px) {
  .to-reveal.plaque--spray.revealed {
    animation: plaque-text-in 90ms linear 320ms both;
  }

  .to-reveal.plaque--spray.revealed::before {
    animation: swatch-in 320ms steps(6, jump-end) both;
  }
}

@keyframes swatch-in {
  from {
    opacity: 1;
    clip-path: polygon(0 0, 5% 0, -13% 100%, 0 100%);
  }
  to {
    opacity: 1;
    clip-path: polygon(0 0, 118% 0, 100% 100%, 0 100%);
  }
}

@keyframes plaque-text-in {
  from { color: transparent; }
  to { color: #ffffff; }
}

/* Manifesto reveal: sharp and one-way, like a spray stencil.
   Mobile default — plain opacity switch, no filters/masks on the reveal. */
.to-reveal {
  opacity: 0;
  transition: opacity 120ms linear;
}

.to-reveal.revealed {
  opacity: 1;
}

/* Desktop: stencil sweep — clip-path quantized into 4 jumps, 140ms total */
@media (min-width: 768px) {
  .to-reveal {
    transition: none;
  }

  .to-reveal.revealed {
    animation: spray-in 140ms steps(4, jump-end) both;
  }
}

@keyframes spray-in {
  from {
    opacity: 1;
    clip-path: inset(0 100% 0 0);
  }
  to {
    opacity: 1;
    clip-path: inset(0 0 0 0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .to-reveal,
  .to-reveal.revealed {
    opacity: 1;
    transition: none;
    animation: none;
  }

  /* Static plaque: `animation: none` above kills plaque-text-in, so the
     text color and swatch must be set directly. */
  .plaque--spray {
    color: #ffffff;
  }

  .plaque--spray::before {
    opacity: 1;
    animation: none;
  }
}

/* Section heading: shared by join + spray */
.section-heading {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.5rem, 7vw, 2.25rem); /* 24px -> 36px, fits one line */
  line-height: 1.05;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  white-space: nowrap;
  margin: 0 0 var(--space-4);
}

/* Slogans (rotation logic lands in NFF-4) */
.slogans {
  min-height: 20vh;
}

/* Join / spray forms: one shared column for both, so every button in it
   shares identical width. */
.form-column {
  max-width: 40rem; /* 640px */
  margin-inline: auto;
}

.join form,
.spray form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.field-label {
  font-size: 1rem; /* 16px */
}

/* "Like writing on the wall": transparent, flush, only a hairline to
   write against. */
.field-input {
  display: block;
  width: 100%;
  background: transparent;
  border: none;
  border-bottom: 2px solid var(--color-border);
  border-radius: 0;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  color: var(--color-fg);
  font-size: 1.125rem; /* 18px */
  padding: var(--space-2) 0;
}

/* Border width stays constant (color-only change) so focus/validation can
   never shift layout — a submit that moves focus to an invalid field must
   not move the page. */
.field-input:focus {
  outline: none;
  border-bottom-color: var(--color-fg);
}

.field-input.has-error {
  border-bottom-color: var(--color-accent);
}

/* Error line: height reserved from the start (empty or not) so validation
   never shifts anything below it — fixed, not min-height, and clipped so
   font-metric rounding can never add a stray pixel. */
.field-error {
  height: 1.1rem;
  overflow: hidden;
  font-size: 0.875rem; /* 14px */
  line-height: 1.1rem;
  color: var(--color-accent);
  margin: 0;
}

/* Kill webkit autofill styling: the yellow/blue fill would break the
   wall illusion. Box-shadow trick keeps the background transparent, the
   endless transition stops webkit repainting it, size stays intact. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px transparent inset;
  -webkit-text-fill-color: #ffffff;
  caret-color: #ffffff;
  transition: background-color 999999s;
}

/* Tile-select: selectable tiles instead of native radios/selects.
   Shared by role and consent — every option is a full-width tile. */
.tile-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.tile-group > .field-label {
  margin-bottom: 0;
}

.tile {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2);
  border: 1px solid rgba(255, 255, 255, 0.25);
  font-size: 1rem; /* 16px */
  cursor: pointer;
}

.tile-dot {
  flex: none;
  width: var(--space-2);
  height: var(--space-2);
  margin-top: 0.2em;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 50%;
}

/* Selected: border stays 1px (no layout shift) — the inset shadow adds
   the second red pixel. */
.tile:has(input:checked) {
  border-color: var(--color-accent);
  box-shadow: inset 0 0 0 1px var(--color-accent);
}

.tile:has(input:checked) .tile-dot {
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.tile-group.has-error .tile {
  border-color: var(--color-accent);
}

/* Button: one component for every call to action on the page. */
.btn {
  display: block;
  width: 100%;
  height: var(--space-8); /* 64px */
  border: none;
  outline: none;
  background: var(--color-accent);
  color: #ffffff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.125rem; /* 18px */
  text-transform: uppercase;
  cursor: pointer;
}

.btn:hover,
.btn:focus-visible {
  background: #ffffff;
  color: var(--color-accent);
}

.spray-note {
  font-size: 1rem; /* 16px */
  opacity: 0.7;
  margin-top: var(--space-2);
}

/* Footer: one baseline-aligned line, no borders — spacing does the work. */
.site-footer {
  padding-bottom: var(--space-4); /* 32px */
}

.footer-top {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: space-between;
  align-items: baseline;
}

.footer-nav {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
}

.footer-nav a {
  text-decoration: none;
  text-transform: uppercase;
  font-size: 0.875rem; /* 14px */
  letter-spacing: 0.05em;
}

.footer-nav a:hover {
  color: var(--color-accent);
}

.lang-switch {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
}

.lang-switch button {
  background: transparent;
  border: none;
  color: var(--color-fg);
  font-size: 0.875rem; /* 14px */
  letter-spacing: 0.05em;
  padding: 0.2em 0.5em;
  cursor: pointer;
}

.lang-switch button:hover {
  color: var(--color-accent);
}

.lang-switch button.active {
  background: var(--color-accent);
  color: #ffffff;
}

.lang-switch button.active:hover {
  color: #ffffff;
}

.footer-signature {
  font-size: 0.8125rem; /* 13px */
  opacity: 0.6;
  margin: var(--space-2) 0 0;
}
