/* &dodesign — step-04, the still sky.
 *
 * Written with CSS logical properties throughout (D-05): inline-start
 * rather than left, block-end rather than bottom. Costs nothing now and
 * means the layout survives any writing direction later.
 */

:root {
  /* The sky — 黒に近い群青色, ultramarine pulled down close to black.
   *
   * Held at hue ~226° with the saturation kept high (~57%) and the
   * lightness very low. Saturation is what makes it read as 群青 rather
   * than as neutral dark grey; the previous value sat at hue 220° but only
   * ~55% saturation at 4% lightness, which the eye resolves as black.
   * --sky-lift is the centre of the radial wash, --sky-base the outside.
   *
   * Ceiling on brightness: the faintest stars are drawn at 20% alpha, so
   * lifting the base much further starts erasing them. This is about as
   * deep a navy as the sky can carry without losing magnitude 6 stars. */
  --sky-base: #060b1c;
  --sky-lift: #0d1838;

  /* The sky is not one colour — it is drawn between these two, according
   * to how far the Sun actually is below the visitor's horizon right now.
   * Someone at local midnight gets --sky-deep-*; someone whose evening has
   * only just started gets --sky-dusk-*. Always night, never the same
   * night twice, and derived from the real Sun rather than a lookup table.
   * The committed colour above sits roughly two-thirds of the way toward
   * deep, and remains the static fallback for the page background. */
  --sky-dusk-base: #0a1433;
  --sky-dusk-lift: #16224a;
  --sky-deep-base: #04081a;
  --sky-deep-lift: #0a1230;

  /* Alternates for the variation pass Michi wants after the mockup:
   *   near-black (the step-04 original)  #04060e / #0a1020
   *   true black                         #000000 / #06080f
   *   warmer indigo                      #0a0a20 / #14163c
   *   colder prussian                    #04101f / #0a2038   */

  --ink:      rgba(233, 237, 248, 0.92);
  --ink-dim:  rgba(233, 237, 248, 0.30);

  /* Placeholder stack. The real typefaces are still open (D-06/D-07) —
   * one Latin covering Extended-A, one Japanese, ideally a matched pair.
   * Japanese faces are listed so 侘寂 / 線香花火 render properly today. */
  --face: ui-sans-serif, -apple-system, "Helvetica Neue", "Inter",
          "Hiragino Kaku Gothic ProN", "Yu Gothic", "Noto Sans JP",
          sans-serif;

  /* The reading voice, and the menu's. Michi asked for the text to be
   * simpler and more compact — "like monotype" — so the concept, the
   * menu and the language names are all set in a monospaced stack while
   * the wordmark keeps --face.
   *
   * The Japanese faces matter as much as the Latin one here. A Latin-only
   * mono stack leaves CJK to the browser's default fallback, which is
   * usually a different family at a different weight — the 換喩 heading
   * would not match its own subtitle. The JP faces listed are the ones
   * macOS and Windows actually ship; CJK is monospaced by nature, so they
   * sit correctly against a monospaced Latin without being "mono" faces
   * themselves. Still a placeholder stack — the real pair is open
   * (D-06/D-07). */
  --mono: ui-monospace, "SF Mono", "SFMono-Regular", "Menlo", "Consolas",
          "Roboto Mono", "Noto Sans Mono",
          "Hiragino Kaku Gothic ProN", "Yu Gothic", "Noto Sans JP",
          monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  block-size: 100%;
  background: var(--sky-base);
}

body {
  font-family: var(--face);
  color: var(--ink);
  overflow: hidden;              /* the sky is the page; nothing scrolls yet */
  -webkit-font-smoothing: antialiased;
}

/* The sky fills everything and sits behind all content. */
#sky {
  position: fixed;
  inset: 0;
  display: block;
  inline-size: 100%;
  block-size: 100%;
}

/* Arrival. The sky fades up rather than snapping in — the first second
 * sets the tone for a site whose whole argument is restraint. */
body > * { opacity: 0; transition: opacity 2.4s ease-out; }
body.ready > * { opacity: 1; }

/* ---------------------------------------------------------------
 * The wordmark — and the way in
 * --------------------------------------------------------------- */

/* The h1 wrapper carries no appearance of its own — it exists so the
 * document has a top-level heading (the wordmark IS that heading). The
 * mark inside is `position: fixed`, so this element is out of flow and
 * zero-height; the reset only stops the UA's default h1 margin from
 * pushing anything else around. */
.mark-h1 {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
  line-height: 0;
}

