/* gelling, pre-launch page. Applies brand/BRAND-IDENTITY-v1.md; see DESIGN.md.

   No third-party requests, no tracking. Everything the page loads comes from
   this origin, which is also what the CSP enforces.

   Rules this file keeps, and why:
   - The signature gradient lives only inside the logo, never on text or buttons
     (brand spec 3.3).
   - Magenta and hot pink are used for large display and graphics only. Magenta
     is 4.08:1 on white and hot pink 3.42:1, both under AA for small text.
   - Shadows are purple-tinted, never black.
   - Motion is transform and opacity only, ease-out, and never loops. */

@font-face {
  font-family: "Manrope";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("/assets/fonts/manrope-latin-400-normal.woff2?v=hJKQ7xKi7r") format("woff2");
}
@font-face {
  font-family: "Manrope";
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  src: url("/assets/fonts/manrope-latin-600-normal.woff2?v=96xiWNogq3") format("woff2");
}
@font-face {
  font-family: "Manrope";
  font-weight: 800;
  font-style: normal;
  font-display: swap;
  src: url("/assets/fonts/manrope-latin-800-normal.woff2?v=dMFh28vOfD") format("woff2");
}

:root {
  --purple: #31065f;          /* Gelling Purple: ink */
  --magenta: #f20b83;         /* graphics and large display only */
  --hot-pink: #ff3198;
  --lavender: #ead5ff;
  --blush: #fce8f3;
  --pale-rose: #fff3f8;
  --paper: #fffbfd;           /* near-white, tinted toward the brand hue; never #fff */
  --body: #4d3a63;            /* purple-grey body text, 9.4:1 on paper */
  --quiet: #6f6976;           /* spec warm grey, one step darker so it clears AA */

  --font: "Manrope", system-ui, -apple-system, "Segoe UI", sans-serif;

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);   /* strong ease-out; the built-ins are too weak */
  --gutter: clamp(1.25rem, 5vw, 4.5rem);
  --measure: 1360px;
  /* keeps content on the grid while backgrounds stay full-bleed */
  --edge: max(var(--gutter), calc((100% - var(--measure)) / 2));
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100svh;
  background: var(--paper);
  color: var(--purple);
  font-family: var(--font);
  font-weight: 400;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

img, svg { display: block; max-width: 100%; }
p, h1, h2, blockquote { margin: 0; }

/* A fixed, non-scrolling layer of grain. It stops the pale gradients banding and
   gives flat colour a little tooth. Baked alpha, no blend mode (see make-grain.py). */
.grain {
  position: fixed;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  background: url("/assets/grain.png?v=OonbejBPrJ") 0 0 / 96px 96px;
}

/* ---------- top bar ---------- */

.top {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: clamp(1.1rem, 2.4vw, 2rem) var(--edge);
  animation: fade 700ms var(--ease-out) 900ms backwards;
}

.sign {
  display: grid;
  place-items: center;
  width: 44px;               /* touch target; the mark itself is 36px */
  height: 44px;
  margin: -4px;
  border-radius: 12px;
  transition: transform 160ms var(--ease-out);
}
.sign img { width: 36px; height: auto; }
.sign:active { transform: scale(0.95); }
.sign:focus-visible { outline: 3px solid var(--magenta); outline-offset: 4px; }

.status {
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.005em;
}

/* ---------- hero: one object in a luminous field ---------- */

.hero {
  min-height: 100svh;
  display: grid;
  align-content: center;
  gap: clamp(1.75rem, 4.5vh, 3.5rem);
  padding: clamp(5.5rem, 12vh, 8rem) var(--edge) clamp(2.5rem, 7vh, 5rem);
  background:
    radial-gradient(110% 80% at 8% 0%, var(--paper) 0%, rgb(255 251 253 / 0) 60%),
    linear-gradient(180deg, var(--pale-rose) 0%, var(--blush) 100%);
}

.stage {
  position: relative;
  width: min(100%, 1180px);
  /* the cutout carries ~1.6% of transparent padding; pull it back so the glyph's
     left edge lands on the same line as the headline below */
  margin-left: -1.6%;
  transform-origin: 50% 60%;
}

.gel {
  width: 100%;
  height: auto;
  /* purple-tinted, wide and low: the one place on the page with real depth */
  filter: drop-shadow(0 30px 38px rgb(49 6 95 / 0.17)) drop-shadow(0 6px 10px rgb(49 6 95 / 0.10));
  animation: arrive 820ms var(--ease-out) 80ms backwards;
}

/* Light on the gel. Clipped to the logo's own silhouette by using the logo as the
   mask, so it can only ever land on the object and never on the background. */
