/* ============================================================
   FATHOM design system
   Kraft paper, drawn in ink. The tank is a sketch; the panels are cards
   resting on the same sheet, outlined rather than glazed.
   ============================================================ */

:root {
  /* ---- the sheet ---------------------------------------------------------
     Warm, low-chroma ground. Nothing here is allowed to reach full saturation:
     the moment a colour does, it stops reading as pigment on paper and starts
     reading as a screen. */
  --bg-0: #e7dcc3;
  --bg-1: #efe7d4;
  --bg-2: #e2d7bd;

  /* Panels are cards on the same sheet, not glass over a dark scene: a paper
     fill, a ruled edge, and the faintest lift. No blur — there is nothing
     behind them to blur, and the frosted look belongs to the old material. */
  --glass-bg: rgba(248, 242, 228, 0.90);
  --glass-border: rgba(74, 58, 40, 0.30);
  --glass-shadow: 0 1px 0 rgba(255, 255, 255, 0.55) inset, 0 2px 4px rgba(60, 46, 30, 0.09);
  --radius: 3px;
  --radius-sm: 2px;

  /* text: graphite, in three weights of pressure */
  --fg-1: #342719;
  --fg-2: #6a5943;
  --fg-3: #98876f;

  /* accents. One accent only — iron-gall ink — reserved for live state, so
     "something is happening now" is the single loudest thing on the page. */
  --accent: #2f4f6f;
  --accent-dim: rgba(47, 79, 111, 0.14);
  --accent-glow: rgba(47, 79, 111, 0.10);
  --danger: #9a3b2e;
  --amber: #a4671c;
  --gold: #8a6a1f;
  --silver: #7b7266;
  --bronze: #8a5a2b;

  /* Every border and divider derives from this one ruled line. */
  --rule: rgba(74, 58, 40, 0.30);
  --rule-soft: rgba(74, 58, 40, 0.15);

  /* type */
  --font-display: "Space Grotesk", "Inter", system-ui, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* rhythm */
  --sp: 4px;
  --ease: 220ms cubic-bezier(0.4, 0, 0.2, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  background: var(--bg-0);
  color: var(--fg-1);
  font-family: var(--font-body);
  font-size: 13px;
  font-feature-settings: 'ss01';
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Subtle grain over the whole site so it does not look flat. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  /* Paper stock. The old layer was a dark noise wash at 5% to break up a black
     screen; on a light sheet the same turbulence has to be much weaker and
     multiplied, or it reads as dirt rather than as fibre. */
  opacity: 0.30;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.6'/%3E%3C/svg%3E");
}

b { font-family: var(--font-display); font-variant-numeric: tabular-nums; font-weight: 500; }

/* ---------- canvas: the tank, absolute protagonist ---------- */
#world {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: crosshair;
  z-index: 0;
}
body.targeting #world { cursor: cell; }

/* ---------- world legend (what the dots mean) ---------- */
/* Docked at the foot of the leaderboard panel: floating it over the tank put it
   behind #board, which is opaque and taller, so it was never readable. */
#world-legend {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: calc(var(--sp) * 3);
  padding-top: calc(var(--sp) * 2.5);
  border-top: 1px solid var(--rule-soft);
  font-family: var(--font-display);
  font-size: 10.5px;
  line-height: 1.45;
  letter-spacing: 0.04em;
  color: var(--fg-3);
}
#world-legend[hidden] { display: none; }
#world-legend span { display: flex; align-items: flex-start; gap: 7px; }
#world-legend i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: none;
  margin-top: 3px;
}
/* The legend swatches are drawn the way the things they name are drawn: an ink
   ring for a creature, a filled ink dot for the whale, a small ringed seed for
   food, an ochre mark for a meteor. Glow is gone — it was doing the work of
   saying "these are lit", and on paper the weight of the mark says it instead. */
#world-legend .lg-creature {
  border: 1.4px solid hsl(28, 26%, 30%);
  background: hsla(200, 26%, 40%, 0.30);
}
#world-legend .lg-whale {
  width: 13px;
  height: 13px;
  margin-top: 1px;
  border: 1.4px solid hsl(36, 46%, 26%);
  background: hsla(36, 58%, 44%, 0.45);
}
.pay-badge {
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-3);
  border: 1px solid var(--rule);
}
.pay-badge[data-mode='x402'] {
  color: hsl(150, 30%, 30%);
  border-color: hsla(150, 30%, 30%, 0.5);
}
#world-legend .lg-food {
  border: 1px solid hsl(28, 24%, 34%);
  background: transparent;
}
#world-legend .lg-meteor {
  background: hsl(32, 62%, 42%);
  border: 1px solid hsl(28, 40%, 30%);
}

/* ---------- depth vignette over the tank ---------- */
#vignette {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Corner shading, as the sheet falls away from the light. A warmth rather
     than a shadow — darkening paper with black makes it look burnt. */
  background: radial-gradient(ellipse at 50% 45%, transparent 52%, rgba(120, 96, 62, 0.26) 100%);
}
body.view-observe #vignette { display: none; }
/* The frozen tank frame must not leak behind the observatory cards. */
body.view-observe #world { display: none; }
body.view-observe {
  /* The observatory is another sheet of the same paper, not a different room. */
  background:
    radial-gradient(1100px 520px at 18% -8%, rgba(150, 118, 72, 0.14), transparent 62%),
    radial-gradient(900px 480px at 84% 4%, rgba(120, 96, 62, 0.10), transparent 60%),
    var(--bg-0);
}

/* ---------- creature hover tooltip ---------- */
#tip {
  position: fixed;
  z-index: 40;
  pointer-events: none;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.5px;
  color: var(--fg-2);
  white-space: nowrap;
}
#tip[hidden] { display: none; }
#tip b { color: var(--fg-1); font-size: 11px; }
#tip .tip-base { color: var(--fg-3); font-size: 9px; }

/* ---------- glass primitive ---------- */
.glass {
  /* A card on the sheet: paper fill, one ruled edge, almost no lift. The old
     version was frosted glass over a dark scene — a look that needs darkness
     behind it to work, and there is none here. */
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  box-shadow: var(--glass-shadow);
  transition: transform var(--ease), box-shadow var(--ease);
}

::selection { background: rgba(47, 79, 111, 0.22); }
:focus-visible { outline: 1px solid rgba(47, 79, 111, 0.6); outline-offset: 2px; }

.rise { animation: rise 320ms ease both; }
@keyframes rise {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* ---------- top strip ---------- */
#topbar {
  position: fixed;
  top: calc(var(--sp) * 3);
  left: calc(var(--sp) * 3);
  right: calc(var(--sp) * 3);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: calc(var(--sp) * 6);
  padding: calc(var(--sp) * 2.5) calc(var(--sp) * 4);
  border-bottom: 1px solid transparent;
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.42), rgba(255, 255, 255, 0) 46%), linear-gradient(to right, transparent, rgba(47, 79, 111, 0.22), transparent);
  background-origin: border-box;
  background-clip: padding-box, border-box;
}
.brand {
  display: flex;
  /* Aligned to the top, not centred. The block is two lines tall and the mark
     is taller than either line, so centring would float it against empty space
     and break its alignment with the wordmark's cap height. */
  align-items: flex-start;
  gap: 9px;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: 3px;
  font-size: 15px;
  /* Set solid, in ink. The old wordmark was a cool gradient clipped to the
     glyphs, which only reads as a *title* against a dark ground; on paper the
     weight of the type carries it. */
  color: var(--fg-1);
  white-space: nowrap;
}
/* The wordmark's mark. Height is the dimension that matters — the drawing is
   0.745 as wide as it is tall — and it is set in em so the lockup survives any
   later change to the topbar's type size. 1.75em of 15px is 26px, which spans
   the two text lines without reaching past the sub-label's baseline. */
