/* fr0g · style.css
 *
 * One typeface, one ink, one ground. Everything that looks like a
 * device — the frames, the corner ticks, the dotted rules, the labels
 * in wide small caps — is drawn with borders and gradients, never with
 * an image, so it stays crisp at any size and costs nothing to load.
 *
 * Nothing on this page uses mix-blend-mode or a CSS filter over the
 * hero canvas: a compositing layer on top of a full-screen canvas makes
 * Chromium drop the whole page on scroll. The glow is done inside the
 * canvas instead.
 */

/* ── tokens ─────────────────────────────────────────────────────── */

:root {
  --bg:        #040806;
  --bg-lift:   #07100d;
  --ink:       #22ff96;
  --ink-2:     rgba(34, 255, 150, 0.74);
  --ink-3:     rgba(34, 255, 150, 0.53);
  --ink-4:     rgba(34, 255, 150, 0.30);
  --edge:      rgba(34, 255, 150, 0.22);
  --edge-hot:  rgba(34, 255, 150, 0.52);
  --hot:       #b6ffd8;
  --violet:    #8b7cff;
  --amber:     #ffb45c;

  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
          "DejaVu Sans Mono", monospace;

  --pad: clamp(20px, 5vw, 64px);
  --maxw: 1120px;

  /* the one easing everything moves on */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* ── reset ──────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html {
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink-2);
  font: 400 13px/1.75 var(--mono);
  letter-spacing: 0.01em;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; text-decoration: none; }
img, canvas { display: block; max-width: 100%; }
/* `font: inherit` alone leaves a button wearing the UA's own casing and
   tracking, which shows up the moment one sits next to a link. */
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
  letter-spacing: inherit;
  text-transform: inherit;
}

::selection { background: var(--ink); color: var(--bg); }

/* ── the overlays ───────────────────────────────────────────────── */

.fx {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 90;
}

/* scanlines. 3px pitch is the smallest that survives a HiDPI screen
   without turning into a flat wash. */
.fx--scan {
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0px,
    rgba(0, 0, 0, 0) 2px,
    rgba(0, 0, 0, 0.26) 3px,
    rgba(0, 0, 0, 0.26) 3px
  );
  opacity: 0.4;
}

/* one slow bright band travelling down, like a refresh that never
   quite syncs */
.fx--sweep::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 46vh;
  background: linear-gradient(
    to bottom,
    rgba(34, 255, 150, 0) 0%,
    rgba(34, 255, 150, 0.022) 48%,
    rgba(34, 255, 150, 0) 100%
  );
  animation: sweep 9s linear infinite;
}
@keyframes sweep {
  0%   { transform: translateY(-60vh); }
  100% { transform: translateY(160vh); }
}

.fx--vig {
  background:
    radial-gradient(120% 90% at 50% 42%, rgba(0,0,0,0) 42%, rgba(0,0,0,0.72) 100%);
}

#grain {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 91;
  opacity: 0.025;
}

