/* Словолов — the landing page.
 *
 * The whole page is ONE dictionary entry for the word «словоло́в»: headword,
 * grammar tag, numbered senses, a usage example. A game about collecting
 * dictionary entries describes itself as one — that is the entire composition.
 * No cards, no feature grids, no section headings; one full-height hero where
 * words fall toward the catcher, then one reading column.
 *
 * The word-fall draws from a large shuffled pool (site.js swaps each word for
 * the next as its fall ends), so the sky does not repeat itself. Without
 * JavaScript the fourteen words in the markup simply loop — a quieter sky,
 * never a broken one.
 */

/* --- hero: the sky, the name, the catcher ---------------------------------- */

.hero {
  position: relative;
  overflow: hidden;
  min-height: 88vh;
  min-height: 88svh;
  display: grid;
  place-items: center;
  text-align: center;
  /* Horizontal drags steer the catcher; vertical ones still scroll the page. */
  touch-action: pan-y;
}

.hero-inner {
  position: relative;
  z-index: 1;
  padding: 56px 40px;
  /* A soft paper halo behind the name, so falling words pass BEHIND the
     light instead of through the letters — load-bearing on narrow screens,
     where the sky and the title share the same columns. */
  background: radial-gradient(
    closest-side,
    var(--paper) 45%,
    color-mix(in srgb, var(--paper) 60%, transparent) 78%,
    transparent 100%
  );
}

.hero h1 {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(3rem, 10vw, 4.75rem);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.05;
}

.hero-rule {
  width: 64px;
  height: 3px;
  margin: 24px auto;
  background: var(--accent);
  border-radius: var(--radius-pill);
}

.hero-tagline {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(1.25rem, 3.5vw, 1.625rem);
  font-style: italic;
  color: var(--ink-2);
}

.hero-stores {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 30px;
}

/* The falling words fill the whole hero. Each span is a SLOT: its position
   and rhythm live here, its text is swapped by site.js at every iteration. */

