/* KUN-1333 — responsive subject composition

   The deployed page historically made every 3:2 source plate cover a viewport-sized box with
   28% vertical overscan. At 2560×1080 that enlarged the character to roughly 1.65× the viewport
   height. These rules give the shared art canvas an explicit 3:2 stage instead: the environment
   can still bleed, while every source pixel of the registered character + bow pair has a stable
   place in the final frame.
*/

.hero {
  isolation: isolate;
}

.art-stage {
  /* Geometry derived from the ART, measured 2026-09-20, not chosen by eye:
       character plate  ink x 38.7%-87.1%, y 3.0%-100.0%  (she is CUT at the plate's
                        bottom row - ink still spans 43.8%-74.6% at y=1023)
       environment      ink x 18.3%-100%, alpha ramps in 23%->46% and OUT again
                        88% @ 91%  ->  52% @ 94.4%  ->  6.5% @ 97.7%
     Two consequences, and they set every number below.

     1. BOTTOM-ANCHORED, never centred. Her legs leave the artwork, so the plate's
        bottom edge IS her cut line; it has to coincide with the viewport floor or
        sit under it, otherwise the cut floats in mid-paper with a dead band beneath
        her. Centring measured -540px of empty paper under her feet on a tablet.
       The vw bound is 108, not 100, and it is the plate's LEFT edge that sets it: with the
     right overhang the plate's left edge sits at V - 0.915W, so it stays on screen while
     W <= 1.093V. Past that the art's own 23%->46% feather is replaced by a hard cut
     exactly where the copy meets the forest. 100vw was leaving that headroom unused and
     costing height on near-square windows - she was 66.9% of the viewport at 1396x1350.

   2. OVERHANGS THE RIGHT (see --stage-overhang), so the forest's baked-in right
        feather passes the screen edge instead of fading out just short of it. That
        soft stop against the frame is what read as "background is cut on the right". */
  position: absolute;
  /* How far the plate hangs past the right edge, as a fraction of its own width.
     Set by the art, not by taste: the forest holds 88% alpha out to 91% of the plate
     and then falls away (52% at 94.4%, 6.5% at 97.7%). At 4.5% the tail was still on
     screen and measurable - column ink density dropped 25 -> 11.5 between 88% and the
     screen edge on every wide viewport. 8.5% puts the last strong column at the frame. */
  --stage-overhang: .085;
  --stage-w: min(108vw, 152svh);
  --stage-w: min(108vw, 152dvh);
  width: var(--stage-w);
  right: calc(var(--stage-w) * -1 * var(--stage-overhang));
  top: auto;
  bottom: 0;
  aspect-ratio: 3 / 2;
  transform: none;
  pointer-events: none;
}

/* Two registered copies of the same stage allow the paper wash to sit between depth planes.
   Transforms create stacking contexts, so z-indexing children inside one stage can never place
   only the subject above a sibling wash. Environment < wash < subject < copy is intentional. */
.art-stage--environment { z-index: 1; }
.art-stage--subject { z-index: 5; }

.art-stage .layer,
.subject-arrival,
.subject-idle {
  position: absolute;
  inset: 0;
}

.art-stage .layer {
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  width: auto;
  overflow: visible;
  background-position: center;
  background-size: 100% 100%;
  transform: none;
  will-change: auto;
}

.art-stage .layer::before {
  background-position: center;
  background-size: 100% 100%;
}

/* The drifting leaves are a TILE, not a plate, so they must escape the rule above.
   `.art-stage .layer` (0,2,0) was beating `.layer--near` (0,1,0), which stretched one
   420x420 SVG across the whole 816x544 stage: a single leaf blown up to ~200px and
   blurred, sitting on bare paper as a green smudge. The base sheet's own comment says
   it - "a crisp leaf at low opacity reads as dirt" - and that is exactly what it was. */
.art-stage .layer--near,
.art-stage .layer--near::before {
  background-repeat: repeat;
  background-size: min(30vw, 300px) auto;
}

.layer--env {
  /* NO left mask. The plate already feathers itself - measured mean alpha 0.1% at
     22.8% across, 8.2% at 29.3%, 35.4% at 35.8%, 73.6% at 45.6%. Masking on top of
     that multiplied two ramps together, so the forest did not reach full strength
     until nearly half the frame: the wide pale band between the copy and the trees. */
  z-index: 1;
  background-position: center;
  -webkit-mask-image: none;
          mask-image: none;
}