.brand .brand-mark {
  height: 1.75em;
  width: auto;
  flex: none;
  display: block;
  /* Optically nudged: the stroke's round cap leaves a hair of padding at the
     top of the drawing, which otherwise sits visibly high against the caps. */
  margin-top: -1px;
}
/* Holds the wordmark and its sub-label so the mark can sit beside the pair.
   The sub-label's own typography stays in the rule below, untouched. */
.brand .word {
  display: block;
  min-width: 0;
}
.brand .tag {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  letter-spacing: 0;
  font-size: 10px;
  color: var(--fg-3);
  margin-top: 1px;
}
/* Scrollable rather than clipped: the English labels (HARVEST / JUDGMENT) are
   long enough that `overflow: hidden` silently amputated the last stat on
   narrow windows, while the shorter CJK labels happened to fit. */
.stats {
  display: flex; align-items: center; gap: calc(var(--sp) * 5);
  flex: 1; min-width: 0;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.stats::-webkit-scrollbar { display: none; }
.stat {
  display: flex; align-items: center; gap: calc(var(--sp) * 1.5);
  font-size: 11px; color: var(--fg-3);
  white-space: nowrap;
  flex: 0 0 auto;
}
.stat b { color: var(--fg-1); font-size: 12px; }
.stat-k {
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--fg-3);
}
.tdot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  box-shadow: none;
  transition: background var(--ease), box-shadow var(--ease);
}
.livedot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #98876f;
  box-shadow: none;
}
.livedot.live { background: #5d7a3c; color: #5d7a3c; animation: pulse 1.6s ease-in-out infinite; }
.livedot.degraded { background: var(--amber); color: var(--amber); }
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
.rain-status { color: var(--fg-3); font-size: 9px; }
.controls { display: flex; align-items: center; gap: calc(var(--sp) * 3); }
.fps { font-size: 10px; color: var(--fg-3); }
.fps b { color: var(--accent); }

.icon-btn {
  background: transparent;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  color: var(--fg-3);
  font-size: 12px;
  padding: 3px 8px;
  cursor: pointer;
  transition: all var(--ease);
}
.icon-btn:hover { border-color: var(--accent); color: var(--fg-1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42); }
.icon-btn:active { transform: scale(0.97); }
.icon-btn.active { border-color: var(--accent); color: var(--accent); box-shadow: none; }

#causal-card {
  position: fixed;
  left: 50%;
  bottom: calc(var(--sp) * 16);
  transform: translateX(-50%);
  z-index: 35;
  max-width: min(560px, 86vw);
  padding: calc(var(--sp) * 2.5) calc(var(--sp) * 4);
  border-radius: 999px;
  font-size: 12px;
  color: var(--fg-1);
  text-align: center;
  pointer-events: none;
}
#causal-card[hidden] { display: none; }
#tax-chip {
  position: fixed;
  top: calc(var(--sp) * 17);
  left: 50%;
  transform: translateX(-50%);
  z-index: 34;
  padding: calc(var(--sp)) calc(var(--sp) * 3);
  border-radius: 999px;
  font-size: 10px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--danger);
  pointer-events: none;
}
#tax-chip[hidden] { display: none; }
.props {
  margin-top: calc(var(--sp) * 3);
  border-top: 1px solid var(--glass-border);
  padding-top: calc(var(--sp) * 2);
  font-size: 10px;
  color: var(--fg-3);
}
.props[hidden] { display: none; }
.props-h { letter-spacing: 1.2px; text-transform: uppercase; margin-bottom: calc(var(--sp)); }
.prop { display: flex; justify-content: space-between; gap: calc(var(--sp) * 2); padding: 1px 0; }
.prop span { flex: 0 0 auto; }
/* A value like a hash fragment must wrap inside a narrow column, not push the
   row wider than its drawer. */
.prop b { color: var(--fg-2); font-weight: 500; min-width: 0; text-align: right; overflow-wrap: anywhere; }
.props-y { margin-top: calc(var(--sp)); }
#pulse-play { margin-left: auto; }
.daily .prop, #reports .prop { padding: 1px 0; }
.memorial { padding: calc(var(--sp)) 0; border-bottom: 1px solid var(--glass-border); }
.memorial .epitaph { color: var(--fg-3); font-size: 9px; margin-top: 2px; }
/* badges, factions and the contribution board */
.chips { display: flex; flex-wrap: wrap; gap: 4px; margin: 3px 0; }
.chip {
  display: inline-flex; align-items: center; gap: 4px; padding: 1px 6px;
  border: 1px solid var(--glass-border); border-radius: 999px;
  background: rgba(74, 58, 40, 0.20); color: var(--fg-2);
  font-family: var(--font-display); font-size: 9px; line-height: 14px;
}
.chip.tiny { font-size: 8px; line-height: 12px; padding: 0 5px; }
.chip.badge { color: var(--amber); border-color: rgba(164, 103, 28, 0.25); }
.chip b { color: var(--fg-1); font-weight: 500; font-variant-numeric: tabular-nums; }
.chip .cdot { width: 5px; height: 5px; border-radius: 50%; background: var(--chip, var(--accent)); flex: 0 0 auto; }
button.chip.faction { cursor: pointer; transition: border-color var(--ease), color var(--ease); }
button.chip.faction:hover { border-color: var(--chip, var(--accent)); color: var(--fg-1); }
button.chip.faction.mine {
  border-color: var(--chip, var(--accent)); color: var(--fg-1);
  background: rgba(74, 58, 40, 0.20);
}
#burners .af { flex-wrap: wrap; cursor: pointer; }
#burners .af .chips { flex-basis: 100%; margin: 0 0 3px; }
#burners .af:hover .who { color: var(--fg-1); }
#burners .af.me .who { color: var(--accent); }
.who-link { color: var(--accent); cursor: pointer; border-bottom: 1px solid transparent; }
.who-link:hover { border-bottom-color: var(--accent); }
/* The memory of the previous answer, sitting under the standing it describes.
   Quieter than a stat line on purpose: it speaks about a stretch of time rather
   than about a number, and it is a sentence rather than a label. */
#me-since { margin-top: calc(var(--sp) * 2); }
.ms-head {
  font-family: var(--font-display); font-size: 9px; line-height: 14px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--fg-3);
}
.ms-more, .ms-none { font-size: 9px; color: var(--fg-3); }
#addr-tank { margin-top: calc(var(--sp) * 2); }
.rules-hint {
  margin-top: calc(var(--sp) * 2);
  font-size: 9px;
  line-height: 1.5;
  color: var(--fg-3);
}
#addr-link, #addr-world { margin-top: calc(var(--sp) * 2); }
/* The X link. Everything visual comes from .icon-btn; only the horizontal
   padding is narrowed, because .icon-btn pads 8px around a text label and this
   button's whole content is a 14px glyph. Padding it as if it held text leaves
   a visibly wider button than the others in the row. */
#x-link {
  display: inline-flex;
  align-items: center;
  padding-left: 6px;
  padding-right: 6px;
  text-decoration: none;
}
#x-link svg { display: block; }
.langs { position: relative; }
.lang-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  min-width: 118px;
  padding: calc(var(--sp));
}
.lang-menu[hidden] { display: none; }
.lang-menu button {
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--fg-2);
  font-size: 12px;
  text-align: left;
  padding: 5px 10px;
  cursor: pointer;
  transition: color var(--ease), background var(--ease);
}
.lang-menu button:hover { color: var(--fg-1); background: rgba(74, 58, 40, 0.20); }
.lang-menu button.active { color: var(--accent); }
.langs .icon-btn svg { display: block; }