.hero-sky {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.hero-sky span {
  position: absolute;
  top: -48px;
  font-family: var(--serif);
  font-weight: 500;
  color: var(--ink-soft);
  /* `left` names the lane's CENTRE: the word hangs symmetrically off it, so
     a long word cannot poke past the right edge of a right-hand lane. The
     fall animates the separate `translate` property, which composes with
     this transform instead of overwriting it. */
  transform: translateX(-50%);
  animation: word-fall linear infinite;
}

@keyframes word-fall {
  0% {
    translate: 0 0;
    opacity: 0;
  }
  10% {
    opacity: 0.75;
  }
  85% {
    opacity: 0.7;
  }
  100% {
    translate: 0 96vh;
    opacity: 0;
  }
}

@supports (height: 100svh) {
  @keyframes word-fall {
    0% {
      translate: 0 0;
      opacity: 0;
    }
    10% {
      opacity: 0.75;
    }
    85% {
      opacity: 0.7;
    }
    100% {
      translate: 0 96svh;
      opacity: 0;
    }
  }
}

.hero-sky .w1 { left: 8%;  font-size: 1.1875rem; animation-duration: 24s; animation-delay: -8s; }
.hero-sky .w2 { left: 15%; font-size: 0.9375rem; animation-duration: 39s; animation-delay: -27s; }
.hero-sky .w3 { left: 22%; font-size: 1.3125rem; animation-duration: 21s; animation-delay: -3s; }
.hero-sky .w4 { left: 29%; font-size: 1rem;      animation-duration: 34s; animation-delay: -19s; }
.hero-sky .w5 { left: 36%; font-size: 1.0625rem; animation-duration: 25s; animation-delay: -12s; }
.hero-sky .w6 { left: 43%; font-size: 0.875rem;  animation-duration: 40s; animation-delay: -31s; }
.hero-sky .w7 { left: 50%; font-size: 1.25rem;   animation-duration: 22s; animation-delay: -16s; }
.hero-sky .w8 { left: 57%; font-size: 0.9375rem; animation-duration: 36s; animation-delay: -7s; }
.hero-sky .w9 { left: 64%; font-size: 1.0625rem; animation-duration: 26s; animation-delay: -22s; }
.hero-sky .w10 { left: 71%; font-size: 0.875rem;  animation-duration: 42s; animation-delay: -35s; }
.hero-sky .w11 { left: 78%; font-size: 1.1875rem; animation-duration: 23s; animation-delay: -11s; }
.hero-sky .w12 { left: 85%; font-size: 1rem;      animation-duration: 38s; animation-delay: -24s; }
.hero-sky .w13 { left: 91%; font-size: 1.0625rem; animation-duration: 28s; animation-delay: -5s; }
.hero-sky .w14 { left: 96%; font-size: 0.875rem;  animation-duration: 33s; animation-delay: -18s; }

/* Narrow screens: fourteen lanes cannot fit fourteen words. Six survive, on
   three well-separated columns with two opposite-phase tenants each — the
   sky stays alive and nothing lands on anything. */
@media (max-width: 640px) {
  .hero-sky .w7,
  .hero-sky .w8,
  .hero-sky .w9,
  .hero-sky .w10,
  .hero-sky .w11,
  .hero-sky .w12,
  .hero-sky .w13,
  .hero-sky .w14 {
    display: none;
  }

  .hero-sky .w1 { left: 18%; font-size: 1rem;      animation-duration: 26s; animation-delay: -6s; }
  .hero-sky .w2 { left: 50%; font-size: 0.9375rem; animation-duration: 38s; animation-delay: -24s; }
  .hero-sky .w3 { left: 80%; font-size: 1.0625rem; animation-duration: 23s; animation-delay: -13s; }
  .hero-sky .w4 { left: 22%; font-size: 0.875rem;  animation-duration: 37s; animation-delay: -30s; }
  .hero-sky .w5 { left: 52%; font-size: 1rem;      animation-duration: 24s; animation-delay: -2s; }
  .hero-sky .w6 { left: 78%; font-size: 0.9375rem; animation-duration: 40s; animation-delay: -35s; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-sky span {
    animation: none;
    opacity: 0.55;
  }
  /* Scatter statically instead of raining. */
  .hero-sky .w1 { top: 12%; }
  .hero-sky .w2 { top: 64%; }
  .hero-sky .w3 { top: 26%; }
  .hero-sky .w4 { top: 78%; }
  .hero-sky .w5 { top: 38%; }
  .hero-sky .w6 { top: 84%; }
  .hero-sky .w7 { top: 16%; }
  .hero-sky .w8 { top: 52%; }
  .hero-sky .w9 { top: 88%; }
  .hero-sky .w10 { top: 44%; }
  .hero-sky .w11 { top: 70%; }
  .hero-sky .w12 { top: 30%; }
  .hero-sky .w13 { top: 8%; }
  .hero-sky .w14 { top: 56%; }
}

/* The catcher waits at the bottom of the hero, mouth open — and breathes on
   the app's own 3200ms cycle. A breath, not a bounce. The wrapper is what
   site.js slides along the bottom (the visitor steers it with the pointer);
   the svg inside carries the breath, so movement and breathing never fight
   over one transform. */

.hero-catcher {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: 28px;
  width: min(190px, 44vw);
  height: auto;
  margin-left: calc(min(190px, 44vw) / -2);
}

.hero-catcher svg {
  display: block;
  width: 100%;
  height: auto;
  transform-origin: 50% 50%;
  animation: catcher-breath 3200ms ease-in-out infinite;
}

.hero-catcher .ring {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.68;
}

.hero-catcher .drop {
  fill: var(--accent);
}

@keyframes catcher-breath {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.02);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-catcher svg {
    animation: none;
  }
}

/* A caught word comes apart into these: one letter each, spawned on the very
   pixel its glyph stood on, driven frame-by-frame by site.js along the dot
   catcher's own inhale. */

.fly-letter {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
  pointer-events: none;
  white-space: pre;
  font-family: var(--serif);
  font-weight: 500;
  color: var(--ink-soft);
  will-change: transform, opacity;
}

/* The tenth catch: the game's own «улов повний», said once, quietly. */

.hero-full {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: 126px;
  transform: translateX(-50%);
  margin: 0;
  padding: 6px 16px;
  white-space: nowrap;
  text-align: center;
  font-family: var(--serif);
  font-size: 0.9375rem;
  font-style: italic;
  color: var(--ink-2);
  background: color-mix(in srgb, var(--paper) 85%, transparent);
  border-radius: var(--radius-pill);
  opacity: 0;
  transition: opacity 0.6s ease-out;
}

.hero-full.show {
  opacity: 1;
}

/* --- the entry: the whole site as one dictionary article ------------------- */

.entry {
  max-width: 44rem;
  margin: 0 auto;
  padding: 48px 24px 0;
  font-family: var(--serif);
  font-size: 1.0625rem;
  line-height: 1.7;
}

.entry-head {
  margin: 0 0 28px;
  font-size: 1.625rem;
  line-height: 1.2;
}

.entry-headword {
  font-weight: 600;
}

.entry-gram {
  font-style: italic;
  color: var(--ink-soft);
  font-size: 1.125rem;
}

/* Senses: a dictionary's numbered meanings. The numbers are set by CSS
   counters in the accent — the one place on the page the wax is allowed to
   count something. */

.senses {
  list-style: none;
  counter-reset: sense;
  margin: 0;
  padding: 0;
}

.sense {
  counter-increment: sense;
  position: relative;
  margin: 0 0 22px;
  padding-left: 40px;
}

.sense::before {
  content: counter(sense) ".";
  position: absolute;
  left: 0;
  top: 0;
  font-weight: 600;
  color: var(--accent);
}

.sense p {
  margin: 0;
}

.sense p + p {
  margin-top: 10px;
}

/* The three groups, spoken inline the way a dictionary marks registers. */

.g-word {
  font-weight: 600;
  white-space: nowrap;
}

.g-word svg {
  display: inline-block;
  width: 15px;
  height: 15px;
  margin-right: 2px;
  vertical-align: -2px;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.g-new { color: var(--group-new); }
.g-new svg { stroke: var(--group-new); }
.g-beautiful { color: var(--group-beautiful); }
.g-beautiful svg { stroke: var(--group-beautiful); }
.g-important { color: var(--group-important); }
.g-important svg { stroke: var(--group-important); }

/* The sample word-of-day inside sense 4 — a word with its stress, the way
   the widget itself sets one. */

.entry-sample {
  font-weight: 600;
}

/* The separator: a hairline with one wax dot — dictionary punctuation. */

.entry-sep {
  position: relative;
  height: 1px;
  margin: 40px 0;
  border: 0;
  background: var(--hairline);
}

.entry-sep::after {
  content: "";
  position: absolute;
  left: 50%;
  top: -3px;
  width: 7px;
  height: 7px;
  transform: translateX(-50%);
  background: var(--accent);
  border-radius: 50%;
}

/* The usage example: the in-app credo, cited the way dictionaries cite. */

.entry-usage {
  margin: 0 0 28px;
  padding: 2px 0 2px 18px;
  border-left: 2px solid var(--accent);
  font-style: italic;
  color: var(--ink-2);
}

.entry-usage p {
  margin: 0;
}

/* The plain facts, in the chrome voice — one quiet line. */

.entry-facts {
  margin: 0 0 32px;
  font-family: var(--sans);
  font-size: 0.9375rem;
  color: var(--ink-soft);
}

.entry-stores {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 0;
}

.store-pill {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 12px 22px;
  font-family: var(--sans);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
}

.store-pill svg {
  width: 22px;
  height: 22px;
  flex: none;
  fill: var(--ink-2);
}

.store-pill small {
  display: block;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* The pills were labels while the game was «скоро»; they are doors now — iOS
   to the App Store, Android to /beta. The lift is the only motion on a quiet
   page, so it stays a millimetre. */

a.store-pill {
  text-decoration: none;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease,
    transform 0.2s ease;
}

a.store-pill:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}

a.store-pill:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  a.store-pill {
    transition: none;
  }

  a.store-pill:hover {
    transform: none;
  }
}

/* --- reveal on scroll ------------------------------------------------------
   Gated behind `html.js` (set by /assets/site.js), so with no JavaScript
   nothing is ever hidden. The curve is the game's own catch curve; the rise
   is a breath, not a bounce. */

.js .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity 0.7s ease-out,
    transform 0.7s cubic-bezier(0.2, 0.8, 0.3, 1);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
