/* ==========================================================================
   Creek Studio V4 — motion stylesheet
   Everything that moves: reveals, the loader and its wordmark stage, the hero C,
   the cursor, page transitions. Tokens come from creek.css (spec 9.1). The html
   classes js / seen / reduced / loaded / motion / has-cursor gate each piece;
   without them the page is complete and still.
   ========================================================================== */

/* ---------- 1. Monogram ----------
   Base rules (stroke, fill, the C group's origin) live in creek.css section 5. */

/* ---------- 2. Reveals ----------
   Only active when JS has confirmed motion is allowed (html.motion). Anything in the
   first viewport is marked in before paint, so the page is readable at rest. */
html.motion [data-reveal="text"] {
  transition:
    opacity var(--t-3) var(--ease) var(--d, 0ms),
    translate var(--t-3) var(--ease) var(--d, 0ms);
}
html.motion [data-reveal="text"]:not(.is-in) { opacity: 0; translate: 0 var(--rise); }

html.motion [data-reveal="media"] .media-frame {
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--t-4) var(--ease) var(--d, 0ms);
}
html.motion [data-reveal="media"]:not(.is-in) .media-frame { clip-path: inset(0 0 100% 0); }

/* The frame opens on schedule whether or not the file has arrived. A picture that is
   already decoded, the normal case with two screens of warning, is opaque as the
   frame opens. Only a late one fades up, on the frame's own mist; the opacity
   transition sits with the image rule in creek.css beside its hover scale. */
html.motion [data-reveal="media"] .media-frame img:not(.is-loaded) { opacity: 0; }

/* ---------- 3. Loader ----------
   In the HTML on HOME, shown from first paint on a first visit with motion. The
   kill-switch animation frees the page after t-loader-max if the script never runs;
   a running script takes that duty over (.is-live) with its own deadline. */
.loader { display: none; }
html.js:not(.seen):not(.reduced) .loader {
  display: grid;
  place-items: center;
  position: fixed;
  inset: 0;
  height: 100dvh;
  z-index: 100;
  background: var(--ink);
  color: var(--ground);
  animation: loader-kill 400ms var(--t-loader-max) forwards;
}
.loader.is-live { animation: none; }
@keyframes loader-kill { to { opacity: 0; visibility: hidden; pointer-events: none; } }

.cs--loader {
  width: var(--loader-size);
  height: var(--loader-size);
  transform-origin: 50% 50%;
  will-change: transform;
}
.cs--loader .cs-c { animation: orbit var(--t-orbit) var(--ease-linear) infinite; }
@keyframes orbit { to { transform: rotate(360deg); } }
/* progress is the arc's own length: pathLength="1", so --p is the lit fraction */
.cs--loader .cs-arc {
  stroke: var(--signal);
  stroke-dasharray: var(--p, 0) 1;
  transition: stroke-dasharray var(--t-1) linear;
}
.cs--loader .cs-s { fill: var(--ground); }

/* The wordmark stage the reveal builds over the page copy's rect */
.loader-stage { position: fixed; pointer-events: none; }
.loader-stage canvas,
.loader-stage svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; overflow: visible; }
.loader-stage .wordmark .wm-wire { display: inline; }
/* opacity is set here as well as display: creek.css hides the page copy's wire by opacity */
.loader-stage .wordmark .wm-wire path {
  fill: none;
  stroke: var(--ground);
  stroke-width: var(--lw-hair);
  vector-effect: non-scaling-stroke;
  opacity: 1;
}
.loader-stage .wordmark .wm-fill path { fill: var(--ground); opacity: 0; }

/* The page's wordmark: fill only; the wire layer exists for the stage */
.wordmark .wm-wire { display: none; }

/* The hero pieces the ink covers stay clear until the loader hands over (html.loaded).
   The same delayed animation shows them if the script dies; under the ink that is
   invisible, and on a page that lost its loader it is the rescue. */
html.js:not(.seen):not(.reduced):not(.loaded) .hero .wordmark,
html.js:not(.seen):not(.reduced):not(.loaded) .hero-mark {
  opacity: 0;
  animation: loader-show 400ms var(--t-loader-max) forwards;
}
@keyframes loader-show { to { opacity: 1; } }

/* ---------- 4. Cursor ----------
   Fine pointers only (html.has-cursor is set by cursor.js). A hairline ring and a dot,
   moved by translate3d alone; state changes over t-1. */
.cursor { display: none; }
html.has-cursor .cursor {
  display: block;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 90;
  pointer-events: none;
  color: var(--ink);
  will-change: transform;
  transition: opacity var(--t-1) linear;
}
.cursor[data-tone="dark"] { color: var(--ground); }
.cursor[data-state="hidden"] { opacity: 0; }

.cursor-ring {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--cursor-ring);
  height: var(--cursor-ring);
  translate: -50% -50%;
  border: var(--lw-ring) solid currentColor;
  border-radius: 50%;
  transition: width var(--t-1) var(--ease), height var(--t-1) var(--ease), border-color var(--t-1) linear;
}
.cursor-ring::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--cursor-dot);
  height: var(--cursor-dot);
  translate: -50% -50%;
  border-radius: 50%;
  background: currentColor;
  transition: opacity var(--t-1) linear;
}
.cursor:not([data-state="default"]) .cursor-ring::after { opacity: 0; }
.cursor[data-state="link"] .cursor-ring,
.cursor[data-state="close"] .cursor-ring { width: var(--cursor-ring-link); height: var(--cursor-ring-link); }
.cursor[data-state="media"] .cursor-ring,
.cursor[data-state="video"] .cursor-ring { width: var(--cursor-ring-media); height: var(--cursor-ring-media); }

.cursor-label {
  position: absolute;
  left: 0;
  top: 0;
  translate: -50% -50%;
  white-space: nowrap;
  font-family: var(--font-sans);
  font-size: var(--fs-meta);
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity var(--t-1) linear;
}
.cursor[data-state="media"] .cursor-label,
.cursor[data-state="video"] .cursor-label,
.cursor[data-state="close"] .cursor-label { opacity: 1; }

/* the native cursor goes only where the custom one carries the message */
html.has-cursor [data-cursor],
html.has-cursor [data-cursor] * { cursor: none; }

/* ---------- 5. Page to page ----------
   Cross-document view transitions. Old root out over --t-out, new root in over
   --t-5. Browsers without the API navigate as usual. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-leave var(--t-out) var(--ease-in) both; }
::view-transition-new(root) { animation: vt-enter var(--t-5) var(--ease) both; }
@keyframes vt-leave { to { opacity: 0; transform: translateY(-16px); } }
@keyframes vt-enter { from { opacity: 0; transform: translateY(16px); } }

/* ---------- 6. Reduced motion ----------
   The tokens are already 0 under reduce (creek.css); the two literals left are the
   orbit, which is never shown, and the page-leave animation. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ---------- 7. Print ---------- */
@media print {
  .loader, .cursor { display: none !important; }
  /* nothing stays hidden waiting for a scroll event that will never fire */
  [data-reveal], [data-reveal] * { opacity: 1 !important; clip-path: none !important; translate: none !important; }
  .hero .wordmark, .hero-mark { opacity: 1 !important; }
}
