/* grid.css — the Grid, and the document it falls back to.
 *
 * Two states live in this file:
 *   html.gl   WebGL is up. #doc becomes the invisible model behind the canvas,
 *             and the HUD, rail and panel are the whole interface.
 *   (default) No WebGL, no JS, reduced motion, or a crawler. #doc is just a
 *             document and reads as one, on black, with the same words.
 *
 * Colour follows Basil: on the Grid the only light is the Grid itself, so
 * cyan is structure and every district owns one hue. Nothing is neutral grey.
 */

:root {
  --ink: #dff4ff;
  --dim: #7fb3c6;
  --faint: #3d6070;
  --cyan: #5aebff;
  --bg: #01050a;
  --edge: rgba(90, 235, 255, 0.22);
  --sans: 'Chakra Petch', ui-sans-serif, system-ui, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body { min-height: 100svh; overflow-x: hidden; }

a { color: inherit; }
::selection { background: rgba(90, 235, 255, 0.28); color: #eafcff; }

button {
  font: inherit; color: inherit; background: none; border: 0;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}

/* ═══════════════════════════════════════════════════ canvas + atmosphere */

#gl { display: none; }
#vignette { display: none; }

html.gl #gl {
  display: block;
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  touch-action: none;       /* we own the gestures */
}

/* A soft dark frame so the horizon never touches the screen edge — the
   Grid should feel looked-at, not pasted on. Pointer-transparent. */
html.gl #vignette {
  display: block;
  position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(130% 100% at 50% 42%, transparent 38%, rgba(0,0,0,.55) 100%),
    linear-gradient(to bottom, rgba(0,0,0,.55) 0%, transparent 18%, transparent 78%, rgba(0,0,0,.72) 100%);
}

html.gl, html.gl body { overflow: hidden; height: 100%; overscroll-behavior: none; }

/* ═══════════════════════════════════════════════════════ document state */

#doc {
  position: relative; z-index: 2;
  max-width: 62rem;
  margin: 0 auto;
  padding: calc(var(--safe-t) + 4rem) 1.5rem calc(var(--safe-b) + 5rem);
}

#intro h1 {
  font-size: clamp(2.6rem, 11vw, 5rem);
  font-weight: 700; letter-spacing: .14em; line-height: 1;
  margin-bottom: 1.1rem;
}
#intro .glyph { color: var(--cyan); }

#intro .tag {
  font-size: clamp(1.05rem, 3.6vw, 1.4rem);
  color: var(--ink); max-width: 34ch; line-height: 1.5;
}

#intro .hint {
  margin-top: 1.4rem;
  font-family: var(--mono); font-size: .72rem;
  letter-spacing: .18em; text-transform: uppercase; color: var(--faint);
}

.city, .self {
  margin-top: 4.5rem;
  padding-top: 2.2rem;
  border-top: 1px solid var(--edge);
}

.city h2, .self h2 {
  font-size: clamp(1.7rem, 6.5vw, 2.6rem);
  font-weight: 700; letter-spacing: .04em; margin-bottom: 1rem;
  color: hsl(var(--h, 190), 90%, 74%);
}

.city .blurb, .self .blurb { font-size: 1.05rem; line-height: 1.65; max-width: 62ch; }
.city .why, .self .why {
  margin-top: 1rem; color: var(--dim);
  font-size: .96rem; line-height: 1.7; max-width: 62ch;
}

.towers { list-style: none; margin: 1.8rem 0 0; display: grid; gap: .5rem; }
.towers li {
  font-family: var(--mono); font-size: .85rem; color: var(--dim);
  display: flex; align-items: baseline; gap: .8rem;
}
.towers li::before {
  content: attr(data-v);
  color: hsl(var(--h, 190), 90%, 74%);
  font-weight: 500; min-width: 5.5ch; text-align: right;
}

.city .stack, .self .stack {
  margin-top: 1.6rem;
  font-family: var(--mono); font-size: .72rem;
  letter-spacing: .1em; color: var(--faint); text-transform: uppercase;
}

#colophon {
  margin-top: 5rem; padding-top: 2rem; border-top: 1px solid var(--edge);
  font-family: var(--mono); font-size: .74rem; line-height: 1.8; color: var(--faint);
  max-width: 60ch;
}

/* Once the Grid is up the document is still in the DOM — it is the model —
   but it must not paint, take hits or be read twice by a screen reader. */
html.gl #doc { position: fixed; opacity: 0; pointer-events: none; z-index: -1; }

/* ═══════════════════════════════════════════════════════════════════ HUD */

#hud[hidden], #panel[hidden], #dots[hidden], #prompt[hidden] { display: none; }

#hud {
  position: fixed; z-index: 4; pointer-events: none;
  top: calc(var(--safe-t) + .9rem);
  left: calc(var(--safe-l) + 1rem);
  right: calc(var(--safe-r) + 1rem);
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
}

