/* Hallmark · macrostructure: Manifesto (single-statement poster) · tone: austere-luminous
 * theme: brand-locked (Ruminous Labs) · paper: deep #0c102c · accent: periwinkle #a0c0e6
 * display: rounded-geometric (Comfortaa 500 wordmark) · body: Plus Jakarta Sans
 * enrichment: Tier B hand-built SVG — the mark rebuilt as a monoline stroke
 *             along centrelines skeletonised from Kami's artwork, IoU 0.939
 * motion: dot-of-light (head, first) · draw-on (per stroke) · rise (type)
 * pre-emit critique: P5 H5 E5 S5 R5 V4
 */

/* tokens.css is linked ahead of this file in the document head, so the two
 * stylesheets download in parallel rather than one discovering the other. */

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

html, body {
  /* clip, never hidden — hidden on the root kills position:sticky descendants
   * and can trap scroll on iOS */
  overflow-x: clip;
  margin: 0;
}

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

body {
  min-height: 100svh;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: var(--color-peri); color: var(--color-paper); }

/* ---------- page frame ----------
 * Three rows: a status masthead, the mark, and the ground floor. The mark row
 * takes the slack, so the lockup holds the optical centre at every height
 * without the other two rows drifting off the edges. */
.page {
  min-height: 100svh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--space-xl);
  padding: clamp(1.25rem, 4vw, 3rem);
  position: relative;
  isolation: isolate;
  /* The glow is absolutely positioned and deliberately bleeds off the left
   * edge. Clipping it here keeps it out of body's scrollWidth, so no browser
   * can decide it deserves a horizontal scrollbar. */
  overflow: clip;
}

/* The glow is the only atmosphere on the page: one soft periwinkle bloom set
 * behind the mark and pushed off-centre, so it reads as light coming off the
 * artwork rather than as a centred wash. */
/* A square box with a centred circular gradient that reaches full transparency
 * well inside its own edges. An off-centre gradient on a border-radius:50% box
 * clips to a visible arc on the far side — the gradient still carries alpha
 * where it meets the rounded edge. Keeping the gradient concentric and fading
 * it out by 62% of closest-side means nothing ever meets the boundary. */
.glow {
  position: absolute;
  z-index: -1;
  left: 0;
  top: 50%;
  width: min(130vw, 66rem);
  aspect-ratio: 1;
  translate: -22% -50%;
  background: radial-gradient(
    circle closest-side at 50% 50%,
    color-mix(in oklab, var(--color-peri) 15%, transparent) 0%,
    color-mix(in oklab, var(--color-peri) 6%, transparent) 32%,
    color-mix(in oklab, var(--color-peri) 1.5%, transparent) 48%,
    transparent 62%
  );
  opacity: 0;
  pointer-events: none;
  animation: glow-in 1200ms var(--ease-out) var(--at-head) forwards;
}

/* ---------- accessible-only text ---------- */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- status masthead ---------- */
.bar {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  opacity: 0;
  animation: rise var(--dur-rise) var(--ease-out) var(--at-status) forwards;
}

.status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-ink-3);
  margin: 0;
}

.status__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-peri);
  flex: none;
  /* Slow, low-amplitude — a status light, not a notification badge. */
  animation: pulse 3.2s var(--ease-in-out) infinite;
}

/* ---------- the mark row ---------- */
.hero {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: start;
  gap: var(--space-lg);
  min-width: 0;
}

/* ---------- the lockup ----------
 * baseline alignment puts the mark's bottom edge (its baseline as a replaced
 * element) on the text baseline; the gap is letter-spacing, not a word-space. */
.lockup {
  display: flex;
  align-items: baseline;
  gap: var(--lockup-gap);
  font-size: var(--text-wordmark);
  min-width: 0;
}

.mark {
  display: block;
  height: var(--lockup-mark-h);
  /* width from the artwork's own ratio, so the mark can never be squeezed by
   * the flex row no matter how tight the viewport gets */
  width: calc(var(--lockup-mark-h) * var(--mark-aspect));
  flex: none;
  overflow: visible;
}

.wordmark {
  margin: 0;
  font-family: var(--font-wordmark);
  font-size: 1em;
  font-weight: var(--wordmark-weight);
  font-style: normal;
  letter-spacing: var(--wordmark-track);
  line-height: 1;
  /* one word, never two lines */
  white-space: nowrap;
  opacity: 0;
  animation: rise var(--dur-rise) var(--ease-out) var(--at-wordmark) forwards;
}

/* ---------- the mark's four parts ----------
 * Real SVG strokes along centrelines skeletonised from Kami's artwork, at the
 * measured monoline width with round caps — so `pathLength="1"` normalises each
 * path to a single unit and one dashoffset draws it, whatever its true length.
 * Nothing here traces an outline, so there is no pixel grid to stair-step. */