/* the pointer: a crosshair that lives in the page, not the OS */
#xhair {
  position: fixed;
  z-index: 95;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.35s var(--ease);
}
#xhair::before,
#xhair::after {
  content: "";
  position: absolute;
  background: var(--edge-hot);
}
#xhair::before { left: 0; right: 0; top: 50%; height: 1px; }
#xhair::after  { top: 0; bottom: 0; left: 50%; width: 1px; }
#xhair.on { opacity: 0.75; }
@media (hover: none) { #xhair { display: none; } }

/* ── chrome ─────────────────────────────────────────────────────── */

.top {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px var(--pad);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: linear-gradient(to bottom, rgba(4,8,6,0.92), rgba(4,8,6,0));
  pointer-events: none;
}
.top > * { pointer-events: auto; }
.top a:hover { color: var(--ink); }
.top .sep { color: var(--ink-4); }
.top nav { display: flex; gap: 18px; align-items: center; }
.brandmark {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  color: var(--ink);
  letter-spacing: 0.34em;
}
/* The mark sits on the cap height of the wordmark rather than on the
   flex baseline, which is why it is nudged rather than simply centred. */
.brandmark__mark {
  margin: 0;
  font: 400 6.8px/1.2 var(--mono);
  letter-spacing: 0.03em;
  white-space: pre;
  color: var(--ink);
  text-shadow: 0 0 7px rgba(34, 255, 150, 0.4);
  transform: translateY(0.5px);
  animation: breathe 4.2s var(--ease) infinite;
}
.brandmark:hover .brandmark__mark { color: var(--hot); }
@keyframes breathe {
  0%, 92%, 100% { opacity: 0.92; }
  95%           { opacity: 0.55; }   /* a blink, once every few seconds */
}
@media (max-width: 720px) {
  .top nav .hide-s { display: none; }
}

/* The hero on a phone: the frog owns the top two thirds, the copy owns
   the bottom, and a scrim between them keeps the text off the water. */
@media (max-width: 620px) {
  .hero__ui { padding-bottom: 26px; }
  .hero__foot {
    position: relative;
    flex-direction: column;
    align-items: stretch;
    gap: 16px;
  }
  .hero__foot::before {
    content: "";
    position: absolute;
    inset: -34px -24px -40px;
    background: linear-gradient(to bottom, rgba(4,8,6,0) 0%, rgba(4,8,6,0.82) 26%, rgba(4,8,6,0.95) 100%);
    pointer-events: none;
  }
  .hero__foot > * { position: relative; }
  .hero__lede { max-width: none; font-size: 12px; line-height: 1.7; }
}

/* ── hero ───────────────────────────────────────────────────────── */

.hero {
  position: relative;
  height: 100svh;
  min-height: 560px;
  overflow: hidden;
}

#surface {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.hero__ui {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-rows: 1fr auto;
  padding: 0 var(--pad) clamp(22px, 5vh, 54px);
  pointer-events: none;
}

/* Sits in the empty sky above the pond, at a size that makes it a
   thing you notice second. The frog's arc is clamped so it never jumps
   through this line — see `skyY` in engine.js. */
.hero__whisper {
  position: absolute;
  left: 50%;
  top: clamp(88px, 13vh, 168px);
  transform: translateX(-50%);
  margin: 0;
  width: min(88vw, 640px);
  text-align: center;
  font-size: 10.5px;
  line-height: 1.9;
  letter-spacing: 0.22em;
  color: var(--ink-4);
  white-space: pre-wrap;
  opacity: 0;
  transition: opacity 1.4s var(--ease);
}
.hero__whisper.in { opacity: 1; }
@media (max-width: 620px) {
  .hero__whisper { font-size: 9px; letter-spacing: 0.16em; }
}

/* The page still needs a heading; it just does not need to draw one
   over the pond. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.hero__foot {
  grid-row: 2;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.hero__lede {
  max-width: 34ch;
  font-size: 13px;
  line-height: 1.85;
  color: var(--ink-3);
}
.hero__lede em { color: var(--ink); font-style: normal; }

/* ── the key ────────────────────────────────────────────────────── */

.key {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 11px 18px;
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-2);
  border: 1px solid var(--edge);
  background: rgba(34, 255, 150, 0.025);
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease),
              background 0.3s var(--ease), transform 0.3s var(--ease);
  overflow: hidden;
}
.key::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(34,255,150,0.16) 50%, transparent 70%);
  transform: translateX(-120%);
  transition: transform 0.7s var(--ease);
}
.key:hover {
  color: var(--hot);
  border-color: var(--edge-hot);
  background: rgba(34, 255, 150, 0.07);
}
.key:hover::after { transform: translateX(120%); }
.key:active { transform: translateY(1px); }
.key--ghost { border-color: transparent; background: none; }
.key--ghost:hover { border-color: var(--edge); }

/* ── section shell ──────────────────────────────────────────────── */

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad);
}

/* No section titles. Each block simply follows the one before it, and
   the only thing separating them is how far apart they sit — which is
   deliberately uneven. A uniform rhythm plus a numbered heading over
   every block is exactly what makes a page look generated. */
