/* ============================================================
   Sounding — cetaceanwisdom.org
   Palette: descent from #103c5b (surface) to #010810 (floor).
   One accent: #8fc5d4, pale bioluminescent cyan.
   Type: Cormorant Garamond for everything read; IBM Plex Mono,
   uppercase and widely tracked, for labels and actions only.
   All motion eases over 1.5s or longer. Nothing snaps.
   ============================================================ */

:root {
  --accent: #8fc5d4;
  --ink: #e2ecf3;
  --ink-soft: rgba(226, 236, 243, 0.74);
  --ink-quiet: rgba(226, 236, 243, 0.36);
  --serif: 'Cormorant Garamond', Georgia, serif;
  --mono: 'IBM Plex Mono', ui-monospace, monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  overflow-x: hidden;    /* NB: keep the clip here, not on a wrapper —
                            a wrapper scrollport breaks position:sticky */
  background: #03090f;
}

body {
  min-height: 100vh;
  font-family: var(--serif);
  color: var(--ink);
  background: linear-gradient(to bottom,
    #103c5b 0%, #092941 18%, #061b2d 42%, #03101c 70%, #010810 100%);
  background-attachment: fixed;
}

/* ---------- atmosphere ---------- */

.sea { position: fixed; inset: 0; pointer-events: none; z-index: 0; }
.layer { position: absolute; inset: 0; }

.shafts { opacity: 1; transition: opacity 6000ms ease; }
.strata { opacity: 0; transition: opacity 7000ms ease; }
/* surface light through the invitation, breaths and wheel; the thermocline
   takes over from stillness onward — the visitor has left the surface */
body[data-phase="stillness"] .shafts,
body[data-phase="reveal"] .shafts { opacity: 0; }
body[data-phase="stillness"] .strata,
body[data-phase="reveal"] .strata { opacity: 1; }

.surface-glow {
  position: absolute; inset: 0;
  background: radial-gradient(ellipse 130% 46% at 50% -8%, rgba(186, 230, 246, 0.42), transparent 64%);
}
.surface-glow.deep {
  background: radial-gradient(ellipse 150% 34% at 50% -4%, rgba(158, 212, 232, 0.3), transparent 72%);
}

.shaft {
  position: absolute; top: -26%; height: 140%;
  background: linear-gradient(to bottom,
    rgba(196, 236, 250, var(--a, 0.4)), rgba(196, 236, 250, 0.08) 46%, transparent 76%);
  animation: shaft var(--dur, 60s) ease-in-out var(--delay, 0s) infinite;
}
.s1 { left: 6%;  width: 150px; filter: blur(30px); --tilt: 9deg;   --a: 0.50; --dur: 52s; }
.s2 { left: 26%; width: 88px;  filter: blur(24px); --tilt: -6deg;  --a: 0.42; --dur: 71s; --delay: 4s; }
.s3 { left: 48%; width: 230px; filter: blur(40px); --tilt: 5deg;   --a: 0.34; --dur: 63s; --delay: 9s; }
.s4 { left: 72%; width: 120px; filter: blur(28px); --tilt: -11deg; --a: 0.44; --dur: 58s; --delay: 15s; }
.s5 { left: 88%; width: 74px;  filter: blur(22px); --tilt: 7deg;   --a: 0.36; --dur: 79s; --delay: 6s; }

@keyframes shaft {
  0%,100% { transform: translate3d(-3vw, 0, 0) rotate(var(--tilt)) scaleX(1);   opacity: 0.3; }
  50%     { transform: translate3d(3vw, 0, 0)  rotate(var(--tilt)) scaleX(1.5); opacity: 0.8; }
}

.band {
  position: absolute; left: -10%; right: -10%;
  animation: strata var(--dur, 60s) ease-in-out var(--delay, 0s) infinite;
}
.b1 { top: 8vh;  height: 26vh; filter: blur(18px); --dur: 54s;
      background: linear-gradient(to bottom, transparent, rgba(150, 204, 226, 0.18) 46%, transparent); }
.b2 { top: 34vh; height: 30vh; filter: blur(22px); --dur: 76s; --delay: 7s; animation-direction: reverse;
      background: linear-gradient(to bottom, transparent, rgba(13, 52, 82, 0.8) 50%, transparent); }
.b3 { top: 62vh; height: 34vh; filter: blur(26px); --dur: 92s; --delay: 3s;
      background: linear-gradient(to bottom, transparent, rgba(2, 11, 20, 0.85) 52%, transparent); }

@keyframes strata {
  0%,100% { transform: translate3d(0, -3vh, 0) scaleY(1);   opacity: 0.55; }
  50%     { transform: translate3d(0, 3vh, 0)  scaleY(1.3); opacity: 0.9; }
}

.current {
  position: absolute; inset: -10%;
  background: radial-gradient(circle at 78% 64%, rgba(21, 74, 110, 0.24), transparent 52%);
  animation: current 63s ease-in-out infinite reverse;
}
@keyframes current {
  0%,100% { transform: translate3d(-6%, 3%, 0) scale(1);    opacity: 0.5; }
  50%     { transform: translate3d(6%, -4%, 0) scale(1.16); opacity: 0.85; }
}

/* central darkening, so type holds its contrast over the lighter water */
.scrim {
  position: absolute; inset: 0;
  background: radial-gradient(ellipse 62% 54% at 50% 46%, rgba(2, 9, 15, 0.62), transparent 72%);
}
.vignette { position: absolute; inset: 0; box-shadow: inset 0 0 210px 50px rgba(2, 7, 12, 0.72); }

.snow { position: absolute; inset: 0; }
.snow i {
  position: absolute; bottom: -6vh; border-radius: 50%;
  background: #cfe6ef; opacity: 0;
  animation: snow linear infinite;
}
@keyframes snow {
  0%   { transform: translate3d(0, 8vh, 0); opacity: 0; }
  12%  { opacity: 0.5; }
  88%  { opacity: 0.4; }
  100% { transform: translate3d(2vw, -105vh, 0); opacity: 0; }
}

/* ---------- stage ---------- */

.stage {
  position: relative; z-index: 1;
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 80px 40px;
}
.phase[hidden] { display: none; }

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeUp { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes surface {
  from { opacity: 0; transform: scale(1.06); filter: blur(14px); }
  to   { opacity: 1; transform: scale(1);    filter: blur(0); }
}

/* ---------- I. invitation ---------- */

#invitation { max-width: 620px; text-align: center; animation: fadeIn 2.2s ease both; }
.eyebrow {
  margin: 0; font-family: var(--mono); font-size: 10.5px;
  letter-spacing: 0.34em; text-transform: uppercase; color: rgba(143, 197, 212, 0.7);
}
.title {
  margin: 0; font-weight: 300; font-size: 82px; line-height: 1.04;
  letter-spacing: 0.02em; color: #eaf3f7;
  /* "Echolocation" is one long word: let it break with a hyphen rather than
     overflow the viewport on a phone. */
  hyphens: auto; -webkit-hyphens: auto; overflow-wrap: break-word;
  max-width: 100%;
}
/* What the page is, in plain words, under its name. */
.title-sub {
  margin: 18px 0 0; font-family: var(--mono); font-size: 12px;
  letter-spacing: 0.28em; text-transform: uppercase; color: rgba(143, 197, 212, 0.82);
}
.return-line {
  margin: 16px 0 0; width: 100%; text-align: center;
  font-family: var(--serif); font-style: italic; font-size: 16px;
  color: rgba(226, 236, 243, 0.62);
}
.lede {
  margin: 34px auto 0; max-width: 430px;
  font-family: var(--serif); font-style: italic; letter-spacing: 0.01em;
  font-size: 22px; line-height: 1.9; color: rgba(226, 236, 243, 0.66); text-wrap: pretty;
}
/* The explanation of echolocation: two paragraphs of real prose, set at
   reading size on a comfortable measure rather than as a subtitle. */
.invitation-body {
  margin: 30px auto 0; max-width: 640px; text-align: left;
}
.invitation-body p {
  margin: 0 0 16px; font-size: 17px; line-height: 1.78;
  color: rgba(226, 236, 243, 0.6); text-wrap: pretty;
}
.invitation-body p:last-child { margin-bottom: 0; }
.lede-sub {
  margin: 22px auto 0; max-width: 400px;
  font-family: var(--mono); font-size: 12px; line-height: 2;
  letter-spacing: 0.14em; color: rgba(143, 197, 212, 0.66);
}
.actions-column { display: flex; flex-direction: column; align-items: center; gap: 26px; margin-top: 58px; }
.actions-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 18px; margin-top: 56px; }

.btn-primary {
  appearance: none; cursor: pointer;
  background: rgba(143, 197, 212, 0.06);
  border: 1px solid rgba(143, 197, 212, 0.34);
  color: #dcecf2; font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.26em; text-transform: uppercase;
  padding: 20px 40px; border-radius: 2px;
  transition: background 700ms ease, border-color 700ms ease, letter-spacing 700ms ease;
}
.btn-primary:hover { background: rgba(143, 197, 212, 0.14); border-color: rgba(143, 197, 212, 0.6); letter-spacing: 0.3em; }

.btn-quiet {
  appearance: none; cursor: pointer; background: none; border: none;
  color: var(--ink-quiet); font-family: var(--mono); font-size: 10px;
  letter-spacing: 0.2em; text-transform: uppercase; transition: color 600ms ease;
}
.btn-quiet:hover { color: rgba(226, 236, 243, 0.66); }

/* ---------- II. the alphabetical index ----------
   The plain way in: all 24 A–Z, tinted by direction. Four columns on a wide
   window, two on a tablet, one on a phone — the list never scrolls sideways. */

.index-wrap {
  display: flex; flex-direction: column; align-items: center; gap: 40px;
  width: min(1080px, 92vw); animation: fadeIn 1.8s ease both;
}
.index-head { text-align: center; }
.index-list {
  display: grid; gap: 10px; width: 100%;
  grid-template-columns: repeat(auto-fit, minmax(228px, 1fr));
}
/* Each name sits in its own lightly screened box, tinted by its direction —
   no direction label needed; the boxes do the separating. */
/* --screen carries the species' Medicine Wheel direction colour as an RGB
   triplet, so the card can be screened at low alpha and lifted on hover
   without a second colour declaration. Gold East, oxblood South, ink West,
   bone North — the same four as the wheel. */
.index-item {
  appearance: none; cursor: pointer; text-align: left;
  background: linear-gradient(160deg, rgba(var(--screen), 0.20), rgba(var(--screen), 0.07));
  border: 1px solid rgba(var(--screen), 0.26); border-radius: 2px;
  padding: 18px 20px;
  transition: background 450ms ease, border-color 450ms ease, transform 450ms ease;
}
.index-item:hover {
  background: linear-gradient(160deg, rgba(var(--screen), 0.36), rgba(var(--screen), 0.14));
  border-color: rgba(var(--screen), 0.6);
  transform: translateY(-1px);
}
.index-item .nm { font-family: var(--serif); font-size: 20px; line-height: 1.2; color: rgba(238, 246, 250, 0.94); }

/* ---------- III. the wheel ----------
   Medicine Wheel colours tempered for deep water: gold East, oxblood South,
   ink West, bone North — all translucent, so the ocean shows through. */

.wheel-wrap {
  display: flex; flex-direction: column; align-items: center;
  gap: min(34px, 3vh); width: 100%;
  animation: fadeIn 2.8s ease both;
}
.wheel-head { text-align: center; max-width: 520px; }
/* Three doors in one row need the width to hold them (~720px measured);
   wrapping to a second line is height the circle pays for. With the centre
   withdrawn there is one door, and the extra width only stretches body copy
   to a 105-character measure. */
[data-centre="on"] .wheel-head { max-width: 860px; }
.wheel-lede {
  margin: 20px 0 0; font-size: 19px; line-height: 1.8;
  color: rgba(226, 236, 243, 0.6); text-wrap: pretty;
}

/* Sized off available WIDTH with a 560px floor. A hard vh cap starves the
   wheel on short-and-wide viewports and scales the 24 names down to ~7px —
   let the section scroll instead; illegible names defeat the whole control. */
.wheel {
  position: relative;
  /* The vh term budgets the chrome ABOVE the circle: 80px stage padding +
     ~101px heading + the flex gap. Drop it and a 900px circle overruns a
     903px window; tighten it and the box shrinks until the names hit their
     clamp() floor. Genuinely small windows scroll. The centre plate sits in
     the heading column, not below the circle, so it costs no height here. */
  /* No floor. The rim names were measured at every wheel size and never
     actually collide — adjacent names sit 15deg apart, and the arc between
     them at their inner radius stays wider than a line of type even on a
     small wheel. (An axis-aligned bounding-box test says otherwise, but that
     is an artifact of rotated text: the AABBs of two diagonal labels overlap
     while the glyph runs do not. Verified with a separating-axis test.) */
  width: min(92vw, 900px, calc(100vh - 260px));
  aspect-ratio: 1 / 1;
  container-type: inline-size;     /* names scale with the WHEEL, not the
                                      viewport, so they stay legible at any size */
}
.wheel svg { display: block; width: 100%; height: 100%; overflow: visible; }

.wedge { cursor: pointer; transition: filter 500ms ease; }
.wedge:hover { filter: brightness(1.55); }

/* The spinner no longer lives at the axis — that place belongs to the centre
   species. The SVG hub, its ring, its colour band and its SPIN label are all
   retired; spinning is a labelled pill beside the heading instead. */
[data-centre="on"] .hub,
[data-centre="on"] .hub-ring,
[data-centre="on"] .hub-btn,
[data-centre="on"] .hub-label,
[data-centre="on"] .hub-band,
[data-centre="on"] .hub-sub,
[data-centre="on"] .hub-html { display: none; }
/* SPIN is HTML over the hub, not SVG <text> — 13px inside a 660-unit viewBox
   paints at ~5px once the wheel is scaled down. */
.hub-label, .hub-sub { display: none; }

/* Real pixels, independent of the viewBox scale — SVG <text> here paints at
   under 5px on a small wheel. */
.centre-mark {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, calc(-50% - 9px));
  pointer-events: none;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.26em;
  text-transform: uppercase; color: #e4d2f7;
}

