/* Cormorant Garamond — the chrome's face.
 *
 * A classical old-style serif: high stroke contrast, small on the body, long
 * extenders, set light on a dark ground so it does not bloom against the sky.
 *
 * Catharsis Fonts, SIL Open Font License; fonts/OFL-CormorantGaramond.txt
 * travels with it. Variable across 300-700, latin subset, 37 KB. */
@font-face {
  font-family: 'Cormorant Garamond';
  src: url('../fonts/CormorantGaramondVar.woff2') format('woff2-variations'),
       url('../fonts/CormorantGaramondVar.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: block;
}

/* style.css — the chrome, the hot-points and the card they open into.
   The chaitya and its mani wall are canvas. */

:root {
  --ink:       #120E0C;
  --ink-2:     #1E1712;
  --cream:     #EDE1CE;
  --cream-dim: #B9A88F;
  --cream-low: #8A7A66;
  --gold:      #D9A441;
  --gold-lit:  #F5D48A;
  --gold-deep: #8A5F28;
  --vermilion: #C1502E;
  /* a warm ivory, so the chrome and the canvas share one white */
  --ivory:     #F5E6CA;
  /* brass, for the borders on the toggles — the same metal as the gilt on the
     building, so the chrome reads as part of the object */
  --brass:      #B98F3C;
  --brass-lit:  #E7C168;
  --brass-deep: #6B4E19;
  --serif: 'Cormorant Garamond', 'Cinzel', 'Iowan Old Style',
           'Palatino Linotype', Palatino, Georgia, 'Times New Roman', serif;
}
* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--ink);
  color: var(--cream-dim);
  font-family: "Noto Sans Devanagari", "Kohinoor Devanagari",
               ui-sans-serif, system-ui, -apple-system, sans-serif;
  overscroll-behavior: none;
}

body { overflow: hidden; }

#stage {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
  cursor: crosshair;
}

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

/* The top bar carries three things and nothing else: what this is, what you
   can do to it, and the two switches. All of it low-contrast on purpose —
   the building is the page, and chrome that competes with it is chrome that
   is in the way. */
.chrome {
  position: fixed;
  top: 0; left: 0; right: 0;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.35rem clamp(1rem, 3vw, 2.4rem);
  pointer-events: none;
  z-index: 4;
}

.brand { display: flex; flex-direction: column; gap: 0.42rem; }

/* Serif, wide-tracked, and deliberately faint. A title at full strength on a
   night sky is the brightest thing on the screen and the eye goes to it
   instead of to the stupa. */
.eyebrow {
  margin: 0;
  font-family: var(--serif);
  font-size: 0.94rem;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.30em;
  text-transform: uppercase;
  color: var(--ivory);
  opacity: 0.34;
}

/* The prompt says what the object answers to, then gets out of the way — it
   breathes rather than sitting there, so it reads as an invitation and not
   as a label. Serif and lighter still than the title above it. */
.prompt {
  margin: 0;
  font-family: var(--serif);
  font-size: 0.78rem;
  font-weight: 300;
  font-style: italic;
  letter-spacing: 0.13em;
  color: var(--brass);
  animation: breathe 7s ease-in-out infinite;
}

html[lang="ne"] .eyebrow {
  letter-spacing: 0.04em;
  text-transform: none;
}
html[lang="ne"] .prompt { letter-spacing: 0.02em; font-style: normal; }

/* Wraps, because three buttons in two scripts is wider than a phone and the
   alternative is one of them hanging off the edge. */
.acts {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.55rem;
  pointer-events: auto;
}

/* ---- the right-hand side ------------------------------------------------
 * The two switches, and under them the name of the building you are looking
 * at. Right-aligned as a column so the buttons keep the corner they have
 * always had and the name hangs off the same edge — one axis on that side,
 * not two things floating near each other.
 * ---------------------------------------------------------------------- */
.side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1.15rem;
  min-width: 0;
}

.plate {
  text-align: right;
  max-width: min(38ch, 30vw);
  /* Held off the right edge by more than the chrome's own padding, and only
     this block is — the buttons keep the corner they have always had. The
     shared axis was the intent and it is still there in the alignment, but the
     two things on it are not the same weight: SOUND is 0.82rem of tracked
     caps inside a pill, and the name is 3rem of a light serif whose sidebearings
     are almost nothing. Set flush to the same line, the pill looks placed and
     the name looks like it ran out of page — the 'h' of Boudhanath sits a hair
     off the glass. The extra margin is not a nudge for taste, it is the optical
     correction that makes the two edges read as one. */
  margin-right: clamp(0.5rem, 1.6vw, 1.6rem);
  /* The canvas under it is the interaction — a hand sweeping the flags passes
     straight through this corner — so the text must not swallow the pointer.
     It is selectable all the same: `text` restores that on the words only. */
  pointer-events: none;
  user-select: text;
}
.plate[hidden] { display: none; }