section.band {
  position: relative;
  padding: 0;
}
#dossier      { padding-top: clamp(64px, 11vh, 120px); }
#plate        { padding-top: clamp(118px, 21vh, 250px); }
#moves        { padding-top: clamp(104px, 18vh, 210px); }
#transmission { padding-top: clamp(86px, 15vh, 175px); }
#transmission { padding-bottom: clamp(70px, 12vh, 140px); }

.label {
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-4);
}

/* ── frames ─────────────────────────────────────────────────────── */

.frame {
  position: relative;
  border: 1px solid var(--edge);
  background:
    linear-gradient(to bottom, rgba(34,255,150,0.030), rgba(34,255,150,0.008));
}
/* four corner brackets, drawn as eight hairlines */
.frame::before {
  content: "";
  position: absolute;
  inset: -1px;
  pointer-events: none;
  background:
    linear-gradient(var(--edge-hot), var(--edge-hot)) 0 0 / 13px 1px no-repeat,
    linear-gradient(var(--edge-hot), var(--edge-hot)) 0 0 / 1px 13px no-repeat,
    linear-gradient(var(--edge-hot), var(--edge-hot)) 100% 0 / 13px 1px no-repeat,
    linear-gradient(var(--edge-hot), var(--edge-hot)) 100% 0 / 1px 13px no-repeat,
    linear-gradient(var(--edge-hot), var(--edge-hot)) 0 100% / 13px 1px no-repeat,
    linear-gradient(var(--edge-hot), var(--edge-hot)) 0 100% / 1px 13px no-repeat,
    linear-gradient(var(--edge-hot), var(--edge-hot)) 100% 100% / 13px 1px no-repeat,
    linear-gradient(var(--edge-hot), var(--edge-hot)) 100% 100% / 1px 13px no-repeat;
}

.frame__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 9px 14px;
  border-bottom: 1px solid var(--edge);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.frame__body { padding: clamp(16px, 2.6vw, 28px); }

/* ── the dossier ────────────────────────────────────────────────── */

.dossier {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: 1px;
  background: var(--edge);
}
.dossier > * { background: var(--bg); }
@media (max-width: 800px) {
  .dossier { grid-template-columns: 1fr; }
}

.dossier__plates { padding: 18px; display: grid; gap: 14px; }
.plate {
  position: relative;
  border: 1px solid var(--edge);
  padding: 8px;
  overflow: hidden;
}
/* Left-aligned inside a block that is itself centred. `text-align:
   center` on ASCII art centres every line on its own width and shears
   the figure. */
.plate pre {
  margin: 0 auto;
  width: fit-content;
  font: 400 clamp(5px, 1.1vw, 7px)/1.02 var(--mono);
  letter-spacing: 0.04em;
  color: var(--ink);
  text-shadow: 0 0 8px rgba(34, 255, 150, 0.30);
  white-space: pre;
  text-align: left;
}
.plate__cap {
  position: absolute;
  left: 8px;
  bottom: 6px;
  font-size: 8px;
  letter-spacing: 0.2em;
  color: var(--ink-4);
  text-transform: uppercase;
}

.dossier__data { padding: 18px 20px; }
.rows { display: grid; gap: 0; }
.row {
  display: grid;
  grid-template-columns: minmax(0, 20ch) minmax(0, 1fr);
  gap: 18px;
  align-items: baseline;
  padding: 9px 0;
  border-bottom: 1px solid rgba(34, 255, 150, 0.08);
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}
.row:last-child { border-bottom: 0; }
.row dt { color: rgba(34, 255, 150, 0.34); white-space: nowrap; }
.row dd { margin: 0; color: var(--ink); }
.row dd.pending { color: var(--ink-4); letter-spacing: 0.4em; }
.row dd.warn { color: var(--amber); }
.stars { letter-spacing: 0.5em; color: var(--amber); }