#mark {
  -webkit-tap-highlight-color: transparent;
  position: fixed;
  inset-block-start: clamp(1.4rem, 3.2vh, 2.6rem);
  inset-inline-start: clamp(1.4rem, 3.2vw, 2.8rem);
  z-index: 20;

  /* Reset the button back to text. It must READ as a wordmark and behave
   * as a control — never look like a control (D-08). */
  appearance: none;
  background: none;
  border: 0;
  padding: 0.5rem 0.6rem;
  margin: -0.5rem -0.6rem;      /* keeps the touch target big, the mark in place */
  font: inherit;
  cursor: pointer;
  text-align: start;

  line-height: 0;
  /* NO transform transition here (D-44).
   *
   * The opening sequence that made this matter is gone (D-54), but the
   * rule stands and test-logo.js still enforces it: any transform applied
   * to #mark and later removed would ANIMATE its reversion and send the
   * mark travelling across the screen. Anything that needs to move lives
   * on .mark-stack instead, which is where the hover already is. */
}

/* The logo is Michi's own artwork, used exactly as drawn — no re-weighting
 * of its parts (D-24). The source SVG is black (#231815) for print, so it
 * is inverted to white here rather than being edited; assets/logo.svg
 * stays the authoritative file and can be replaced without touching this
 * CSS.
 *
 * A filter chain rather than a CSS mask because mask-image with an
 * external SVG is unreliable over file://, and this page must open by
 * double-clicking (D-10). */

.mark-stack {
  position: relative;
  display: block;
  inline-size: clamp(9.5rem, 15vw, 13.5rem);
}

.mark-stack img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  user-select: none;
  filter: brightness(0) invert(1)
          drop-shadow(0 0 10px rgba(150, 184, 244, 0.22))
          drop-shadow(0 0 28px rgba(120, 160, 235, 0.08));
  opacity: 0.92;
  transition: opacity 0.5s ease;
}

/* Hover does one thing: the mark gives a little of itself away. No
 * outline, no resizing — the team asked for plain. */
#mark:hover .mark-src,
#mark:focus-visible .mark-src { opacity: 0.55; }

#sky { transition: opacity 1.6s ease; }

/* ---------------------------------------------------------------
 * A drawn constellation is a thing you can open
 * --------------------------------------------------------------- */

/* Laid over the figure by sky.js. Clickable and completely invisible —
 * the constellation itself is the invitation, and drawing a box behind it
 * only announced the mechanism (the team asked for it gone).
 *
 * The keyboard focus ring is the one exception and is not negotiable: a
 * control that cannot be seen when focused is unusable without a mouse.
 * It appears only for :focus-visible, so it never shows on a click. */
#figure-hit {
  position: fixed;
  z-index: 12;
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  /* No tap flash. This button is invisible and sits directly over the
   * figure, so a mobile browser's default highlight paints a coloured
   * rectangle across the constellation on every tap — the one thing the
   * page must never draw around a figure. */
  -webkit-tap-highlight-color: transparent;
}

#figure-hit:focus-visible {
  outline: 1px solid rgba(180, 206, 255, 0.45);
  outline-offset: 4px;
  border-radius: 8px;
}

/* ---------------------------------------------------------------
 * The concept — a reading window on the page itself
 * --------------------------------------------------------------- */

/* Custom properties are NOT animatable unless they are registered with a
 * type — an unregistered one jumps rather than easing. These two drive
 * the soft edges of the reading window, so without this the fade would
 * snap on and off as you scroll. Where @property is unsupported the
 * values still apply; only the easing is lost, which is a fair
 * degradation. */
@property --fade-t {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}
@property --fade-b {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

/* `[hidden]` is restated explicitly because this element needs a `display`
 * value, and ANY author display rule beats the browser's own
 * `[hidden] { display: none }`. Omitting this is what once left the panel
 * as an invisible sheet over the page, swallowing every click and looking
 * exactly like a close button that did not work (D-41). */
#concept[hidden] { display: none; }

.concept {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  /* Held in the vertical middle, aligned to the left margin. */
  place-content: center start;
  padding-inline-start: clamp(2.6rem, 7vw, 6rem);
  padding-inline-end: clamp(1.6rem, 6vw, 5rem);

  /* Reserve the language switch's own row at the foot of the page, so the
   * reading window is centred in the space ABOVE it rather than in the
   * whole viewport (D-66). Without this the close control drifted down
   * beside the language stars on a phone, where the viewport is tall and
   * narrow and the text column is at its longest. Costs nothing on a
   * desktop, where the two were never near each other. */
  padding-block-end: clamp(5.5rem, 13vh, 7.5rem);
  padding-block-start: clamp(1rem, 3vh, 2rem);
  cursor: pointer;                /* outside the frame, a click closes */

  /* The scrim (D-67).
   *
   * THIS ELEMENT MUST NOT CARRY `filter`. It used to have `filter: blur()`
   * for the soft entry, and on WebKit a `filter` on an element suppresses
   * its own `backdrop-filter` — so on iOS the blur behind the text never
   * ran and the constellation stayed legible through it, while the desktop
   * looked right. That is the difference Michi saw between the two. The
   * entry blur now lives on .concept-frame, which has no backdrop-filter
   * to break.
   *
   * The gradient is darker than it was as well, so the text still wins
   * where backdrop-filter is unsupported or switched off: the blur is an
   * enhancement, never the thing legibility depends on. */
  background: radial-gradient(ellipse at center,
              rgba(4, 7, 18, 0.94) 0%,
              rgba(4, 7, 18, 0.88) 50%,
              rgba(4, 7, 18, 0.78) 100%);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);

  /* LEAVING IS FAST (D-62). These are the closing values; `.concept.in`
   * overrides them with the slow ones for arriving. Concept.C.fadeOutMs
   * must match the 0.26s, or the element is hidden before the fade ends. */
  opacity: 0;
  transition: opacity 0.26s ease-in;
}