.layer--near {
  z-index: 2;
  opacity: .24;
  background-position: center;
  background-size: 14rem auto;
  -webkit-mask-image:
    radial-gradient(ellipse 42% 34% at 61% 29%, transparent 0 66%, #000 100%),
    linear-gradient(90deg, transparent 0 36%, #000 62%);
          mask-image:
    radial-gradient(ellipse 42% 34% at 61% 29%, transparent 0 66%, #000 100%),
    linear-gradient(90deg, transparent 0 36%, #000 62%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

.subject-arrival {
  z-index: 3;
}

.subject-idle {
  transform-origin: 68% 48%;
}

.subject-idle .layer--char,
.subject-idle .layer--bow {
  background-position: center;
  -webkit-mask-image: none;
          mask-image: none;
}

.subject-idle .layer--char { z-index: 0; }
.subject-idle .layer--bow { z-index: 1; }

/* kuno, 2026-09-20: "current https://mythoku.com/ has much better animation than
   what u used here". The recovered sheet REPLACED the live animation system with
   its own (mythoku-subject-enter / -breathe on wrapper divs) and killed the
   original here with `animation: none`. His call: keep the live one. The
   wrappers stay — they are what gives the two planes independent geometry — but
   they no longer animate, so the page's own layer-arrive-near + character-breathe
   run exactly as they do in production. */

/* The distant plate settles first. It never scales after arrival, which keeps the AVIF sharp. */
@keyframes mythoku-environment-enter {
  0%   { opacity: 0; transform: translate3d(2.4%, 0, 0) scale(1.025); }
  64%  { opacity: 1; }
  100% { opacity: 1; transform: none; }
}

@keyframes mythoku-environment-idle {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-3px, 0, 0); }
}

@keyframes mythoku-subject-enter {
  0%   { opacity: 0; transform: translate3d(4.5%, 1.25%, 0) scale(.982); }
  68%  { opacity: 1; }
  100% { opacity: 1; transform: none; }
}

@keyframes mythoku-subject-breathe {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -2px, 0); }
}

@keyframes mythoku-near-enter {
  from { opacity: 0; transform: translate3d(2%, 0, 0); }
  to   { opacity: .24; transform: none; }
}

@keyframes mythoku-copy-enter {
  from { opacity: 0; transform: translate3d(0, .7rem, 0); }
  to   { opacity: 1; transform: none; }
}












/* Wide composition: copy and art share the frame without either scaling with ultrawide width. */
@media (min-width: 880px) {
  .art-stage .layer,
  .art-stage .layer::before,
  .subject-idle .layer--char,
  .subject-idle .layer--bow {
    background-position: center;
  }

  .hero__inner {
    padding-left: var(--copy-pad);
  }
}

/* Portrait is a separate composition: a larger 3:2 stage sits behind the upper half, then both
   subject and forest dissolve into the copy's independent paper panel. */
/* PORTRAIT phones only. Without the aspect guard this whole block also caught a small
   phone held SIDEWAYS - 568x320 and 667x375 - where it is catastrophic: `.hero` becomes
   display:block with `padding-top: 105vw`, i.e. 596px of padding in a 320px-tall
   viewport, and the 200vw/225vw crop pair lands the two planes at 2.37x different
   scales. Measured there: 65% of the frame dead paper, her ink 0% visible, the page
   scrolling 647px. The short-landscape block below is what should own that shape, and
   it could not, because these rules come later and win. */
@media (max-width: 699px) and (max-aspect-ratio: 1/1) {
  .hero {
    display: block;
    min-height: 100svh;
    min-height: 100dvh;
    padding-top: 105vw;
  }

  .art-stage {
    /* A CROP composition, not a fit: at 225vw only the middle ~44% of the plate is on
       screen, so the art's right feather is far off-frame and the 4.5% overhang the
       wide rule needs would be meaningless here. `bottom: auto` is load-bearing -
       leaving the base rule's `bottom: 0` in play over-constrains the insets, and an
       over-constrained box drops `aspect-ratio` and STRETCHES the plate. */
    top: 0;
    bottom: auto;
    right: -63vw;
    width: 225vw;
    transform: none;
  }

  .layer--env {
    -webkit-mask-image: linear-gradient(180deg, #000 48%, rgba(0, 0, 0, .62) 67%, transparent 86%);
            mask-image: linear-gradient(180deg, #000 48%, rgba(0, 0, 0, .62) 67%, transparent 86%);
  }

  .subject-idle .layer--char,
  .subject-idle .layer--bow {
    -webkit-mask-image: linear-gradient(180deg, #000 58%, rgba(0, 0, 0, .9) 72%, transparent 94%);
            mask-image: linear-gradient(180deg, #000 58%, rgba(0, 0, 0, .9) 72%, transparent 94%);
  }

  .layer--near {
    /* Same third gradient as the tablet rule. A phone crops so hard that the plate's
       left feather is usually off-screen, but not at every width, and a blurred leaf
       over 20%-opaque forest reads as a thumbprint rather than as foliage. Gated at
       42% of the plate, where the art's own alpha finally passes ~60%. */
    opacity: .18;
    background-size: 11rem auto;
    -webkit-mask-image:
      radial-gradient(ellipse 44% 36% at 57% 27%, transparent 0 70%, #000 100%),
      linear-gradient(180deg, #000 0 43%, transparent 76%),
      linear-gradient(90deg, transparent 0 42%, #000 58%);
            mask-image:
      radial-gradient(ellipse 44% 36% at 57% 27%, transparent 0 70%, #000 100%),
      linear-gradient(180deg, #000 0 43%, transparent 76%),
      linear-gradient(90deg, transparent 0 42%, #000 58%);
    -webkit-mask-composite: source-in;
            mask-composite: intersect;
  }

  .hero__inner {
    padding-top: 1.2rem;
    padding-bottom: 1.2rem;
  }

  footer {
    position: relative;
    margin-top: clamp(1rem, 4vw, 1.5rem);
    padding-top: .8rem;
    padding-bottom: calc(1rem + env(safe-area-inset-bottom));
    background: var(--paper);
  }
}

/* Compact phones use a short, intentional document scroll instead of crushing art, form and footer
   into one viewport. Tighten the copy rhythm, but preserve a real section break before copyright. */
@media (max-width: 699px) and (max-height: 700px) {
  .hero__inner {
    padding-top: .75rem;
    padding-bottom: .75rem;
  }

  .wordmark { font-size: 2.75rem; }
  .coming { margin-top: .25rem; font-size: .72rem; }
  .tagline { margin-top: .4rem; font-size: .9rem; }
  .signup { margin-top: .65rem; }
  .signup input[type=email],
  .signup button { padding-block: .62rem; }
  .consent { margin-top: .5rem; font-size: .75rem; line-height: 1.32; }
  #msg { margin-top: .3rem; min-height: .8rem; }
  .note { margin-top: .3rem; font-size: .69rem; line-height: 1.28; }

  .platforms {
    flex-wrap: nowrap;
    gap: .65rem;
    margin-top: .45rem;
  }
  .platform { gap: .3rem; min-width: 0; }
  .platform svg { width: 1.4rem; height: 1.4rem; }
  .platform b { font-size: .66rem; letter-spacing: .12em; }
  .platform small { font-size: .5rem; letter-spacing: .1em; }

  footer {
    margin-top: 1rem;
    padding-top: .65rem;
    padding-bottom: calc(.8rem + env(safe-area-inset-bottom));
  }
}

/* Portrait tablets need their own crop. The desktop stage is vertically centred, which leaves a
   large dead band above the art at 768×1024; the phone crop, on the other hand, is needlessly
   aggressive on a wider screen. This middle composition keeps her full upper body visible while
   handing the lower edge cleanly to the copy's paper panel. */
@media (min-width: 700px) and (max-width: 879px) and (max-aspect-ratio: 1/1) {
  .layer--env {
    -webkit-mask-image: linear-gradient(180deg, #000 46%, rgba(0, 0, 0, .62) 67%, transparent 88%);
            mask-image: linear-gradient(180deg, #000 46%, rgba(0, 0, 0, .62) 67%, transparent 88%);
  }

  .subject-idle .layer--char,
  .subject-idle .layer--bow {
    -webkit-mask-image: linear-gradient(180deg, #000 55%, rgba(0, 0, 0, .88) 74%, transparent 96%);
            mask-image: linear-gradient(180deg, #000 55%, rgba(0, 0, 0, .88) 74%, transparent 96%);
  }

  .layer--near {
    /* The third gradient is the one that was missing here: a leaf may only appear where
       the forest already is. The plate's own alpha is 0.1% at 22.8% across and only
       reaches 35% at 35.8%, so anything drifting left of that lands on blank paper. */
    opacity: .2;
    -webkit-mask-image:
      radial-gradient(ellipse 44% 36% at 58% 28%, transparent 0 70%, #000 100%),
      linear-gradient(180deg, #000 0 45%, transparent 78%),
      linear-gradient(90deg, transparent 0 42%, #000 58%);
            mask-image:
      radial-gradient(ellipse 44% 36% at 58% 28%, transparent 0 70%, #000 100%),
      linear-gradient(180deg, #000 0 45%, transparent 78%),
      linear-gradient(90deg, transparent 0 42%, #000 58%);
    -webkit-mask-composite: source-in;
            mask-composite: intersect;
  }

  footer {
    position: relative;
    margin-top: 1rem;
    padding-top: 1rem;
    padding-bottom: calc(1rem + env(safe-area-inset-bottom));
    background: var(--paper);
  }
}

/* Short landscape keeps the form completely reachable and composes the subject on the right.
   It may scroll a few pixels on unusually large system-font settings, but never clips the form. */
/* The paper-card layout: copy on its own card at the left with a feathered right edge,
   artwork to the right of it. Owns every SHORT landscape window at any width - a phone on
   its side is the common one - plus the 700-799 band, which is too narrow for the full
   editorial split (the copy column's 17rem floor alone overruns her ink there) but still
   wide enough for a card beside the art.

   `min-width: 560px` because the card is 30rem wide: under that it is the whole frame and
   there is nothing left for the artwork beside it. Below 560 the top-band crop takes over.
   Measured at 360x316 without this: the copy card overflowed, `.hero` grew TALLER than the
   viewport, and the plate - anchored to the hero's bottom, not the screen's - left 53% of
   the frame empty above it.

   Split from the block below by HEIGHT, deliberately: 520px is the boundary and the two
   are mutually exclusive, so nothing falls through the gap between them. An earlier
   attempt divided them by ASPECT and left 568x498, 640x481 and 430x377 matching no block
   at all - the footer landed on the platform row and 0-3% of her ink was on screen. */
@media (min-width: 560px) and (max-height: 520px) and (min-aspect-ratio: 1001/1000),
       (min-width: 700px) and (max-width: 799px) and (min-aspect-ratio: 1001/1000) {
  .hero {
    min-height: 100svh;
    min-height: 100dvh;
  }

  .art-stage {
    /* Fit composition, so it uses the same derivation as the wide rule: stand her on
       the floor, overhang the right by 4.5% of the plate. 165dvh cut her feet by 19px
       (measured at 844x390) because the plate ran past the viewport - the ceiling is
       her HEAD, at plate height <= 1.031 x viewport height, i.e. width <= 1.547dvh. */
    /* 112vw, not 88vw. On a SHORT window 150dvh wins either way, so nothing moves there
       (568x320, 932x430 measured identical); on the taller members of this band 88vw left
       the plate narrower than the frame and pushed it a third of the way down. */
    --stage-w: min(112vw, 150svh);
    --stage-w: min(112vw, 150dvh);
    width: var(--stage-w);
    right: calc(var(--stage-w) * -1 * var(--stage-overhang));
    top: auto;
    bottom: 0;
    transform: none;
  }

  .layer--env {
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, .4) 18%, #000 45%);
            mask-image: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, .4) 18%, #000 45%);
  }

  .subject-idle .layer--char,
  .subject-idle .layer--bow {
    -webkit-mask-image: none;
            mask-image: none;
  }

  .hero__inner {
    /* STRETCH, not centre. Centred, the paper is only as tall as the copy, so it reads as
       an opaque rectangle dropped on the artwork with two hard straight edges - measured at
       568x498, a box from (0,0) to (310,435) with art visible below it. It only ever looked
       right at 844x390 because there the copy happened to fill the frame. As a full-height
       column the paper is the ground the copy sits on, and the only visible boundary is the
       feathered right edge it already had. */
    align-self: stretch;
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: min(55vw, 30rem);
    max-width: none;
    margin: 0;
    padding: .85rem 1.1rem;
  }

  .hero__inner > * { max-width: 27rem; }

  /* Defined HERE, in full. It used to set only a width and inherit its position and gradient
     from the base sheet's short-landscape rule - which is gated `min-aspect-ratio: 3/2`, so
     the moment this block widened to own aspect 1.0-1.5 the feather stopped existing and the
     paper met the artwork on a hard vertical line. Measured at 568x498: a straight cut at
     x=310. */
  .hero__inner::before {
    display: block;
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 100%;
    right: auto;
    width: clamp(3rem, 7vw, 5rem);
    height: auto;
    background: linear-gradient(to right, var(--paper), rgba(233, 216, 197, 0));
    pointer-events: none;
  }
  .wordmark { font-size: clamp(2.55rem, 7vw, 3.35rem); }
  .coming { margin-top: .25rem; font-size: .72rem; }
  .tagline { margin-top: .35rem; font-size: .92rem; }
  .signup { margin-top: .65rem; }
  .signup input[type=email] { padding-block: .6rem; }
  .signup button { padding-block: .63rem; }
  .consent { margin-top: .5rem; font-size: .72rem; }
  .note { margin-top: .35rem; font-size: .69rem; }
  #msg { margin-top: .25rem; min-height: .8rem; }
  .platforms { margin-top: .55rem; gap: 1rem; }
  .platform svg { width: 1.5rem; height: 1.5rem; }
  .platform b { font-size: .72rem; }
  .platform small { font-size: .56rem; }
  footer { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .layer--env,
  .layer--env::before,
  .layer--near,
  .subject-arrival,
  .subject-idle,
  .wordmark,
  .coming,
  .tagline,
  .signup,
  .platforms {
    animation: none !important;
    opacity: 1;
    transform: none;
  }
}
/* ── Layer stack ──────────────────────────────────────────────────────────
   The fade lives UNDER the art, not over it. Each art layer is MASKED so it
   dissolves into the paper at its own edge; nothing is painted on top of the
   character, so she stays full-strength and crisp. This is how the reference
   composes it — mask-image on the layer, never a scrim above it.

       z0  paper (body)
       z1  environment  — masked, fades toward the text
       z2  character    — masked far softer; her face never dims
       z3  content
*/

/* ------------------------------------------------------------------------
   KUN-1333 v12 — reconstructed 2026-09-20 from the session log.

   The original v12 was lost when its worktree was deleted with the work
   uncommitted. Everything above this line is the recovered file, verified
   line-for-line against v12's own structure. The two blocks below are v12's
   additions, rebuilt from the assertions in its test file — which survived
   verbatim and are reproduced in test.mjs.
   ------------------------------------------------------------------------ */

/* v12.1 — she does not fade on WIDE screens; in PORTRAIT the plate must still
   meet the paper.

   kuno, 2026-09-20: "she should not be fading away like that at bottom" — that
   was a wide desktop, where the fade ate her mid-body for no reason, because the
   plate runs off the bottom of the viewport anyway and there is nothing to meet.

   Portrait is the opposite case and removing it there was my error: the plate
   ENDS on screen, above the copy, so with no dissolve it terminates in a hard
   horizontal cut straight across her thighs with the forest cut on the same
   line — which is the second thing he caught. So the fade is scoped to where it
   does work: off above 880px and in landscape, kept in portrait where the art
   has to hand off to the copy's paper ground. */
/* 1001/1000, not 1/1. `max-aspect-ratio: 1/1` and `min-aspect-ratio: 1/1` BOTH match an
   exactly square window, so 320x320, 360x360, 390x390 and 430x430 were taking half their
   rules from the portrait block and half from this one: `padding-top: 105vw` (336px in a
   320px-tall viewport, a 117% dead band) with the portrait 200vw/225vw plate pair, which
   also de-registered the planes to 1.786. Portrait owns exactly square; landscape starts
   one thousandth above it. */
@media (min-width: 800px) and (min-aspect-ratio: 1001/1000) {
  .subject-idle .layer--char,
  .subject-idle .layer--char::before {
    -webkit-mask-image: none;
            mask-image: none;
  }
}

/* v12.3 — portrait subject framing.
   With the two planes separated, the phone stage fits the whole 3:2 plate, which
   puts the character at 66% of an 878px box hanging off a 390px screen: she ends
   up small and cropped against the right edge. Moving only the SUBJECT plane
   keeps the environment full-bleed — the point of splitting them — while landing
   her face near the centre of the frame. Measured at 390px: face x=337 before,
   x=257 after. Chosen off a four-way ladder, not guessed. */
/* PORTRAIT phones only. Without the aspect guard this whole block also caught a small
   phone held SIDEWAYS - 568x320 and 667x375 - where it is catastrophic: `.hero` becomes
   display:block with `padding-top: 105vw`, i.e. 596px of padding in a 320px-tall
   viewport, and the 200vw/225vw crop pair lands the two planes at 2.37x different
   scales. Measured there: 65% of the frame dead paper, her ink 0% visible, the page
   scrolling 647px. The short-landscape block below is what should own that shape, and
   it could not, because these rules come later and win. */
@media (max-width: 699px) and (max-aspect-ratio: 1/1) {
  /* The only place the two planes are DELIBERATELY off one scale: 200vw against the
     environment's 225vw. On a phone the composition is a hard crop - roughly the middle
     44% of the plate is on screen - so the plates are not being read side by side as one
     flat picture, and the 11% difference reads as depth between them. It also puts her
     face where a 225vw subject plate would not: registered, she sits at 79% of the width
     with her head against the frame. The wide rule keeps both planes locked at 1.000,
     and render.test.mjs asserts that for every WIDE viewport but not for these. */
  .art-stage--subject {
    right: -34vw;
    width: 200vw;
  }
}

/* v12.5 — the bow and the body move as ONE.
   kuno, 2026-09-20: "the other masked part of the bow is not being animated
   together with remaining".

   The production page animates `.layer--char::before` and leaves `.layer--bow`
   static, so during the entrance and the breathe the bow sits still while she
   slides and rises — they visibly separate. That is the exact problem v12's
   wrapper was built to solve, and I reintroduced it when I reverted to the
   production animation on his say-so.

   This keeps HIS animation — the page's own layer-arrive-near and
   character-breathe, same keyframes, same timings, same easing — and just moves
   it onto the wrappers so both planes carry it together. Two nested wrappers,
   because entrance and breathe both animate `transform` and one element cannot
   run both. */
.subject-arrival {
  animation: layer-arrive-near 1200ms cubic-bezier(.16, .8, .2, 1) 120ms both;
}

.subject-idle {
  animation: character-breathe 9s ease-in-out 1300ms infinite alternate;
}

.subject-idle .layer--char::before,
.subject-idle .layer--bow::before {
  animation: none;
}

/* v12.6 — BOTH planes use the production animation.
   kuno, 2026-09-20, final call: "best to use that animation which is on current
   mythoku after all for both then".

   So nothing from the recovered sheet's animation system is used. The background
   keeps the page's own layer-arrive-far + forest-drift, which it already has and
   which this file must not override. The character keeps layer-arrive-near +
   character-breathe — the same keyframes, timings and easing as production — the
   single difference being that they run on the wrappers rather than on
   .layer--char::before, so the bow travels with her instead of sitting still.
   That was his other complaint and it is a defect that is live today. */

/* v12.7 — the forest resolves out of blur as it arrives.
   kuno, 2026-09-20: "but for background also add from blur to normal".

   This rides on the ENVIRONMENT wrapper rather than on .layer--env::before,
   which already runs the production layer-arrive-far + forest-drift. Two
   animations on one element cannot both own `transform`, but `filter` is a
   separate property on a separate element, so the blur composes with the
   production entrance instead of replacing it — his "use the production
   animation for both" still holds. Timed to 1100ms to land with layer-arrive-far
   rather than after it. */
@keyframes mythoku-environment-focus {
  from { filter: blur(12px); }
  to   { filter: blur(0); }
}

.layer--env {
  animation: mythoku-environment-focus 1100ms cubic-bezier(.2, .72, .18, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  .layer--env { animation: none !important; filter: none !important; }
}

/* v12.10 — the character comes into focus with the forest.
   kuno, 2026-09-20: "for character the one from current mythoku combined with
   this moving" → "combine and get the most optimal".

   So: production's entrance is kept exactly — layer-arrive-near for the travel
   and character-breathe for the idle, its own keyframes, timings and easing —
   and the blur-resolve he asked for on the background is extended to her too, so
   the two planes come into focus as one image rather than the forest sharpening
   behind an already-sharp character. `filter` lives on the stage while
   `transform` lives on the wrappers, so nothing competes for a property and the
   production animation is untouched. Slightly shorter and lighter than the
   forest's: she is the subject and should resolve first. */
@keyframes mythoku-subject-focus {
  from { filter: blur(8px); }
  to   { filter: blur(0); }
}

.art-stage--subject {
  animation: mythoku-subject-focus 900ms cubic-bezier(.2, .72, .18, 1) 120ms both;
}

@media (prefers-reduced-motion: reduce) {
  .art-stage--subject { animation: none !important; filter: none !important; }
}

/* v12.12 — in portrait she keeps her legs; the COPY PANEL hides the join.
   kuno, 2026-09-20: "her legs are cut off here", on an 866x1250 window.

   The portrait mask fades her from 58 % of the plate and is fully transparent at
   94 %. Her boots are at 100 %, so the fade eats her legs before the plate even
   ends — it reads as a clean horizontal cut across her thighs.

   It does not need to fade at all here. The copy block carries its own paper
   ground with a feathered top edge (that is why it was given one), and it starts
   ABOVE the plate bottom, so the join is already covered. She stays opaque to
   92 % and only the last sliver feathers, which removes the cut without leaving
   a hard edge if the copy ever sits lower. The FOREST keeps its own earlier
   dissolve — it is the thing that should melt into the paper. */
@media (max-width: 879px) and (max-aspect-ratio: 1/1) {
  .subject-idle .layer--char,
  .subject-idle .layer--bow,
  .subject-idle .layer--char::before,
  .subject-idle .layer--bow::before {
    /* No feather at all. Her boots are at 100 % of the plate, so ANY fade eats
       her feet; and with the plate sized to end where the copy begins, the join
       is already under the copy's own paper ground and its feathered top edge.
       The FOREST still dissolves — that is the layer that should melt away. */
    -webkit-mask-image: none;
            mask-image: none;
  }

  /* Size the subject plate so her whole body lands ABOVE the copy. The plate is
     3:2 and her boots sit on its bottom edge, so the plate height IS her full
     height: at 866x1250 the copy starts at y=707, and 122vw gives a plate ending
     at 704. Wider than this and the panel cuts her legs off, which is what kuno
     saw; much narrower and she becomes small for no gain. */
  /* Sized by HEIGHT, not width. A fixed vw cannot work here: between 700 and
     879 the hero is a grid, the copy sits at the bottom and its height is
     content-driven, so where the plate must stop depends on viewport HEIGHT.
     Measured copy height across 700-866 wide and 1024-1250 tall: 499, 519, 532,
     543 — call it 540 and the plate always lands just above it. width:auto with
     the 3:2 aspect keeps her proportions, and her boots sit on the plate's
     bottom edge, so plate height IS her full height. */
}

/* Tablet portrait only. Phones are excluded deliberately: there the band is
   `padding-top: 105vw`, a function of WIDTH, and `100dvh - 540px` would shrink
   her to 304px on a 390x844 screen — measured, and half the size she should be.
   Between 700 and 879 the hero is a grid instead, the copy sits at the bottom
   with a content-driven height, and HEIGHT is what decides where the plate must
   stop. */
/* ── Tall desktop windows: compose the band, it cannot be removed ───────────────
   A 3:2 plate in a window taller than 3:2 leaves paper. Growing the plate is not an
   option and the reason is measured, not aesthetic: past ~108vw her ink crosses the
   copy column, and the plate's LEFT edge leaves the screen — which puts a hard cut
   exactly where the art's own 23%->46% feather is doing the copy/forest transition.
   So the plate stays <= 100vw and the leftover paper gets composed instead:

     1. the plate's top edge DISSOLVES rather than cutting. The env plate's top row
        carries 43.7% mean alpha, so unmasked it draws a hard rule across the full
        width - at 1396x1350 that seam sat at y=415 with 31% dead paper above it.
     2. the copy moves INTO that band instead of floating below it, which turns the
        empty upper-left into the other half of an editorial split. */
@media (min-aspect-ratio: 1001/1000) and (max-aspect-ratio: 3 / 2) and (min-width: 800px) {
  .layer--env {
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .55) 9%, #000 22%);
            mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .55) 9%, #000 22%);
  }

  /* Her plate is transparent above her hair (ink starts at 3.0% of the plate), so a
     short dissolve here costs nothing and stops the bow's upper limb ending on a line. */
  .subject-idle .layer--char,
  .subject-idle .layer--bow {
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 4%);
            mask-image: linear-gradient(180deg, transparent 0%, #000 4%);
  }

  .hero__inner {
    align-self: start;
    padding-top: clamp(2.5rem, 7vh, 6rem);
  }
}

/* ── The one layout shift on this page ─────────────────────────────────────────
   Measured on slow 3G at 1440x900: CLS 0.1263, over the 0.1 "good" threshold, and
   almost all of it one jump at 2852ms when the webfonts swap in. The cause is this
   row, not the artwork: with the fallback font the three platform labels are wide
   enough to WRAP onto three lines (91px tall), and in Barlow Condensed they fit on
   one (30px). `.hero__inner` is vertically centred on wide screens, so a 61px height
   change moves the whole copy block by half of it - the wordmark, the form, all of it,
   2.8 seconds after the text first appears.

   `nowrap` holds the row on one line in both states, so the swap changes glyphs and
   nothing else. There is room: the row is ~3 short labels against a 34rem column, and
   the phone rule below already did exactly this for its own reasons. */
@media (min-width: 800px) and (min-aspect-ratio: 1001/1000) {
  .platforms { flex-wrap: nowrap; }
}

/* ── ONE CROP RULE FOR EVERY PORTRAIT-SHAPED WINDOW AT 700px AND UP ───────────
   This used to be two blocks - 700-879 and 880-up - describing the same shape class with
   different constants, which is how the 700-879 band kept its own un-floored
   `100dvh - 480px`. Merged, so a tablet and a half-screen desktop window get the same
   composition and the same safety floor.

   ── A PORTRAIT-SHAPED WINDOW IS NOT A DESKTOP ────────────────────────────────
   kuno, 2026-09-20, at 935x1215. The base sheet switches to the editorial split on
   `min-width: 880px` alone, so a browser snapped to half a screen — portrait shaped,
   but wide enough to trip that breakpoint — got art in one corner and copy in the
   other, with the whole top of the frame empty. Measured across the band:

       900x1400  ar 0.64   she is 45% of the height, 54% of the screen is dead paper
       935x1215  ar 0.77            54%                         45%
      1000x1200  ar 0.83            58%                         40%
      1100x1300  ar 0.85            59%                         39%
      1396x1350  ar 1.03            72%                         26%
      1200x1000  ar 1.20            84%                         14%

   A 3:2 plate simply cannot fill a frame taller than it is wide, so the editorial
   composition has nothing to give here. The fix is the composition tablets already
   use and that portrait phones use: CROP — a plate wider than the viewport across the
   top, the copy on its own paper below. Keyed on SHAPE, not width. */
@media (min-width: 700px) and (max-aspect-ratio: 1/1) {
  /* undo the editorial split the base sheet applies from 880px up */
  .hero__inner {
    align-self: end;
    background: var(--paper);
    padding: clamp(1.5rem, 3.5vw, 3rem) clamp(2rem, 6vw, 5rem);
    max-width: none;
    width: 100%;
  }
  .hero__inner > * { max-width: 34rem; }
  .hero__inner::before {
    display: block;
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 100%; top: auto;
    width: auto; height: clamp(4rem, 9vh, 8rem);
    background: linear-gradient(to top, var(--paper), rgba(233, 216, 197, 0));
  }
  .wash { display: none; }

  /* The base sheet pins the footer `position: absolute; bottom: 0` so the art can reach
     the true bottom edge in the editorial layout. Here the copy's paper owns the bottom,
     so an absolute footer lands ON TOP of the platform row — which is exactly what it did.
     It flows below the copy instead, on its own paper. */
  footer {
    position: relative;
    margin-top: clamp(.75rem, 2vh, 1.25rem);
    padding-top: .8rem;
    padding-bottom: calc(1rem + env(safe-area-inset-bottom));
    background: var(--paper);
  }

  /* Crop composition: the plate is sized by the room ABOVE the copy, not by the
     viewport width, so she stays large however tall the window is. Same derivation as
     the tablet rule — see the invariant written there. */
  .art-stage {
    /* Whichever is LARGER: the room above the copy, or enough to stay a real crop.
       Sized by height alone, the plate stops being wider than the frame as the window
       approaches square — at 1396x1350 it came out 1320px against a 1396px viewport, so
       paper showed down the left and the composition fell apart. `112vw / 1.5` floors it
       at 112vw wide; past that it simply crops further under the copy's opaque paper,
       which is what portrait phones already do. */
    --stage-h: max(calc(100svh - 470px), calc(112vw / 1.5));
    --stage-h: max(calc(100dvh - 470px), calc(112vw / 1.5));
    height: var(--stage-h);
    width: auto;
    aspect-ratio: 3 / 2;
    top: 0;
    bottom: auto;
    right: calc(var(--stage-h) * 1.5 * -1 * var(--stage-overhang));
    transform: none;
  }

  /* Her boots sit on the plate's bottom edge and that edge is under the copy's own
     paper, so any fade here eats her legs. The forest still dissolves downward. */
  .subject-idle .layer--char,
  .subject-idle .layer--bow {
    -webkit-mask-image: none;
            mask-image: none;
  }
  .layer--env {
    -webkit-mask-image: linear-gradient(180deg, #000 46%, rgba(0, 0, 0, .62) 68%, transparent 88%);
            mask-image: linear-gradient(180deg, #000 46%, rgba(0, 0, 0, .62) 68%, transparent 88%);
  }
}

/* ── The copy column has to know where the ARTWORK starts ──────────────────────
   `--copy-w` was a flat 26rem, and the wash derives from it, so neither had any idea
   where her ink begins. In the 4:3 band that is where the two meet: measured, the
   "JOIN THE WAITLIST" button's right edge sat 43px PAST her ink at 1024x768, and the
   consent line and "WEB / COMING SOON" ran into the foliage unreadable — which is the
   exact failure the base sheet's own comment says it fixed once already.

       1024x768   copy ran to 483, her ink starts at 440   -> 43px over
       1080x810              486                464        -> 22px over
       1152x960              490                495        ->  5px clear
       1200x1000             492                516        -> 24px clear

   Her ink's left edge is derivable from the plate, not guessable: with the right
   overhang the plate's left edge is at 100vw - 0.915W, and her ink starts 38.7% into
   the plate, so ink-left = 100vw - 0.528W. The column now takes whichever is smaller,
   26rem or the room actually available, with a legibility floor under it. */
@media (min-width: 800px) and (min-aspect-ratio: 1001/1000) {
  :root {
    --plate-w: min(108vw, 152svh);
    --plate-w: min(108vw, 152dvh);
    --ink-left: calc(100vw - 0.528 * var(--plate-w));
    --copy-w: max(17rem, min(26rem, calc(var(--ink-left) - var(--copy-pad) - 1.5rem)));
  }

  /* Two children carry their own max-width, later in the base sheet and at the same
     specificity as `.hero__inner > *`, so they ignore the column: `.signup` at 27rem and
     `.tagline` at 26rem. The signup being 1rem wider than the column is deliberate — it
     is what lets the email field and the button sit on one line — so the relationship is
     kept rather than flattened. On wide screens both resolve to exactly what they were. */
  .signup { max-width: calc(var(--copy-w) + 1rem); }
  .tagline { max-width: var(--copy-w); }
}

/* ── One focus ring, on everything that takes focus ────────────────────────────
   The email field already had a designed `:focus-visible` ring; the button, the consent
   checkbox and both links fell through to the browser default (1px auto blue). That is
   accessible but it is not this page, and on a page whose entire purpose is one form the
   focus state is part of the design. Same 2px moss the input uses — #3c6632 measures
   4.80:1 against the paper, well past the 3:1 that non-text contrast needs. */
.signup button:focus-visible,
.consent input[type=checkbox]:focus-visible,
a:focus-visible {
  outline: 2px solid var(--moss);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ── A NARROW WINDOW TURNED SIDEWAYS ───────────────────────────────────────────
   Under 700px and landscape-ish, neither of the other two compositions works, and until
   now nothing claimed this band at all - it fell through to the bare base rules. Measured
   there: 360x316, 430x377, 568x498, 640x481 all showed the footer sitting on the platform
   row and 0-3% of her ink on screen.

   The editorial split is out because the copy column cannot fit beside her: at 568px its
   17rem floor alone exceeds the room left of her ink. The phone-portrait block is out
   because `padding-top: 105vw` is a function of WIDTH - in a landscape window that is most
   of the viewport. So: an art band across the top sized by HEIGHT, copy on its own paper
   below, and the page scrolls if the copy needs it, exactly as a small phone already does.

   `min-height: 521px` hands the SHORT ones to the paper-card layout above, which already
   handles a phone on its side well - without it this block swallowed 568x319 and 640x360
   and left 4-8% of her ink on screen. The two queries meet exactly at 520/521px, so every
   narrow landscape window is claimed by one of them and none by both. */
@media (max-width: 559px) and (min-aspect-ratio: 1001/1000),
       (max-width: 699px) and (min-aspect-ratio: 1001/1000) and (min-height: 521px) {
  .hero {
    display: block;
    min-height: 100svh;
    min-height: 100dvh;
    padding-top: 44dvh;
  }

  .art-stage {
    /* the band, or enough to stay wider than the frame - whichever is larger */
    --stage-h: max(44svh, calc(112vw / 1.5));
    --stage-h: max(44dvh, calc(112vw / 1.5));
    height: var(--stage-h);
    width: auto;
    aspect-ratio: 3 / 2;
    top: 0;
    bottom: auto;
    right: calc(var(--stage-h) * 1.5 * -1 * var(--stage-overhang));
    transform: none;
  }

  .hero__inner {
    background: var(--paper);
    padding: 1rem clamp(1rem, 4vw, 2rem);
    max-width: none;
    width: 100%;
  }
  .hero__inner > * { max-width: 34rem; }
  .hero__inner::before {
    display: block;
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 100%; top: auto;
    width: auto; height: clamp(3rem, 10vh, 6rem);
    background: linear-gradient(to top, var(--paper), rgba(233, 216, 197, 0));
  }
  .wash { display: none; }

  /* her boots sit on the plate's bottom edge and that edge is under the copy's paper */
  .subject-idle .layer--char,
  .subject-idle .layer--bow {
    -webkit-mask-image: none;
            mask-image: none;
  }

  footer {
    position: relative;
    margin-top: .75rem;
    padding-top: .6rem;
    padding-bottom: calc(.8rem + env(safe-area-inset-bottom));
    background: var(--paper);
  }
}

/* The editorial copy block is vertically CENTRED and the footer is absolute at the bottom,
   so on a short window the copy grows down into it - measured as the platform row sitting
   on top of the footer at 700x467, 760x507, 820x547 and 866x577. Reserve its height. */
@media (min-width: 800px) and (min-aspect-ratio: 1001/1000) {
  /* measured: the footer is 44px tall here, and at 820x547 a 44px reserve left the
     platform row touching it with zero clearance. 3.25rem floors it above that. */
  .hero__inner { padding-bottom: clamp(3.25rem, 8vh, 5rem); }
}


/* The plate is bottom-anchored in the card layout, so once the window is taller than about
   4:3 it no longer reaches the top of the frame and its own top row - 43.7% mean alpha - draws
   a hard rule across the artwork. Measured at 568x498: a straight edge at y=74. Above 4:3 the
   plate overshoots the viewport and there is nothing to dissolve, which is why this is scoped
   rather than applied to the whole band. */
@media (min-width: 560px) and (max-height: 520px) and (min-aspect-ratio: 1001/1000) and (max-aspect-ratio: 4/3),
       (min-width: 700px) and (max-width: 799px) and (min-aspect-ratio: 1001/1000) and (max-aspect-ratio: 4/3) {
  .layer--env {
    -webkit-mask-image:
      linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, .4) 18%, #000 45%),
      linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .55) 8%, #000 20%);
            mask-image:
      linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, .4) 18%, #000 45%),
      linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .55) 8%, #000 20%);
    -webkit-mask-composite: source-in;
            mask-composite: intersect;
  }
  .subject-idle .layer--char,
  .subject-idle .layer--bow {
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 5%);
            mask-image: linear-gradient(180deg, transparent 0%, #000 5%);
  }
}