/* ---------- the centre ----------
   Risso's Dolphin, at the axis. Amethyst, so it reads as belonging to none of
   the four quadrants, and lit rather than filled so it holds its own weight
   without pulling the eye off the sounding line. */
.centre { cursor: pointer; transition: filter 600ms ease; }
.centre:hover, .centre.is-lit {
  filter: brightness(1.5) drop-shadow(0 0 10px rgba(178, 132, 226, 0.6));
}

/* The name of the centre lives OUTSIDE the wheel, in real pixels: anything
   set inside the viewBox scales with the circle, and on a small window the
   band is under 10px tall. One row with the index button, so it adds no
   height to the heading column — height there is taken from the circle. */
.head-row {
  display: flex; align-items: center; gap: 14px;
  flex-wrap: wrap; margin-top: 18px;
}
.head-row .btn-wheelcolours, .head-row .btn-quiet { margin-top: 0; }
.centre-plate {
  appearance: none; cursor: pointer;
  display: flex; align-items: center; gap: 9px;
  margin: 0; padding: 12px 20px;
  border-radius: 999px;
  background: rgba(150, 104, 196, 0.1);
  border: 1px solid rgba(190, 150, 232, 0.4);
  transition: background 500ms ease, border-color 500ms ease, box-shadow 500ms ease;
}
.centre-plate:hover, .centre-plate.is-lit {
  background: rgba(150, 104, 196, 0.2);
  border-color: rgba(206, 172, 240, 0.75);
  box-shadow: 0 0 26px rgba(150, 104, 196, 0.3);
}
.cp-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: rgba(206, 172, 240, 0.9);
  box-shadow: 0 0 12px rgba(178, 132, 226, 0.8);
}
.cp-eyebrow {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.26em;
  text-transform: uppercase; color: rgba(206, 172, 240, 0.7);
}
.cp-name {
  font-family: var(--serif); font-size: 17px; font-weight: 300;
  letter-spacing: 0.01em; color: #e8d8f7; line-height: 1;
}