.part--arm,
.part--legs-a,
.part--legs-b {
  fill: none;
  stroke: var(--mark-stroke);
  stroke-width: var(--mark-stroke-w);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}

.part--arm    { animation: draw var(--dur-arm)    var(--ease-out) var(--at-arm)    forwards; }
.part--legs-a { animation: draw var(--dur-legs-a) var(--ease-out) var(--at-legs-a) forwards; }
.part--legs-b { animation: draw var(--dur-legs-b) var(--ease-out) var(--at-legs-b) forwards; }

/* ---------- the head: a dot of light ----------
 * It arrives first and alone, so it has to read as a light coming on rather
 * than a shape sliding in. Two elements do that: the dot itself fades up and
 * settles out to full size with no overshoot, while a soft bloom behind it
 * flares wide and dissipates. Nothing bounces — a bounce reads mechanical,
 * and light does not bounce. */
.part--head {
  fill: var(--mark-head);
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  transform: scale(0.28);
  animation: dot var(--dur-dot) var(--ease-out) var(--at-head) forwards;
}

.flare {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  transform: scale(0.2);
  pointer-events: none;
  animation: flare var(--dur-flare) var(--ease-out) var(--at-head) forwards;
}

/* ---------- slogan ----------
 * Set the way Kami's brand sheet sets it: lowercase, with the full stop, in the
 * light supporting weight rather than the wordmark face. */
.slogan {
  margin: 0;
  font-size: var(--text-slogan);
  font-weight: 400;
  font-style: normal;
  letter-spacing: 0.06em;
  line-height: 1.2;
  color: var(--color-peri);
  opacity: 0;
  animation: rise var(--dur-rise) var(--ease-out) var(--at-slogan) forwards;
}

/* ---------- ground floor ---------- */
.foot {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: var(--space-lg);
  padding-top: var(--space-lg);
  border-top: var(--rule-hair) solid var(--color-rule);
  opacity: 0;
  animation: rise var(--dur-rise) var(--ease-out) var(--at-foot) forwards;
}

.foot__copy {
  margin: 0;
  max-width: 44ch;
  color: var(--color-ink-2);
  text-wrap: balance;
}

.foot__contact {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  align-items: start;
}

.foot__label {
  margin: 0;
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-ink-3);
}

/* ---------- email link — all eight states ---------- */
.email {
  display: inline-block;
  color: var(--color-ink);
  font-weight: 600;
  text-decoration: none;
  /* drawn underline, so the hover can travel without shifting layout */
  background-image: linear-gradient(var(--color-peri), var(--color-peri));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 1px;
  padding-bottom: 2px;
  transition: background-size var(--dur-quick) var(--ease-out),
              color var(--dur-quick) var(--ease-out);
  white-space: nowrap;
}

.email:hover,
.email.is-hover { color: var(--color-peri); background-size: 100% 2px; }
.email:active,
.email.is-active { color: var(--color-peri); background-size: 100% 1px; }

/* Never animate the focus ring — it must appear instantly. */
.email:focus-visible,
.email.is-focus {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
  transition: none;
}

/* ---------- keyframes — transform and opacity only ---------- */
@keyframes draw { to { stroke-dashoffset: 0; } }

@keyframes dot {
  0%   { opacity: 0; transform: scale(0.28); }
  40%  { opacity: 1; }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes flare {
  0%   { opacity: 0;    transform: scale(0.2); }
  18%  { opacity: 0.95; transform: scale(0.7); }
  100% { opacity: 0;    transform: scale(1); }
}

@keyframes rise {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}

@keyframes glow-in { to { opacity: 1; } }

@keyframes pulse {
  0%, 100% { opacity: 0.45; transform: scale(0.88); }
  50%      { opacity: 1;    transform: scale(1); }
}

/* ---------- mobile ---------- */
@media (max-width: 600px) {
  .page { gap: var(--space-lg); }

  .foot {
    flex-direction: column;
    align-items: start;
  }

  .glow { left: 50%; translate: -50% -50%; width: 150vw; }
}

/* ---------- reduced motion ----------
 * Everything lands in its final state; spatial motion collapses to a short
 * crossfade and the two looping animations stop entirely. */
@media (prefers-reduced-motion: reduce) {
  .part--arm,
  .part--legs-a,
  .part--legs-b { stroke-dashoffset: 0; animation: none; }

  .part--head {
    transform: none;
    opacity: 1;
    animation: none;
  }

  /* the bloom only exists to animate, so it does not appear at all */
  .flare { display: none; }

  .bar,
  .wordmark,
  .slogan,
  .foot {
    opacity: 1;
    transform: none;
    animation: fade 150ms linear both;
  }

  .glow  { opacity: 1; animation: none; }
  .status__dot { animation: none; opacity: 1; transform: none; }

  .email { transition-duration: 1ms; }

  @keyframes fade { from { opacity: 0; } to { opacity: 1; } }
}