#mark {
  pointer-events: auto;
  display: flex; align-items: center; gap: .5rem;
  font-weight: 700; letter-spacing: .2em; font-size: .82rem;
  padding: .45rem .2rem;
  transition: opacity .2s;
}
#mark .glyph { color: var(--cyan); font-size: 1rem; }
#mark:active { opacity: .6; }

#readout { text-align: right; font-family: var(--mono); line-height: 1.35; }
#ro-label {
  display: block; font-size: .78rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink);
}
#ro-sub { display: block; font-size: .66rem; letter-spacing: .1em; color: var(--faint); }


/* ═══════════════════════════════════════════════════ marks and prompt */

/* Seven marks, not seven buttons. The row of labels was doing two jobs — getting
   you somewhere, and telling you where you were. The first was clutter. This is
   the second, kept: how many there are, which one you are on, and the silent fact
   that they run sideways. */
#dots {
  position: fixed; z-index: 4;
  left: 0; right: 0;
  bottom: calc(var(--safe-b) + 1.1rem);
  display: flex; justify-content: center; align-items: center; gap: .55rem;
  transition: opacity .35s;
}

#dots button {
  width: 2rem; height: 1.6rem;          /* a thumb-sized target around a small mark */
  display: grid; place-items: center;
  -webkit-tap-highlight-color: transparent;
}

#dots button::before {
  content: '';
  width: .42rem; height: .42rem;
  transform: rotate(45deg);
  background: hsla(var(--h), 90%, 70%, .3);
  box-shadow: 0 0 0 1px hsla(var(--h), 90%, 70%, .35) inset;
  transition: background .3s, box-shadow .3s, transform .3s;
}

#dots button[aria-current="true"]::before {
  background: hsl(var(--h), 95%, 70%);
  box-shadow: 0 0 10px hsl(var(--h), 95%, 62%), 0 0 22px hsla(var(--h), 95%, 58%, .75);
  transform: rotate(45deg) scale(1.5);
}

/* The one line that says what to do, where the thumb already is. It leaves for good
   the moment you touch anything — it is a hint, not a caption. */
#prompt {
  position: fixed; z-index: 4;
  left: 0; right: 0;
  bottom: calc(var(--safe-b) + 3.1rem);
  text-align: center;
  font-family: var(--mono); font-size: .64rem;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--dim);
  pointer-events: none;
  text-shadow: 0 2px 12px #000;
  animation: breathe 3.4s ease-in-out infinite;
  transition: opacity .6s;
}
@keyframes breathe { 0%, 100% { opacity: .45; } 50% { opacity: .95; } }
#prompt.gone { opacity: 0; animation: none; }

/* While the slab is up there is no room under it on a phone, and nothing to index. */
body.slab #prompt { opacity: 0; animation: none; }
@media (max-width: 819px) { body.slab #dots { opacity: 0; pointer-events: none; } }

@media (min-width: 820px) {
  #dots { bottom: calc(var(--safe-b) + 1.6rem); }
  #prompt { bottom: calc(var(--safe-b) + 3.7rem); font-size: .68rem; }
}

@media (prefers-reduced-motion: reduce) { #prompt { animation: none; opacity: .8; } }


/* ═══════════════════════════════════════════════════ connections + signal */

/* What a district is wired to. Only drawn where a tube actually exists, so the
   line on the slab and the line in the world say the same thing. */
#p-links { list-style: none; margin: 1.1rem 0 0; display: grid; gap: .38rem; }
#p-links[hidden] { display: none; }
#p-links li {
  font-family: var(--mono); font-size: .7rem; line-height: 1.5;
  color: var(--dim); display: flex; gap: .55rem; align-items: baseline;
}
#p-links b {
  color: hsl(var(--lh, 190), 92%, 74%);
  font-weight: 500; white-space: nowrap;
}
#p-links i { font-style: normal; color: var(--faint); }

/* The form. Same instrument grammar as the rest: hairlines, mono labels, no
   rounded chrome, nothing that looks like a website contact box. */
#signal { margin-top: 1.6rem; display: grid; gap: .8rem; }

.signal__lede {
  font-family: var(--mono); font-size: .64rem;
  letter-spacing: .24em; text-transform: uppercase; color: hsl(var(--h), 90%, 72%);
}

#signal label { display: grid; gap: .3rem; }
#signal label span {
  font-family: var(--mono); font-size: .6rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--faint);
}