/* Spin, as an explicit door rather than a hidden hub. */
.spin-plate {
  appearance: none; cursor: pointer;
  display: flex; align-items: center; gap: 9px;
  margin: 0; padding: 12px 22px; border-radius: 999px;
  background: rgba(143, 197, 212, 0.06);
  border: 1px solid rgba(143, 197, 212, 0.34);
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.24em;
  text-transform: uppercase; color: rgba(234, 243, 247, 0.92);
  transition: background 500ms ease, border-color 500ms ease, box-shadow 500ms ease;
}
.spin-plate:hover {
  background: rgba(143, 197, 212, 0.15);
  border-color: rgba(206, 238, 248, 0.8);
  box-shadow: 0 0 26px rgba(143, 197, 212, 0.26);
}
.sp-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: rgba(206, 238, 248, 0.9);
  box-shadow: 0 0 12px rgba(143, 197, 212, 0.8);
}

/* The two ways onward from the deep teaching. */
.depth-onward {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: center; gap: 14px; margin-top: 46px;
}

/* The 24 names are HTML laid over the wheel, not SVG <text> — real px sizing
   independent of the viewBox scale, and they wrap when long. Each .spoke is a
   zero-height arm from the centre, rotated to its wedge. */
.wheel-names { position: absolute; inset: 0; pointer-events: none; }
.spoke { position: absolute; left: 50%; top: 50%; width: 50%; height: 0; transform-origin: 0 0; }
/* NEVER let a name wrap: rotated to its radius, a second line stacks
   perpendicular to the radius and prints straight across the 15deg
   neighbours on both sides. buildWheel() scales long names down instead. */