/* ---------- floating panels ---------- */
.panel {
  position: fixed;
  top: calc(var(--sp) * 20);
  z-index: 10;
  width: 232px;
  /* The board now carries the legend too, so clamp it above the dock instead of
     letting it run off the bottom of short windows. */
  max-height: calc(100vh - var(--sp) * 34);
  overflow-y: auto;
  transition: width var(--ease);
}
#board { left: calc(var(--sp) * 3); }
#panel { right: calc(var(--sp) * 3); }

.panel-head, .sub-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: calc(var(--sp) * 2.5) calc(var(--sp) * 3.5);
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 1.4px;
  color: var(--fg-1);
  cursor: pointer;
  user-select: none;
  background: linear-gradient(135deg, rgba(47, 79, 111, 0.22) 0%, transparent 60%);
}
.panel-head { position: relative; }
.panel-head .ph-title { position: relative; padding-left: calc(var(--sp) * 2.5); }
.panel-head .ph-title::before {
  content: '';
  position: absolute; left: 0; top: 1px; bottom: 1px;
  width: 3px; border-radius: 2px;
  background: var(--accent);
  box-shadow: none;
}
.sub-head {
  font-size: 10px;
  color: var(--fg-2);
  padding: calc(var(--sp) * 2) 0 calc(var(--sp));
  margin-top: calc(var(--sp) * 2);
  border-top: 1px solid rgba(74, 58, 40, 0.20);
}
.chev { width: 8px; height: 8px; border-right: 1.5px solid var(--fg-3); border-bottom: 1.5px solid var(--fg-3); transform: rotate(45deg); transition: transform var(--ease); }
.collapsed .chev { transform: rotate(-45deg); }
.panel-body { padding: 0 calc(var(--sp) * 3.5) calc(var(--sp) * 3); }
.collapsed .panel-body { display: none; }
.sub-body.collapsed { display: none; }

/* ---------- leaderboard ---------- */
.board-group { margin-bottom: calc(var(--sp) * 2.5); }
.board-group:last-child { margin-bottom: 0; }
.board-title { font-size: 10px; color: var(--fg-3); margin-bottom: calc(var(--sp)); letter-spacing: 0.02em; text-transform: uppercase; }
.board-list .row {
  display: flex; align-items: center; gap: calc(var(--sp) * 1.5);
  padding: 2px 0;
  font-size: 12px;
  transition: transform var(--ease), background var(--ease);
  border-radius: 4px;
}
.board-list .row:nth-child(even) { background: rgba(255, 255, 255, 0.42); }
.board-list .row:hover { transform: translateX(3px); background: rgba(47, 79, 111, 0.22); }
.board-list .row.ev-new { animation: ev-in 240ms ease-out; }
.board-list .row .odot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; box-shadow: none; }
.board-list .row .val { margin-left: auto; color: var(--fg-2); font-family: var(--font-display); font-variant-numeric: tabular-nums; }
.board-list .empty { color: var(--fg-3); font-size: 11px; }
/* followed creatures: clickable, and a dead one can be dropped from the list */
#following .row { cursor: pointer; }
#following .row:hover .nm { color: var(--fg-1); }
#following .row.gone .nm { color: var(--fg-3); text-decoration: line-through; }
#following .row .val b { cursor: pointer; padding: 0 3px; color: var(--fg-3); font-weight: 400; }
#following .row .val b:hover { color: var(--danger); }

/* creature card: story lines first, the raw numbers folded away */
#creature-card .prop span { color: var(--fg-3); }
#creature-card .prop b { color: var(--fg-1); font-weight: 500; }
.card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin: calc(var(--sp) * 1.5) 0 calc(var(--sp)); }
#card-adv {
  border-top: 1px solid var(--glass-border);
  padding-top: calc(var(--sp));
  margin-top: calc(var(--sp));
}
#card-adv .prop b { color: var(--fg-2); font-weight: 400; }

/* ---------- intervention buttons ---------- */
.hint { font-size: 11px; color: var(--fg-3); line-height: 1.6; margin-bottom: calc(var(--sp) * 2.5); }
button.iv {
  display: flex; justify-content: space-between; align-items: center;
  width: 100%;
  margin-bottom: calc(var(--sp) * 2);
  padding: calc(var(--sp) * 2.5) calc(var(--sp) * 3);
  background: linear-gradient(180deg, rgba(248, 242, 228, 0.90), rgba(248, 242, 228, 0.88));
  color: var(--fg-1);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13px;
  font-family: var(--font-body);
  transition: all var(--ease);
}
button.iv:hover {
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42);
}
button.iv:active { transform: scale(0.97); }
button.iv.armed { border-color: var(--amber); box-shadow: none; }
button.iv .price { color: var(--fg-3); font-size: 10px; font-family: var(--font-display); font-variant-numeric: tabular-nums; }
/* Sub-heading that separates the tank-wide actions from the ones aimed at a
   single creature, so the panel reads as two shelves instead of a list of nine. */
.iv-group {
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-3);
  font-family: var(--font-display);
  margin: calc(var(--sp) * 3) 0 calc(var(--sp) * 2);
  padding-top: calc(var(--sp) * 2);
  border-top: 1px solid var(--glass-border);
}

/* ---------- toast / creature card ---------- */
#toast {
  margin-top: calc(var(--sp) * 2);
  padding: calc(var(--sp) * 2);
  font-size: 11px;
  border-radius: var(--radius-sm);
  background: rgba(20, 60, 40, 0.5);
  border: 1px solid rgba(88, 118, 62, 0.40);
  color: #5d7a3c;
  word-break: break-all;
  animation: spring-in 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
#toast.error { background: rgba(154, 59, 46, 0.12); border-color: rgba(154, 59, 46, 0.30); color: #9a3b2e; }

@keyframes spring-in {
  0% { transform: translateY(-20px) scale(0.95); opacity: 0; }
  60% { transform: translateY(4px) scale(1.02); }
  100% { transform: translateY(0) scale(1); opacity: 1; }
}

#creature-card {
  margin-top: calc(var(--sp) * 2.5);
  padding: calc(var(--sp) * 2.5);
  font-size: 12px;
  background: rgba(248, 242, 228, 0.92);
  border: 1px solid var(--glass-border);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius-sm);
  line-height: 1.8;
  animation: ev-in 200ms ease-out;
  box-shadow: none;
}
#creature-card .cid { color: var(--accent); font-weight: 600; font-family: var(--font-display); }
#creature-card .dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; box-shadow: none; }
/* A paid name is the one ornament the card puts above the species codename. */
#creature-card .cname { color: var(--gold); font-weight: 600; font-family: var(--font-display); }
#creature-card .cbase { color: var(--fg-3); font-size: 10px; }
.card-tag {
  display: inline-block;
  font-size: 9px;
  letter-spacing: 0.06em;
  padding: 1px 6px;
  margin-left: 6px;
  border-radius: 999px;
  border: 1px solid currentColor;
  vertical-align: 1px;
}
.card-tag.ark { color: var(--accent); }
.card-tag.legend { color: var(--gold); }
.card-ark { color: var(--accent); font-size: 11px; }
.card-ark code { font-size: 10px; color: var(--fg-3); }

/* ---------- paid-intervention dialog ---------- */
/* One modal, four shapes: a text field for name and wish, a trait picker for
   the genome edit, a plain confirmation for the ark. Backdrop click and Esc
   both close it, matching how targeting already cancels. */
.modal-back {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--sp) * 4);
  background: rgba(248, 242, 228, 0.90);
  animation: fade-in 160ms ease-out;
}
.modal-back[hidden] { display: none; }
.modal {
  width: min(420px, 100%);
  max-height: min(80vh, 620px);
  overflow-y: auto;
  padding: calc(var(--sp) * 4);
  border-radius: var(--radius);
  animation: spring-in 280ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: calc(var(--sp) * 2); }
