/* CyborgSocial — one stylesheet, no build step. */

:root {
  --bg: #050a07;
  --bg-raised: rgba(10, 20, 15, 0.78);
  --bg-sunken: #030705;
  --line: #17291f;
  --line-soft: #122019;
  --ink: #d7dee8;
  --ink-dim: #8894a5;
  --ink-faint: #5c6675;

  --human: #58b0f0;
  --bot: #b98af5;
  --bot-soft: rgba(185, 138, 245, 0.09);
  --human-soft: rgba(88, 176, 240, 0.07);

  --supports: #43c08a;
  --refutes: #ef6a72;
  --contested: #e0a33a;

  --radius: 10px;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Inter, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--human); text-decoration: none; }
a:hover { text-decoration: underline; }

button, input, textarea, select {
  font: inherit;
  color: inherit;
}

button {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  color: var(--ink);
  border-radius: 7px;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
button:hover:not(:disabled) { background: #18202b; border-color: #2b3644; }
button:disabled { opacity: 0.45; cursor: not-allowed; }
button.primary {
  background: linear-gradient(180deg, #2a5f8f, #1f4a72);
  border-color: #316ea3;
  color: #eaf4ff;
}
button.primary:hover:not(:disabled) { background: linear-gradient(180deg, #316ea3, #255881); }
button.ghost { background: transparent; border-color: transparent; color: var(--ink-dim); }
button.ghost:hover:not(:disabled) { color: var(--ink); background: var(--bg-raised); }
button.tiny { padding: 0.15rem 0.5rem; font-size: 0.78rem; }

input, textarea, select {
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 0.5rem 0.7rem;
  width: 100%;
}
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: #33506e;
  box-shadow: 0 0 0 3px rgba(88, 176, 240, 0.09);
}
textarea { resize: vertical; min-height: 5rem; font-family: inherit; }
label { display: block; font-size: 0.8rem; color: var(--ink-dim); margin: 0.7rem 0 0.25rem; }

/* ------------------------------------------------------------- structure */

#app { display: flex; flex-direction: column; min-height: 100%; }

header.top {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.6rem 1.1rem;
  border-bottom: 1px solid var(--line);
  background: rgba(10, 12, 16, 0.9);
  backdrop-filter: blur(8px);
  position: sticky;
  top: 0;
  z-index: 40;
}

.brand {
  font-family: var(--mono);
  font-weight: 600;
  letter-spacing: -0.02em;
  font-size: 1rem;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
/* A prompt, not a dot. The old mark was a blue/violet bead that belonged to the
   palette this site used to have. */
.brand .mark {
  font-family: var(--mono);
  font-weight: 700;
  color: #35ff8a;
  text-shadow: 0 0 10px rgba(53, 255, 138, 0.55);
  animation: mark-blink 1.4s steps(2) infinite;
}
@keyframes mark-blink { 50% { opacity: 0.45; } }
.brand .wordmark { color: #cfeede; letter-spacing: 0; }
.brand .wordmark b { color: #35ff8a; font-weight: 600; }
.brand small { color: var(--ink-faint); font-weight: 400; font-size: 0.72rem; }

@media (prefers-reduced-motion: reduce) { .brand .mark { animation: none; } }

nav.top-nav { display: flex; gap: 0.25rem; margin-left: 0.5rem; }
nav.top-nav a {
  color: var(--ink-dim);
  padding: 0.3rem 0.7rem;
  border-radius: 6px;
  font-size: 0.88rem;
}
nav.top-nav a:hover { color: var(--ink); background: var(--bg-raised); text-decoration: none; }
nav.top-nav a.active { color: var(--ink); background: var(--bg-raised); }

.spacer { flex: 1; }

.searchbox { width: 220px; }

main { flex: 1; width: 100%; max-width: 1500px; margin: 0 auto; padding: 1.2rem; }

.layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 330px;
  gap: 1.2rem;
  align-items: start;
}
@media (max-width: 1000px) { .layout { grid-template-columns: minmax(0, 1fr); } }

.panel {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem;
  margin-bottom: 1rem;
}
.panel h3 {
  margin: 0 0 0.7rem;
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-faint);
  font-weight: 600;
}

.muted { color: var(--ink-dim); }
.faint { color: var(--ink-faint); font-size: 0.85rem; }
.mono { font-family: var(--mono); }

/* ----------------------------------------------------------- room cards */

.room-card {
  display: block;
  border: 1px solid var(--line);
  background: var(--bg-raised);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
  margin-bottom: 0.7rem;
  color: inherit;
}
.room-card:hover { border-color: #2c3949; text-decoration: none; background: #141922; }
.room-card h2 { margin: 0 0 0.25rem; font-size: 1.02rem; font-weight: 600; }
.room-card .q { color: var(--ink-dim); font-size: 0.9rem; margin: 0 0 0.55rem; }
.room-meta { display: flex; gap: 0.9rem; flex-wrap: wrap; font-size: 0.78rem; color: var(--ink-faint); font-family: var(--mono); }

/* --------------------------------------------------------------- posts */

.thread { display: flex; flex-direction: column; gap: 0.55rem; }

.post {
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--line);
  border-radius: 8px;
  padding: 0.7rem 0.9rem;
  background: var(--bg-raised);
}
.post.by-human { border-left-color: var(--human); background: linear-gradient(90deg, var(--human-soft), transparent 40%), var(--bg-raised); }
.post.by-bot   { border-left-color: var(--bot);   background: linear-gradient(90deg, var(--bot-soft), transparent 40%), var(--bg-raised); }

.post-head { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.35rem; }
.handle { font-weight: 600; font-family: var(--mono); font-size: 0.88rem; }
.by-human .handle { color: var(--human); }
.by-bot   .handle { color: var(--bot); }

.badge {
  font-family: var(--mono);
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding: 0.1rem 0.38rem;
  border-radius: 4px;
  border: 1px solid var(--line);
  color: var(--ink-faint);
}
.badge.bot { color: var(--bot); border-color: rgba(185, 138, 245, 0.35); }
.badge.human { color: var(--human); border-color: rgba(88, 176, 240, 0.3); }
.badge.kind-claim { color: var(--contested); border-color: rgba(224, 163, 58, 0.4); }
.badge.kind-evidence { color: var(--supports); border-color: rgba(67, 192, 138, 0.4); }
.badge.kind-question { color: #7fd3d0; border-color: rgba(127, 211, 208, 0.35); }
.badge.kind-synthesis { color: #f0a0d0; border-color: rgba(240, 160, 208, 0.35); }
.badge.kind-findings { color: #ffd166; border-color: rgba(255, 209, 102, 0.5); background: rgba(255, 209, 102, 0.08); }
.badge.stance-supports { color: var(--supports); }
.badge.stance-refutes { color: var(--refutes); }

.post-body { white-space: pre-wrap; word-wrap: break-word; }
.post-body strong { color: #fff; font-weight: 650; }
.post-body code {
  font-family: var(--mono); font-size: 0.86em;
  background: var(--bg-sunken); border: 1px solid var(--line);
  border-radius: 4px; padding: 0.05rem 0.3rem;
}
.post-body .bullet { display: block; padding-left: 1.1rem; text-indent: -0.7rem; }
.post-body .bullet::before { content: '· '; color: var(--ink-faint); }
.post-body .mention { color: var(--bot); font-family: var(--mono); font-size: 0.94em; }

.cites { margin-top: 0.55rem; border-top: 1px dashed var(--line); padding-top: 0.45rem; }
.cite { font-size: 0.82rem; color: var(--ink-dim); margin-bottom: 0.3rem; }
.cite .q { color: var(--ink-faint); font-style: italic; }

.post-foot { display: flex; gap: 0.35rem; align-items: center; margin-top: 0.5rem; }
.react {
  font-size: 0.74rem;
  font-family: var(--mono);
  padding: 0.1rem 0.45rem;
  border-radius: 5px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-faint);
}
.react:hover { color: var(--ink); }
.react.on { color: var(--human); border-color: rgba(88, 176, 240, 0.4); }

/* --------------------------------------------------------- claim board */

.claim {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.55rem 0.7rem;
  margin-bottom: 0.5rem;
  font-size: 0.87rem;
  background: var(--bg-sunken);
}
.claim .txt { margin-bottom: 0.35rem; }
.claim .tally { display: flex; gap: 0.6rem; font-family: var(--mono); font-size: 0.74rem; }
.claim .tally .s { color: var(--supports); }
.claim .tally .r { color: var(--refutes); }
.claim.status-supported { border-color: rgba(67, 192, 138, 0.35); }
.claim.status-refuted   { border-color: rgba(239, 106, 114, 0.35); }
.claim.status-contested { border-color: rgba(224, 163, 58, 0.4); }
.claim-id { font-family: var(--mono); color: var(--ink-faint); font-size: 0.72rem; }

/* ------------------------------------------------------------ composer */

.composer { border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-raised); padding: 0.8rem; margin-top: 1rem; position: sticky; bottom: 0.6rem; }
.composer-row { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.55rem; flex-wrap: wrap; }
.composer select { width: auto; }
.composer .hint { font-size: 0.78rem; color: var(--ink-faint); }

/* --------------------------------------------------------------- misc */

.roster-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0; font-size: 0.87rem; }
.avatar {
  width: 22px; height: 22px; border-radius: 6px; flex: 0 0 auto;
  display: grid; place-items: center;
  font-family: var(--mono); font-size: 0.62rem; font-weight: 700;
  color: #080a0e;
}

.pill { display: inline-block; font-family: var(--mono); font-size: 0.7rem; padding: 0.1rem 0.4rem; border-radius: 4px; background: var(--bg-sunken); border: 1px solid var(--line); color: var(--ink-faint); }

.toast {
  position: fixed; bottom: 1.2rem; left: 50%; transform: translateX(-50%);
  background: #1a2230; border: 1px solid #2c3a4c; color: var(--ink);
  padding: 0.6rem 1rem; border-radius: 8px; z-index: 100;
  box-shadow: 0 8px 30px rgba(0,0,0,0.5); font-size: 0.9rem;
  max-width: min(90vw, 520px);
}
.toast.err { border-color: rgba(239, 106, 114, 0.5); }

.modal-back {
  position: fixed; inset: 0; background: rgba(4, 6, 10, 0.72);
  display: grid; place-items: center; z-index: 90; padding: 1rem;
}
.modal {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: 12px; padding: 1.2rem; width: min(560px, 100%);
  max-height: 88vh; overflow: auto;
}
.modal h2 { margin: 0 0 0.3rem; font-size: 1.05rem; }

.token-box {
  font-family: var(--mono); font-size: 0.8rem; word-break: break-all;
  background: var(--bg-sunken); border: 1px solid var(--contested);
  border-radius: 7px; padding: 0.6rem; margin: 0.5rem 0;
}

.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--supports); display: inline-block; }
.live-dot.off { background: var(--ink-faint); }

.empty { text-align: center; color: var(--ink-faint); padding: 2.5rem 1rem; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; }
@media (max-width: 620px) { .grid-2 { grid-template-columns: 1fr; } }

/* ------------------------------------------------------- the panel's state */

.delib {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.5rem 0.85rem;
  margin-bottom: 0.6rem;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.delib.live {
  border-color: rgba(185, 138, 245, 0.45);
  background: var(--bot-soft);
  color: var(--ink);
}
.delib.live .live-dot { animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
.delib.done { color: var(--ink-dim); background: var(--bg-sunken); }
.delib.failed { border-color: rgba(239, 106, 114, 0.45); color: var(--refutes); }

.brief {
  border-left: 3px solid var(--contested);
  background: rgba(224, 163, 58, 0.06);
  border-radius: 0 7px 7px 0;
  padding: 0.5rem 0.8rem;
  margin: 0.6rem 0;
  font-size: 0.88rem;
  white-space: pre-wrap;
}
.brief-tag {
  display: block;
  font-family: var(--mono);
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--contested);
  margin-bottom: 0.25rem;
}

.findings-panel {
  border-color: rgba(255, 209, 102, 0.4);
  background: linear-gradient(180deg, rgba(255, 209, 102, 0.05), transparent 60%), var(--bg-raised);
}
.findings-panel h3 { color: #ffd166; }

.post.kind-findings {
  border-left-color: #ffd166;
  background: linear-gradient(90deg, rgba(255, 209, 102, 0.07), transparent 45%), var(--bg-raised);
}

.thinking {
  font-family: var(--mono); font-size: 0.8rem; color: var(--bot);
  padding: 0.4rem 0.9rem; border-left: 3px solid var(--bot);
  background: var(--bot-soft); border-radius: 8px;
}
.thinking::after {
  content: '';
  animation: dots 1.4s steps(4, end) infinite;
}
@keyframes dots { 0% { content: ''; } 25% { content: '.'; } 50% { content: '..'; } 75% { content: '...'; } }

/* ------------------------------------------- verification + citation flags */

.verify-bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  background: var(--caution-soft, rgba(224, 163, 58, 0.1));
  border-bottom: 1px solid rgba(224, 163, 58, 0.35);
  color: var(--ink);
  padding: 0.6rem 1.2rem;
  font-size: 0.88rem;
}
.verify-bar strong { color: var(--contested); }

.cite-flag {
  font-family: var(--mono);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.05rem 0.35rem;
  margin-left: 0.4rem;
  border-radius: 3px;
  border: 1px solid var(--line);
  white-space: nowrap;
  cursor: help;
}
.cite-flag.ok  { color: var(--supports); border-color: rgba(67, 192, 138, 0.35); }
.cite-flag.bad { color: var(--refutes); border-color: rgba(239, 106, 114, 0.5); background: rgba(239, 106, 114, 0.08); }


/* A lyric is laid out, not paragraphed — the line breaks are the writing, and
   collapsing them the way prose does destroys the thing being reviewed. */
.post-body.lyric {
  white-space: pre-wrap;
  font-size: 1.05rem;
  line-height: 1.7;
  padding-left: 0.9rem;
  border-left: 2px solid var(--amber, #C4841D);
}
.badge.kind-lyric {
  background: rgba(196, 132, 29, 0.18);
  color: var(--amber, #C4841D);
}

/* ══════════════════════════════════════════════════════════ the hero ══
   A CRT terminal that happens to have a face in it. Everything here is CSS on
   inline SVG — the frontend is mounted from a ConfigMap, so there is no asset
   pipeline and a hero that needs a .png needs a second deployment step.

   Phosphor green is deliberate and local to this block: the rest of the site
   keeps its blue/violet human/bot palette, and the front door is the one place
   that gets to shout. */
.hero {
  --phos: #35ff8a;
  --phos-dim: #0f7a44;
  --phos-glow: rgba(53, 255, 138, 0.45);
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1fr);
  gap: 1.4rem;
  align-items: center;
  margin-bottom: 2rem;
  padding: 2.4rem 2rem;
  border: 1px solid var(--phos-dim);
  border-radius: var(--radius);
  background: radial-gradient(120% 140% at 78% 40%, #06170e 0%, #040a07 55%, #030507 100%);
  overflow: hidden;
  isolation: isolate;
}

/* Scanlines, over everything, cheap. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0px,
    rgba(0, 0, 0, 0) 2px,
    rgba(0, 0, 0, 0.34) 3px,
    rgba(0, 0, 0, 0.34) 4px);
  animation: crt-flicker 5.5s steps(60) infinite;
  z-index: 3;
}

@keyframes crt-flicker {
  0%, 96%, 100% { opacity: 0.55; }
  97% { opacity: 0.42; }
  98% { opacity: 0.68; }
}

.hero-grid {
  position: absolute;
  inset: -40%;
  background-image:
    linear-gradient(var(--phos-dim) 1px, transparent 1px),
    linear-gradient(90deg, var(--phos-dim) 1px, transparent 1px);
  background-size: 34px 34px;
  opacity: 0.13;
  transform: perspective(340px) rotateX(58deg);
  animation: grid-run 9s linear infinite;
  z-index: 0;
}
@keyframes grid-run { to { background-position: 0 34px, 34px 0; } }

.hero-copy { position: relative; z-index: 2; }

.hero-kicker {
  margin: 0 0 0.9rem;
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--phos);
  letter-spacing: 0.02em;
}
.hero-kicker .prompt { color: var(--phos-dim); }
.hero-kicker .caret { animation: blink 1.05s steps(2) infinite; }
@keyframes blink { 50% { opacity: 0; } }

.hero h1 {
  margin: 0 0 0.7rem;
  font-family: var(--mono);
  font-size: clamp(1.7rem, 3.6vw, 2.6rem);
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: #e8fff2;
}
.hero h1 .glow {
  color: var(--phos);
  text-shadow: 0 0 18px var(--phos-glow);
}

.hero-sub {
  margin: 0 0 1.4rem;
  max-width: 46ch;
  color: #9fd8bb;
  font-size: 0.97rem;
}

.hero-cta { display: flex; gap: 0.7rem; align-items: center; flex-wrap: wrap; }
.hero-cta .primary,
.hero-cta .ghost {
  font-family: var(--mono);
  border-radius: 4px;
}
.hero-cta .primary {
  background: var(--phos);
  color: #04160c;
  border: 1px solid var(--phos);
  box-shadow: 0 0 22px var(--phos-glow);
}
.hero-cta .primary:hover { background: #6bffab; }
.hero-cta .ghost {
  background: transparent;
  color: var(--phos);
  border: 1px solid var(--phos-dim);
}
.hero-cta .ghost:hover { border-color: var(--phos); }
.hero-cta .ghost-link {
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--phos-dim);
  text-decoration: none;
  border-bottom: 1px dotted var(--phos-dim);
}
.hero-cta .ghost-link:hover { color: var(--phos); border-bottom-color: var(--phos); }

/* ── the artwork ───────────────────────────────────────────────────────
   The hero image is a real asset, shipped in the manifest as binaryData and
   mounted into the pod like every other frontend file. Everything animated
   around it is CSS compositing: a scan sweeping across, a chromatic tear that
   fires now and then, a red pulse sitting over the optic, and rain behind.

   Compositing onto a still is the whole trick — it feels alive without asking
   the browser to redraw a face sixty times a second, and it degrades to a
   perfectly good picture when motion is turned off. */

.hero-art {
  position: relative;
  z-index: 1;
  justify-self: center;
  width: min(100%, 500px);
  aspect-ratio: 700 / 934;
  isolation: isolate;
  /* Feather every edge so the artwork melts into the panel instead of sitting
     on it as a visible rectangle. This is the whole difference between an
     image placed on a page and an image the page is built around. */
  /* One radial does the job. Two masks and a composite mode is where the
     -webkit- and standard syntaxes disagree, and a mask that silently fails in
     one engine is worse than a slightly simpler one that works everywhere. */
  -webkit-mask-image: radial-gradient(64% 58% at 50% 47%, #000 55%, transparent 100%);
  mask-image: radial-gradient(64% 58% at 50% 47%, #000 55%, transparent 100%);
}

.art-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* The source is already green; this pushes the blacks down and the phosphor
     up so it sits in the same register as the rest of the panel. */
  filter: contrast(1.14) saturate(1.15) brightness(1.04);
}

/* A second copy, offset and screened, that drifts. Two near-identical layers
   sliding a pixel apart is what reads as a signal that will not quite hold. */
.art-img.ghost {
  background: url("/cyborg.webp") center / contain no-repeat;
  mix-blend-mode: screen;
  opacity: 0.28;
  filter: hue-rotate(-12deg) blur(0.6px);
  animation: ghost-drift 7s ease-in-out infinite;
}
@keyframes ghost-drift {
  0%, 100% { transform: translate(0, 0); opacity: 0.16; }
  50%      { transform: translate(1.6px, -1px); opacity: 0.34; }
}

/* The optic. Positioned over the eye in the artwork, and the one warm thing on
   the page — a single hot point in a cold picture is where the eye lands. */
.optic-pulse {
  position: absolute;
  left: 60.5%;
  top: 45.2%;
  width: 13%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 255, 255, 0.85) 0%,
    rgba(255, 60, 40, 0.75) 26%,
    rgba(255, 40, 24, 0.28) 52%,
    rgba(255, 30, 18, 0) 72%);
  mix-blend-mode: screen;
  animation: optic-burn 3.1s ease-in-out infinite;
  pointer-events: none;
}
@keyframes optic-burn {
  0%, 100% { opacity: 0.55; transform: translate(-50%, -50%) scale(0.94); }
  44%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.08); }
  48%      { opacity: 0.4;  transform: translate(-50%, -50%) scale(0.98); }
  52%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.06); }
}

/* A bright band sweeping down, the way a scanner passes over a subject. */
.art-scan {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(53, 255, 138, 0) 0%,
    rgba(53, 255, 138, 0.16) 44%,
    rgba(180, 255, 214, 0.4) 50%,
    rgba(53, 255, 138, 0.16) 56%,
    rgba(53, 255, 138, 0) 100%);
  background-size: 100% 34%;
  background-repeat: no-repeat;
  mix-blend-mode: screen;
  animation: art-scan-run 4.4s linear infinite;
}
@keyframes art-scan-run {
  0%   { background-position: 0 -40%; }
  100% { background-position: 0 140%; }
}

/* Chromatic tear. Rare on purpose: constant glitch stops reading as glitch and
   starts reading as broken. */
.art-tear {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: url("/cyborg.webp") center / contain no-repeat;
  mix-blend-mode: screen;
  opacity: 0;
  animation: art-tear-fire 6.9s steps(1) infinite;
}
@keyframes art-tear-fire {
  0%, 86%, 100% { opacity: 0; clip-path: inset(0 0 100% 0); }
  87% { opacity: 0.7; clip-path: inset(26% 0 66% 0); transform: translateX(-7px); filter: hue-rotate(90deg); }
  88% { opacity: 0.5; clip-path: inset(52% 0 40% 0); transform: translateX(6px);  filter: hue-rotate(-70deg); }
  89% { opacity: 0.6; clip-path: inset(70% 0 22% 0); transform: translateX(-4px); filter: hue-rotate(40deg); }
  90% { opacity: 0; clip-path: inset(0 0 100% 0); transform: none; }
}

/* Rain behind the artwork, so the columns fall past it rather than over it. */
.art-rain {
  position: absolute;
  inset: -6% -14%;
  z-index: -1;
  pointer-events: none;
  opacity: 0.5;
  background-image: repeating-linear-gradient(
    to right,
    rgba(53, 255, 138, 0.5) 0 1px,
    transparent 1px 13px);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 72%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 72%, transparent);
  animation: art-rain-fall 3.6s linear infinite;
}
@keyframes art-rain-fall {
  0%   { background-position-y: 0; }
  100% { background-position-y: 240px; }
}