/* Big, because it is the one thing on the page that names what you are
   looking at — but held in the same faint ivory as the title opposite, and
   set in the serif at a light weight, so "big" is a matter of SIZE and not of
   contrast. A heading at full strength on a night sky is the brightest object
   in the window and the eye goes to it instead of to the building. */
.place {
  margin: 0 0 0.5rem;
  font-family: var(--serif);
  font-size: clamp(1.9rem, 3.2vw, 3rem);
  font-weight: 400;
  line-height: 0.98;
  letter-spacing: 0.015em;
  color: var(--ivory);
  opacity: 0.82;
}

.blurb {
  margin: 0;
  font-family: var(--serif);
  font-size: 0.92rem;
  font-weight: 300;
  line-height: 1.62;
  letter-spacing: 0.01em;
  color: var(--cream-dim);
  opacity: 0.72;
}

/* Cormorant has no Devanagari in it. Left on var(--serif) the browser falls
   back PER GLYPH to whatever it finds, which means a Nepali line can come out
   set in two faces at two weights — and the name is 3rem, so it shows. Name
   the stack instead, the same one the body element carries, and both lines are
   one face. The metrics move with it: Devanagari hangs from a headline rather
   than sitting on a baseline, it needs the leading opened up, and the tracking
   that suits a wide-set Latin small-caps title closes a conjunct. */
html[lang="ne"] .place,
html[lang="ne"] .blurb {
  font-family: "Noto Sans Devanagari", "Kohinoor Devanagari",
               ui-sans-serif, system-ui, -apple-system, sans-serif;
}
html[lang="ne"] .place { letter-spacing: 0; line-height: 1.15; font-weight: 300; }
html[lang="ne"] .blurb { letter-spacing: 0; line-height: 1.75; font-size: 0.86rem; }

/* The count of what has been opened. Set as the faintest thing in the block —
   it is a progress readout, not a fact about the building — with LINING
   TABULAR figures so 1/8 and 8/8 occupy the same width and the line does not
   shift by a hair every time a node is read. */
.tally {
  margin: 0.7rem 0 0;
  font-family: var(--serif);
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  font-variant-numeric: lining-nums tabular-nums;
  text-transform: uppercase;
  color: var(--brass);
  opacity: 0.72;
}
.tally[hidden] { display: none; }
html[lang="ne"] .tally { letter-spacing: 0.04em; text-transform: none; }

/* Brass, the same brass as the gilt on the building: a thin ring with a warm
   inner light, seated on almost nothing. */
.chrome button {
  appearance: none;
  border: 1px solid rgba(185, 143, 60, 0.45);
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(231,193,104,0.10), rgba(107,78,25,0.06)),
    rgba(20, 15, 11, 0.55);
  color: var(--ivory);
  font-family: var(--serif);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.4rem 1.05rem 0.42rem;
  cursor: pointer;
  opacity: 0.72;
  transition: color 0.28s, border-color 0.28s, opacity 0.28s, box-shadow 0.28s;
  backdrop-filter: blur(6px);
}
.chrome button:hover, .chrome button:focus-visible {
  color: var(--brass-lit);
  border-color: rgba(231, 193, 104, 0.75);
  opacity: 1;
  box-shadow: 0 0 14px rgba(185, 143, 60, 0.18) inset;
}
.chrome button:focus-visible { outline: 2px solid rgba(231,193,104,0.5); outline-offset: 2px; }

html[lang="ne"] .chrome button { letter-spacing: 0.03em; text-transform: none; }

/* ---- the switch that has not been thrown yet ----------------------------
 * A browser will not start audio without a press, and on the stupa there is
 * nothing you would naturally press — the whole interaction is a hand passing
 * over the flags. So until the context is running the sound button breathes,
 * on the same keyframes the prompt uses: the one moving thing on a still page
 * is the one worth pressing. It stops the moment there is sound to hear.
 * See wake() in scene.js.
 * ---------------------------------------------------------------------- */
.chrome button.is-idle {
  animation: breathe 5s ease-in-out infinite;
  border-color: rgba(231, 193, 104, 0.7);
  color: var(--brass-lit);
}

/* ---- the hot-points -----------------------------------------------------
 * Eight nodes on the building, each anchored to a member the crown publishes.
 * Two things pull against each other here: they have to be FOUND, and eight
 * bright dots on a night scene make a map rather than a night. Findability
 * wins — a node at rest is a small blue point carrying a real glow, breathing
 * on a long cycle. What still waits for a hand is everything that would turn
 * the page into a diagram: the ring opening, the name, the card.
 * ---------------------------------------------------------------------- */
