/* Hashling — dark, data-first, density with identity.
   Self-hosted fonts only: no Google Fonts request, so the page works offline
   and under a strict CSP. Tile art is generated from the address, so a project
   with no image still reads as itself. */

/* Oleo Script: the wordmark ONLY, subset to the eight glyphs in "Hashling"
   (2.2KB). A brush script reads warm and character-driven, which suits the
   name, and it's unmistakable next to the geometric sans every launchpad uses.
   It never touches UI text or contract addresses. */
/* Wordmark candidates, all subset to just the logo glyphs. Swap --display
   below to compare. Mogra: heavy, warm, holds at small sizes. Oleo Script:
   brush script, needs mixed case to flow. Mouse Memoirs: condensed glyphic,
   tightens up small. Oswald: neutral condensed grotesque, built for caps. */
@font-face {
  font-family: 'Mogra';
  src: url('fonts/Mogra-Wordmark.ttf?v=2') format('truetype');
  font-display: swap;
}
@font-face {
  font-family: 'Oleo Script';
  src: url('fonts/OleoScript-Wordmark.ttf?v=2') format('truetype');
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: 'Mouse Memoirs';
  src: url('fonts/Mouse_Memoirs-Wordmark.ttf?v=2') format('truetype');
  font-display: swap;
}
@font-face {
  font-family: 'Oswald Wordmark';
  src: url('fonts/Oswald-Wordmark.ttf?v=2') format('truetype');
  font-display: swap;
}

/* Geologica: everything else. Variable, so one file covers every weight. */
@font-face {
  font-family: 'Geologica';
  src: url('fonts/Geologica-Latin.ttf?v=2') format('truetype');
  font-weight: 100 900;
  font-display: swap;
}

:root {
  --bg: #0b0d10;
  --panel: #14171c;
  --panel-2: #1b1f26;
  --line: #262c35;
  --text: #e8edf4;
  --muted: #8b95a4;
  --dim: #5e6875;

  --hatched: #6b7a8f;
  --bonding: #4a9eff;
  --near: #f2b544;
  --graduated: #46d17f;
  --unclassified: #6f5bd0;
  --flag: #ff6b5e;

  --radius: 10px;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --sans: 'Geologica', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --display: 'Mogra', Georgia, serif;   /* candidates: Mogra | Oleo Script | Mouse Memoirs | Oswald Wordmark */
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
}

a { color: inherit; text-decoration: none; }

/* ----------------------------------------------------------- sample banner */

.live-banner {
  background: #12261a;
  color: #7fe3a6;
  border-bottom: 1px solid #1e4030;
  text-align: center;
  padding: 7px 12px;
  font-size: 12px;
  letter-spacing: 0.02em;
}

/* ------------------------------------------------------------------ header */

header {
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}

.header-inner {
  max-width: 1560px;
  margin: 0 auto;
  padding: 18px 20px;
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

.logo {
  font-family: var(--display);
  font-weight: 400;
  /* Scripts read smaller than their point size: the x-height sits low and the
     ascenders eat the box, so it needs a bump to hold its own in the header.
     All caps also loses the connecting flow a script is drawn for, so it gets
     a touch of tracking back to stop the letters colliding. */
  font-size: 40px;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  display: flex;
  align-items: center;
  /* Mogra's glyphs ride high in the em box, so centring the boxes leaves the
     mark sitting visually low. Trim the line box and nudge the mark to match
     the optical centre of the letterforms rather than their metrics. */
  line-height: 1;
  gap: 12px;
}

.logo-mark {
  /* Cracked shell. Inline SVG so it scales, inherits the palette, and costs no
     request. The crack is its own path — it can widen with bonding progress
     later, letting one shape serve as logo, favicon, and status icon. */
  width: 30px;
  height: 38px;
  transform: translateY(-2px);
  flex-shrink: 0;
  overflow: visible;
}

/* The header was 1560px wide with everything crammed into the left third.
   The nav takes the slack so the three groups — mark, navigation, search —
   sit across the measure instead of huddling. */
.nav {
  display: flex;
  gap: 28px;
  align-items: center;
  justify-content: center;
  flex: 1 1 auto;
  margin: 0 24px;
  font-size: 14px;
}

.nav a { color: var(--muted); padding: 4px 0; border-bottom: 2px solid transparent; }
.nav a:hover { color: var(--text); }
.nav a[aria-current="true"] { color: var(--text); border-bottom-color: var(--graduated); }

/* Kept for the hero; the header is navigation now. */
.tagline {
  /* Sits beside a 40px wordmark, so it needs weight and air or it reads as a
     caption instead of the promise. */
  color: var(--text);
  font-size: 16px;
  font-weight: 500;
  letter-spacing: 0.01em;
  padding-left: 22px;
  margin-left: 10px;
  border-left: 1px solid var(--line);
  line-height: 1;
}

.search {
  flex: 0 1 360px;
  max-width: 420px;
}

.search input {
  width: 100%;
  padding: 9px 12px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--text);
  font-family: var(--mono);
  font-size: 13px;
}