.modal-head .section-title { font-size: 12px; color: var(--fg-1); margin-bottom: 0; }
.modal-x {
  flex: 0 0 auto;
  background: none;
  border: none;
  color: var(--fg-3);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
  transition: color var(--ease);
}
.modal-x:hover { color: var(--danger); }
.modal-body { margin: calc(var(--sp) * 3) 0; font-size: 12px; color: var(--fg-2); line-height: 1.7; }
.modal-body .iv-sub { color: var(--fg-3); font-size: 11px; margin-top: calc(var(--sp) * 2); }
.modal-body .iv-warn { color: var(--gold); font-size: 11px; margin-top: calc(var(--sp) * 2); }
.modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: calc(var(--sp) * 2);
  padding-top: calc(var(--sp) * 3);
  border-top: 1px solid var(--glass-border);
}
.modal-price {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 10px;
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  color: var(--amber);
  overflow-wrap: anywhere;
}
.cta.mini {
  padding: calc(var(--sp) * 2) calc(var(--sp) * 3.5);
  font-size: 12px;
}
.iv-field { display: block; width: 100%; }
.iv-field input[type="text"] {
  display: block;
  box-sizing: border-box;
  width: 100%;
  padding: calc(var(--sp) * 2.5);
  font-size: 13px;
  font-family: var(--font-body);
  color: var(--fg-1);
  background: rgba(248, 242, 228, 0.92);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color var(--ease), box-shadow var(--ease);
}
.iv-field input[type="text"]:focus { border-color: var(--accent); box-shadow: none; }
.iv-field .iv-count {
  display: block;
  text-align: right;
  margin-top: calc(var(--sp));
  font-size: 10px;
  color: var(--fg-3);
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
}
.iv-field .iv-count.over { color: var(--amber); }
/* Trait list + boost/suppress segment: radios styled as pills so the whole
   choice is one glance and one click each. */
.trait-list { display: flex; flex-wrap: wrap; gap: 6px; }
.trait-opt { position: relative; }
.trait-opt input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.trait-opt span {
  display: inline-block;
  padding: calc(var(--sp) * 1.5) calc(var(--sp) * 2.5);
  font-size: 11px;
  color: var(--fg-2);
  border: 1px solid var(--glass-border);
  border-radius: 999px;
  transition: all var(--ease);
}
.trait-opt input:checked + span { color: var(--accent); border-color: var(--accent); background: var(--accent-dim); }
.trait-opt input:focus-visible + span { box-shadow: none; }
.seg { display: flex; gap: 6px; margin-top: calc(var(--sp) * 2); }
.seg .trait-opt { flex: 1 1 0; }
.seg .trait-opt span { display: block; width: 100%; text-align: center; border-radius: var(--radius-sm); }
.seg[data-dir="boost"] .trait-opt input:checked + span { color: #5d7a3c; border-color: rgba(88, 118, 62, 0.40); background: rgba(20, 60, 40, 0.5); }
.seg[data-dir="suppress"] .trait-opt input:checked + span { color: #9a3b2e; border-color: rgba(154, 59, 46, 0.30); background: rgba(154, 59, 46, 0.12); }
.iv-target { color: var(--fg-1); font-family: var(--font-display); }
@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .modal-back, .modal { animation: none; }
}

/* ---------- live events / chain rain ---------- */
#events { max-height: 150px; overflow-y: auto; font-size: 11px; line-height: 1.7; color: var(--fg-2); }
#events .ev { padding: 2px 0; border-bottom: 1px solid rgba(74, 58, 40, 0.20); animation: ev-in 250ms ease-out; }
#events .ev.predation { color: #9a3b2e; }
#events .ev.judgment { color: var(--danger); }
#events .ev.harvest { color: var(--amber); }
#events .ev.intervention { color: var(--accent); }
#events .ev.whale { color: #3f5570; }
/* The four paid creature actions each get their own hue in the ticker, so a
   scan of the feed separates money spent on the tank from money spent on one
   life inside it. */
#events .ev.naming { color: var(--gold); }
#events .ev.wish { color: #b8832c; }
#events .ev.mutation { color: #5a4a78; }
#events .ev.ark { color: #5d7a3c; }
@keyframes ev-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

#rain { font-size: 10px; font-family: var(--font-display); font-variant-numeric: tabular-nums; color: var(--fg-3); }
#rain .tx { display: flex; gap: 6px; align-items: center; padding: 2px 0; cursor: pointer; transition: color var(--ease); }
#rain .tx:hover { color: var(--accent); }
#rain .tx .sizebar { height: 3px; background: var(--accent); border-radius: 2px; opacity: 0.7; }
#rain .tx .sim { opacity: 0.5; }
#rain .tx .amt { color: var(--fg-2); }
#rain .tx .x4 { color: #a4671c; font-size: 8px; border: 1px solid rgba(164, 103, 28, 0.28); border-radius: 3px; padding: 0 3px; }
/* A wishing meteor's row carries the words instead of a dollar amount. */
#rain .tx .wish {
  flex: 1 1 auto;
  min-width: 0;
  color: #b8832c;
  font-size: 10px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- bottom dock + drawers ---------- */
#dock {
  position: fixed;
  bottom: calc(var(--sp) * 3);
  /* Centering must not ride on `transform`: the shared .rise entrance
     animation fills forwards with `transform: none`, which would otherwise
     cancel a translateX(-50%) and leave the pill hanging off the midline. */
  left: 0;
  right: 0;
  width: fit-content;
  margin-inline: auto;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: calc(var(--sp) * 2);
  padding: calc(var(--sp) * 1.5) calc(var(--sp) * 2);
  border-radius: 999px;
}
.dock-btn {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--fg-2);
  font-size: 11px;
  padding: calc(var(--sp) * 1.5) calc(var(--sp) * 3);
  cursor: pointer;
  transition: all var(--ease);
  white-space: nowrap;
}
.dock-btn:hover { color: var(--fg-1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42); }
.dock-btn:active { transform: scale(0.97); }
.dock-btn.open { color: var(--accent); border-color: var(--accent); background: var(--accent-dim); }
#dock .token { font-size: 10px; color: var(--fg-3); padding: 0 calc(var(--sp) * 2); border-left: 1px solid var(--glass-border); }