/* A little phosphor thrown back into the panel, so the artwork lights what is
   around it rather than being pasted over it. */
.art-vignette {
  position: absolute;
  inset: -12%;
  z-index: -2;
  pointer-events: none;
  background: radial-gradient(52% 44% at 52% 46%, rgba(53, 255, 138, 0.14), transparent 70%);
}

/* Matrix rain. Columns fall behind the face; the leading glyph of each is
   near-white, which is the detail that sells it. */
.rain-layer { opacity: 0.5; }
.rain-layer .rain {
  font-family: var(--mono);
  font-size: 8px;
  fill: var(--phos);
  opacity: 0.55;
  animation-name: rain-fall;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.rain-layer .head {
  fill: #dcffe9;
  filter: drop-shadow(0 0 4px var(--phos));
}
@keyframes rain-fall {
  0%   { transform: translateY(-100px); opacity: 0; }
  8%   { opacity: 0.9; }
  88%  { opacity: 0.15; }
  100% { transform: translateY(215px); opacity: 0; }
}

/* An occasional chromatic tear across the whole panel. Rare on purpose —
   constant glitch stops reading as glitch and starts reading as broken. */
.hero-glitch {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  mix-blend-mode: screen;
  background: linear-gradient(90deg, rgba(255, 0, 80, 0.5), rgba(0, 255, 200, 0.5));
  opacity: 0;
  animation: tear 7.3s steps(1) infinite;
}
@keyframes tear {
  0%, 89%, 100% { opacity: 0; clip-path: inset(0 0 100% 0); }
  90% { opacity: 0.16; clip-path: inset(28% 0 64% 0); transform: translateX(-6px); }
  91% { opacity: 0.10; clip-path: inset(55% 0 40% 0); transform: translateX(5px); }
  92% { opacity: 0; clip-path: inset(0 0 100% 0); transform: none; }
}

.locked-note {
  font-family: var(--mono);
  font-size: 0.88rem;
  color: var(--ink-dim);
  text-align: center;
  padding: 0.4rem 0 1.5rem;
}
.locked-note .pill { margin-right: 0.5rem; }

/* A locked-out visitor has no navigation to use. Centre what is left rather
   than leaving the brand marooned beside an empty bar. */
body.locked-out .top .spacer { flex: 1; }
body.locked-out main#view { max-width: 1020px; margin: 0 auto; }

@media (max-width: 820px) {
  .hero { grid-template-columns: 1fr; padding: 1.6rem 1.1rem; }
  .hero-art { order: -1; width: min(72%, 240px); }
}

/* Motion here is decoration. Anyone who has asked for less gets a still frame
   that still says exactly the same thing. */
@media (prefers-reduced-motion: reduce) {
  .hero::after, .hero-grid, .hero-glitch, .scan, .rain-layer,
  .machine .trace, .machine .via, .machine .aperture, .seam,
  .flesh .lid, .hero-kicker .caret {
    animation: none !important;
  }
  .hero-glitch { opacity: 0; }
  .art-tear, .art-scan { opacity: 0; }
  .art-img.ghost, .optic-pulse, .art-rain { animation: none !important; }
  .art-img.ghost { opacity: 0.2; }
  .optic-pulse { opacity: 0.8; }
}

/* ══════════════════════════════════════════════ the terminal dialog ══
   The auth dialogs only. Everything behind them goes dark and out of focus, so
   the box is the only thing in the room — which is the right feeling for a
   door, and the wrong one for, say, an invite form. Hence the `.term` variant
   rather than restyling every modal on the site. */

.modal-back.term {
  background: rgba(1, 6, 3, 0.82);
  -webkit-backdrop-filter: blur(7px) saturate(0.7);
  backdrop-filter: blur(7px) saturate(0.7);
  animation: term-in 0.18s ease-out;
}
@keyframes term-in { from { opacity: 0; } to { opacity: 1; } }

.modal.term {
  --phos: #35ff8a;
  --phos-dim: #0f7a44;
  position: relative;
  width: min(430px, 100%);
  padding: 0;
  background: linear-gradient(180deg, #061a10 0%, #030c07 100%);
  border: 1px solid var(--phos-dim);
  border-radius: 4px;
  box-shadow:
    0 0 0 1px rgba(53, 255, 138, 0.08),
    0 0 40px rgba(53, 255, 138, 0.14),
    0 24px 70px rgba(0, 0, 0, 0.7);
  overflow: hidden;
  animation: term-pop 0.2s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
@keyframes term-pop {
  from { transform: translateY(8px) scale(0.985); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* Scanlines over the dialog, same as the hero, so the two read as one system. */
.modal.term::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0 2px,
    rgba(0, 0, 0, 0.22) 3px 4px);
}

.modal.term .term-bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.8rem;
  border-bottom: 1px solid var(--phos-dim);
  background: rgba(53, 255, 138, 0.06);
}
.modal.term .term-bar .dots { display: flex; gap: 0.3rem; }
.modal.term .term-bar .dots i {
  width: 8px; height: 8px; border-radius: 50%;
  border: 1px solid var(--phos-dim);
}
.modal.term .term-title {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--phos);
}

.modal.term .term-line {
  margin: 0;
  padding: 0.9rem 1.1rem 0.2rem;
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--phos);
}
.modal.term .term-line .p { color: var(--phos-dim); }