.sheen {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  -webkit-mask: url("/assets/logo-gel.webp?v=kCOIT-AQQh") 0 0 / 100% 100% no-repeat;
          mask: url("/assets/logo-gel.webp?v=kCOIT-AQQh") 0 0 / 100% 100% no-repeat;
}
.sheen i {
  position: absolute;
  top: -25%;
  bottom: -25%;
  left: 0;
  width: 44%;
  /* HORIZONTAL (90deg), not angled: an angled gradient on a tall narrow box is
     measured to the box's corners, so it never reaches zero at the side edges and
     the band ends in a hard vertical cut (it did, straight across the "e"). The
     slant comes from skewX instead, and the falloff is eased so no edge is visible. */
  background: linear-gradient(90deg,
    rgb(255 255 255 / 0) 0%,
    rgb(255 255 255 / 0.07) 22%,
    rgb(255 255 255 / 0.32) 50%,
    rgb(255 255 255 / 0.07) 78%,
    rgb(255 255 255 / 0) 100%);
  transform: translate3d(-5%, 0, 0) skewX(-14deg);
}

.intro {
  display: grid;
  grid-template-columns: minmax(0, 8fr) minmax(0, 4fr);
  align-items: end;
  gap: 1.5rem clamp(2rem, 6vw, 6rem);
}

h1 {
  font-weight: 800;
  font-size: clamp(2.15rem, 0.9rem + 4.4vw, 4.6rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  text-wrap: balance;
}
.ln {
  display: block;
  animation: arrive-text 800ms var(--ease-out) backwards;
}
.ln:nth-child(1) { animation-delay: 420ms; }
.ln:nth-child(2) { animation-delay: 500ms; }

.lede {
  max-width: 24ch;
  padding-bottom: 0.35em;          /* sits on the headline's last baseline, not its descender */
  font-size: clamp(1.1rem, 0.98rem + 0.5vw, 1.375rem);
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -0.012em;
  color: var(--body);
  text-wrap: pretty;
  animation: arrive-text 800ms var(--ease-out) 600ms backwards;
}

/* ---------- the word ---------- */

.meaning {
  padding: clamp(5rem, 12vw, 11rem) 0;
  background: var(--paper);
}

.meaning-grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  align-items: start;               /* a headword sits at the top of its entry, level with the first line */
  gap: 2.5rem clamp(2rem, 6vw, 7rem);
  padding-inline: var(--edge);
}

h2 {
  font-weight: 800;
  font-size: clamp(4.25rem, 1rem + 14vw, 12.5rem);
  line-height: 0.88;
  letter-spacing: -0.055em;
  /* optical: the display g hangs left of its box; nudge it onto the grid line */
  margin-left: -0.04em;
}

.pos {
  font-size: 1.0625rem;
  font-style: italic;
  color: var(--quiet);
}

.sense { margin-top: 0.9rem; }

.def {
  font-size: clamp(1.4rem, 0.95rem + 1.45vw, 2.2rem);
  font-weight: 800;
  line-height: 1.14;
  letter-spacing: -0.028em;
  text-wrap: balance;
}
/* The invitation under the definition: a hairline and a step in weight set it apart
   from the entry, so it reads as the page speaking, not the dictionary. */
.invite {
  margin-top: clamp(1.75rem, 3.4vw, 2.75rem);
  padding-top: clamp(1.25rem, 2.4vw, 1.75rem);
  border-top: 1px solid rgb(49 6 95 / 0.12);
}
.invite-line {
  font-size: clamp(1.5rem, 1rem + 1.9vw, 2.5rem);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
.invite-sub {
  margin-top: 0.75rem;
  max-width: 34ch;
  font-size: clamp(1.0625rem, 0.98rem + 0.4vw, 1.25rem);
  font-weight: 600;
  line-height: 1.4;
  color: var(--body);
  text-wrap: balance;               /* a two-line sentence, so even the lines out; no stranded "100% free." */
}
.invite-sub strong { font-weight: 800; color: var(--purple); }

.example { margin-top: clamp(1rem, 2vw, 1.5rem); }
.example::before {
  display: block;
  content: "\201C";
  content: "\201C" / "";          /* decorative; alt text keeps it out of screen readers */
  height: 3.6rem;
  margin-left: -0.07em;           /* the glyph's left bearing; seats it on the text edge */
  font-size: 7.5rem;
  font-weight: 800;
  line-height: 1;
  color: var(--magenta);          /* large display graphic, so 4.08:1 is enough */
}
.example p {
  margin-top: 0.4rem;
  font-size: clamp(1.25rem, 1rem + 1vw, 1.75rem);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--body);
}

/* ---------- the app ---------- */

.app {
  padding: clamp(4rem, 9vw, 8rem) 0;
  background: linear-gradient(180deg, var(--paper) 0%, var(--pale-rose) 100%);
}

.app-grid {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 5rem);
  padding-inline: var(--edge);
}

/* A white tile so the code keeps the quiet zone a phone camera needs, whatever sits
   behind it. Purple-tinted shadow, never black. */
.qr {
  padding: clamp(1rem, 1.6vw, 1.5rem);
  border-radius: 28px;
  background: #fff;
  box-shadow: 0 1px 0 rgb(49 6 95 / 0.06), 0 24px 48px -16px rgb(49 6 95 / 0.22);
}
.qr img { width: clamp(11rem, 16vw, 14.5rem); height: auto; }