/* Arriving is slow, so the text resolves into the page rather than
 * switching on. Concept.C.fadeMs matches the 1.6s. */
.concept.in {
  opacity: 1;
  transition: opacity 1.6s cubic-bezier(0.33, 0, 0.2, 1);
}

/* The window itself: transparent, no border, no card. It exists as a real
 * element so that "click outside to close" has an inside to be outside
 * of. */
.concept-frame {
  /* Item 5, Michi: more room, so lines stop breaking early. Was 44rem.
   * The frame is the outer bound; the per-element measures below are what
   * actually set the line length. */
  inline-size: min(62rem, 100%);
  cursor: auto;

  /* The soft entry lives HERE, not on .concept — a `filter` on the scrim
   * element suppresses its own `backdrop-filter` on WebKit (D-67). The
   * text settles the last of the way out of a small lift and a hair of
   * blur, so it resolves into the page instead of switching on. */
  transform: translateY(6px);
  filter: blur(6px);
  transition: transform 0.26s ease-in, filter 0.2s ease-in;
}

.concept.in .concept-frame {
  transform: translateY(0);
  filter: blur(0);
  transition: transform 1.6s cubic-bezier(0.33, 0, 0.2, 1),
              filter 1.2s ease;
}

.concept-track {
  position: relative;
  /* Item 5, Michi: taller. Was a third of the page (33vh), which made
   * even the short Japanese lead scroll. 62vh with a floor for short
   * windows, and a ceiling so the text never runs to the very top and
   * bottom edges of a tall screen. */
  block-size: clamp(14rem, 56vh, 44rem);
  overflow-y: auto;
  overscroll-behavior: contain;

  /* No scrollbar at all — the faded edges say the same thing more
   * quietly, and they work identically in every browser, which the
   * scrollbar never did. */
  scrollbar-width: none;
  -ms-overflow-style: none;

  /* The edges are soft only where there is genuinely more to read. The
   * distances are set by .fade-top / .fade-bottom below, which JS toggles
   * from the real scroll position — so a soft edge always means "more
   * that way" rather than being decoration. */
  --fade-t: 0px;
  --fade-b: 0px;
  -webkit-mask-image: linear-gradient(to bottom,
      transparent 0, #000 var(--fade-t),
      #000 calc(100% - var(--fade-b)), transparent 100%);
  mask-image: linear-gradient(to bottom,
      transparent 0, #000 var(--fade-t),
      #000 calc(100% - var(--fade-b)), transparent 100%);
  transition: --fade-t 0.4s ease, --fade-b 0.4s ease;
}

.concept-track::-webkit-scrollbar { display: none; }

.concept-track.fade-top    { --fade-t: 3.2rem; }
.concept-track.fade-bottom { --fade-b: 3.2rem; }

.concept-panel {
  grid-area: 1 / 1;
  text-align: start;
  /* Item 4, Michi: simpler and more compact, "like monotype". Set once
   * here so every part of the copy inherits one voice; the wordmark keeps
   * --face and stays the only thing on the page in another. */
  font-family: var(--mono);

  /* THE LANGUAGES MUST NEVER OVERLAP (D-76).
   *
   * All three panels share one grid cell (`grid-area: 1 / 1`), so they are
   * stacked exactly on top of each other. With a plain 0.55s opacity
   * transition on both states that is a CROSSFADE: for more than half a
   * second the outgoing language and the incoming one are both painted in
   * the same place, each around 50% — which is precisely when superimposed
   * text is most legible as two texts. A team member saw two languages at
   * once on Safari; it is not intermittent, it is every switch, and
   * whether you catch it is only a matter of when you look.
   *
   * So the fade is SEQUENCED rather than crossed: the outgoing panel
   * leaves first, and the incoming one waits for it. `visibility` is a
   * discrete property, stepped at the end of the out-phase so the old text
   * is genuinely gone rather than merely transparent. */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.22s ease,
              visibility 0s linear 0.22s;
}

/* align-items: start matters. In a fixed-height grid the items stretch to
 * the row by default, and their content then overflows the ITEM rather
 * than the container — so the container never scrolls. Letting them take
 * their natural height is what makes the window scrollable at all. */
.concept-track { display: grid; align-items: start; }

.concept-panel.current {
  opacity: 1;
  visibility: visible;
  /* Waits out the other panel's 0.22s exit before beginning. */
  transition: opacity 0.34s ease 0.22s,
              visibility 0s linear 0.22s;
}

.concept-title {
  margin: 0;
  font-size: clamp(1.25rem, 2.6vw, 1.72rem);
  font-weight: 400;
  letter-spacing: 0.06em;
  line-height: 1.45;
  color: rgba(240, 245, 255, 0.96);
}

.concept-sub {
  margin: 0.4em 0 2em;
  font-size: clamp(0.68rem, 1.1vw, 0.76rem);
  font-weight: 400;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(196, 212, 244, 0.5);
}

.concept-body {
  margin: 0 0 1.2em;
  font-size: clamp(0.92rem, 1.45vw, 1.02rem);
  font-weight: 300;
  /* Generous and non-negotiable: Japanese needs it, and one measure has
   * to serve all three languages (D-05). */
  line-height: 1.85;
  color: rgba(228, 236, 250, 0.86);
  max-inline-size: 74ch;
}

/* No tracking on Japanese — letter-spacing is wrong for CJK (D-05) — and
 * a slightly taller line than the Latin. */
/* CJK takes no tracking (D-05), and a slightly taller line than Latin.
 *
 * THE MEASURE IS IN em, NOT ch (D-65). `ch` is the width of the FONT's
 * "0" glyph, and it does not change with the language of the content — in
 * a monospaced stack it is the same physical width whatever is written in
 * it. So the earlier "52ch for Japanese, 74ch for Latin" did not make two
 * equal lines out of different units; it made the Japanese column literally
 * narrower, 511px against 727px, which is exactly what Michi saw.
 *
 * `em` is the right unit here because a CJK glyph is one em wide, so the
 * number reads as "characters per line" — 44em is about 44 kanji, and it
 * lands within a few pixels of the Latin column's width. */
.concept-panel[lang="ja"] .concept-body {
  letter-spacing: 0;
  line-height: 2.0;
  max-inline-size: 44em;
}

.concept-panel[lang="ja"] .concept-motto,
.concept-panel[lang="ja"] .concept-close-line { max-inline-size: 44em; }
.concept-panel[lang="ja"] .concept-triad { max-inline-size: 46em; }

/* ---------------------------------------------------------------
 * Content shapes — lead, motto, triad, close
 * --------------------------------------------------------------- */

/* The lead. Its 30ch measure was what broke 「ひとつのきっかけから、次の
 * 可能性へ。」 across two lines mid-phrase — it now has room to sit on one
 * (item 5). */
.concept-lead {
  margin: 0 0 1.9em;
  font-size: clamp(0.98rem, 1.7vw, 1.14rem);
  font-weight: 400;
  line-height: 1.85;
  color: rgba(240, 246, 255, 0.94);
  max-inline-size: 64ch;
}

.concept-panel[lang="ja"] .concept-lead {
  letter-spacing: 0;
  max-inline-size: 40em;      /* em, not ch — see the note below (D-65) */
}

/* The motto is the argument in three words. It gets space around it and
 * nothing else does the work — no rule, no box, no colour. */
.concept-motto {
  margin: 2.3em 0;
  font-size: clamp(0.82rem, 1.35vw, 0.92rem);
  font-weight: 400;
  letter-spacing: 0.2em;
  line-height: 2.05;
  color: rgba(224, 234, 252, 0.9);
  max-inline-size: 74ch;
}

/* RELATE / ORIENT / FORM. A definition list, laid out as a grid so the
 * three terms line up and the descriptions hang off them. */
.concept-triad {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: clamp(1.2rem, 3vw, 2.4rem);
  row-gap: 1.15em;
  margin: 2.5em 0;
  max-inline-size: 78ch;
}

.concept-triad dt {
  font-size: clamp(0.68rem, 1.1vw, 0.76rem);
  font-weight: 500;
  letter-spacing: 0.2em;
  line-height: 2.1;
  color: rgba(238, 245, 255, 0.92);
  white-space: nowrap;
}

.concept-triad dd {
  margin: 0;
  font-size: clamp(0.8rem, 1.25vw, 0.88rem);
  font-weight: 400;
  line-height: 1.8;
  color: rgba(216, 228, 248, 0.78);
}

.concept-fields {
  display: block;
  margin-block-start: 0.25em;
  font-size: 0.86em;
  letter-spacing: 0.06em;
  color: rgba(190, 208, 240, 0.5);
}

/* CJK takes no tracking (D-05); the fields line is dense in Japanese. */
.concept-panel[lang="ja"] .concept-motto { letter-spacing: 0.06em; }
.concept-panel[lang="ja"] .concept-fields { letter-spacing: 0; }

.concept-close-line {
  margin: 1.7em 0 0;
  font-size: clamp(0.82rem, 1.3vw, 0.9rem);
  font-weight: 400;
  letter-spacing: 0.08em;
  line-height: 1.9;
  color: rgba(226, 236, 252, 0.84);
  max-inline-size: 74ch;
}

/* ---------------------------------------------------------------
 * Instagram
 * --------------------------------------------------------------- */

/* Bottom-right, answering the language switch bottom-left — the same star
 * and the same mono word, so it joins the page's vocabulary instead of
 * introducing a second one. No Instagram glyph: it would be the only
 * borrowed branding on the page and the only icon anywhere in it.
 *
 * It sits at z-index 20, BELOW the reading layer, unlike the language
 * switch. A translation is something you reach for mid-sentence; a social
 * link is not, and leaving it over the scrim would just be one more thing
 * between the reader and the text. */
#social {
  position: fixed;
  inset-block-end: clamp(1.6rem, 4vh, 2.8rem);
  inset-inline-end: clamp(1.4rem, 3.2vw, 2.8rem);
  z-index: 20;
}

.social-star {
  -webkit-tap-highlight-color: transparent;
  display: flex;
  align-items: center;
  gap: 0.85rem;
  /* A real target, for the same reason the language stars have one. */
  padding: 0.9rem 0.6rem;
  margin: -0.9rem -0.6rem;
  text-decoration: none;
}

.social-dot {
  flex: none;
  inline-size: 5px;
  block-size: 5px;
  border-radius: 50%;
  background: rgba(206, 222, 250, 0.34);
  transition: background-color 0.45s ease, box-shadow 0.45s ease, transform 0.45s ease;
}

/* The name is ALWAYS shown, where the language names are not. A lit dot
 * tells you which language you are in; nothing about a dot says
 * "instagram", and on a touch screen there is no hover to reveal it. */
.social-name {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  line-height: 1.6;
  white-space: nowrap;
  color: rgba(206, 222, 250, 0.44);
  transition: color 0.35s ease;
}

.social-star:hover .social-dot,
.social-star:focus-visible .social-dot {
  background: rgba(255, 255, 255, 0.95);
  box-shadow: 0 0 9px rgba(190, 216, 255, 0.85), 0 0 20px rgba(150, 190, 255, 0.35);
  transform: scale(1.25);
}

.social-star:hover .social-name,
.social-star:focus-visible .social-name { color: rgba(240, 247, 255, 0.88); }

.social-star:focus-visible {
  outline: 1px solid rgba(180, 206, 255, 0.45);
  outline-offset: 4px;
  border-radius: 8px;
}

/* ---------------------------------------------------------------
 * The development readout — ?debug=1 only
 * --------------------------------------------------------------- */

/* Had NO styling at all, so switching it on would have dropped an
 * unstyled block of text into the top-left corner of the page and pushed
 * nothing else around only because everything else is fixed. It exists to
 * be read on a phone, where there is no console, so it has to be legible
 * and out of the way (D-70). Removed before launch along with the flag. */
#readout {
  position: fixed;
  inset-block-end: 0;
  inset-inline-end: 0;
  z-index: 90;
  max-inline-size: min(30rem, 92vw);
  padding: 0.6rem 0.8rem;
  font-family: var(--mono);
  font-size: 0.66rem;
  line-height: 1.7;
  white-space: pre-wrap;
  color: rgba(210, 226, 255, 0.82);
  background: rgba(2, 4, 12, 0.82);
  border-start-start-radius: 6px;
  /* Never intercepts a tap — it sits over the sky, and the sky is the
   * thing being diagnosed. */
  pointer-events: none;
}