.drawer {
  position: fixed;
  bottom: calc(var(--sp) * 16);
  left: 50%;
  transform: translateX(-50%);
  z-index: 25;
  width: min(860px, calc(100vw - 24px));
  max-height: 40vh;
  padding: calc(var(--sp) * 3) calc(var(--sp) * 4);
  overflow-y: auto;
  animation: drawer-in 240ms ease-out;
}
@keyframes drawer-in {
  from { opacity: 0; transform: translate(-50%, 10px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

.section-title { font-size: 10px; color: var(--fg-3); margin-bottom: calc(var(--sp)); letter-spacing: 0.02em; }
.legend { margin-left: 10px; }
.legend .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin: 0 4px 0 10px; }
.legend .dot.harvest { background: var(--amber); }
.legend .dot.judgment { background: var(--danger); }

.day-anchor { display: flex; align-items: baseline; gap: calc(var(--sp) * 2); margin-bottom: calc(var(--sp) * 2); font-size: 10px; }
.day-anchor .da-label { color: var(--fg-3); text-transform: uppercase; letter-spacing: 1.2px; }
.day-anchor code { font-family: var(--font-display); font-size: 11px; color: #a4671c; letter-spacing: 0.5px; }
.day-anchor .da-note { color: var(--fg-3); font-size: 9px; }
.day-anchor .mini { margin-left: auto; }

/* Compact button for header rows, where a full dock button shouts. */
.dock-btn.mini {
  font-size: 9px;
  padding: 2px calc(var(--sp) * 2);
  letter-spacing: 0.6px;
  border-color: var(--glass-border);
  color: var(--fg-3);
}
.dock-btn.mini:hover { color: var(--fg-1); border-color: var(--accent); }
.obs-actions { margin-left: auto; display: flex; gap: 6px; align-items: center; }

/* ---- Pulse time-travel range selector (OBSERVE) ----
   A segmented "depth gauge": thin pills in a recessed track. The active
   range fills with the cyan accent and, for Live only, a breathing sonar
   pip marks the real-time feed; historical ranges sit frozen. */
.pulse-ranges {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  margin-left: calc(var(--sp) * 2);
  border-radius: 999px;
  border: 1px solid var(--glass-border);
  background: rgba(240, 233, 216, 0.70);
  box-shadow: inset 0 1px 3px rgba(60, 46, 30, 0.20);
}
.range-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 0;
  background: transparent;
  color: var(--fg-3);
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.9px;
  line-height: 1;
  padding: 4px 10px;
  border-radius: 999px;
  cursor: pointer;
  transition: color var(--ease), background var(--ease), box-shadow var(--ease), transform 120ms var(--ease);
  white-space: nowrap;
}
.range-btn:hover { color: var(--fg-1); background: rgba(47, 79, 111, 0.22); }
.range-btn:active { transform: scale(0.95); }
.range-btn.active {
  color: #efe7d4;
  background: linear-gradient(135deg, var(--accent), #2f4f6f);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
}
.range-btn .rb-pip {
  width: 5px; height: 5px; border-radius: 50%;
  background: currentColor;
  box-shadow: none;
}
.range-btn.active .rb-pip { background: #efe7d4; box-shadow: none; }
/* Only the Live pip breathes; it is the one range that is still moving. */
.range-btn[data-range="live"]:not(.active) .rb-pip { animation: rb-pip 1.9s ease-in-out infinite; }
@keyframes rb-pip {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.3; transform: scale(0.65); }
}
/* A historical range turns the canvas into a brush surface. */
#pulse.brushable { cursor: crosshair; }
#pulse.brushing { cursor: ew-resize; }

.sig-guide { display: flex; flex-direction: column; gap: 4px; margin-bottom: calc(var(--sp) * 2); font-size: 10px; color: var(--fg-2); }
.sig-guide .sg-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 6px; }
.sig-guide .sg-dot.chain { background: var(--accent); }
.sig-guide .sg-dot.market { background: #8a6a1f; }

/* The data drawer gets the room the charts need: text down the left, the two
   charts stacked on the right at close to full drawer width. */
#drawer-analytics { width: min(1180px, calc(100vw - 24px)); max-height: 74vh; }
.analytics-grid {
  display: grid;
  grid-template-columns: minmax(260px, 330px) 1fr;
  gap: calc(var(--sp) * 4);
  align-items: start;
}
.analytics-side {
  display: flex;
  flex-direction: column;
  gap: calc(var(--sp));
  max-height: 60vh;
  overflow-y: auto;
  padding-right: 4px;
  min-width: 0;
}
.analytics-charts {
  display: grid;
  grid-template-rows: 1fr 1fr minmax(160px, 1.15fr);
  gap: calc(var(--sp) * 3);
  height: 60vh;
  min-width: 0;
}
#chart-wrap, #temps-wrap, #census-wrap { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
#chart, #temps, #census {
  flex: 1;
  width: 100%;
  min-height: 0;
  background: rgba(248, 242, 228, 0.92);
  border-radius: var(--radius-sm);
  cursor: crosshair;
}

/* ---------- census: the same book in words ---------- */
/* A stack of four translucent colours cannot answer "is anything dying?", so the
   trend words sit under the chart. Colours follow the existing semantic tokens:
   accent for growth, amber for a decline that is still alive, danger for zero. */
.census-coverage { margin-left: 10px; font-size: 10px; color: var(--fg-3); }
.census-rows { display: flex; flex-direction: column; gap: 1px; margin-top: calc(var(--sp)); }
.census-row { display: flex; align-items: center; gap: 6px; font-size: 10px; color: var(--fg-2); }
.census-row .cr-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.census-row .cr-name { font-family: var(--font-display); letter-spacing: 0.02em; color: var(--fg-1); }
.census-row .cr-state { font-weight: 600; }
.census-row .cr-state.cr-expanding { color: var(--accent); }
.census-row .cr-state.cr-shrinking { color: var(--amber); }
.census-row .cr-state.cr-gone { color: var(--danger); }
.census-row .cr-state.cr-steady { color: var(--fg-2); }
.census-row .cr-state.cr-unknown { color: var(--fg-3); font-style: italic; }
.census-row .cr-num { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--fg-1); }
.census-row .cr-num em { font-style: normal; color: var(--fg-3); }
.census-row.cr-gap span { color: var(--fg-3); font-style: italic; }
/* The pinned day, in words. A crosshair on a canvas is not something a forwarded
   URL can carry, so the day a link points at has to be readable as text. */
#census-pin {
  margin-top: calc(var(--sp));
  font-family: var(--font-display);
  font-size: 10px;
  color: var(--fg-1);
  font-variant-numeric: tabular-nums;
}
/* The pointer to the transaction that carried this day's commitment. Present only
   on days that have one, which is the whole point of styling it as a link rather
   than as a sentence: no link is the answer to "is this on chain yet?". */
#census-pin a { color: var(--accent); text-decoration: none; border-bottom: 1px solid transparent; }
#census-pin a:hover { border-bottom-color: var(--accent); }

/* ---------- obituaries ---------- */
#death-stats { font-size: 10px; color: var(--fg-3); margin-bottom: calc(var(--sp)); }
#judgments { font-size: 11px; color: var(--fg-2); }
.obit-group { margin-bottom: calc(var(--sp) * 2); }
.obit-title { font-weight: 600; font-size: 11px; margin-bottom: 2px; font-family: var(--font-display); }
.obit-group.judgment .obit-title { color: var(--danger); }
.obit-group.harvest .obit-title { color: var(--amber); }
.obit { display: flex; align-items: center; gap: 6px; padding: 1px 0; color: var(--fg-1); }
.obit .odot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.obit .obit-sub { color: var(--fg-3); margin-left: auto; }
.obit-note { font-size: 10px; color: var(--fg-3); font-style: italic; margin: 2px 0 4px; }
/* The ones an ark ticket pulled out of the cull: listed above the deaths, in
   the green of a save rather than the grey of an obituary. */
.obit.saved { color: #5d7a3c; }
.obit.saved .obit-sub { color: rgba(88, 118, 62, 0.45); }

/* ---------- kill banner ---------- */
#banner {
  position: fixed;
  top: calc(var(--sp) * 18);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  padding: calc(var(--sp) * 2.5) calc(var(--sp) * 6);
  background: rgba(248, 242, 228, 0.90);
  border: 1px solid rgba(154, 59, 46, 0.30);
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 500;
  color: var(--fg-1);
  box-shadow: none;
  animation: banner-spring 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
  pointer-events: none;
  white-space: nowrap;
}
@keyframes banner-spring {
  0% { transform: translateX(-50%) translateY(-20px) scale(0.95); opacity: 0; }
  60% { transform: translateX(-50%) translateY(4px) scale(1.02); }
  100% { transform: translateX(-50%) translateY(0) scale(1); opacity: 1; }
}
#banner .pred, #banner .prey { font-weight: 700; }