.app-copy h2 {
  margin: 0;
  font-size: clamp(2rem, 1rem + 3vw, 3.75rem);
  line-height: 1.02;
  letter-spacing: -0.04em;
  text-wrap: balance;
}
.app-lede {
  margin-top: 1rem;
  max-width: 44ch;
  font-size: clamp(1.0625rem, 0.98rem + 0.4vw, 1.25rem);
  font-weight: 600;
  line-height: 1.4;
  color: var(--body);
  text-wrap: pretty;
}

/* Not links: no store listing exists, so there is nothing to link to. They read as
   the pills they will become, quietly, and they are plain text for assistive tech. */
.stores {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 1.75rem 0 0;
  padding: 0;
  list-style: none;
}
.stores li {
  padding: 0.7rem 1.25rem;
  border: 1.5px solid rgb(49 6 95 / 0.28);
  border-radius: 999px;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.005em;
}

.open-site { margin-top: 1.5rem; font-size: 1rem; color: var(--quiet); }
.open-site strong { font-weight: 800; color: var(--purple); }

/* ---------- closing ---------- */

.closing {
  position: relative;
  overflow: hidden;
  padding: clamp(5rem, 12vw, 10rem) var(--edge);
  background: var(--purple);
  color: var(--paper);
}

.ghost {
  position: absolute;
  right: -5vw;
  bottom: -22%;
  width: min(48vw, 680px);
  height: auto;
  opacity: 0.13;
  pointer-events: none;
}

.closing-inner { position: relative; }

.closing-line {
  font-size: clamp(2.75rem, 0.8rem + 8.4vw, 8.75rem);
  font-weight: 800;
  line-height: 0.94;
  letter-spacing: -0.05em;
  text-wrap: balance;
}

.fine {
  margin-top: clamp(1.25rem, 2.5vw, 2rem);
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--lavender);
}

/* ---------- 404 ---------- */

.lost { max-width: calc(var(--measure) + 2 * var(--gutter)); margin: 0 auto; padding: 1.5rem var(--gutter) 6rem; }
.lost .brand img { width: clamp(9rem, 28vw, 13rem); height: auto; }
.lost h1 { margin-top: clamp(2.5rem, 8vw, 6rem); }
.lost .lede { max-width: 36ch; margin-top: 1.25rem; padding: 0; animation: none; }
.lost a { color: var(--purple); text-underline-offset: 0.2em; }
.lost a:focus-visible { outline: 3px solid var(--magenta); outline-offset: 4px; border-radius: 0.25rem; }

/* ---------- motion ---------- */

/* Arrival: opacity, a short rise, and a blur that resolves. The blur hides the
   moment the image decodes; without it the logo pops in as two overlapping states. */
@keyframes arrive {
  from {
    opacity: 0;
    transform: translate3d(0, 18px, 0) scale(0.985);
    filter: blur(10px) drop-shadow(0 30px 38px rgb(49 6 95 / 0.17)) drop-shadow(0 6px 10px rgb(49 6 95 / 0.10));
  }
}
@keyframes arrive-text {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
}
@keyframes fade { from { opacity: 0; } }

/* The word, and the sign-off, rise into place as they enter the viewport. Only
   where the browser can drive it from scroll; everywhere else they are simply
   there, which is the right fallback. */
@keyframes rise {
  from { opacity: 0; transform: translate3d(0, 32px, 0); }
}
@supports (animation-timeline: view()) {
  .meaning-grid,
  .app-grid,
  .closing-inner {
    animation: rise linear both;
    animation-timeline: view();
    animation-range: entry 4% entry 52%;
  }
}

/* ---------- small screens ---------- */

@media (max-width: 52rem) {
  /* On a phone the hero is as tall as its content; 100svh left ~400px of dead space. */
  .hero { min-height: auto; align-content: start; padding: 5.5rem var(--edge) 3.5rem; }
  .stage { width: 104%; margin-left: -2%; }      /* the logo is the page on a phone */
  .intro { grid-template-columns: minmax(0, 1fr); }
  .lede { max-width: 30ch; padding-bottom: 0; }
  .meaning-grid { grid-template-columns: minmax(0, 1fr); }
  /* On a phone the visitor is already on the page, so a QR code to open it is
     pointless: show the message and the pills only. */
  .app-grid { grid-template-columns: minmax(0, 1fr); }
  .qr, .open-site, .scan-hint { display: none; }   /* the sentence goes with the code it describes */
  .ghost { width: 70vw; bottom: -12%; }
}

/* ---------- reduced motion: keep what aids comprehension, drop movement ---------- */

@media (prefers-reduced-motion: reduce) {
  .top, .gel, .ln, .lede { animation: fade 300ms ease both; }
  .meaning-grid, .app-grid, .closing-inner { animation: none; }
}