#readout:empty { display: none; }

/* ---------------------------------------------------------------
 * The language switch — three stars, and a line between them
 * --------------------------------------------------------------- */

/* A permanent part of the page now, not part of the reading window
 * (D-59). It sits on the same left rail as the mark and the menu, at the
 * foot of the page, so someone who never opens the text can still change
 * language.
 *
 * z-index 50 — ABOVE the reading layer. The mark may be blurred under the
 * text (D-55) because it is not wanted while reading; this is exactly what
 * someone reaching for a translation mid-sentence wants, so it stays crisp
 * and clickable. */
#lang {
  position: fixed;
  inset-block-end: clamp(1.6rem, 4vh, 2.8rem);
  inset-inline-start: clamp(1.4rem, 3.2vw, 2.8rem);
  z-index: 50;
  display: flex;
  align-items: center;
  padding: 0.4rem 0;
  inline-size: fit-content;

  /* THE SPACING IS PADDING, NOT GAP (D-74).
   *
   * The dots need to sit far apart — the names are absolutely positioned
   * and centred under a 5px dot, so the spacing has to clear the WIDEST
   * name ("Português", about 62px), not the dot. That used to be `gap`,
   * which puts 5-6rem of DEAD SPACE between the buttons: a tap that
   * landed between two languages hit nothing, and then closed the reading
   * window or the menu instead, which reads as a broken control rather
   * than a miss.
   *
   * The same distance expressed as padding on each button makes the three
   * targets TILE — every point on the row belongs to its nearest language
   * and there is nowhere left to miss. The dots do not move. */
  --lang-pad: clamp(2.25rem, 5.5vw, 3.25rem);
  gap: 0;
}