#signal input, #signal textarea {
  font-family: var(--sans); font-size: .9rem; color: var(--ink);
  background: rgba(90, 235, 255, .04);
  border: 1px solid rgba(90, 235, 255, .18);
  border-radius: 0;
  padding: .6rem .7rem;
  width: 100%;
  transition: border-color .2s, background .2s;
  -webkit-appearance: none; appearance: none;
}
#signal textarea { resize: vertical; min-height: 5.2rem; line-height: 1.5; }
#signal input:focus, #signal textarea:focus {
  outline: none;
  border-color: hsla(var(--h), 90%, 70%, .7);
  background: hsla(var(--h), 90%, 50%, .07);
}

#signal button {
  justify-self: start;
  margin-top: .2rem; padding: .74rem 1.2rem;
  font-family: var(--mono); font-size: .72rem;
  letter-spacing: .12em; text-transform: uppercase;
  color: hsl(var(--h), 92%, 82%);
  border: 1px solid hsla(var(--h), 90%, 70%, .5);
  background: hsla(var(--h), 90%, 50%, .12);
  transition: background .2s, opacity .2s;
}
#signal button:active { background: hsla(var(--h), 90%, 55%, .28); }
#signal button[disabled] { opacity: .45; }

#signal-say {
  font-family: var(--mono); font-size: .66rem; line-height: 1.5;
  letter-spacing: .06em; color: var(--dim); min-height: 1rem;
}
#signal-say.bad { color: #ff9aa2; }
#signal-say.good { color: hsl(var(--h), 92%, 76%); }

/* In the fallback document the form is just a form, on the page. */
html:not(.gl) #signal { max-width: 34rem; }

/* ═════════════════════════════════════════════════════════════════ panel */

/* Not a sheet docked to the bottom of a phone — that reads as an app, and this is supposed to
   read as a thing hanging in the air over a city. So: a floating slab, detached on every side,
   lit in the district's hue, with corner brackets and a hairline that make it instrumentation
   rather than a card. The city stays visible around it. */

#panel {
  position: fixed; z-index: 5;
  left: max(var(--safe-l), .75rem);
  right: max(var(--safe-r), .75rem);
  bottom: calc(var(--safe-b) + 1rem);
  max-height: 62svh;
  background:
    linear-gradient(160deg, hsla(var(--h, 190), 80%, 50%, .10), transparent 46%),
    rgba(2, 9, 16, .82);
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  border: 1px solid hsla(var(--h, 190), 90%, 70%, .42);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, .55),
    0 24px 70px rgba(0, 0, 0, .8),
    0 0 70px hsla(var(--h, 190), 90%, 50%, .22);
  opacity: 0;
  transform: translateY(18px) scale(.97);
  transform-origin: 50% 100%;
  transition: transform .52s cubic-bezier(.22, 1, .3, 1), opacity .32s ease;
  will-change: transform, opacity;
}
#panel.up { opacity: 1; transform: none; }

/* Corner brackets — the cheapest way to say "readout" instead of "dialog". */
#panel::before, #panel::after {
  content: ''; position: absolute; width: 14px; height: 14px; pointer-events: none;
  border: 1px solid hsl(var(--h, 190), 92%, 72%);
}
#panel::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
#panel::after { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

#panel-scroll {
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  max-height: 62svh;
  padding: 1.5rem 1.3rem 1.6rem;
  overscroll-behavior: contain;
}

#close {
  position: absolute; z-index: 1;
  top: .3rem; right: .35rem;
  width: 2.6rem; height: 2.6rem;
  font-size: .95rem; color: var(--dim);
  display: grid; place-items: center;
}
#close:active { color: var(--ink); }

#p-kicker {
  font-family: var(--mono); font-size: .62rem;
  letter-spacing: .24em; text-transform: uppercase;
  color: hsl(var(--h), 90%, 72%); margin-bottom: .45rem;
}

#p-name {
  font-size: clamp(1.6rem, 7.5vw, 2.2rem); font-weight: 700;
  letter-spacing: .02em; line-height: 1.05; margin-bottom: .8rem;
  padding-right: 2.2rem;
}

#p-blurb { font-size: .95rem; line-height: 1.58; max-width: 54ch; }
#p-why {
  margin-top: .8rem; color: var(--dim);
  font-size: .86rem; line-height: 1.66; max-width: 54ch;
}

#p-towers { list-style: none; margin: 1.2rem 0 0; display: grid; gap: .1rem; }
#p-towers li {
  display: grid; grid-template-columns: 7ch 1fr; align-items: baseline; gap: .85rem;
  font-family: var(--mono); font-size: .76rem; color: var(--dim);
  padding: .4rem 0; border-bottom: 1px solid rgba(90, 235, 255, .07);
}
#p-towers b {
  font-weight: 500; text-align: right;
  color: hsl(var(--h), 92%, 74%);
  text-shadow: 0 0 14px hsla(var(--h), 92%, 60%, .55);
}

#p-stack {
  margin-top: 1.1rem; font-family: var(--mono); font-size: .64rem;
  letter-spacing: .09em; text-transform: uppercase; color: var(--faint);
  line-height: 1.7;
}