.wedge-name {
  position: absolute; top: 0; left: 62%; width: 44%;   /* outside the rim */
  transform: translateY(-50%);
  cursor: pointer; pointer-events: auto;
  font-family: var(--serif); white-space: nowrap; line-height: 1;
  transition: text-shadow 400ms ease;
}
.wedge-name:hover { text-shadow: 0 0 14px rgba(207, 233, 242, 0.5); }
.wedge-name.is-centered { text-align: center; }
/* Every arm points at its own wedge (mid - 90). The left half is flipped in
   place — NOT by rotating the arm 180deg, which lands the name in the opposite
   quadrant, because right:22.5% still anchors within the same positive-x arm. */
.wedge-name.is-right { text-align: left; }
.wedge-name.is-left {
  text-align: right;
  transform-origin: center;
  transform: translateY(-50%) rotate(180deg);
}

.compass {
  fill: rgba(226, 236, 243, 0.5);
  font-family: var(--mono); font-size: 15px; letter-spacing: 3.6px;
}

/* No CSS transition: aimAt() runs a multi-keyframe animation so the line can
   accelerate, glide, overshoot and swing back within one sweep. */
/* The sounding line must never intercept a click meant for the wheel beneath
   it — it crosses every wedge and the centre band. */
#needle { transform-origin: 330px 330px; pointer-events: none; }
.needle-glow { stroke: rgba(143, 197, 212, 0.32); stroke-width: 7; stroke-linecap: round; filter: blur(5px); }
.needle-line { stroke: #cfe9f2; stroke-width: 1.4; stroke-linecap: round; }
.needle-halo { fill: rgba(207, 233, 242, 0.22); filter: blur(4px); }
.needle-tip  { fill: #eaf7fb; }

.hub { fill: rgba(3, 12, 20, 0.92); stroke: rgba(143, 197, 212, 0.28); stroke-width: 1; }
.hub-btn { cursor: pointer; transition: opacity 600ms ease; }
.hub-btn:hover { opacity: 0.72; }
/* SPIN is HTML, not SVG <text>, for the reason the rim names are: 13px inside
   a 660-unit viewBox paints at ~5px once the wheel is scaled down. */
.hub-label, .hub-sub { display: none; }
.hub-html {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  pointer-events: none;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.24em;
  text-transform: uppercase; color: rgba(226, 236, 243, 0.86);
  white-space: nowrap;
}

/* ---------- III. stillness ---------- */

.still { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 50vh; animation: fadeIn 1.4s ease both; }
.point { position: relative; width: 9px; height: 9px; }
.point .pulse { position: absolute; inset: 0; border-radius: 50%; background: #a9d7e3; animation: pulse 5.5s ease-in-out infinite; }
.point .core  { position: absolute; inset: 0; border-radius: 50%; background: #dff1f6; opacity: 0.85; }
@keyframes pulse {
  0%,100% { transform: scale(1);   opacity: 0.75; }
  50%     { transform: scale(2.6); opacity: 0.08; }
}
.rising {
  margin: 56px 0 0; font-family: var(--mono); font-size: 10px;
  letter-spacing: 0.34em; text-transform: uppercase; color: rgba(143, 197, 212, 0.5);
  animation: fadeIn 3s ease 1.4s both;
}

/* ---------- IV. reveal ---------- */

/* The arrival holds on ONE screen: name, painting beneath it, the teaching
   filling what is left, the four doors at the foot. The three length classes
   step the type down so a 300-word teaching sits as whole as a 180-word one. */
#reveal { width: 100%; }
body[data-phase="reveal"] .stage, body[data-phase="depth"] .stage { padding-top: 26px; padding-bottom: 26px; min-height: 0; }
.reveal-inner {
  width: min(1080px, 100%); margin: 0 auto;
  min-height: calc(100vh - 142px);       /* stage padding + site head */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
}

/* The painting holds at the top of the viewport and fades to 30% as the
   reader descends past it. The fade is set on .painting (inline opacity by
   JS); the arrival animation lives on the inner .painting-in so the two
   never fight over the same property. */
/* Centred under the name and sized off the viewport HEIGHT as well as the
   width, so the whole arrival clears a 768px-tall laptop window. */
/* The 560px constant is the measured height of everything else on the arrival
   (head + teaching + doors + gaps); *1.5 converts the leftover height into
   plate WIDTH at the 3:2 frame ratio. So the painting always spends exactly
   the space the rest of the page has not claimed. */
.painting { width: max(120px, min(330px, 70vw, calc((100vh - 660px) * 1.5))); margin: 16px 0 0; }
.is-long .painting { width: max(115px, min(300px, 64vw, calc((100vh - 700px) * 1.5))); margin-top: 12px; }
.painting-in { position: relative; animation: surface 3.4s cubic-bezier(0.22, 0.61, 0.36, 1) both; }
.painting-in::before {
  content: ''; position: absolute; inset: -16%; pointer-events: none;
  /* No ground of its own — any tinted pool reads as a halo against the page.
     The sea layers behind show through the plate unbroken. */
}
/* BORDERLESS. No edge, no ground of its own — the sea behind the page runs
   straight through the plate, so the animal reads as swimming in open water
   rather than hanging in a window. */
.frame {
  position: relative; width: 100%; aspect-ratio: 11 / 6;   /* the supplied landscape Sumi-e */
}
/* LUMINOUS INK. The paintings arrive as black ink on white paper; inverting
   and screening them onto near-black drops the paper out entirely and turns
   the ink to light — the whale drawn in bioluminescence.
   Art generated already light-on-dark should carry class="is-native" on its
   .frame, which skips the inversion and screens the file as it is. */
.frame img {
  width: 100%; height: 100%; object-fit: contain; display: block;
  filter: brightness(1.05) url(#ink-to-light) brightness(0.94); mix-blend-mode: normal;
  /* Two masks composited: the first dissolves the plate's own rectangle into
     the water at its edges; the second removes the artist's seal, which
     inverts to a glowing cyan block. Native light-on-dark art (.is-native)
     has no seal and no mask. */
  -webkit-mask-image:
    radial-gradient(ellipse 80% 80% at 50% 50%, #000 30%, transparent 96%),
    radial-gradient(ellipse 17% 20% at 93% 88%, transparent 52%, #000 100%);
  mask-image:
    radial-gradient(ellipse 80% 80% at 50% 50%, #000 30%, transparent 96%),
    radial-gradient(ellipse 17% 20% at 93% 88%, transparent 52%, #000 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.frame.is-native img { filter: none; mix-blend-mode: normal; -webkit-mask-image: none; mask-image: none; }

/* Two columns let a long teaching fill the width instead of running down the
   page; one column reads better once there is less than ~640px to play with. */
.teaching { width: 100%; margin-top: 16px; }
.is-long .teaching { margin-top: 14px; }
.body { columns: 2; column-gap: 46px; text-align: left; }
.is-short .body { columns: 1; max-width: 720px; margin: 0 auto; }
.body p { break-inside: avoid; }

.reveal-head { display: flex; flex-direction: column; align-items: center; }
.direction {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.3em;
  text-transform: uppercase; color: rgba(143, 197, 212, 0.6);
  margin-bottom: 14px; animation: fadeUp 2s ease 2.4s both;
}

.species { margin: 0; font-weight: 300; font-size: clamp(30px, 3vw, 42px); line-height: 1.08; letter-spacing: 0.01em; color: #eaf3f7; text-wrap: pretty; animation: fadeUp 2.2s ease 2.6s both; }
.binomial { margin-top: 12px; font-family: var(--mono); font-style: italic; font-size: 12px; letter-spacing: 0.06em; color: rgba(226, 236, 243, 0.4); animation: fadeUp 2.2s ease 2.9s both; }
.rule { height: 1px; width: 64px; margin: 26px 0 0; background: linear-gradient(to right, rgba(143, 197, 212, 0.5), transparent); animation: fadeIn 2.4s ease 3.1s both; }

/* the teaching arrives WHOLE — one block, never staggered per paragraph */
.teaching { animation: fadeUp 3s ease 3.4s both; }
/* The instruction closes Level 1 the way the question closes Level 2: in italic. */
.teaching .body p:last-child { font-style: italic; color: rgba(214, 240, 250, 0.9); }
.body .turn-mark { margin: 4px 0 20px; break-inside: avoid; break-after: avoid; }
.teaching-title { font-size: 16px; font-style: italic; letter-spacing: 0.04em; color: rgba(143, 197, 212, 0.82); margin-bottom: 14px; }
/* Type size and leading are SOLVED, not guessed: sounding.js measures the
   rendered arrival and tightens these two variables until the doors clear the
   bottom of the window. The word-count classes still govern columns and
   spacing; they no longer set size, or they would fight the solver. */
.body p { margin: 0 0 18px; font-size: var(--tsize, 22px); line-height: var(--tlead, 1.66); color: var(--ink-soft); text-wrap: pretty; }
.is-long .body p { margin-bottom: 14px; }

/* ---------- the one-screen guarantee ----------
   sounding.js solves the arrival against the real window height, adding these
   classes in order as it needs more room. Each is a lever the solver can pull
   before it reaches the last one. */

/* 1 — the head gives up its air, and the name a few points */
.reveal-inner.tight-head .reveal-head { padding-top: 46px; }
.reveal-inner.tight-head .species { font-size: clamp(28px, 2.6vw, 40px); }
.reveal-inner.tight-head .binomial { margin-top: 5px; }

/* 2 — the teaching leaves the narrow left column and runs beneath the
   aperture as ONE full-width column. Not two: parallel paragraphs read badly
   here and were rejected. A 396px measure makes even a 100-word teaching
   470px tall; the same words at ~700px are half that, which is what buys the
   fit without shrinking the type. */
.reveal-inner.wide-single .teaching { grid-column: 1 / -1; grid-row: 2; }
/* The aperture normally spans both rows; once the teaching is widened into
   row 2 it must give that row up, or the two share the same cell and the
   text runs across the photograph. */
.reveal-inner.wide-single .painting { grid-row: 1; }
.reveal-inner.wide-single .body { columns: 1; max-width: none; margin: 0; }

/* Door placement — every row spans the grid and centres itself. Grid items
   with a max-width resolve to START, not centre, so this must be explicit. */
.btn-dive, .btn-absent { grid-column: 1 / -1; justify-self: center; }
/* the doors sit within an inch of the teaching above them */
.actions { margin-top: 22px; }
.again-row { margin-top: 14px; }
.actions, .again-row { grid-column: 1 / -1; justify-self: center; }

/* ---------- IN DEPTH MESSAGE ----------
   The long teaching, paged rather than scrolled: any length, one screen at a
   time, in the same water. The sigil box at the head is a placeholder — drop
   a transparent PNG in as a background and remove the label. */
#depth { width: 100%; }
.depth-inner {
  width: min(760px, 100%); margin: 0 auto;
  display: flex; flex-direction: column; align-items: center;
  animation: fadeUp 1.8s ease both;
}
/* The depth view's species image is only used by the porthole variant; the
   ink page keeps its sigil and hides it. */
.depth-port { display: none; }

.sigil {
  width: 148px; height: 148px; display: grid; place-items: center;
  border: 1px solid rgba(143, 197, 212, 0.24); border-radius: 2px;
  background: rgba(143, 197, 212, 0.045);
  color: rgba(207, 233, 242, 0.8);          /* the sigil draws in currentColor */
}
.sigil svg { width: 104px; height: 104px; display: block; }
.depth-head { text-align: center; margin-top: 30px; }
.depth-label {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.3em;
  text-transform: uppercase; color: rgba(143, 197, 212, 0.55);
}
.depth-species { margin: 20px 0 0; font-weight: 300; font-size: clamp(30px, 3vw, 40px); line-height: 1.1; color: #eaf3f7; }
.depth-title { margin-top: 12px; font-size: 15px; font-style: italic; color: rgba(143, 197, 212, 0.8); }

/* ---------- the verse ----------
   Four lines at the threshold, between the invitation and the two doors.
   Its own air on both sides, so it reads as an invocation rather than as
   subtitle copy for the buttons beneath it. */
.verse {
  margin: 26px auto 24px; max-width: none; width: fit-content;
  text-align: center; font-family: var(--serif); font-style: italic;
  font-weight: 300; font-size: 20px; line-height: 1.62;
  color: rgba(190, 224, 238, 0.82);
  /* the <br> breaks alone set the four lines — balance re-wraps the segments
     between them and turns line 1 into two */
  text-wrap: normal;
}
/* The invitation has no solver of its own, so the threshold pays for itself
   on short windows rather than pushing the two doors past the fold. */
@media (max-height: 860px) {
  .invitation-body { margin-top: 24px; }
  .invitation-body p { font-size: 16px; line-height: 1.66; margin-bottom: 13px; }
}
@media (max-height: 700px) {
  .invitation-body { margin-top: 20px; }
  .invitation-body p { font-size: 15px; line-height: 1.56; margin-bottom: 11px; }
}
/* Genuinely short windows: the invitation has no solver, so the threshold and
   the copy above it give ground together to keep the two doors on screen. */
@media (max-height: 640px) {
  #invitation .title { font-size: 54px; }
  #invitation /* What the page is, in plain words, under its name. */
.title-sub {
  margin: 18px 0 0; font-family: var(--mono); font-size: 12px;
  letter-spacing: 0.28em; text-transform: uppercase; color: rgba(143, 197, 212, 0.82);
}
.return-line {
  margin: 16px 0 0; width: 100%; text-align: center;
  font-family: var(--serif); font-style: italic; font-size: 16px;
  color: rgba(226, 236, 243, 0.62);
}
.lede { margin-top: 22px; font-size: 19px; line-height: 1.6; }
  #invitation .lede-sub { margin-top: 14px; line-height: 1.7; }
  #invitation .actions-row { margin-top: 26px; }
  .invitation-body { margin-top: 16px; max-width: 600px; }
  .invitation-body p { font-size: 14px; line-height: 1.5; margin-bottom: 9px; }
}
@media (max-height: 560px) {
  #invitation .title { font-size: 42px; }
  #invitation /* What the page is, in plain words, under its name. */
.title-sub {
  margin: 18px 0 0; font-family: var(--mono); font-size: 12px;
  letter-spacing: 0.28em; text-transform: uppercase; color: rgba(143, 197, 212, 0.82);
}
.return-line {
  margin: 16px 0 0; width: 100%; text-align: center;
  font-family: var(--serif); font-style: italic; font-size: 16px;
  color: rgba(226, 236, 243, 0.62);
}
.lede { margin-top: 16px; font-size: 17px; line-height: 1.5; }
  #invitation .lede-sub { margin-top: 10px; font-size: 11px; line-height: 1.5; }
  #invitation .actions-row { margin-top: 20px; }
  .invitation-body { margin-top: 12px; }
  .invitation-body p { font-size: 14px; line-height: 1.46; margin-bottom: 8px; }
}
@media (max-width: 560px) {
  .title { font-size: 44px; }
  .invitation-body p { font-size: 16px; line-height: 1.7; }
}

.depth-body { margin-top: 40px; width: 100%; }
.depth-body p { margin: 0 0 26px; font-size: 19px; line-height: 1.9; color: var(--ink-soft); text-wrap: pretty; }

/* ---------- the three movements of a deep teaching ----------
   A teaching in Deeper Water moves through three registers, and the layout
   has to make them audible: the species speaking of itself, the same voice
   turning to address the reader, and the question it leaves behind. The
   distinction is carried by measure, weight and light — not by boxes. */

/* the subject of the descent, under the epithet */
.depth-subject {
  margin-top: 20px; font-size: 22px; font-style: italic; font-weight: 300;
  line-height: 1.35; color: rgba(226, 236, 243, 0.82); max-width: 26ch;
  margin-left: auto; margin-right: auto; text-wrap: pretty;
}

/* THE VOICE — the whale describing its own life. Full measure, larger, and
   lit brighter than the page: this is sound arriving from somewhere else. */
/* The voice opens at full measure with no drop capital: several of these
   teachings begin with "I", which as a floated 3em capital reads as a bare
   vertical bar rather than an initial. */
.movement.voice p {
  font-size: 21px; line-height: 1.95;
  color: rgba(238, 246, 250, 0.93);
  margin-bottom: 30px;
}
.movement.voice p:first-child { margin-top: 0; }

/* THE TURN — the hinge of the teaching, where the species stops describing
   itself and speaks to you. A sounding line across the column. */
.turn-mark {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  margin: 14px 0 40px;
}
.turn-mark i {
  display: block; height: 1px; width: 84px;
  background: linear-gradient(90deg, transparent, rgba(143, 197, 212, 0.5));
}
.turn-mark i + i { background: linear-gradient(90deg, rgba(143, 197, 212, 0.5), transparent); }
.turn-mark b {
  display: block; width: 6px; height: 6px; border-radius: 50%;
  background: rgba(180, 222, 236, 0.75);
  box-shadow: 0 0 14px rgba(143, 197, 212, 0.6);
}

/* THE ADDRESS — spoken to you. Narrower measure, quieter, closer in. */
.movement.address {
  max-width: 60ch; margin: 0 auto;
}
.movement.address p { font-size: 19px; line-height: 1.86; }

/* THE QUESTION — left standing on its own, in its own air.
   Scoped under .depth-body: the generic paragraph rule there carries an
   extra class of specificity and would otherwise win on every shared
   property, flattening the question back to body text. */
.depth-body .depth-question {
  margin: 46px auto 8px; margin-left: auto; margin-right: auto;
  max-width: 42ch;
  font-size: 26px; line-height: 1.5; font-style: italic; font-weight: 300;
  text-align: center; color: rgba(214, 240, 250, 0.94);
  text-wrap: pretty;
}

/* ---------- the practice ----------
   Not the teaching. An offer of something to do, set apart from the voice
   entirely: instruction, so it is smaller, cooler and plainly bounded. */
.practice {
  margin-top: 64px; padding-top: 40px;
  border-top: 1px solid rgba(143, 197, 212, 0.16);
}
.practice-label {
  margin: 0; text-align: center;
  font-family: var(--mono); font-size: 10px; font-weight: 400;
  letter-spacing: 0.3em; text-transform: uppercase;
  color: rgba(143, 197, 212, 0.6);
}
.depth-body .practice-lead {
  margin: 30px auto 0; margin-left: auto; margin-right: auto;
  max-width: 52ch; text-align: center;
  font-size: 18px; font-style: italic; line-height: 1.75;
  color: rgba(226, 236, 243, 0.7);
}
.practice-steps {
  list-style: none; counter-reset: step;
  margin: 30px auto 0; padding: 0; max-width: 58ch;
  display: flex; flex-direction: column; gap: 26px;
}
.practice-steps li {
  counter-increment: step;
  display: grid; grid-template-columns: 30px minmax(0, 1fr); align-items: baseline;
  font-size: 18px; font-style: italic; line-height: 1.8;
  color: rgba(226, 236, 243, 0.72); text-wrap: pretty;
}
.practice-steps li::before {
  content: counter(step);
  font-family: var(--mono); font-style: normal; font-size: 11px;
  letter-spacing: 0.1em; color: rgba(143, 197, 212, 0.55);
}
/* a single step needs no number — there is no sequence to keep */
.practice-steps li:only-child { grid-template-columns: minmax(0, 1fr); }
.practice-steps li:only-child::before { content: none; }

.depth-body .practice-close {
  margin: 34px auto 0; margin-left: auto; margin-right: auto;
  max-width: 48ch; text-align: center;
  font-size: 18px; font-style: italic; line-height: 1.78;
  color: rgba(180, 222, 236, 0.86);
}
/* the descent ends with the doors, set off from the last paragraph */
.depth-inner .actions {
  grid-template-columns: repeat(4, minmax(0, 1fr)); max-width: 860px;
  margin-top: 22px; padding-top: 30px; border-top: 1px solid rgba(143, 197, 212, 0.14);
}
#depth .stage, body[data-phase="depth"] .stage { padding-bottom: 12vh; }

/* The primary door. It gets the full width, a lit ground and a soft glow, and
   a line of subcopy the three secondary doors do not have — so the descent
   reads as the offer and Species Page / Share / Home as the ways out. */
.btn-dive {
  appearance: none; cursor: pointer; display: grid; gap: 8px; justify-items: center;
  width: 100%; max-width: 640px; margin-top: 16px;
  background: linear-gradient(180deg, rgba(143, 197, 212, 0.2), rgba(143, 197, 212, 0.09));
  border: 1px solid rgba(143, 197, 212, 0.62); border-radius: 2px;
  box-shadow: 0 0 36px rgba(143, 197, 212, 0.14), inset 0 1px 0 rgba(207, 233, 242, 0.14);
  padding: 13px 30px; color: #f0f8fb;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.28em; text-transform: uppercase;
  transition: background 700ms ease, border-color 700ms ease, box-shadow 700ms ease, letter-spacing 700ms ease;
}
.btn-dive small {
  font-family: var(--serif); font-size: 14px; font-style: italic;
  letter-spacing: 0.02em; text-transform: none; color: rgba(207, 233, 242, 0.68);
}
.btn-dive:hover {
  background: linear-gradient(180deg, rgba(143, 197, 212, 0.3), rgba(143, 197, 212, 0.14));
  border-color: rgba(180, 222, 236, 0.9);
  box-shadow: 0 0 52px rgba(143, 197, 212, 0.24), inset 0 1px 0 rgba(207, 233, 242, 0.2);
  letter-spacing: 0.32em;
}

/* Four doors out of the teaching: deeper, the species page, share, home.
   Equal columns so they read as one bar rather than a ragged row. */
.actions {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px; margin-top: 10px; width: 100%; max-width: 640px;
}
.is-long .actions { margin-top: 18px; }
/* Shown in place of In Depth Message for a species whose long teaching has
   not been written yet — deliberately inert, and it says why on hover. */
/* The same primary slot, empty: a species whose long teaching is not written. */
.btn-absent {
  display: grid; place-items: center; gap: 7px; cursor: default;
  width: 100%; max-width: 640px; margin-top: 16px;
  border: 1px dashed rgba(143, 197, 212, 0.2); border-radius: 2px;
  padding: 13px 30px; color: rgba(226, 236, 243, 0.3);
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.28em; text-transform: uppercase;
}
.btn-absent small {
  font-family: var(--serif); font-size: 14px; font-style: italic;
  letter-spacing: 0.02em; text-transform: none; color: rgba(226, 236, 243, 0.2);
}
/* The same door slot, inert: a species whose site page is not live yet. */
.actions .door-pending {
  display: grid; place-items: center; gap: 4px; cursor: default;
  padding: 13px 10px; text-align: center;
  border: 1px solid rgba(143, 197, 212, 0.14);
  background: rgba(143, 197, 212, 0.02);
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.22em;
  text-transform: uppercase; color: rgba(226, 236, 243, 0.26);
}
.actions .door-pending small {
  font-family: var(--serif); font-size: 12px; font-style: italic;
  letter-spacing: 0.02em; text-transform: none; color: rgba(226, 236, 243, 0.2);
}
.actions a, .actions button {
  appearance: none; cursor: pointer; text-decoration: none;
  background: rgba(143, 197, 212, 0.06);
  border: 1px solid rgba(143, 197, 212, 0.32); border-radius: 2px;
  padding: 13px 18px; color: #dcecf2; text-align: center;
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase;
  transition: background 700ms ease, border-color 700ms ease, letter-spacing 700ms ease;
}
.actions a:hover, .actions button:hover {
  background: rgba(143, 197, 212, 0.14); border-color: rgba(143, 197, 212, 0.6); letter-spacing: 0.28em;
}
/* "[Species] Speaks" sits in the row of doors, first and a shade brighter. */
.actions.has-speaks {
  display: flex; flex-wrap: wrap; justify-content: center; max-width: 860px;
}
.actions .door-speaks {
  background: rgba(143, 197, 212, 0.16); border-color: rgba(180, 222, 236, 0.7); color: #f0fafd;
}
.actions .door-speaks:hover { background: rgba(143, 197, 212, 0.26); border-color: rgba(206, 238, 248, 1); }

.again-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 28px; margin-top: 12px; }

/* ---------- site chrome ---------- */

.site-head {
  position: relative; z-index: 2;
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: 14px 34px;
  padding: 30px 40px 0;
}
.site-head a {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.22em;
  text-transform: uppercase; color: rgba(226, 236, 243, 0.42);
  text-decoration: none; transition: color 600ms ease;
}
.site-head a:hover { color: var(--accent); }
.site-head a:not(.wordmark) { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 5px; }
.site-head a small {
  font-family: var(--serif); font-style: italic; font-size: 13px;
  letter-spacing: 0.01em; text-transform: none; line-height: 1.2;
  color: rgba(226, 236, 243, 0.5);
}
.site-head a:hover small { color: var(--accent); }
.site-head .wordmark { color: rgba(226, 236, 243, 0.72); letter-spacing: 0.3em; margin-right: auto; }
/* With taglines the full row needs ~1150px. Below that, centre everything
   under a full-width wordmark instead of wrapping flush left. */
@media (max-width: 1180px) {
  .site-head { justify-content: center; gap: 14px 28px; }
  .site-head .wordmark { width: 100%; text-align: center; margin-right: 0; }
}
@media (max-width: 700px) {
  .site-head { gap: 12px 20px; padding: 20px 18px 0; }
}

.stage { min-height: calc(100vh - 90px); }

/* The one-screen arrival is right on a full-height display. On a short laptop
   window the plate would shrink to a thumbnail to preserve it — the painting is
   the point, so below this height the arrival scrolls a little instead. */
@media (max-height: 880px) {
  .reveal-inner { min-height: 0; }
  #reveal .painting { width: min(300px, 70vw); margin-top: 14px; }
  #reveal .is-long .painting { width: min(270px, 64vw); }
}

@media (max-width: 860px) {
  .body, .is-mid .body, .is-long .body { columns: 1; max-width: 620px; margin: 0 auto; }
  .actions { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  .stage { padding: 56px 24px; }
  .title { font-size: 52px; }
  /* What the page is, in plain words, under its name. */
.title-sub {
  margin: 18px 0 0; font-family: var(--mono); font-size: 12px;
  letter-spacing: 0.28em; text-transform: uppercase; color: rgba(143, 197, 212, 0.82);
}
.return-line {
  margin: 16px 0 0; width: 100%; text-align: center;
  font-family: var(--serif); font-style: italic; font-size: 16px;
  color: rgba(226, 236, 243, 0.62);
}
.lede { font-size: 20px; }
  .species { font-size: 40px; }
  .wheel { width: 92vw; }
  .wheel-lede { font-size: 17px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 1ms !important; }
}

/* Once-a-day return: a quiet note and a modest door, not the big primary button. */
.today-note {
  margin: 32px auto 14px; max-width: 560px; text-align: center;
  font-size: 18px; line-height: 1.5; color: rgba(232, 244, 248, 0.92); text-wrap: pretty;
}
#today-door.btn-primary {
  font-size: 13px; letter-spacing: 0.22em; padding: 11px 26px; min-width: 0; width: auto;
}