/* The line that makes three points a figure. It runs between the first
 * and last DOT, which the padding above moved inward from the container's
 * edges — hence the arithmetic rather than a flat inset (D-74). */
.lang-line {
  position: absolute;
  inset-inline-start: calc(var(--lang-pad) + 2.5px);
  inset-block-start: 50%;
  inline-size: calc(100% - 2 * (var(--lang-pad) + 2.5px));
  block-size: 1px;
  background: linear-gradient(to right,
    rgba(170, 198, 250, 0.06),
    rgba(170, 198, 250, 0.26),
    rgba(170, 198, 250, 0.06));
  pointer-events: none;
}

.lang-star {
  -webkit-tap-highlight-color: transparent;
  position: relative;
  appearance: none;
  background: none;
  border: 0;
  /* Half the spacing on each side, so adjacent targets meet (D-74). The
   * block padding is generous for the same reason — a thumb is about
   * 9mm, and the dot is 5px. */
  padding-inline: var(--lang-pad);
  /* 1.25rem each side plus the 5px dot is a ~45px tall target, which is
   * the 44px minimum a fingertip needs. It was 34px. */
  padding-block: 1.25rem;
  margin-block: -1.25rem;
  font: inherit;
  cursor: pointer;
  line-height: 0;
}

.lang-dot {
  display: block;
  inline-size: 5px;
  block-size: 5px;
  border-radius: 50%;
  background: rgba(206, 222, 250, 0.34);
  transition: background-color 0.45s ease, box-shadow 0.45s ease, transform 0.45s ease;
}

