/* becoming.to — a dawn that breathes. Tokens first; everything derives.
   No control chrome exists: the card is the interface (see app.ts). */

:root {
  --dawn: #f7f0e8;
  --dawn-2: #f2e0d6;
  --ink: #453b36;
  --ink-soft: #8a7a72;
  --rose: #c98a7d;
  --gold: #b78147;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* Hold progress, written by app.ts; the words brighten on the inhale. */
  --gather: 0;
}

* {
  box-sizing: border-box;
  margin: 0;
}

html,
body {
  height: 100%;
}

body {
  background: linear-gradient(180deg, var(--dawn) 0%, var(--dawn-2) 100%);
  color: var(--ink);
  font-family: var(--sans);
  overflow: hidden;
}

#dawn {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  overflow: hidden;
}

/* The pool's glory overlay (bloom) sits over the stage canvas, inside #dawn. */
.pool-glory {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.wordmark {
  position: fixed;
  top: 1.25rem;
  left: 1.5rem;
  z-index: 1;
  font-family: var(--serif);
  font-size: 1.05rem;
  font-style: italic;
  letter-spacing: 0.06em;
  color: color-mix(in srgb, var(--gold) 65%, transparent);
  pointer-events: none;
}

/* The whole viewport takes the gesture — there is no smaller target to find. */
.stage {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 1.5rem;
  touch-action: none;
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
}

.stage:active {
  cursor: grabbing;
}

/* Law 1: turn.ts drives --turn-rot/--turn-x/--turn-shadow directly, once per
   animation frame — never a CSS transition on transform here, since a
   transition would fight turn.ts's own eased tween instead of letting it
   own the timing (the fixed MIN_TURN_MS pace IS the law). The unset
   defaults (0deg / 0px / none) are a flat, resting page. Arrival is an
   opacity fade alone, so the base transform stays free for the turn. */
.card {
  max-width: 32rem;
  text-align: center;
  opacity: 0;
  transform: perspective(900px) rotateY(var(--turn-rot, 0deg)) translateX(var(--turn-x, 0px));
  transition: opacity 900ms ease, filter 400ms ease;
  filter: drop-shadow(0 0 calc(var(--gather) * 22px) color-mix(in srgb, var(--gold) 45%, transparent));
  box-shadow: var(--turn-shadow, none);
}

.card.risen {
  opacity: 1;
}

/* While the finger is down the card tracks it 1:1 — no easing in the way. */
.card.dragging {
  transition: filter 400ms ease;
}

/* Arrival hint: the card drifts once, teaching the gesture without a word. */
.card.hinting {
  animation: drift 900ms ease-in-out;
}

@keyframes drift {
  0%,
  100% {
    transform: none;
  }
  50% {
    transform: translateX(-14px);
  }
}

/* Carried: the light rises through the words once and settles. */
.card.carried {
  animation: carried 800ms ease-out;
}

@keyframes carried {
  0% {
    filter: drop-shadow(0 0 0 transparent);
  }
  35% {
    filter: drop-shadow(0 0 26px color-mix(in srgb, var(--gold) 70%, transparent));
    transform: translateY(-5px);
  }
  100% {
    filter: drop-shadow(0 0 0 transparent);
    transform: none;
  }
}

/* The breath let go with nothing in it: the hold was heard, honestly. */
.card.released {
  animation: released 800ms ease-out;
}

@keyframes released {
  0%,
  100% {
    opacity: 1;
    transform: none;
  }
  45% {
    opacity: 0.7;
    transform: translateY(3px);
  }
}

/* Law 5, the memory palace: keeping an affirmation collapses the card toward
   the very morning point its mote lands at (--keep-dx/--keep-dy, written by
   app.ts from world.landingPoint), released as the mote over 480ms, then the
   class is dropped (app.ts's animationend) and the kept card fades back via
   .card's own opacity transition — still readable, now with its mote of light
   overhead. Recall from the morning is the opposite and instant
   (paint({instant}), no animation at all). ease-in so the words gather speed
   as they're drawn up into the light. */
.card.keeping {
  animation: keeping 480ms ease-in forwards;
}

@keyframes keeping {
  0% {
    transform: perspective(900px) translate(0, 0) scale(1);
    opacity: 1;
  }
  100% {
    transform: perspective(900px) translate(var(--keep-dx, 0px), var(--keep-dy, 0px)) scale(0.12);
    opacity: 0;
  }
}

/* The one word on the card that answers to touch: it tunes the trail to its
   own kind. Styled as part of the writing, never as a control. */
.category {
  display: inline-block;
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--rose);
  margin-bottom: 1.25rem;
  cursor: pointer;
  transition: color 300ms ease, text-shadow 300ms ease;
}