.marks {
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;          /* the field underneath is the interaction */
}
.marks[hidden] { display: none; }

/* The button is a generous hit area with a small dot in the middle of it —
   28px is about a fingertip, and the dot is 8. Positioned by transform rather
   than left/top: it is moved every frame on a line that is itself moving, and
   transform is the one that does not force a layout. */
.mark {
  position: absolute;
  left: 0; top: 0;
  width: 28px; height: 28px;
  margin: -14px 0 0 -14px;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 50%;
  cursor: pointer;
  pointer-events: auto;
  -webkit-appearance: none;
  appearance: none;
}

/* ---- the node, and the ring outside it ----------------------------------
 * BLUE, against a night that is otherwise entirely warm — every other light on
 * this page is a butter lamp, gilt or a star, so a cool point is the one
 * colour here that could not be part of the building. That is what makes eight
 * of them read as something added FOR you rather than as lights someone left
 * on, and it is why they are legible at an opacity this low.
 *
 * Two circles, both lit. The inner one is the node; the outer is a thin ring
 * standing off it with its own glow, close enough to read as one object and
 * far enough to give it a halo. The ring is there AT REST — it is half of what
 * the thing looks like, not a hover state — and what a hand does is brighten
 * and open it, not conjure it.
 * ---------------------------------------------------------------------- */
.mark::before {
  content: '';
  position: absolute;
  inset: 9px;
  border-radius: 50%;
  background: radial-gradient(circle, #F4FAFF 0%, #9CCCFF 50%, rgba(96,158,232,0) 78%);
  box-shadow: 0 0 18px 5px rgba(110, 175, 250, 0.62);
  opacity: 0.92;
  transition: opacity 0.35s ease, inset 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  animation: nodeBreathe 6s ease-in-out infinite;
}

.mark::after {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  border: 1px solid rgba(168, 212, 255, 0.72);
  /* the glow goes both ways: outside it is air, inside it is the light coming
     off the node it rings */
  box-shadow: 0 0 14px 2px rgba(110, 175, 250, 0.42),
              inset 0 0 8px rgba(110, 175, 250, 0.30);
  opacity: 0.76;
  transform: scale(0.92);
  transition: opacity 0.3s ease, transform 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              box-shadow 0.35s ease;
}

.mark:hover::before,
.mark:focus-visible::before,
.mark.is-open::before { opacity: 1; inset: 8px; animation: none; }
.mark:hover::after,
.mark:focus-visible::after,
.mark.is-open::after {
  opacity: 1;
  transform: scale(1.12);
  box-shadow: 0 0 16px 3px rgba(120, 180, 250, 0.45),
              inset 0 0 8px rgba(120, 180, 250, 0.3);
}
.mark:focus-visible { outline: 2px solid rgba(150, 200, 250, 0.5); outline-offset: 4px; }

/* The name, on hover. It is a label and not a card — the card is what the
   click is for — so it stays one line and never wraps. */
.mark .tip {
  position: absolute;
  left: 50%; bottom: 100%;
  transform: translate(-50%, -2px);
  margin-bottom: 4px;
  padding: 0.28rem 0.6rem 0.3rem;
  white-space: nowrap;
  font-family: var(--serif);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  color: var(--ivory);
  background: rgba(14, 10, 8, 0.82);
  border: 1px solid rgba(185, 143, 60, 0.35);
  border-radius: 2px;
  backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease, transform 0.22s ease;
}
.mark:hover .tip,
.mark:focus-visible .tip { opacity: 1; transform: translate(-50%, -6px); }
.mark.is-open .tip { opacity: 0; }

/* ---- the card -----------------------------------------------------------
 * One card, moved to whichever node was pressed, rather than eight cards that
 * are nearly always hidden. It grows out of the node it belongs to — the
 * transform-origin is set from the node's side of the screen, so a node on the
 * right opens leftward and the card never runs off the edge.
 * ---------------------------------------------------------------------- */
.spot {
  position: fixed;
  left: 0; top: 0;
  z-index: 6;
  width: min(30ch, 78vw);
  padding: 1rem 1.15rem 1.05rem;
  border: 1px solid rgba(217, 164, 65, 0.3);
  border-top: 2px solid rgba(217, 164, 65, 0.6);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(217,164,65,0.10), transparent 70%),
    linear-gradient(180deg, rgba(30, 22, 17, 0.96), rgba(14, 11, 9, 0.97));
  box-shadow: 0 20px 48px rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(6px);
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 0.26s ease, transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.spot[hidden] { display: none; }
.spot.is-in { opacity: 1; transform: scale(1); pointer-events: auto; }

.spot-name {
  /* clear of the close button, which sits in the same corner — without this a
     long name in either script runs under the × */
  margin: 0 0 0.45rem;
  padding-right: 1.3rem;
  font-family: var(--serif);
  font-size: 1.22rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.04em;
  color: var(--gold-lit);
}
.spot-body {
  margin: 0;
  font-family: var(--serif);
  font-size: 0.9rem;
  font-weight: 300;
  line-height: 1.6;
  color: var(--cream-dim);
}
.spot-x {
  position: absolute;
  top: 0.35rem; right: 0.45rem;
  width: 1.5rem; height: 1.5rem;
  border: 0;
  background: none;
  color: var(--cream-low);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.spot-x:hover, .spot-x:focus-visible { color: var(--gold-lit); }

/* ---- about --------------------------------------------------------------
 * The hot-point card grows out of the node it belongs to. This one belongs to
 * nothing on the building, so it is centred instead — but built from the same
 * materials, because two card styles on one page is one too many.
 * ---------------------------------------------------------------------- */
.about {
  position: fixed;
  left: 50%; top: 50%;
  z-index: 7;
  width: min(40ch, 84vw);
  padding: 1.5rem 1.6rem 1.4rem;
  border: 1px solid rgba(217, 164, 65, 0.3);
  border-top: 2px solid rgba(217, 164, 65, 0.6);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(217,164,65,0.10), transparent 70%),
    linear-gradient(180deg, rgba(30, 22, 17, 0.97), rgba(14, 11, 9, 0.98));
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(8px);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.92);
  transition: opacity 0.26s ease, transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.about[hidden] { display: none; }
.about.is-in {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  pointer-events: auto;
}

/* Where she is, set as a caption under the name: brass and tracked, so it
   reads as a byline rather than as the first line of the paragraph. */
.about-where {
  margin: 0 0 0.85rem;
  font-family: var(--serif);
  font-size: 0.74rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brass);
  opacity: 0.8;
}
html[lang="ne"] .about-where { letter-spacing: 0.04em; text-transform: none; }