#p-link {
  display: inline-flex; align-items: center; gap: .5rem;
  margin-top: 1.3rem; padding: .74rem 1.2rem;
  font-family: var(--mono); font-size: .72rem;
  letter-spacing: .12em; text-transform: uppercase; text-decoration: none;
  color: hsl(var(--h), 92%, 82%);
  border: 1px solid hsla(var(--h), 90%, 70%, .5);
  background: hsla(var(--h), 90%, 50%, .12);
  transition: background .2s;
}
#p-link:active { background: hsla(var(--h), 90%, 55%, .28); }

/* ══════════════════════════════════════════════════════════════════ boot */

#boot {
  position: fixed; inset: 0; z-index: 9;
  display: none; place-items: center; align-content: center; gap: 1.1rem;
  background: var(--bg);
  font-family: var(--mono); font-size: .7rem;
  letter-spacing: .3em; color: var(--faint);
  transition: opacity .7s;
}
html.booting #boot { display: grid; }
#boot.gone { opacity: 0; pointer-events: none; }
#boot i {
  display: block; width: 8rem; height: 1px;
  background: rgba(90,235,255,.16); position: relative; overflow: hidden;
}
#boot i::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--cyan), transparent);
  animation: sweep 1.25s linear infinite;
}
@keyframes sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* ════════════════════════════════════════════════════════════ responsive */

@media (min-width: 820px) {
  #hud { top: calc(var(--safe-t) + 1.5rem); left: 2rem; right: 2rem; }
  .city-label { font-size: .74rem; }

  /* On a wide screen the slab floats off to one side and hangs at eye level, so the city it
     belongs to stays in view beside it rather than behind it. */
  #panel {
    left: auto; right: 2.2rem;
    top: 50%; bottom: auto;
    width: min(31rem, 38vw);
    max-height: min(78svh, 46rem);
    transform-origin: 100% 50%;
    transform: translateY(-50%) translateX(26px) scale(.98);
  }
  #panel.up { transform: translateY(-50%); }
  #panel-scroll { max-height: min(78svh, 46rem); padding: 2rem 1.9rem; }
  #close { top: .5rem; right: .55rem; }
}

/* A phone in landscape has almost no height: drop the slab to a side column there too, or the
   city disappears behind it entirely. */
@media (max-height: 560px) and (orientation: landscape) {
  #panel {
    left: auto; right: max(var(--safe-r), .75rem);
    top: max(var(--safe-t), .6rem); bottom: max(var(--safe-b), .6rem);
    width: min(24rem, 52vw); max-height: none;
    transform-origin: 100% 50%;
    transform: translateX(22px) scale(.98);
  }
  #panel.up { transform: none; }
  #panel-scroll { max-height: none; height: 100%; padding: 1.1rem 1.2rem; }
}

/* ═════════════════════════════════════════════════════════ reduced motion */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .12s !important;
  }
}

/* ════════════════════════════════════════════════════════ city labels (WebGL) */

/* Projected over each district. Big enough to be a thumb target on a phone,
   quiet enough that seven of them do not become a menu. */
.city-label {
  position: absolute; top: 0; left: 0;
  display: flex; align-items: center; gap: .5rem;
  padding: .5rem .8rem;
  font-family: var(--mono); font-size: .7rem;
  letter-spacing: .14em; text-transform: uppercase;
  color: hsl(var(--h), 92%, 82%);
  white-space: nowrap;
  text-shadow: 0 0 18px hsla(var(--h), 92%, 55%, .8), 0 2px 10px #000;
  transition: opacity .45s ease;
  will-change: transform, opacity;
}
.city-label i {
  width: .34rem; height: .34rem; flex: 0 0 auto;
  background: hsl(var(--h), 95%, 72%);
  box-shadow: 0 0 10px hsl(var(--h), 95%, 62%), 0 0 22px hsla(var(--h), 95%, 58%, .7);
  transform: rotate(45deg);
}
.city-label::before {
  content: ''; position: absolute; left: 50%; top: 100%;
  width: 1px; height: 2.2rem;
  background: linear-gradient(hsla(var(--h), 92%, 70%, .55), transparent);
}
@media (min-width: 820px) { .city-label { font-size: .74rem; } }

/* Basil is not one of the seven, so it does not wear a district hue. */
.basil-label {
  color: #ffe6ad;
  letter-spacing: .34em; font-weight: 600; font-size: .78rem;
  text-shadow: 0 0 22px rgba(255, 105, 185, .75), 0 2px 10px #000;
}
.basil-label i { background: #ff69b9; box-shadow: 0 0 12px #ff69b9, 0 0 26px rgba(255,105,185,.8); }
.basil-label::before { background: linear-gradient(rgba(255,217,138,.5), transparent); }

#p-link[hidden] { display: none; }