/* ---------- welcome overlay ---------- */
#welcome {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(248, 242, 228, 0.94);
  animation: welcome-in 400ms ease both, breathe 6s ease-in-out infinite;
}
#welcome[hidden] { display: none; }
@keyframes welcome-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes breathe {
  /* This used to pulse a backdrop blur, which was a way of saying "the tank is
     still alive behind this card". There is nothing to blur now, so the same
     sentence is said with the card's own opacity — fainter, and cheap enough
     to run for as long as the overlay is up. */
  0%, 100% { opacity: 1; }
  50% { opacity: 0.94; }
}
.welcome-card {
  max-width: 480px;
  padding: calc(var(--sp) * 10) calc(var(--sp) * 12);
  text-align: center;
}
.welcome-card h1 {
  font-family: var(--font-display);
  font-size: 42px;
  font-weight: 700;
  letter-spacing: 8px;
  background: linear-gradient(100deg, #efe7d4 10%, #5c6b72 50%, #464a78 92%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;

  margin-bottom: calc(var(--sp) * 2);
}
.welcome-card .wtag { color: var(--accent); font-size: 13px; margin-bottom: calc(var(--sp) * 6); }
.welcome-card p:not(.wtag) { color: var(--fg-2); font-size: 13px; line-height: 1.8; margin-bottom: calc(var(--sp) * 3); }
.cta {
  margin-top: calc(var(--sp) * 4);
  padding: calc(var(--sp) * 3) calc(var(--sp) * 8);
  background: linear-gradient(135deg, rgba(47, 79, 111, 0.20), rgba(70, 74, 120, 0.22));
  border: 1px solid var(--accent);
  border-radius: 999px;
  color: var(--accent);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 1px;
  cursor: pointer;
  transition: all var(--ease);
}
.cta:hover { background: linear-gradient(135deg, rgba(47, 79, 111, 0.20), rgba(70, 74, 120, 0.22)); box-shadow: none; transform: translateY(-1px); }
.cta:active { transform: scale(0.97); }

/* ---------- view tabs ---------- */
.viewtabs { display: flex; gap: 2px; padding: 2px; border-radius: 999px; background: rgba(248, 242, 228, 0.92); border: 1px solid var(--glass-border); }
.viewtabs button {
  position: relative;
  background: transparent;
  border: none;
  border-radius: 999px;
  color: var(--fg-3);
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 1.5px;
  padding: 4px 14px;
  cursor: pointer;
  transition: all var(--ease);
  overflow: hidden;
}
.viewtabs button::after {
  content: '';
  position: absolute;
  bottom: 2px;
  left: 20%;
  right: 20%;
  height: 1.5px;
  background: var(--accent);
  border-radius: 1px;
  transform: scaleX(0);
  transition: transform var(--ease);
}
.viewtabs button:hover { color: var(--fg-1); }
.viewtabs button:hover::after { transform: scaleX(0.6); }
.viewtabs button.active { color: var(--bg-0); background: var(--accent); box-shadow: none; }
.viewtabs button.active::after { transform: scaleX(1); background: var(--bg-0); }

/* ---------- live ticker (below topbar) ---------- */
#ticker {
  position: fixed;
  top: calc(var(--sp) * 16);
  left: calc(var(--sp) * 3);
  right: calc(var(--sp) * 3);
  z-index: 20;
  height: 28px;
  display: flex;
  align-items: center;
  padding: 0 calc(var(--sp) * 3);
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.42), transparent 50%), var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-bottom-color: rgba(47, 79, 111, 0.22);
  box-shadow: var(--glass-shadow);
  overflow: hidden;
  user-select: none;
  mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}
#ticker[hidden] { display: none; }
#ticker .tick-track {
  display: inline-flex;
  flex: 0 0 auto;
  gap: calc(var(--sp) * 8);
  white-space: nowrap;
  will-change: transform;
  animation: tick-scroll var(--tick-dur, 40s) linear infinite;
}
#ticker .tick-track .tick { cursor: pointer; transition: color 0.15s ease; }
#ticker .tick-track .tick:hover { color: var(--fg-1); }
#ticker:hover .tick-track { animation-play-state: paused; }
/* Content shorter than the bar: rest centered instead of scrolling. */
#ticker.static { justify-content: center; }
#ticker.static .tick-track { animation: none; }
#ticker .tick { display: inline-flex; align-items: center; gap: 7px; font-family: var(--font-display); font-size: 11px; font-variant-numeric: tabular-nums; color: var(--fg-3); }
#ticker .tick .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); flex: 0 0 auto; animation: pulse 2.4s ease-in-out infinite; }
#ticker .tick .amt { color: var(--fg-1); }
#ticker .tick .arrow { color: var(--fg-3); }
#ticker .tick.x402 .dot { background: #a4671c; box-shadow: none; }
#ticker .tick.x402 .amt { color: #a4671c; }
@keyframes tick-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ---------- observe view layout ---------- */
#observe-view {
  position: fixed;
  top: calc(var(--sp) * 24);
  left: calc(var(--sp) * 3);
  right: calc(var(--sp) * 3);
  bottom: calc(var(--sp) * 3);
  z-index: 10;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: calc(var(--sp) * 3);
}
#observe-view[hidden] { display: none; }
.obs-col { display: flex; flex-direction: column; gap: calc(var(--sp) * 3); min-height: 0; }
.obs-card {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: calc(var(--sp) * 3) calc(var(--sp) * 4);
  overflow: hidden;
  border-radius: 16px;
  box-shadow: var(--glass-shadow);
}
.obs-flowmap-card { flex: 3 1 0; }
.obs-pulse-card { flex: 2 1 0; }
.obs-stats-card { flex: 0 0 auto; }
/* The two side lists split what is left of the column instead of one sizing to
   its content: the rails card holds up to twelve rows plus a chip per rail, and
   left to `flex: 0 1 auto` it took all of it and squeezed Top endpoints — a
   `flex: 1 1 0` sibling with `min-height: 0` — down to zero height, which reads
   as the panel having vanished rather than as the panel being short. Both lists
   scroll internally, so a weighted split costs a scrollbar and nothing else. */
.obs-venues-card { flex: 1.15 1 0; min-height: 0; }
/* A floor rather than a share, because a share can still be outvoted by the
   card above it growing. This is the height of the header plus three rows: enough
   that the panel is legibly a list, and small enough that a short viewport
   spends the squeeze on the rails card, which has more to give. */
.obs-endpoints-card { flex: 1 1 0; min-height: calc(var(--sp) * 34); }
.obs-head {
  position: relative;
  display: flex; align-items: baseline; justify-content: space-between; gap: calc(var(--sp) * 2);
  flex-wrap: wrap;
  font-family: var(--font-display);
  font-size: 12px; font-weight: 500; letter-spacing: 1.4px; color: var(--fg-1);
  margin-bottom: calc(var(--sp) * 2);
  padding-left: calc(var(--sp) * 2.5);
  background: linear-gradient(135deg, rgba(47, 79, 111, 0.22) 0%, transparent 60%);
  border-radius: 6px;
  padding-top: calc(var(--sp));
  padding-bottom: calc(var(--sp));
}
.obs-head::before {
  content: '';
  position: absolute; left: 0; top: 2px; bottom: 2px;
  width: 3px; border-radius: 2px;
  background: var(--accent);
  box-shadow: none;
}
.obs-sub { font-family: var(--font-body); font-size: 10px; font-weight: 400; letter-spacing: 0; color: var(--fg-3); }
#flowmap, #pulse { flex: 1 1 0; width: 100%; min-height: 0; }