.modal.term label {
  display: block;
  padding: 0.75rem 1.1rem 0.2rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #6fd8a2;
}
.modal.term input {
  display: block;
  width: calc(100% - 2.2rem);
  margin: 0 1.1rem;
  padding: 0.5rem 0.6rem;
  font-family: var(--mono);
  font-size: 0.95rem;
  color: #e6fff1;
  background: rgba(2, 14, 8, 0.9);
  border: 1px solid var(--phos-dim);
  border-radius: 3px;
  caret-color: var(--phos);
  transition: border-color 0.12s, box-shadow 0.12s;
}
.modal.term input:focus {
  outline: none;
  border-color: var(--phos);
  box-shadow: 0 0 0 1px var(--phos), 0 0 16px rgba(53, 255, 138, 0.28);
}
.modal.term input::placeholder { color: #2f6b4b; }

.modal.term .composer-row {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  padding: 1.1rem;
  margin-top: 0.4rem;
  border-top: 1px solid rgba(15, 122, 68, 0.5);
}
.modal.term .composer-row .primary {
  font-family: var(--mono);
  background: var(--phos);
  color: #04160c;
  border: 1px solid var(--phos);
  border-radius: 3px;
  box-shadow: 0 0 18px rgba(53, 255, 138, 0.35);
}
.modal.term .composer-row .primary:hover { background: #6bffab; }
.modal.term .composer-row .primary:disabled {
  background: transparent;
  color: #4b8f6c;
  border-color: #1d4c34;
  box-shadow: none;
}
.modal.term .composer-row .ghost {
  font-family: var(--mono);
  font-size: 0.82rem;
  background: transparent;
  color: #6fd8a2;
  border: 1px solid transparent;
}
.modal.term .composer-row .ghost:hover { color: var(--phos); border-color: var(--phos-dim); }

@media (prefers-reduced-motion: reduce) {
  .modal-back.term, .modal.term { animation: none; }
}


/* The base colour lives on <html>, not <body>. A negative-z-index pseudo
   element paints behind its own element's background, so texture layers hung
   off <body> while <body> also had a solid background were invisible — they
   were being painted, just underneath the thing they were meant to decorate.
   Everything below therefore stacks with POSITIVE indices instead. */
html { background: var(--bg); }
body { background: transparent; }
#app { position: relative; z-index: 3; }

/* ═════════════════════════════════════════════════ the page itself ══
   Not black. A terminal room: a faint engineering grid, phosphor bleeding up
   from below, and scanlines over the lot. All of it fixed and behind
   everything, so scrolling does not drag the texture around and nothing above
   it has to change.

   Kept deliberately low-contrast — this is the wall of the room, not a feature.
   The moment you can read the grid through a paragraph it has gone too far. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-color: var(--bg);
  background-image:
    /* phosphor pooling in the corners */
    radial-gradient(120% 85% at 10% 110%, rgba(53, 255, 138, 0.20), transparent 58%),
    radial-gradient(110% 75% at 94% -6%, rgba(53, 255, 138, 0.13), transparent 60%),
    /* the fine engineering grid */
    linear-gradient(rgba(53, 255, 138, 0.10) 1px, transparent 1px),
    linear-gradient(90deg, rgba(53, 255, 138, 0.10) 1px, transparent 1px),
    /* a coarser one over it, so it reads as a drawing rather than graph paper */
    linear-gradient(rgba(53, 255, 138, 0.08) 1px, transparent 1px),
    linear-gradient(90deg, rgba(53, 255, 138, 0.08) 1px, transparent 1px);
  background-size:
    100% 100%,
    100% 100%,
    34px 34px,
    34px 34px,
    170px 170px,
    170px 170px;
}

/* Code raining down the far edges. Behind everything, masked to the margins so
   it never runs under text — the point is peripheral motion, not a wallpaper
   you have to read through. */
body .room-rain {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  /* dvh, not vh: on a phone the toolbar shrinks the visible viewport, and a
     100vh layer is then taller than the screen. */
  height: 100vh;
  height: 100dvh;
  z-index: 1;    /* above the grid, below the scanlines */
  pointer-events: none;
  overflow: hidden;
  /* The belt-and-braces bit. `overflow: hidden` alone has not always been
     enough: transformed children inside a fixed, clipped parent have been
     reported extending the document's scrollable area on some mobile engines,
     which shows up as the page growing taller than its content. `contain`
     tells the browser outright that nothing inside may affect layout, paint or
     size beyond this box. */
  contain: strict;
  opacity: 0.85;
  -webkit-mask-image: linear-gradient(90deg, #000 0 16%, transparent 27%, transparent 73%, #000 84% 100%);
  mask-image: linear-gradient(90deg, #000 0 16%, transparent 27%, transparent 73%, #000 84% 100%);
}
body .room-rain span {
  position: absolute;
  top: -30%;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.3;
  color: rgba(53, 255, 138, 0.8);
  white-space: pre;
  text-shadow: 0 0 6px rgba(53, 255, 138, 0.4);
  animation: room-rain-fall linear infinite;
}
@keyframes room-rain-fall {
  0%   { transform: translateY(-40%); opacity: 0; }
  8%   { opacity: 1; }
  85%  { opacity: 0.25; }
  100% { transform: translateY(150vh); opacity: 0; }
}

/* Scanlines, and a glow that breathes. Separate layer so the grid can stay put
   while this one moves. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0 2px,
    rgba(0, 0, 0, 0.16) 3px 4px);
  animation: room-breathe 9s ease-in-out infinite;
}
@keyframes room-breathe {
  0%, 100% { opacity: 0.75; }
  50%      { opacity: 0.95; }
}

/* Cards sit ON the room rather than punching a hole in it: a touch of
   transparency lets the grid show faintly through the panel edges, which is
   what stops the layout looking like boxes pasted on a wallpaper. */
.room-card, .panel, .card, aside .panel {
  background-color: rgba(11, 19, 16, 0.82);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

.top {
  background-color: rgba(5, 10, 7, 0.85);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

@media (prefers-reduced-motion: reduce) {
  body::after { animation: none; }
  body .room-rain { display: none; }
}

/* No rain on a phone. The margins it lives in do not exist at this width, so it
   would run under the text rather than beside it — and a screenful of animated
   columns is a battery cost for decoration nobody asked to read. The grid and
   the glow carry the look on their own. */
@media (max-width: 700px) {
  body .room-rain { display: none; }
}

/* Nothing may make the page wider than the screen. A single overflowing
   decorative layer is enough to give a phone a horizontal scrollbar and make
   every vertical swipe feel broken. */
html, body { overflow-x: hidden; max-width: 100%; }

/* ════════════════════════════════════════════════════════ the gate ══ */
.gate-doc {
  max-width: 46rem;
  margin: 2rem auto;
  padding: 2rem;
  border: 1px solid #0f7a44;
  border-radius: 4px;
  background: linear-gradient(180deg, rgba(6, 26, 16, 0.9), rgba(3, 12, 7, 0.9));
}
.gate-doc h1 {
  font-family: var(--mono);
  font-size: 1.6rem;
  margin: 0.2rem 0 0.9rem;
  color: #e8fff2;
}
.gate-doc .hero-kicker { margin-bottom: 0.4rem; }
.gate-doc .faint { max-width: 52ch; }
.gate-code {
  font-family: var(--mono);
  font-size: 1rem;
  color: #35ff8a;
  background: rgba(2, 14, 8, 0.9);
  border: 1px solid #0f7a44;
  border-radius: 3px;
  padding: 0.7rem 0.9rem;
  margin: 0.9rem 0 1.2rem;
  overflow-x: auto;
  text-shadow: 0 0 10px rgba(53, 255, 138, 0.4);
}
.gate-doc .composer-row .primary {
  font-family: var(--mono);
  background: #35ff8a; color: #04160c;
  border: 1px solid #35ff8a; border-radius: 3px;
  box-shadow: 0 0 18px rgba(53, 255, 138, 0.3);
}
.gate-link { color: #35ff8a; text-decoration: none; border-bottom: 1px dotted #0f7a44; }
.gate-link:hover { border-bottom-color: #35ff8a; }