/* The one you are in is simply the one that is lit. No underline, no
 * pill, no colour — it is a star, and stars indicate by brightness. */
.lang-star[aria-pressed="true"] .lang-dot {
  background: rgba(255, 255, 255, 0.97);
  box-shadow: 0 0 9px rgba(190, 216, 255, 0.9), 0 0 20px rgba(150, 190, 255, 0.4);
  transform: scale(1.25);
}

.lang-star:hover .lang-dot {
  background: rgba(238, 246, 255, 0.8);
  box-shadow: 0 0 7px rgba(180, 210, 255, 0.5);
}

/* The name hangs ABOVE the star, because the switch is at the foot of the
 * page and there is nothing below it. */
/* Lifted clear of the dots (Michi): 1.5rem sat the name almost on top of
 * its star, and the row is a control people aim at with a thumb. */
.lang-name {
  position: absolute;
  inset-block-end: 2.15rem;
  inset-inline-start: 50%;
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  line-height: 1.6;
  white-space: nowrap;
  color: rgba(226, 238, 255, 0.72);
  opacity: 0;
  transition: opacity 0.35s ease;
  pointer-events: none;
}

.lang-star:hover .lang-name,
.lang-star:focus-visible .lang-name,
.lang-star[aria-pressed="true"] .lang-name { opacity: 1; }

.lang-star[aria-pressed="true"] .lang-name { color: rgba(255, 255, 255, 0.88); }

.lang-star:focus-visible {
  outline: 1px solid rgba(180, 206, 255, 0.5);
  outline-offset: 3px;
  border-radius: 50%;
}

/* ---------------------------------------------------------------
 * The figure's name
 * --------------------------------------------------------------- */