/* Stacked rather than run together on one line: two links of very different
   lengths side by side leave a ragged gap, and each is a separate place to
   go rather than a pair of options. */
.about-links {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.55rem;
  margin: 1.1rem 0 0;
}

/* The only links on the page, so they are allowed to look like links —
   underlined on the brass rule the chrome uses, rather than the blue a browser
   would give them. */
.about-link {
  display: inline-block;
  font-family: var(--serif);
  font-size: 0.86rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--brass);
  text-decoration: none;
  border-bottom: 1px solid rgba(185, 143, 60, 0.45);
  padding-bottom: 2px;
  transition: color 0.28s, border-color 0.28s;
}
.about-link:hover, .about-link:focus-visible {
  color: var(--brass-lit);
  border-color: rgba(231, 193, 104, 0.8);
}
.about-link:focus-visible { outline: 2px solid rgba(231,193,104,0.5); outline-offset: 3px; }

@keyframes nodeBreathe {
  0%, 100% { opacity: 0.72; }
  50%      { opacity: 1; }
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@keyframes breathe {
  0%, 100% { opacity: 0.32; }
  50%      { opacity: 0.72; }
}

@media (prefers-reduced-motion: reduce) {
  .prompt { animation: none; opacity: 0.52; }
  .mark::before { animation: none; opacity: 0.95; }
  .spot { transition: opacity 0.2s ease; transform: none; }
  .spot.is-in { transform: none; }
  /* the centring transform has to survive — only the scale goes */
  .about { transition: opacity 0.2s ease; transform: translate(-50%, -50%); }
  .about.is-in { transform: translate(-50%, -50%); }
  /* no breathing, but it still has to read as the live one, so it simply sits
     brighter than the switches around it */
  .chrome button.is-idle { animation: none; opacity: 1; }
}

@media (max-width: 900px) {
  /* On a narrow window the name would run into the title opposite it, so the
     paragraph goes and the name stays: which building this is survives, the
     essay does not. */
  .plate { max-width: 46vw; }
  .place { font-size: 1.5rem; }
  .blurb { display: none; }
  .eyebrow { font-size: 0.8rem; letter-spacing: 0.2em; }
  .prompt { font-size: 0.7rem; }
  .chrome button { padding: 0.34rem 0.8rem; font-size: 0.74rem; }
}


/* THE CURSOR STAYS. The fireflies orbit the pointer and lag it; none of them
   sits where the click will land, so the arrow is not a duplicate of anything
   — it is the one thing on the screen that says where you actually are.
   #stage carries the crosshair a few rules up and the chrome carries the arrow.

   firefly.js sets .firefly-on on <html> while it is running and nothing styles
   it: it is the hook the night would need if it ever does want a cursor of its
   own. */