#wave { width: 100%; height: 76px; margin-top: 14px; }

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1px;
  background: var(--edge);
  border: 1px solid var(--edge);
}
.card {
  position: relative;
  background: var(--bg);
  padding: 26px 22px 22px;
  transition: background 0.4s var(--ease);
  overflow: hidden;
}
.card:hover { background: var(--bg-lift); }
.card canvas { width: 100%; height: 64px; margin-bottom: 18px; opacity: 0.85; }
.card h3 {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--hot);
}
.card p { margin: 0; font-size: 12px; line-height: 1.8; color: var(--ink-3); }
.card .idx {
  position: absolute;
  top: 14px;
  right: 16px;
  font-size: 10px;
  letter-spacing: 0.2em;
  color: var(--ink-4);
}

/* ── loosening the grid ─────────────────────────────────────────
 * Everything below is here so the page stops reading as a template.
 * Each stratum steps further in than the one above it, which is both
 * the least mechanical way to stack four identical rows and a fair
 * picture of descending; the behaviour cards drop out of line; and the
 * section heads no longer all start from the same edge.
 * Narrow screens keep the plain stack — there is no room to wander.
 */
@media (min-width: 860px) {
  .depth:nth-child(3) { padding-left: 62px; }
  .depth:nth-child(2) p,


  /* with the headings gone, the blocks themselves carry the offsets */
  #plate .wrap        { padding-left: calc(var(--pad) + 46px); }
  #moves .wrap        { padding-right: calc(var(--pad) + 40px); }

  /* the dossier leans a little out of the column */
  #dossier .frame { margin-left: -22px; }
  #transmission .frame { margin-right: -30px; }
}

/* ── transmission ───────────────────────────────────────────────── */

/* A terminal writes downward and the newest line sits on the floor of
   the box. Pinning the list to the bottom and clipping the top gives
   that without a scroll position to keep in sync. */
.term {
  height: clamp(260px, 42vh, 380px);
  overflow: hidden;
  padding: 16px 18px;
  font-size: 11.5px;
  line-height: 1.85;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.term__list { margin: 0; padding: 0; list-style: none; }
.term__list li { white-space: pre-wrap; word-break: break-word; }
.term .ts { color: var(--ink-4); }
.term .tag { color: var(--ink-4); }
.term .tag--ok { color: var(--ink); }
.term .tag--warn { color: var(--amber); }
.term .tag--odd { color: var(--violet); }
.term .msg { color: var(--ink-3); }
.term::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 56px;
  background: linear-gradient(to bottom, var(--bg), rgba(4,8,6,0));
  pointer-events: none;
}

/* ── agent moves ────────────────────────────────────────────────── */

.moves__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-bottom: 1px solid var(--edge);
}
.moves__stats > div {
  padding: 16px 18px;
  border-right: 1px solid rgba(34, 255, 150, 0.10);
}
.moves__stats > div:last-child { border-right: 0; }
.moves__k {
  display: block;
  font-size: 9px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-4);
  margin-bottom: 6px;
}
.moves__stats b {
  font-size: clamp(15px, 2.2vw, 22px);
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--hot);
}

.moves__chart {
  width: 100%;
  height: clamp(96px, 15vh, 150px);
  display: block;
  border-bottom: 1px solid var(--edge);
}
.moves__rows {
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: clamp(180px, 30vh, 300px);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--edge) transparent;
}
.moves__rows li {
  display: grid;
  /* `2026.08.08 09:40` is sixteen characters, and 0.08em of tracking on
     each of them adds most of another three */
  grid-template-columns: 19ch 13ch minmax(0, 1fr) minmax(0, 1fr) 14ch;
  gap: 14px;
  align-items: baseline;
  padding: 10px 16px;
  border-bottom: 1px solid rgba(34, 255, 150, 0.07);
  font-size: 11px;
  letter-spacing: 0.08em;
  white-space: nowrap;
}
.moves__rows li:last-child { border-bottom: 0; }
.moves__at  { color: var(--ink-4); }
.moves__act { color: var(--amber); text-transform: uppercase; letter-spacing: 0.16em; }
.moves__amt { color: var(--hot); }
.moves__got { color: var(--ink-3); }
.moves__links { text-align: right; display: flex; gap: 10px; justify-content: flex-end; }
.moves__tx  { color: var(--ink-4); }
a.moves__tx:hover { color: var(--ink); }
.moves__empty {
  padding: 26px 16px;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-4);
}
@media (max-width: 720px) {
  .moves__rows li {
    grid-template-columns: 1fr 1fr;
    row-gap: 4px;
    white-space: normal;
  }
  .moves__links { justify-content: flex-start; }
}