.search input:focus { outline: none; border-color: var(--bonding); }
.search input::placeholder { color: var(--dim); font-family: var(--sans); }

/* ------------------------------------------------------------------ layout */

/* Wider since the ad column arrived: at 1240px the 300px rail cost the board a
   whole column of tiles. 1560 leaves ~1200px for the grid, which is six tiles
   across at the 178px minimum — the density the board had before the rail. */
main { max-width: 1560px; margin: 0 auto; padding: 20px; }

.hero {
  padding: 8px 0 22px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 18px;
}

.hero h1 {
  margin: 0 0 6px;
  font-size: 30px;
  letter-spacing: -0.02em;
}

.hero p { margin: 0; color: var(--muted); max-width: 78ch; font-size: 15px; }

/* ------------------------------------------------------------------ filters */

.filters {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 16px;
}

.chip {
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  cursor: pointer;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.chip:hover { border-color: var(--dim); color: var(--text); }
.chip[aria-pressed="true"] { background: var(--panel-2); color: var(--text); border-color: var(--dim); }
.chip .count { color: var(--dim); font-family: var(--mono); font-size: 11px; }

/* Anchor, so it needs the underline off; tinted so it does not read as one of
   the lifecycle filters sitting next to it. */
.chip-promoted { text-decoration: none; color: var(--near); border-color: var(--near); }
.chip-promoted:hover { color: var(--text); }

.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dim); }
.dot.hatched { background: var(--hatched); }
.dot.bonding { background: var(--bonding); }
.dot.near_graduation { background: var(--near); }
.dot.graduated { background: var(--graduated); }
.dot.unclassified { background: var(--unclassified); }
/* Amber, not grey: something happened here. Greying it out would repeat the
   mistake of treating an exited project as an untouched one. */
.dot.abandoned { background: #d08a3c; }

.sort { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.sort label { color: var(--dim); font-size: 12px; }

select {
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 13px;
}

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

.board {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
  gap: 12px;
}

.tile {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  transition: border-color .12s, transform .12s;
  display: flex;
  flex-direction: column;
}

.tile:hover { border-color: var(--dim); transform: translateY(-1px); }

.tile-art {
  aspect-ratio: 1 / 1;
  position: relative;
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 26px;
  letter-spacing: -0.02em;
  color: rgba(255,255,255,.9);
  text-shadow: 0 1px 8px rgba(0,0,0,.5);
}

.tile-age {
  position: absolute;
  top: 6px; left: 6px;
  background: rgba(0,0,0,.55);
  color: #cfd6e0;
  font-family: var(--mono);
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 5px;
}

.tile-badges {
  position: absolute;
  top: 6px; right: 6px;
  display: flex;
  gap: 4px;
}

.badge {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 5px;
  font-weight: 600;
  letter-spacing: .02em;
}

.badge.verified { background: rgba(70,209,127,.18); color: var(--graduated); }
.badge.flagged { background: rgba(255,107,94,.18); color: var(--flag); }
.badge.awake { background: rgba(242,181,68,.18); color: var(--near); margin-left: 8px; }

/* ---------------------------------------------------------------- movement */

/* Paid placement lives in its own column, never interleaved with the board.
   The separation is the product promise made visible. */
/* Page-wide, so the rail is on every view rather than only the board. One
   column until something is actually sold: a hidden aside is display:none and
   creates no grid item, but the track it sits in is still reserved, which
   silently narrows the content to make room for an empty rail. */
.page-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
  align-items: start;
}
.page-layout.has-featured { grid-template-columns: minmax(0, 1fr) 300px; }

/* Below 900px the rail stacks under the content rather than squeezing it. On a
   phone the board is already down to two tiles a row, and taking width from it
   for an ad column would leave neither readable — which is exactly why the
   promoted page exists: on mobile the ads get a page of their own instead of
   fighting the board for the same 400px. */
.page-main { min-width: 0; }

.featured {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px;
  background: var(--panel);
}
.featured-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 4px;
}
.featured-title {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
}
/* Said in words at the top of the column, not left to a badge to imply. */
.featured-tag {
  font-size: 11px;
  color: var(--near);
  border: 1px solid var(--near);
  border-radius: 999px;
  padding: 1px 7px;
}
.featured-tile { margin-top: 10px; }
.featured-paid {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--dim);
  padding: 6px 8px 2px;
}