.category:hover {
  color: var(--gold);
}

/* Tuned: this kind is lit, and the trail holds to it. */
.category.tuned {
  color: var(--gold);
  text-shadow: 0 0 14px color-mix(in srgb, var(--gold) 55%, transparent);
}

/* Kept: these words already have a light in your morning, on top of the same
   material shadow every affirmation carries (see .affirmation below and tilt.ts). */
.card.kept .affirmation {
  text-shadow:
    0 0 24px color-mix(in srgb, var(--gold) 24%, transparent),
    calc(var(--tilt-x, 0.6px)) calc(var(--tilt-y, 0.9px)) 6px rgba(69, 59, 54, 0.28);
}

/* Law 3: the words are material. tilt.ts writes --tilt-x/--tilt-y/--tilt-track
   at most once a frame from the device's orientation, low-pass filtered and
   scaled down for heavier affirmations (see cardWeight) — the shadow drifts and
   the letters breathe as the card tilts. Every var default below is the same
   fixed, gentle look tilt.ts itself applies when the device offers nothing to
   read (no sensor, permission not granted, prefers-reduced-motion): the
   degraded state is designed, not an absence, and needs no JS at all to look
   intentional. The shadow is ink-toned and soft — a warm dawn casts less than
   a night sky, so its alpha sits lower than the ember world's. */
.affirmation {
  font-family: var(--serif);
  font-size: clamp(1.6rem, 4.8vw, 2.5rem);
  line-height: 1.4;
  font-weight: 400;
  letter-spacing: calc(0em + var(--tilt-track, 0.01em));
  text-shadow: calc(var(--tilt-x, 0.6px)) calc(var(--tilt-y, 0.9px)) 6px rgba(69, 59, 54, 0.22);
}

/* Law 4: each phrase of the affirmation fades in on its own, a beat after the
   last (see unveil.ts). A kept card re-expanding from its mote paints the
   words directly, with no spans at all — instant is its own honest state. */
.phrase {
  display: inline-block;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 320ms ease-out, transform 320ms ease-out;
}

.phrase.lit {
  opacity: 1;
  transform: translateY(0);
}

/* Present to assistive tech and to the keyboard, invisible to the world. */
.sr-only button {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.sr-only button:focus-visible {
  position: fixed;
  bottom: 1rem;
  left: 50%;
  transform: translateX(-50%);
  width: auto;
  height: auto;
  clip: auto;
  padding: 0.55rem 1.3rem;
  border-radius: 999px;
  border: 1px solid var(--gold);
  background: var(--dawn);
  color: var(--gold);
  font-family: var(--sans);
  font-size: 0.9rem;
  outline: none;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--gold) 40%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .card,
  .card.hinting,
  .card.carried,
  .card.released,
  .card.keeping,
  .phrase {
    transition: none;
    animation: none;
  }

  /* turn.ts never writes --turn-rot/--turn-x in this mode (progress() is a
     no-op there) and drives a plain opacity crossfade for commit() instead
     — this is belt-and-suspenders so the card is flat even if some other
     path ever left a stale var behind. */
  .card {
    transform: none;
  }
}