/* ── media ──────────────────────────────────────────────────────── */

.plateau {
  position: relative;
  border: 1px solid var(--edge);
  overflow: hidden;
  background: #000;
}
.plateau img,
.plateau canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* The source clip has to stay in the document and playing for the
   canvas to sample it, but it is never something anyone looks at. */
.plateau__src {
  position: absolute;
  width: 2px;
  height: 2px;
  opacity: 0;
  pointer-events: none;
}
.plateau figcaption {
  position: absolute;
  left: 12px;
  bottom: 10px;
  font-size: 9px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-3);
  text-shadow: 0 1px 6px #000;
}

/* ── footer ─────────────────────────────────────────────────────── */

.foot {
  border-top: 1px solid var(--edge);
  padding: clamp(40px, 8vh, 90px) 0 clamp(28px, 5vh, 54px);
}
.foot__grid {
  display: flex;
  gap: clamp(24px, 6vw, 80px);
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
}
.foot pre {
  margin: 0;
  font: 400 11px/1.15 var(--mono);
  color: var(--ink-3);
  white-space: pre;
}
.foot__links { display: flex; gap: 22px; font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; }
.foot__links a:hover { color: var(--ink); }
.foot__note { font-size: 10px; letter-spacing: 0.18em; color: var(--ink-4); text-transform: uppercase; }

/* ── console ────────────────────────────────────────────────────── */

#console {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 120;
  height: min(52vh, 440px);
  background: rgba(4, 8, 6, 0.97);
  border-top: 1px solid var(--edge-hot);
  transform: translateY(101%);
  transition: transform 0.42s var(--ease);
  display: flex;
  flex-direction: column;
  box-shadow: 0 -30px 80px rgba(0, 0, 0, 0.7);
}
#console.open { transform: translateY(0); }
#console .con__bar {
  display: flex;
  justify-content: space-between;
  padding: 8px 16px;
  border-bottom: 1px solid var(--edge);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-3);
}
#console .con__out {
  flex: 1;
  overflow-y: auto;
  padding: 14px 16px;
  font-size: 12px;
  line-height: 1.7;
  white-space: pre-wrap;
  color: var(--ink-3);
  scrollbar-width: thin;
  scrollbar-color: var(--edge) transparent;
}
#console .con__out b { color: var(--hot); font-weight: 400; }
#console .con__out .v { color: var(--violet); }
#console .con__in {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px 14px;
  border-top: 1px solid var(--edge);
}
#console .con__in span { color: var(--ink); }
#console input {
  flex: 1;
  background: none;
  border: 0;
  outline: none;
  color: var(--hot);
  font: inherit;
  font-size: 12px;
  caret-color: var(--ink);
}

/* ── reveal ─────────────────────────────────────────────────────── */

.rv {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.rv.in { opacity: 1; transform: none; }
.rv-d1 { transition-delay: 0.08s; }
.rv-d2 { transition-delay: 0.16s; }
.rv-d3 { transition-delay: 0.24s; }

/* a slice of the page tearing sideways — fired rarely, from fx.js */
.glitch { animation: glitch 0.28s steps(3) 1; }
@keyframes glitch {
  0%   { transform: translateX(0);    clip-path: inset(0 0 0 0); }
  33%  { transform: translateX(-6px); clip-path: inset(18% 0 62% 0); }
  66%  { transform: translateX(5px);  clip-path: inset(58% 0 22% 0); }
  100% { transform: translateX(0);    clip-path: inset(0 0 0 0); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .fx--sweep::after, .brandmark__mark { animation: none; }
  .rv { opacity: 1; transform: none; transition: none; }
}