/* The rail drops below the board rather than squeezing it. Both selectors are
   listed because .has-featured outranks a bare .page-layout, and a media query
   adds no specificity of its own — so the one-column rule has to match it. */
@media (max-width: 900px) {
  .page-layout,
  .page-layout.has-featured { grid-template-columns: minmax(0, 1fr); }
}

/* A claim, not a measurement — styled so the two never read alike. */
.statement-panel { margin-top: 18px; border-left: 3px solid var(--near); }
.statement-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.statement-tag {
  font-size: 11px;
  color: var(--near);
  border: 1px solid var(--near);
  border-radius: 999px;
  padding: 1px 7px;
}
.statement-headline { margin: 10px 0 4px; font-size: 15px; }
.statement-body { margin: 0 0 10px; color: var(--text); white-space: pre-wrap; max-width: 72ch; }
.statement-links { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
.statement-link {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
  word-break: break-all;
}
.statement-link:hover { color: var(--text); }
.statement-past { border-top: 1px solid var(--line); padding-top: 10px; margin-top: 10px; }

.promote-form { display: flex; flex-direction: column; gap: 10px; margin: 12px 0; max-width: 60ch; }
.promote-field { display: flex; flex-direction: column; gap: 4px; }
.promote-field > span { font-size: 12px; color: var(--muted); }
.promote-field input,
.promote-field textarea {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--text);
  padding: 8px 10px;
  font: inherit;
  font-size: 13px;
}
.promote-field textarea { min-height: 84px; resize: vertical; }

/* The one money-adjacent block on a project page, deliberately set apart from
   the panels that report what the chain says. */
.promote-panel {
  margin-top: 18px;
  border-style: dashed;
}
.promote-panel .btn { margin-top: 10px; }

.fill-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.fill-row label { color: var(--muted); font-size: 12px; margin-right: 2px; }

.move-body { margin-top: 14px; }

.move-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 72px 84px 128px 72px;
  gap: 12px;
  align-items: center;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  margin-bottom: 6px;
  color: var(--text);
  text-decoration: none;
}
.move-row:hover { background: var(--panel-2); }

.move-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.move-pct,
.move-delta,
.move-eth,
.move-when {
  font-family: var(--mono);
  font-size: 13px;
  text-align: right;
}
.move-pct { color: var(--muted); }
.move-eth { color: var(--muted); }
.move-when { color: var(--dim); }

/* Direction is the point of the row, so it is the one thing that carries
   colour — matching the chart's buy/sell dots rather than inventing a scale. */
.move-delta.up { color: var(--graduated); }
.move-delta.down { color: #d0603c; }

@media (max-width: 640px) {
  .move-row { grid-template-columns: minmax(0, 1fr) 76px 108px 64px; }
  .move-pct { display: none; }
}

.tile-body { padding: 8px 9px 10px; }

.tile-name {
  font-weight: 600;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tile-symbol {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--dim);
  margin-bottom: 7px;
}

.tile-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 11px;
  color: var(--muted);
  margin-top: 6px;
}

.status-label { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; }

.bar {
  height: 4px;
  background: var(--panel-2);
  border-radius: 3px;
  overflow: hidden;
}

.bar > span { display: block; height: 100%; background: var(--bonding); }
.bar.near > span { background: var(--near); }