/* Sits under the constellation once its drawing closes, and leaves on the
 * figure's own fade — sky.js drives the opacity from the figure's alpha,
 * so there is no transition here to fight it (D-78).
 *
 * Quiet on purpose. The figures are what the page is FOR (D-17); the name
 * is a caption on one, not a headline over it, so it is small, tracked
 * wide, and dimmer than the line it belongs to. */
#figure-name {
  position: fixed;
  z-index: 14;                  /* over the sky, under the mark and the menu */
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.28em;
  line-height: 1.6;
  white-space: nowrap;
  color: rgba(226, 238, 255, 0.72);
  text-shadow: 0 0 16px rgba(8, 12, 28, 0.9);
  opacity: 0;

  /* NO TRANSITION — and it has to be said, not merely omitted (D-79).
   *
   * This element is a direct child of <body>, so it matched
   * `body > * { transition: opacity 2.4s ease-out }`, the page's arrival
   * fade. sky.js writes this opacity EVERY FRAME from the figure's alpha,
   * and a 2.4s transition turns each of those writes into a target the
   * browser eases toward instead of a value it paints. The name therefore
   * lagged seconds behind its figure — still at 24% when the figure had
   * reached zero — and then vanished in one step when the figure retired.
   * Delayed, and abrupt at the end: exactly what Michi saw.
   *
   * Anything whose opacity is driven per frame must declare this. */
  transition: none;

  /* Never takes a click. The figure's own target sits over this area and
   * is what opens the text; a caption intercepting that would break the
   * one interaction the sky has. */
  pointer-events: none;
  user-select: none;
}

#figure-name[hidden] { display: none; }

/* ---------------------------------------------------------------
 * The way out of the text
 * --------------------------------------------------------------- */

/* Item 8, Michi: clicking outside works but nothing says so, and it was
 * "a bit difficult to infer". This is the visible one — the same
 * star-and-name idiom as the language switch, at the END of the reading
 * window, where a reader who has finished already is. Not a dialog's ×,
 * and not in a corner: it is the last thing in the text.
 *
 * Click-outside and Escape both still work. */
.concept-close {
  -webkit-tap-highlight-color: transparent;
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-block-start: 1.9rem;
  padding: 0.45rem 0.6rem 0.45rem 0;
  margin-inline-start: -0.1rem;
  appearance: none;
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
  inline-size: fit-content;
}

.concept-close-dot {
  inline-size: 5px;
  block-size: 5px;
  border-radius: 50%;
  background: rgba(206, 222, 250, 0.34);
  transition: background-color 0.45s ease, box-shadow 0.45s ease, transform 0.45s ease;
}

.concept-close-name {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  line-height: 1.6;
  white-space: nowrap;
  color: rgba(206, 222, 250, 0.5);
  transition: color 0.35s ease;
}

.concept-close:hover .concept-close-dot,
.concept-close:focus-visible .concept-close-dot {
  background: rgba(255, 255, 255, 0.95);
  box-shadow: 0 0 9px rgba(190, 216, 255, 0.85), 0 0 20px rgba(150, 190, 255, 0.35);
  transform: scale(1.25);
}

.concept-close:hover .concept-close-name,
.concept-close:focus-visible .concept-close-name { color: rgba(255, 255, 255, 0.9); }

.concept-close:focus-visible {
  outline: 1px solid rgba(180, 206, 255, 0.45);
  outline-offset: 4px;
  border-radius: 8px;
}

/* ---------------------------------------------------------------
 * The menu — RELATE, ORIENT, FORM, as a vertical asterism
 * --------------------------------------------------------------- */

/* A layer under the mark, not over the whole page. It sits at the mark's
 * own inline margin so the terms hang directly beneath the wordmark, and
 * it never stretches across the sky — the figures are what the page is
 * FOR (D-17) and a full-page layer here would be one more thing between
 * the reader and them. */
#menu {
  position: fixed;
  inset-block-start: clamp(4.6rem, 11vh, 6.4rem);
  inset-inline-start: clamp(1.4rem, 3.2vw, 2.8rem);
  z-index: 30;
  /* The container is only ever as wide as its words, and still refuses
   * clicks so that the sky behind it stays live. */
  inline-size: max-content;
  pointer-events: none;
}

/* Same obligation as #concept (D-41): this element needs a `display`
 * value, and ANY author display rule beats the browser's own
 * `[hidden] { display: none }`. Without this the layer stays in the page
 * once closed. */
#menu[hidden] { display: none; }

.menu-branch {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 1.45rem;
  padding-block: 0.4rem;
}

/* The line that makes three points a figure — drawn down the column of
 * dots, where the language switch draws it across. */