.flowmap-legend { display: flex; gap: calc(var(--sp) * 5); margin-top: calc(var(--sp) * 2); font-size: 10px; color: var(--fg-3); }
.flowmap-legend span { display: flex; align-items: center; gap: 6px; }
.flowmap-legend i { width: 16px; height: 2px; border-radius: 2px; display: inline-block; }
.lg-x402 { background: #a4671c; box-shadow: none; }
.lg-plain { background: var(--accent); box-shadow: none; }

#obs-stats { display: grid; grid-template-columns: 1fr 1fr; gap: calc(var(--sp) * 3) calc(var(--sp) * 3); }
.obs-stat {
  display: flex; flex-direction: column; gap: 2px;
  border-top: 1px solid rgba(47, 79, 111, 0.22);
  padding-top: calc(var(--sp) * 1.5);
  transition: box-shadow var(--ease);
}
.obs-stat:hover { box-shadow: none; }
.obs-stat .label { font-size: 9px; color: var(--fg-3); text-transform: uppercase; letter-spacing: 1.2px; }
.obs-stat .value { font-family: var(--font-display); font-size: 20px; font-variant-numeric: tabular-nums; color: var(--fg-1); line-height: 1.1; }
.obs-stat .value.gold { color: #a4671c; }
.obs-stat .value.sm { font-size: 15px; }

#endpoints { display: flex; flex-direction: column; gap: calc(var(--sp) * 1.5); overflow-y: auto; min-height: 0; }
.ep { display: flex; align-items: center; gap: calc(var(--sp) * 2); padding: 5px calc(var(--sp) * 2); border-radius: var(--radius-sm); background: rgba(248, 242, 228, 0.92); cursor: pointer; transition: background var(--ease), transform var(--ease); border-left: 2px solid transparent; }
.ep:hover { background: rgba(47, 79, 111, 0.20); transform: translateX(2px); }
.ep:nth-child(-n+3) .rank { font-weight: 700; }
.ep:nth-child(1) { border-left-color: var(--gold); }
.ep:nth-child(1) .rank { color: var(--gold); }
.ep:nth-child(2) { border-left-color: var(--silver); }
.ep:nth-child(2) .rank { color: var(--silver); }
.ep:nth-child(3) { border-left-color: var(--bronze); }
.ep:nth-child(3) .rank { color: var(--bronze); }
.ep .rank { font-family: var(--font-display); font-size: 11px; color: var(--fg-3); width: 16px; flex: 0 0 auto; }
.ep.top .rank { color: #a4671c; }
.ep.top .addr { color: var(--fg-1); }
.ep .mid { flex: 1 1 auto; min-width: 0; }
.ep .addr { font-family: var(--font-display); font-size: 11px; color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ep .volbar { height: 3px; border-radius: 2px; background: var(--vc, var(--accent)); margin-top: 3px; opacity: 0.7; }
.ep .right { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; flex: 0 0 auto; }
.ep .amt { font-family: var(--font-display); font-size: 12px; font-variant-numeric: tabular-nums; color: var(--fg-1); }
.ep .x4tag { font-size: 9px; color: #a4671c; font-family: var(--font-display); }

/* ---------- rails: the venue breakdown ---------- */
/*
 * One colour per rail, defined as a custom property so the chip, the row's
 * volume bar and the row's tag all read as the same kind without repeating the
 * hex. `.ep .volbar` picks it up through `var(--vc, var(--accent))`, which is
 * why the endpoint rows — carrying no `v-*` class — still render in accent.
 * x402 keeps the gold the flow ticker and the chords already use for it, so the
 * panel and the tank cannot disagree about which transfers are machine payments.
 */
.v-x402 { --vc: #a4671c; }
.v-swap { --vc: var(--accent); }
.v-aa { --vc: #5a4a78; }
.v-direct { --vc: #3f6f5f; }
.v-contract { --vc: #6a5943; }
.v-unknown { --vc: #6a5943; }

#venues { display: flex; flex-direction: column; gap: calc(var(--sp) * 1.5); overflow-y: auto; min-height: 0; }
.vchips { display: flex; flex-wrap: wrap; gap: 4px; flex: 0 0 auto; }
.vchip {
  display: inline-flex; align-items: baseline; gap: 4px; padding: 2px 6px;
  border-radius: var(--radius-sm); border-left: 2px solid var(--vc);
  background: rgba(248, 242, 228, 0.92);
  font-family: var(--font-display); font-size: 10px; color: var(--vc);
}
.vchip b { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--fg-1); }
.vrows { display: flex; flex-direction: column; gap: calc(var(--sp) * 1.5); }
.vtag { font-size: 9px; font-family: var(--font-display); color: var(--vc); }
/* The name and its count share a line, so the count has to be the part that
   does not shrink: `.addr` already carries an ellipsis, and letting the number
   be the thing that gets truncated would drop the one figure the rows are
   ordered by. */
.vhead { display: flex; align-items: baseline; gap: 4px; min-width: 0; }
.vhead .addr { flex: 0 1 auto; }
.vcount {
  flex: 0 0 auto; font-family: var(--font-display); font-size: 10px;
  font-weight: 400; font-variant-numeric: tabular-nums; color: var(--fg-3);
}
/* Unattributed flows are a statement about our own coverage, so it reads as
   metadata rather than as another row competing for attention. */
.vcov { flex: 0 0 auto; font-size: 10px; color: var(--fg-3); }

.obs-empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.obs-empty[hidden] { display: none; }
.obs-empty .obs-card { align-items: center; max-width: 440px; gap: calc(var(--sp) * 2); }
.obs-empty p { color: var(--fg-2); font-size: 13px; }
.obs-empty .obs-hint { color: var(--fg-3); font-size: 11px; }
.obs-empty code { display: inline-block; margin-top: calc(var(--sp) * 2); padding: 7px 16px; background: rgba(248, 242, 228, 0.92); border: 1px solid var(--glass-border); border-radius: var(--radius-sm); color: var(--accent); font-family: var(--font-display); font-size: 12px; }

/* ---------- address detail drawer (observe) ---------- */
#addr-card {
  position: fixed;
  top: calc(var(--sp) * 24);
  right: calc(var(--sp) * 3);
  bottom: calc(var(--sp) * 3);
  z-index: 15;
  width: 320px;
  padding: calc(var(--sp) * 3.5);
  overflow-y: auto;
  font-size: 12px;
  animation: ev-in 200ms ease-out;
  border-left: 2px solid var(--accent);
  box-shadow: var(--glass-shadow);
}
#addr-card[hidden] { display: none; }
/* In WORLD the dossier floats over the tank instead of vanishing: whales only
   exist here, so hiding the card made their dossiers unreachable. */
body.view-world #addr-card { bottom: auto; max-height: 70vh; }
#addr-card .tx-close { position: absolute; top: 6px; right: 11px; cursor: pointer; color: var(--fg-3); font-size: 15px; line-height: 1; }
#addr-card .tx-close:hover { color: var(--fg-1); }
#addr-card .addr-h { font-family: var(--font-display); font-size: 13px; color: var(--accent); margin-bottom: calc(var(--sp) * 2.5); padding-right: 16px; }
#addr-card .addr-sub { margin: calc(var(--sp) * 2.5) 0 calc(var(--sp)); font-size: 9px; color: var(--fg-3); text-transform: uppercase; letter-spacing: 1.2px; }
.af { display: flex; gap: 8px; align-items: center; padding: 3px 0; border-bottom: 1px solid rgba(74, 58, 40, 0.20); font-family: var(--font-display); font-size: 11px; font-variant-numeric: tabular-nums; }
.af .dir { width: 12px; flex: 0 0 auto; }
.af.in .dir { color: #5d7a3c; }
.af.out .dir { color: #9a3b2e; }
.af .who { color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.af .amt { margin-left: auto; color: var(--fg-1); flex: 0 0 auto; }
.af.x402 .amt { color: #a4671c; }
.af .when { color: var(--fg-3); font-size: 9px; flex: 0 0 auto; }
/* Fixed-basis so the rail tag keeps its wording while `.who` is what ellipsizes. */
.af .vtag { flex: 0 0 auto; }
.addr-empty { padding: calc(var(--sp) * 2) 0; font-size: 11px; line-height: 1.6; color: var(--fg-3); }
#addr-card a.explore { display: inline-block; margin-top: calc(var(--sp) * 2.5); color: var(--accent); font-size: 11px; text-decoration: none; border-bottom: 1px solid transparent; transition: border-color var(--ease); }
#addr-card a.explore:hover { border-bottom-color: var(--accent); }

/* ---------- tx provenance card ---------- */
#tx-card {
  position: fixed;
  left: calc(var(--sp) * 3);
  bottom: calc(var(--sp) * 3);
  z-index: 22;
  width: 320px;
  max-height: 62vh;
  overflow-y: auto;
  padding: calc(var(--sp) * 3.5);
  font-size: 12px;
  line-height: 1.7;
  animation: ev-in 200ms ease-out;
}
#tx-card[hidden] { display: none; }
#tx-card .tx-close { position: absolute; top: 6px; right: 11px; cursor: pointer; color: var(--fg-3); font-size: 15px; line-height: 1; }
#tx-card .tx-close:hover { color: var(--fg-1); }
#tx-card .tx-h { font-family: var(--font-display); font-size: 11px; color: var(--accent); word-break: break-all; margin-bottom: calc(var(--sp) * 2.5); padding-right: 16px; }
#tx-card .tx-row, #addr-card .tx-row { display: flex; justify-content: space-between; gap: calc(var(--sp) * 2); padding: 1px 0; }
#tx-card .tx-row .k, #addr-card .tx-row .k { color: var(--fg-3); flex: 0 0 auto; }
#tx-card .tx-row .v, #addr-card .tx-row .v { color: var(--fg-1); font-family: var(--font-display); font-variant-numeric: tabular-nums; text-align: right; word-break: break-all; }
#addr-card .tx-row .v.gold { color: #a4671c; }
#tx-card .x402-badge { display: inline-block; margin-top: calc(var(--sp) * 2.5); padding: 2px 9px; border-radius: 999px; background: rgba(164, 103, 28, 0.28); border: 1px solid rgba(164, 103, 28, 0.28); color: #a4671c; font-size: 10px; font-family: var(--font-display); }
#tx-card a.explore { display: inline-block; margin-top: calc(var(--sp) * 2.5); color: var(--accent); font-size: 11px; text-decoration: none; border-bottom: 1px solid transparent; transition: border-color var(--ease); }
#tx-card a.explore:hover { border-bottom-color: var(--accent); }

/* ---------- observe view dims/hides the world UI ---------- */
body.view-observe #board, body.view-observe #panel, body.view-observe #dock,
body.view-observe .drawer, body.view-observe #banner { display: none !important; }

/* ---------- scrollbars ---------- */
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(74, 58, 40, 0.20); border-radius: 3px; transition: background 0.2s; }
::-webkit-scrollbar-thumb:hover { background: rgba(74, 58, 40, 0.20); }
* { scrollbar-width: thin; scrollbar-color: rgba(74, 58, 40, 0.20) transparent; }

/* ---------- hover lift on interactive cards ---------- */
.obs-card:hover {
  transform: translateY(-1px);
  box-shadow: var(--glass-shadow);
}
.drawer:hover {
  transform: translateX(-50%) translateY(-1px);
  box-shadow: var(--glass-shadow);
}

/* ---------- typography polish ---------- */
.props-h { letter-spacing: 0.02em; }
.obs-stat .value, .stat b, .ep .amt, #rain .tx .amt, .board-list .row .val {
  font-variant-numeric: tabular-nums;
}

/* ---------- responsive: panels become bottom sheets ---------- */
/* The topbar needs 847 px to hold one row — brand, tabs, controls, their gaps
   and its own padding, with .stats absorbed because it is the only child that
   will shrink. Below that the bar overflowed its right edge from 769 px down to
   the 768 px wrap, so the sub-label yields early and hands its 353 px back to
   the stats, which were being crushed to nothing in that range anyway. */
@media (max-width: 900px) {
  .brand .tag { display: none; }
}

@media (max-width: 768px) {
  #topbar { flex-wrap: wrap; gap: calc(var(--sp) * 3); }
  .stats { order: 3; width: 100%; gap: calc(var(--sp) * 3); }
  .panel {
    top: auto;
    bottom: calc(var(--sp) * 16);
    width: calc(50vw - 18px);
    max-height: 42vh;
    overflow-y: auto;
  }
  #board { left: calc(var(--sp) * 3); }
  #panel { right: calc(var(--sp) * 3); }
  .drawer { bottom: calc(var(--sp) * 24); max-height: 50vh; }
  /* One column on a phone: the drawer itself scrolls, the charts keep a height
     a finger can read instead of splitting the viewport in two. */
  #drawer-analytics { max-height: 62vh; }
  .analytics-grid { grid-template-columns: 1fr; gap: calc(var(--sp) * 3); }
  .analytics-side { max-height: none; overflow: visible; }
  .analytics-charts { grid-template-rows: auto auto auto; height: auto; }
  #chart, #temps, #census { height: 30vh; flex: 0 0 auto; }
  #observe-view { top: calc(var(--sp) * 26); display: flex; flex-direction: column; overflow-y: auto; }
  .obs-col { flex: 0 0 auto; }
  /* Flex-basis-0 canvases contribute nothing to grid row sizing, so give them
     explicit heights here or the cards collapse and overlap. */
  #flowmap { flex: 0 0 auto; height: 36vh; }
  #pulse { flex: 0 0 auto; height: 120px; }
  .obs-flowmap-card { flex: 0 0 auto; }
  .obs-pulse-card { flex: 0 0 auto; }
  .obs-endpoints-card { flex: 0 0 auto; }
  #endpoints { max-height: 38vh; }
  .obs-venues-card { flex: 0 0 auto; }
  #venues { max-height: 30vh; }
  .viewtabs button { padding: 4px 10px; letter-spacing: 1px; }
  #tx-card { left: calc(var(--sp) * 3); right: calc(var(--sp) * 3); width: auto; bottom: calc(var(--sp) * 20); }
}

/* ---------- Day Digest on-chain status ---------- */
.digest-status {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 9px;
  letter-spacing: 0.4px;
  padding: 1px 6px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.42);
  border: 1px solid rgba(255, 255, 255, 0.42);
  transition: all 0.3s ease;
}
.digest-status .ds-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  flex-shrink: 0;
}
.digest-status.confirmed { color: #5d7a3c; border-color: rgba(88, 118, 62, 0.30); }
.digest-status.confirmed .ds-dot { background: #5d7a3c; box-shadow: none; }
.digest-status.pending { color: #a4671c; border-color: rgba(164, 103, 28, 0.25); }
.digest-status.pending .ds-dot { background: #a4671c; animation: ds-pulse 1.2s ease-in-out infinite; }
.digest-status.failed { color: #9a3b2e; border-color: rgba(154, 59, 46, 0.28); }
.digest-status.failed .ds-dot { background: #9a3b2e; }
.digest-status.unconfigured { color: var(--fg-3); border-color: rgba(255, 255, 255, 0.42); }
.digest-status.unconfigured .ds-dot { background: var(--fg-3); opacity: 0.5; }
.digest-status .ds-link {
  color: inherit;
  text-decoration: none;
  font-family: var(--font-display);
  font-size: 9px;
}
.digest-status .ds-link:hover { text-decoration: underline; }
@keyframes ds-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.75); }
}