.untouched { color: var(--dim); font-style: italic; }

/* ------------------------------------------------------------------ detail */

.back { color: var(--muted); font-size: 13px; display: inline-block; margin-bottom: 14px; }
.back:hover { color: var(--text); }

.detail-head { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }

.detail-art {
  width: 96px; height: 96px;
  border-radius: 12px;
  display: grid; place-items: center;
  font-weight: 800; font-size: 30px;
  color: rgba(255,255,255,.92);
  flex-shrink: 0;
}

.detail-title h1 { margin: 0 0 2px; font-size: 24px; letter-spacing: -0.02em; }
.detail-title .sym { font-family: var(--mono); color: var(--muted); }

.ca-row { display: flex; gap: 8px; align-items: center; margin-top: 10px; flex-wrap: wrap; }

.ca {
  font-family: var(--mono);
  font-size: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 7px 10px;
  color: var(--text);
  word-break: break-all;
}

.btn {
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 8px;
  padding: 7px 12px;
  cursor: pointer;
  font-size: 12px;
}

.btn:hover { border-color: var(--dim); }
.btn.copied { color: var(--graduated); border-color: var(--graduated); }

.panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
  margin-top: 20px;
}

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px;
}

.panel h3 {
  margin: 0 0 10px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--dim);
  font-weight: 600;
}

.kv { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; font-size: 13px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; font-family: var(--mono); font-size: 12px; }

.flag-item {
  border-left: 2px solid var(--flag);
  padding: 8px 10px;
  background: rgba(255,107,94,.06);
  border-radius: 0 6px 6px 0;
  margin-bottom: 8px;
}

.flag-item strong { color: var(--flag); font-size: 12px; display: block; margin-bottom: 2px; }
.flag-item span { color: var(--muted); font-size: 12px; }

.note {
  color: var(--dim);
  font-size: 11px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.comment-locked {
  color: var(--muted);
  font-size: 13px;
  padding: 14px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  text-align: center;
}

.comment-locked b { color: var(--text); }

footer {
  max-width: 1240px;
  margin: 40px auto 0;
  padding: 18px 20px 40px;
  border-top: 1px solid var(--line);
  color: var(--dim);
  font-size: 12px;
}

.empty { color: var(--muted); padding: 40px; text-align: center; }

/* ------------------------------------------------------------- community */

.wallet-status { color: var(--muted); font-size: 12px; margin: 8px 0; min-height: 1em; }

.composer textarea {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  font-family: var(--sans);
  font-size: 13px;
  padding: 9px 10px;
  resize: vertical;
}

.composer textarea:focus { outline: none; border-color: var(--bonding); }

.composer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 8px;
}

/* Stake is share of supply, never a dollar value. */
.stake-badge {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--graduated);
  background: rgba(70,209,127,.12);
  border-radius: 5px;
  padding: 3px 7px;
}

.comment-list { margin-top: 12px; }

.comment {
  border-top: 1px solid var(--line);
  padding: 10px 0;
}

.comment-head {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-bottom: 4px;
}