.menu-line {
  position: absolute;
  inset-inline-start: 0.5rem;
  inset-block-start: 0.9rem;
  inline-size: 1px;
  block-size: calc(100% - 1.8rem);
  background: linear-gradient(to bottom,
    rgba(170, 198, 250, 0.05),
    rgba(170, 198, 250, 0.24),
    rgba(170, 198, 250, 0.05));
  pointer-events: none;
}

.menu-item {
  /* Each term rises a little as it arrives, staggered by menu.js. */
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 620ms ease, transform 620ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.menu-item.in {
  opacity: 1;
  transform: translateY(0);
}

/* A term. Reset to text — it must read as a word in the sky, never as a
 * control (D-08, D-15). */
.menu-star,
.menu-leaf-btn {
  -webkit-tap-highlight-color: transparent;
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.85rem;
  pointer-events: auto;
  appearance: none;
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
  padding: 0.3rem 0.5rem 0.3rem 0;
  margin: -0.3rem 0;          /* a real touch target, the word unmoved */
  text-align: start;
}

.menu-dot {
  flex: none;
  inline-size: 5px;
  block-size: 5px;
  margin-inline-start: 0.35rem;
  border-radius: 50%;
  background: rgba(206, 222, 250, 0.34);
  transition: background-color 0.45s ease, box-shadow 0.45s ease, transform 0.45s ease;
}

.menu-dot-small {
  inline-size: 3px;
  block-size: 3px;
  margin-inline-start: 0.46rem;
}

/* Stars indicate by brightness (D-45): the open term is simply the lit
 * one. No underline, no pill, no colour. */
.menu-star[aria-expanded="true"] .menu-dot {
  background: rgba(255, 255, 255, 0.97);
  box-shadow: 0 0 9px rgba(190, 216, 255, 0.9), 0 0 20px rgba(150, 190, 255, 0.4);
  transform: scale(1.25);
}

.menu-star:hover .menu-dot,
.menu-leaf-btn:hover .menu-dot {
  background: rgba(238, 246, 255, 0.8);
  box-shadow: 0 0 7px rgba(180, 210, 255, 0.5);
}

.menu-name {
  font-family: var(--mono);
  font-size: 0.78rem;
  font-weight: 400;
  letter-spacing: 0.22em;
  line-height: 1.6;
  white-space: nowrap;
  /* WHITE, NOT DIM — and this is temporary (D-77).
   *
   * The dim-to-bright pair reads as inactive-to-active, and nothing here
   * is inactive yet: the leaves have no destinations, so the site ships
   * with every word equally live. When the sub-items become real links,
   * put the dim resting state back — that is when the distinction starts
   * carrying information instead of implying a disabled control.
   *
   * The lit dot still says which term is open, so nothing is lost by the
   * names not carrying that too (D-45). */
  color: rgba(255, 255, 255, 0.92);
  transition: color 0.35s ease;
}

.menu-star:hover .menu-name,
.menu-star:focus-visible .menu-name,
.menu-star[aria-expanded="true"] .menu-name { color: #fff; }

/* The work under a term: smaller, dimmer, indented onto its own line of
 * dots — a lesser asterism hanging off the greater one. */
.menu-leaves {
  list-style: none;
  margin: 0;
  padding: 0.7rem 0 0.1rem 1.35rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.menu-leaves[hidden] { display: none; }

.menu-leaf {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 480ms ease, transform 480ms cubic-bezier(0.2, 0.7, 0.3, 1);
  /* Each one after the last, so the set unfolds rather than appearing. */
  transition-delay: calc(var(--k, 0) * 55ms);
}

.menu-leaves.in .menu-leaf {
  opacity: 1;
  transform: translateY(0);
}

.menu-name-small {
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: lowercase;
  /* White too, for the reason above (D-77). These are the words that will
   * become links; until they do, greying them promises an interaction the
   * page cannot honour. */
  color: rgba(255, 255, 255, 0.88);
}

.menu-leaf-btn:hover .menu-name-small,
.menu-leaf-btn:focus-visible .menu-name-small { color: #fff; }

.menu-star:focus-visible,
.menu-leaf-btn:focus-visible {
  outline: 1px solid rgba(180, 206, 255, 0.45);
  outline-offset: 4px;
  border-radius: 8px;
}

@media (prefers-reduced-motion: reduce) {
  .mark-stack img, .concept-dot, .concept-name, .concept-track { transition: none; }
  /* The terms still have to arrive, they just must not travel (D-08). */
  .menu-item, .menu-leaf { transition: opacity 200ms ease; }
  .menu-item, .menu-item.in, .menu-leaf, .menu-leaves.in .menu-leaf { transform: none; }
}