.comment-addr { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.comment-stake { font-size: 11px; color: var(--graduated); }
.comment-body { font-size: 13px; }

.coverage {
  margin-top: 10px !important;
  color: var(--dim);
  font-size: 12px;
  font-family: var(--mono);
}

/* Loud on purpose: a visitor who posts must not believe it published. */
.local-only {
  margin-top: 12px;
  padding: 8px 10px;
  border: 1px dashed var(--near);
  border-radius: 8px;
  color: var(--near);
  font-size: 12px;
}

/* ------------------------------------------------------------- coverage */

.gap-item {
  border-left: 2px solid var(--near);
  padding: 8px 10px;
  background: rgba(242,181,68,.06);
  border-radius: 0 6px 6px 0;
  margin-bottom: 8px;
}

.gap-item strong { color: var(--near); font-size: 12px; display: block; margin-bottom: 2px; }
.gap-item span { color: var(--muted); font-size: 12px; }

/* ---------------------------------------------------------- life support */

.lede {
  max-width: 60ch;
  color: var(--fg-2, #b9c2cc);
  line-height: 1.55;
  margin: 0 0 22px;
}

ul.plain {
  margin: 0;
  padding-left: 18px;
  color: var(--fg-2, #b9c2cc);
  line-height: 1.5;
}

ul.plain li {
  margin-bottom: 8px;
}

.muted {
  color: var(--fg-3, #7d8892);
  line-height: 1.5;
}

.muted.small {
  font-size: 12px;
  max-width: 70ch;
  margin-top: 18px;
}

/* The guarantee panel is deliberately the loudest thing on the page: it is the
   product's whole argument, and burying it would undercut it. */
.panel-guarantee {
  border-color: rgba(127, 227, 166, 0.35);
}

.panel-guarantee h3 {
  color: #7fe3a6;
}

/* -------------------------------------------------------------- showcase */

.showcase-section {
  margin-bottom: 40px;
}

.section-head {
  font-size: 15px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-3, #7d8892);
  margin: 0 0 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line, #1e242b);
}

.showcase-section .muted {
  max-width: 72ch;
  margin: 0 0 18px;
  font-size: 13px;
}

.share-wrap {
  display: inline-flex;
  gap: 8px;
}

/* ----------------------------------------------------------------- chart */

.chart-panel {
  margin-bottom: 18px;
}

.chart-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 10px;
  gap: 12px;
}

.chart-title {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-3, #7d8892);
}

.chart-range {
  font-size: 12px;
  color: var(--fg-3, #7d8892);
  font-variant-numeric: tabular-nums;
}

.chart {
  width: 100%;
  height: 220px;
  display: block;
  overflow: visible;
}

.chart-line {
  fill: none;
  stroke: #7fe3a6;
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
}

/* Buys and sells are coloured, but the LINE is not: tinting the whole series
   by the last trade's side is how a chart starts editorialising. */
.chart-dot.buy { fill: #7fe3a6; }
.chart-dot.sell { fill: #d0603c; }

/* Swatches take the dot colours verbatim, so the legend cannot drift from the
   chart it explains. */
.chart-legend { display: flex; gap: 14px; margin: 6px 0 0; }
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.swatch { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.swatch.buy { background: #7fe3a6; }
.swatch.sell { background: #d0603c; }

/* Graduation is an event, not a trade — dashed and unsaturated so it reads as
   an annotation on the series rather than part of it. */
.chart-grad-rule {
  stroke: #6b7a8c;
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
}

.chart-foot {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 12px;
  color: var(--fg-3, #7d8892);
  font-variant-numeric: tabular-nums;
}

/* Wallet chooser. Only rendered when there is an actual choice — a picker with
   one option is friction without a decision. */
.wallet-picker {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 8px 0;
}
.wallet-icon {
  width: 16px;
  height: 16px;
  border-radius: 4px;
  margin-right: 6px;
  vertical-align: -3px;
}

/* Status text people are meant to act on, so it is not muted grey like the
   explanatory copy around it. A refusal that reads as a footnote gets missed. */
.promote-status {
  font-size: 13px;
  color: var(--text);
  margin: 10px 0 0;
  min-height: 1em;
}
.promote-status.warn {
  color: var(--near);
  border-left: 3px solid var(--near);
  padding-left: 10px;
}
.promote-status.ok { color: var(--graduated); }

/* The wallet a signature must come from, stated before anyone signs. */
.promote-required {
  font-size: 13px;
  color: var(--muted);
  margin: 10px 0 0;
}
.promote-required code {
  font-family: var(--mono);
  color: var(--text);
  word-break: break-all;
}

.promote-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }

/* A link that will be dropped, said while it is being typed rather than
   discovered afterwards by its absence. */
.field-hint { font-size: 12px; color: var(--near); }
.promote-field input.invalid,
.promote-field textarea.invalid { border-color: var(--near); }

/* The pay step. Boxed and quiet — the address is the only thing that matters
   and it must be readable and copyable without hunting. */
.pay-box {
  border: 1px dashed var(--line);
  border-radius: 8px;
  padding: 12px;
  margin: 12px 0;
}
.pay-label { font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.pay-address {
  display: block;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--text);
  word-break: break-all;
  margin-bottom: 10px;
}

/* Ad cards. Distinct from board tiles on purpose — a tile reports what the
   chain says, a card carries what the project paid to say. */
.ad-card {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-2);
  margin-bottom: 10px;
  overflow: hidden;
}
.ad-main { display: block; color: inherit; }
.ad-art {
  height: 110px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.ad-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ad-art-label {
  font-family: var(--mono);
  font-size: 20px;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.85);
}
.ad-body { padding: 10px 12px 8px; }
.ad-name { font-size: 14px; font-weight: 600; }
.ad-symbol { font-family: var(--mono); font-size: 12px; color: var(--muted); }
/* Launch descriptions run to paragraphs — one project's is 400 characters of
   manifesto. Clamped to four lines so a card stays a card; the full text is on
   the project page. */
.ad-copy {
  font-size: 13px;
  color: var(--text);
  margin-top: 6px;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.ad-links { display: flex; gap: 8px; padding: 0 12px 8px; flex-wrap: wrap; }
.ad-link {
  font-size: 12px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
}
.ad-link:hover { color: var(--text); border-color: var(--dim); }
.ad-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 12px 10px;
}
/* Never subtle. A reader should not have to work out which parts were paid for. */
.ad-mark {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--near);
  border: 1px solid var(--near);
  border-radius: 4px;
  padding: 0 5px;
}
.ad-paid { font-family: var(--mono); font-size: 11px; color: var(--dim); }

.featured-sell {
  display: block;
  margin-top: 12px;
  font-size: 12px;
  color: var(--muted);
  border-top: 1px solid var(--line);
  padding-top: 10px;
}
.featured-sell:hover { color: var(--text); }

.promoted-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 14px;
  margin: 16px 0;
}
.featured-more {
  display: block;
  font-size: 12px;
  color: var(--muted);
  margin-top: 6px;
}
.featured-more:hover { color: var(--text); }

/* Movement on an ad — the checkable part, so it gets the colour. */
.ad-move {
  font-family: var(--mono);
  font-size: 12px;
  padding: 0 12px 6px;
}
.ad-move.up { color: var(--graduated); }
.ad-move.down { color: #d0603c; }
.ad-move.flat { color: var(--dim); }

/* Curation wears a different badge from advertising, in a different colour,
   because conflating them would misrepresent a project that paid nothing. */
.featured-tag.curated,
.ad-mark.curated {
  color: var(--bonding);
  border-color: var(--bonding);
}

/* Underlined, unlike the rest of the muted text — it is the one thing on the
   page meant to be copied out and taken elsewhere. */
.feed-link { color: var(--near); text-decoration: underline; }
.feed-link:hover { color: var(--text); }

/* ------------------------------------------------------------- subscribe */

/* Anchored popover rather than a modal: subscribing is a small decision and
   should not take over the page. */
.subscribe { position: relative; display: inline-block; }

.subscribe-pop {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 20;
  width: min(420px, 90vw);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
}

.subscribe-note { margin: 0 0 8px; color: var(--muted); font-size: 12px; }
.subscribe-pop .subscribe-note:last-child { margin: 10px 0 0; }

.subscribe-url { display: flex; gap: 8px; align-items: center; }

/* The URL is the thing being copied, so it gets to be readable — but it is
   long, and it must never widen the popover past the viewport. */
.subscribe-url code {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 6px 8px;
  overflow-x: auto;
  white-space: nowrap;
}

.subscribe-readers { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

/* Full width on a phone: an absolutely-positioned popover anchored to a button
   near the right edge would otherwise hang off the screen. */
@media (max-width: 640px) {
  .subscribe { position: static; }
  .subscribe-pop { left: 0; right: 0; width: auto; }
}

/* Row alignment: `.ca`, `.btn` and the `.subscribe` wrapper each computed a
   slightly different height — different padding, and an inline-block wrapper
   sitting in a flex row carries a baseline offset a bare button does not. Pin
   one line-height and let the flex row centre them, so the chip, the buttons
   and the popover trigger all sit on the same line. */
.ca-row { align-items: center; row-gap: 8px; }
.ca-row > * { margin: 0; }
.ca, .ca-row .btn { line-height: 1.25; }
.subscribe { display: inline-flex; align-items: center; }

/* Breathing room before the first panel: the action row was sitting flush
   against the chart, which read as part of it rather than as the header. */
.ca-row { margin-bottom: 4px; }
.detail-title { margin-bottom: 14px; }
