/*
 * portal.css — FastTrade
 *
 * Design direction: a trading terminal that grew in a desert. Near-black with a
 * warm undertone rather than the usual blue-black, cactus green as the primary,
 * sand amber as the accent, and tabular monospace for every number so columns
 * line up and prices stop jittering as they change.
 *
 * Deliberately sharper than glassmorphism: 8px radii, hairline borders, and
 * light used as a thin edge rather than a heavy blur. Speed is signalled by the
 * ticker rule and the live pulse, not by animating everything.
 *
 * Components are plain classes with no framework, so a Uiverse component can be
 * pasted in with its own CSS block and will not fight the layout.
 */

:root {
  --bg:        #0a0b09;
  --bg-raise:  #11130f;
  --panel:     #14170f;
  --line:      #23281c;
  --line-soft: #1a1e15;

  --ink:       #eaf0e2;
  --ink-dim:   #8d9781;
  --ink-faint: #5d6653;

  --cactus:    #4ade80;
  --cactus-d:  #16a34a;
  --sand:      #fbbf24;
  --clay:      #fb7185;
  --sky:       #38bdf8;

  --r: 8px;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  color: var(--ink);
  font: 15px/1.62 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  background-color: var(--bg);
  /* Faint dot grid — a plotting surface, not a gradient wash. */
  background-image:
    radial-gradient(circle at 1px 1px, rgba(74, 222, 128, .055) 1px, transparent 0);
  background-size: 26px 26px;
  background-attachment: fixed;
}

a { color: var(--cactus); text-decoration: none; }
a:hover { color: #86efac; }

.wrap { max-width: 960px; margin: 0 auto; padding: 0 20px 80px; }

.num, code, pre, .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ── masthead ─────────────────────────────────────────────────────────── */

.nav {
  display: flex; align-items: center; gap: 22px;
  padding: 20px 0; margin-bottom: 34px;
  border-bottom: 1px solid var(--line-soft);
  /* Wraps. It did not, and every page scrolled sideways by 20px on a 360px
     phone once the masthead grew a fifth link -- on every page, not just this
     one, and invisibly, because nobody tests a nav. */
  flex-wrap: wrap; row-gap: 8px;
}
@media (max-width: 460px) {
  /* The live pulse is decoration; the links are navigation. At this width the
     decoration goes rather than the destinations. */
  .nav { gap: 14px; }
  .nav .pulse { display: none; }
  .nav .spacer { flex-basis: 100%; height: 0; }
}
.brand {
  display: flex; align-items: baseline; gap: 2px;
  font-weight: 800; font-size: 18px; letter-spacing: -.035em; color: var(--ink);
}
.brand b { color: var(--cactus); font-weight: 800; }
.brand:hover { color: var(--ink); }
.nav .spacer { flex: 1; }
.nav a:not(.brand) {
  color: var(--ink-dim); font-size: 13.5px; font-weight: 500;
  padding: 6px 0; border-bottom: 1px solid transparent;
}
.nav a:not(.brand):hover { color: var(--ink); border-bottom-color: var(--cactus); }

/* Live pulse — the feed is running. */
.pulse {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint);
}
.pulse i {
  width: 7px; height: 7px; border-radius: 50%; background: var(--cactus);
  box-shadow: 0 0 0 0 rgba(74, 222, 128, .6);
  animation: beat 2.2s ease-out infinite;
}
@keyframes beat {
  0%   { box-shadow: 0 0 0 0 rgba(74, 222, 128, .55); }
  70%  { box-shadow: 0 0 0 9px rgba(74, 222, 128, 0); }
  100% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0); }
}

/* ── panels ───────────────────────────────────────────────────────────── */

.card {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 26px 28px;
}
.card + .card { margin-top: 18px; }

/* One hairline of colour along the top edge, instead of a glow. */
.card.lead::after {
  content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1px;
  border-radius: var(--r) var(--r) 0 0;
  background: linear-gradient(90deg, transparent, var(--cactus) 22%, var(--sand) 62%, transparent);
}

h1 {
  font-size: clamp(30px, 5vw, 42px); line-height: 1.08;
  letter-spacing: -.045em; margin: 0 0 14px; font-weight: 800;
}
h2 { font-size: 17px; letter-spacing: -.02em; margin: 0 0 16px; font-weight: 700; }
.eyebrow {
  font-family: var(--mono); font-size: 11px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--cactus); margin: 0 0 14px;
}
.lede { color: var(--ink-dim); font-size: 16px; margin: 0 0 24px; max-width: 60ch; }
.muted { color: var(--ink-dim); }
.faint { color: var(--ink-faint); }
.small { font-size: 13px; }

/* ── buttons ──────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px 19px; border: 1px solid transparent; border-radius: var(--r);
  font: 600 14px/1 inherit; cursor: pointer; color: #06210f;
  background: var(--cactus);
  transition: background .14s ease, transform .14s ease, box-shadow .14s ease;
}
.btn:hover { background: #6ee79b; transform: translateY(-1px);
             box-shadow: 0 6px 18px rgba(74, 222, 128, .18); }
.btn:active { transform: translateY(0); }
.btn[disabled] {
  background: transparent; color: var(--ink-faint);
  border-color: var(--line); cursor: not-allowed; transform: none; box-shadow: none;
}
.btn.ghost {
  background: transparent; color: var(--ink); border-color: var(--line);
}
.btn.ghost:hover { background: var(--bg-raise); border-color: var(--cactus); box-shadow: none; }
.btn.danger { background: transparent; color: var(--clay); border-color: rgba(251,113,133,.4); }
.btn.danger:hover { background: rgba(251,113,133,.1); box-shadow: none; }

/* ── forms ────────────────────────────────────────────────────────────── */

.field { margin-bottom: 15px; }
.field label {
  display: block; font-size: 12px; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 7px;
}
.input {
  width: 100%; padding: 11px 13px; border-radius: var(--r);
  background: var(--bg); color: var(--ink); border: 1px solid var(--line);
  font: 14px/1.4 inherit; transition: border-color .14s ease, box-shadow .14s ease;
}
.input:focus {
  outline: none; border-color: var(--cactus);
  box-shadow: 0 0 0 3px rgba(74, 222, 128, .13);
}
.input::placeholder { color: var(--ink-faint); }
.input.mono { font-family: var(--mono); }

/* ── notices ──────────────────────────────────────────────────────────── */

.notice {
  border-radius: var(--r); padding: 12px 14px; margin-bottom: 18px;
  font-size: 13.5px; border-left: 2px solid; background: var(--bg-raise);
}
.notice.error { border-color: var(--clay); color: #ffd0d7; }
.notice.ok    { border-color: var(--cactus); color: #c6f6d5; }
.notice.warn  { border-color: var(--sand); color: #fde9bb; }

/* ── pricing ──────────────────────────────────────────────────────────── */

.tiers { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 700px) { .tiers { grid-template-columns: 1fr; } }

.tier .price {
  font-family: var(--mono); font-size: 32px; font-weight: 700;
  letter-spacing: -.03em; margin: 4px 0 2px;
}
.tier .price span { font-size: 13px; font-weight: 400; color: var(--ink-faint); }
.tier ul { list-style: none; padding: 0; margin: 18px 0 22px; }
.tier li {
  padding: 7px 0 7px 22px; position: relative;
  color: var(--ink-dim); font-size: 13.5px;
  border-bottom: 1px solid var(--line-soft);
}
.tier li:last-child { border-bottom: 0; }
.tier li::before { content: "+"; position: absolute; left: 0; color: var(--cactus); font-weight: 700; }
.tier li.no { color: var(--ink-faint); }
.tier li.no::before { content: "\2013"; color: var(--ink-faint); }
.tier.soon { opacity: .6; }

.badge {
  display: inline-block; padding: 3px 8px; border-radius: 4px;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; border: 1px solid;
}
.badge.live { color: var(--cactus); border-color: rgba(74,222,128,.35); background: rgba(74,222,128,.08); }
.badge.soon { color: var(--ink-faint); border-color: var(--line); }
.badge.off  { color: var(--clay); border-color: rgba(251,113,133,.3); background: rgba(251,113,133,.07); }

/* ── key reveal ───────────────────────────────────────────────────────── */

.keybox {
  display: flex; gap: 10px; align-items: center;
  background: var(--bg); border: 1px solid var(--line);
  border-left: 2px solid var(--sand);
  border-radius: var(--r); padding: 13px; margin: 14px 0;
}
.keybox code { flex: 1; font-size: 13px; word-break: break-all; color: #d9f7c4; }

/* ── data ─────────────────────────────────────────────────────────────── */

table { width: 100%; border-collapse: collapse; font-size: 13px; }
th {
  text-align: left; padding: 8px 10px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint);
  border-bottom: 1px solid var(--line);
}
td { padding: 10px; border-bottom: 1px solid var(--line-soft); }
td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
tr:last-child td { border-bottom: 0; }

.stat-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 1px; background: var(--line-soft);
  border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; margin: 2px 0 20px;
}
.stat { background: var(--panel); padding: 15px 16px; }
.stat .n {
  font-family: var(--mono); font-size: 23px; font-weight: 700;
  letter-spacing: -.02em; line-height: 1.1;
}
.stat .l {
  font-family: var(--mono); font-size: 10px; color: var(--ink-faint);
  text-transform: uppercase; letter-spacing: .1em; margin-top: 5px;
}

.steps { counter-reset: s; list-style: none; padding: 0; margin: 0; }
.steps li {
  counter-increment: s; position: relative;
  padding: 0 0 22px 38px; margin-left: 11px;
  border-left: 1px solid var(--line);
}
.steps li:last-child { border-left-color: transparent; padding-bottom: 0; }
.steps li::before {
  content: counter(s, decimal-leading-zero);
  position: absolute; left: -12px; top: -3px;
  width: 24px; height: 24px; border-radius: 5px;
  display: grid; place-items: center;
  background: var(--bg-raise); border: 1px solid var(--line);
  font-family: var(--mono); font-size: 10px; font-weight: 700; color: var(--cactus);
}

pre.snippet {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r);
  padding: 14px; overflow-x: auto; font-size: 12.5px;
  color: var(--ink-dim); margin: 10px 0 16px;
}
pre.snippet b { color: var(--cactus); font-weight: 600; }

footer {
  margin-top: 44px; padding-top: 20px; border-top: 1px solid var(--line-soft);
  color: var(--ink-faint); font-size: 12.5px;
}
footer .client { font-family: var(--mono); }
footer .client b { color: var(--ink-dim); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .pulse i { animation: none; }
  .btn:hover { transform: none; }
}

/* ── meters ───────────────────────────────────────────────────────────────
 *
 * A bar for one bounded quantity: disk used, memory used, a shard's tick time
 * against its budget, engines against the ceiling.
 *
 * The fill is coloured by band rather than by a gradient, because these are
 * read to answer "is this fine" and three states answer it. The bands are
 * generous on purpose -- amber at 75% and clay at 90% -- so that a panel which
 * is mostly green stays worth glancing at. A meter that is always half red
 * teaches people to stop looking, which is worse than having no meter.
 */

.meter {
  height: 6px; border-radius: 3px; background: var(--line-soft);
  overflow: hidden; margin: 5px 0 0;
}
.meter > i { display: block; height: 100%; background: var(--cactus-d); }
.meter.warn > i { background: var(--sand); }
.meter.hot  > i { background: var(--clay); }

/* A shard row that is not being heard from. Not the same as one that was
 * stopped on purpose: colouring those alike teaches people to ignore it. */
tr.stale td { color: var(--clay); }
tr.stopped td { color: var(--ink-faint); }

/* ── ticket threads ───────────────────────────────────────────────────── */
/*
 * A reply to the buyer and a staff note are one button apart and have very
 * different consequences, so they must not look alike. Reading the label is
 * not enough: the mistake happens while somebody is scanning a thread, not
 * while they are reading one line carefully. So a note carries the sand accent
 * and a tinted ground, a staff reply carries the cactus one, and the buyer's
 * own messages are unmarked.
 */
.msg { border-top: 1px solid var(--line-soft); padding: 12px 0; }
.msg:first-child { border-top: 0; }
.msg .who {
  font-size: 12px; color: var(--ink-faint); margin: 0 0 5px;
  letter-spacing: .03em;
}
.msg .said { margin: 0; white-space: pre-wrap; }
.msg.staff { border-left: 2px solid var(--cactus-d); padding-left: 12px; }
.msg.note {
  border-left: 2px solid var(--sand); padding-left: 12px;
  background: rgba(251, 191, 36, .05);
}
.msg.note .who { color: var(--sand); }

/* A ticket whose claim is somebody else's. Stated rather than implied by a
 * missing button: "no button here" reads as a broken page. */
.held { color: var(--ink-dim); }

/* ── status dots ──────────────────────────────────────────────────────── */
/*
 * Never colour alone. Every dot sits beside the status word in the next
 * column, because roughly one man in twelve cannot tell the green from the
 * amber, and a status page is the worst possible place to require that.
 * Shape differs too: `down` is a ring rather than a disc.
 */
.dot {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  background: var(--ink-faint);
}
.dot.up       { background: var(--cactus); }
.dot.degraded { background: var(--sand); }
.dot.down     { background: transparent; border: 2px solid var(--clay);
                width: 8px; height: 8px; }
.dot.unknown  { background: var(--line); border: 1px solid var(--ink-faint); }

/* ══════════════════════════════════════════════════════════════════════
   The bench — the landing page's own idiom
   ══════════════════════════════════════════════════════════════════════

   Everything sold in this genre looks like a Discord advertisement: a purple
   gradient, a number with a plus in front of it, and no way to tell it from
   the scam three posts down. The one thing this product genuinely has that
   they do not is that it publishes its own control experiment — so the page
   is built to look like the instrument that produced it. A bench sheet:
   numbered sections, registration marks, hairlines, tabular figures, and a
   chart whose job is to stop the big number dominating rather than to help it.

   That is not decoration. A buyer who has been burned twice is not persuaded
   by a bigger number; they are persuaded by seeing the thing that would have
   caught a lie.
   ------------------------------------------------------------------------ */

/* Section markers. `§ 01 · SIGNAL` on a hairline, specimen-sheet style. */
.mark {
  display: flex; align-items: center; gap: 14px;
  margin: 38px 0 16px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-faint);
}
.mark::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, var(--line) 0%, transparent 100%);
}
.mark b { color: var(--cactus); font-weight: 600; }

/* Registration marks: four corner brackets, as on a technical drawing.
   Pure ::before/::after on the panel, so no extra elements in the markup. */
.reg { position: relative; }
.reg::before, .reg::after {
  content: ""; position: absolute; width: 9px; height: 9px;
  border: 1px solid var(--cactus); opacity: .45; pointer-events: none;
}
.reg::before { top: 9px; left: 9px; border-right: 0; border-bottom: 0; }
.reg::after  { bottom: 9px; right: 9px; border-left: 0; border-top: 0; }

/* ── The tape ──────────────────────────────────────────────────────────
   A strip of real signal shapes under the hero. Marquee, but slow and
   monospaced, so it reads as a wire rather than as an animation. Motion is
   the one thing on this page that must yield: `prefers-reduced-motion`
   stops it dead rather than slowing it, because a moving strip of text is
   exactly what that setting exists for. */
.tape {
  position: relative; overflow: hidden;
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
  padding: 9px 0; margin-top: 26px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.tape ul {
  display: flex; gap: 30px; margin: 0; padding: 0; list-style: none;
  width: max-content; animation: tape 48s linear infinite;
}
.tape li {
  font-family: var(--mono); font-size: 11.5px; color: var(--ink-faint);
  white-space: nowrap; letter-spacing: .02em;
}
.tape li b { color: var(--ink-dim); font-weight: 500; }
.tape li i { color: var(--cactus); font-style: normal; }
@keyframes tape { from { transform: translateX(0); }
                  to   { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .tape ul { animation: none; }
  .pulse i { animation: none; }
}

/* ── The control experiment ────────────────────────────────────────────
   A diverging bar around a zero baseline: the job is polarity, not
   magnitude.

   Colour is clay ↔ sky rather than the site's clay ↔ cactus, and that is a
   measured choice, not a taste one. Under deuteranopia the brand green and
   the clay separate by ΔE 7.5, which is inside the 6–8 band that is only
   legal with secondary encoding; clay against sky separates by 17.3 and
   passes outright. Red↔green is the classic failure and roughly one man in
   twelve cannot read it. Green stays the site's action colour — buttons,
   links, the pulse — because identity and polarity are different jobs and
   sharing one hue between them muddles both.

   Position relative to the zero rule and a signed, direct label carry the
   meaning as well, so nothing here depends on hue alone.

   The scale is capped at ±100% and the strategy bar runs off the end on
   purpose. A linear axis to +943% would render the −80.4% control as a
   nine-pixel stub and turn the page into the advertisement it is trying not
   to be. The clipping IS the statement: that figure is in-sample on one day
   and is not comparable to the arms beside it. */
.exp { display: grid; gap: 2px; margin: 4px 0 18px; }

/* Three columns: name, the diverging track, and the value. Nothing here is
   absolutely positioned, and that is the whole design of it. The first version
   floated each label over the track at a percentage offset computed from its
   bar -- which looked right at 1100px and pushed the page sideways at 640 and
   360, because a monospaced nowrap label does not scale with a percentage. Six
   widths, three of them broken, and each fix moved the breakage somewhere else.
   A column costs one grid track and is correct at every width by construction. */
.exp-row {
  display: grid; grid-template-columns: 150px minmax(90px, 1fr) minmax(0, 15em);
  align-items: center; gap: 14px; padding: 7px 0;
}
.exp-name {
  font-family: var(--mono); font-size: 12px; color: var(--ink-dim);
  text-align: right; letter-spacing: .02em;
}
.exp-track {
  position: relative; height: 22px;
  /* The zero rule, down the middle of the track. */
  background: linear-gradient(90deg, transparent calc(50% - 1px),
              var(--line) calc(50% - 1px), var(--line) calc(50% + 1px),
              transparent calc(50% + 1px));
}
.exp-bar {
  position: absolute; top: 3px; height: 16px; min-width: 2px; border-radius: 3px;
}
.exp-bar.neg { right: 50%; background: var(--clay); border-radius: 3px 0 0 3px; }
.exp-bar.pos { left: 50%;  background: var(--sky);  border-radius: 0 3px 3px 0; }

/* Runs off the end. It fades rather than stopping, so it reads as "continues
   past the axis" instead of as a bar that happens to be wide. It was a stripe
   pattern first, which cut through the glyphs of the label then sitting on it
   and turned the most important caveat on the page into what looked like a
   rendering fault. */
.exp-bar.over {
  right: 0; border-radius: 3px 0 0 3px;
  background: linear-gradient(90deg, var(--sky) 0%, var(--sky) 42%,
              rgba(56, 189, 248, .12) 100%);
}

.exp-val {
  font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums;
  color: var(--ink); line-height: 1.35;
}
.exp-val b { font-weight: 600; }
.exp-note {
  display: block;
  font-family: var(--mono); font-size: 10.5px; color: var(--ink-faint);
  letter-spacing: .03em;
}
.exp-axis {
  display: grid; grid-template-columns: 150px minmax(90px, 1fr) minmax(0, 15em);
  gap: 14px;
  font-family: var(--mono); font-size: 10.5px; color: var(--ink-faint);
  padding-top: 6px; border-top: 1px solid var(--line-soft); margin-top: 4px;
}
.exp-axis > span:nth-child(2) { display: flex; justify-content: space-between; }

/* ── The unknowns panel ────────────────────────────────────────────────
   Sand-edged and the same size as the claims. Most pages put this in 9px
   grey at the bottom; giving it equal weight is the whole point, because it
   is the part a returning buyer checks first. */
.card.unknowns::after {
  content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1px;
  border-radius: var(--r) var(--r) 0 0;
  background: linear-gradient(90deg, transparent, var(--sand) 30%, var(--clay) 70%, transparent);
}
.unknowns .eyebrow { color: var(--sand); }
.unknowns dl { margin: 0; display: grid; gap: 14px; }
.unknowns dt {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .04em;
  color: var(--sand); margin-bottom: 3px;
}
.unknowns dd { margin: 0; color: var(--ink-dim); font-size: 13.5px; }

/* ── Figures ───────────────────────────────────────────────────────────
   A hero number and its label, in a row. Tabular so the columns line up. */
.figs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
  gap: 1px; background: var(--line-soft);
  border: 1px solid var(--line-soft); border-radius: var(--r);
  overflow: hidden; margin: 2px 0 18px;
}
.fig { background: var(--panel); padding: 15px 16px; }
.fig .n {
  font-family: var(--mono); font-size: 21px; font-variant-numeric: tabular-nums;
  letter-spacing: -.02em; color: var(--ink); line-height: 1.2;
}
.fig .l {
  font-size: 11px; color: var(--ink-faint); margin-top: 3px;
  letter-spacing: .04em;
}
.fig.zero .n { color: var(--ink-faint); }

@media (max-width: 680px) {
  /* One column. The track keeps its zero rule and its bar, so the comparison
     still reads; the name and the value simply stop sharing a row with it. */
  .exp-row, .exp-axis { grid-template-columns: 1fr; gap: 3px; }
  .exp-name { text-align: left; }
  .exp-axis > span:first-child { display: none; }
  .exp-val { padding-bottom: 4px; }
}



/* ── The offer ─────────────────────────────────────────────────────────
   One product, one price, one button. The page sold a feed and an engine
   separately for a while and the split never survived the question a buyer
   actually asks -- "what do I do with this" -- so the card that used to be
   two is one, and it is the loudest thing below the hero. */
.offer {
  position: relative; overflow: hidden;
  background:
    radial-gradient(120% 140% at 12% 0%, rgba(74, 222, 128, .10), transparent 58%),
    radial-gradient(90% 120% at 95% 10%, rgba(251, 191, 36, .07), transparent 55%),
    var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 34px 32px;
}
.offer::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: linear-gradient(90deg, var(--cactus), var(--sand) 55%, var(--sky));
}
.offer h2 { font-size: 27px; letter-spacing: -.035em; margin: 0 0 8px; }
.offer .tagline { color: var(--ink-dim); margin: 0 0 22px; font-size: 15.5px; }

.offer-price {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 0 0 22px;
}
.offer-price b {
  font-family: var(--mono); font-size: 46px; font-weight: 700;
  letter-spacing: -.045em; color: var(--ink); line-height: 1;
}
.offer-price span { color: var(--ink-faint); font-size: 14px; }

/* Two columns of what you get, each ticked. Not a feature table -- a table
   invites comparison, and there is nothing left to compare it against. */
.gets {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 9px 26px; margin: 0 0 26px; padding: 0; list-style: none;
}
.gets li {
  display: grid; grid-template-columns: 17px 1fr; gap: 9px;
  font-size: 14.2px; color: var(--ink-dim); line-height: 1.5;
}
.gets li::before {
  content: "\2713"; color: var(--cactus); font-weight: 700; font-size: 13px;
  line-height: 1.6;
}
.gets li b { color: var(--ink); font-weight: 600; }

.btn.big {
  padding: 15px 30px; font-size: 15.5px; border-radius: 10px;
}

/* A claim with the number that backs it, three across. Advertising, but each
   one is a figure from this repository rather than an adjective. */
.pitch {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 16px; margin: 18px 0;
}
.pitch > div {
  background: var(--bg-raise); border: 1px solid var(--line-soft);
  border-radius: var(--r); padding: 18px 19px;
}
.pitch .big {
  font-family: var(--mono); font-size: 25px; letter-spacing: -.03em;
  color: var(--cactus); line-height: 1.15;
}
.pitch .big.warn { color: var(--sand); }
.pitch .big.cool { color: var(--sky); }
.pitch h3 { font-size: 14.5px; margin: 9px 0 5px; letter-spacing: -.01em; }
.pitch p { margin: 0; font-size: 13px; color: var(--ink-dim); line-height: 1.55; }

@media (max-width: 560px) {
  .offer { padding: 26px 20px; }
  .offer h2 { font-size: 23px; }
  .offer-price b { font-size: 38px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   The app: charts and book readings.

   Series colours are amber and blue, NOT the cactus green and clay red this
   sheet uses elsewhere. In every trading chart ever drawn, green and red mean
   up and down -- so using them for the ask and bid sides would be misread on
   sight. Amber and blue are semantically neutral here and were validated
   against --panel for colour-vision separation (worst-pair dE 21.7 protan,
   target is 8) and for 3:1 contrast. Do not "brighten" them: both sit inside
   the dark-mode lightness band and moving them out is what the check catches.
   ------------------------------------------------------------------------ */
:root {
  --ask: #b8860b;   /* what you pay    */
  --bid: #3f8fc4;   /* what you receive */
}

.app-head { display:flex; gap:20px; align-items:flex-start; margin:8px 0 18px; }
.app-title { font-size:26px; letter-spacing:-.03em; margin:2px 0 2px; }
.app-head .spacer { flex:1; }

.app-search { position:relative; margin-left:auto; min-width:min(320px, 60vw); }
.app-search input {
  width:100%; padding:9px 12px; background:var(--bg-raise);
  border:1px solid var(--line); border-radius:var(--r); color:var(--ink);
  font:inherit;
}
.app-search input:focus { outline:2px solid var(--cactus); outline-offset:1px; }
.app-results {
  position:absolute; z-index:20; top:calc(100% + 4px); left:0; right:0;
  margin:0; padding:4px; list-style:none; max-height:300px; overflow:auto;
  background:var(--bg-raise); border:1px solid var(--line);
  border-radius:var(--r);
}
.app-results a {
  display:block; padding:7px 10px; border-radius:6px;
  color:var(--ink); text-decoration:none; font-size:13px;
}
.app-results a:hover, .app-results a:focus { background:var(--panel); }

.app-card { margin-bottom:16px; }
.app-h2 { font-size:15px; letter-spacing:-.01em; margin:0 0 12px; }
summary.app-h2 { cursor:pointer; }

.app-bar { display:flex; align-items:center; gap:16px; margin-bottom:10px;
           flex-wrap:wrap; }
.app-bar .spacer { flex:1; }

/* Legend: always present for two series, so identity is never colour-alone. */
.legend { display:flex; gap:16px; margin:0; padding:0; list-style:none;
          font-size:12px; color:var(--ink-dim); flex-wrap:wrap; }
.legend li { display:flex; align-items:center; gap:6px; }
.sw { width:10px; height:10px; border-radius:2px; display:inline-block;
      flex:0 0 auto; }
.sw-ask { background:var(--ask); }
.sw-bid { background:var(--bid); }

/* Resolution switcher. One row, above the chart. */
.seg { display:flex; border:1px solid var(--line); border-radius:var(--r);
       overflow:hidden; }
.seg a {
  padding:5px 12px; font-size:12px; color:var(--ink-dim);
  text-decoration:none; border-right:1px solid var(--line);
  font-family:var(--mono);
}
.seg a:last-child { border-right:0; }
.seg a:hover { color:var(--ink); background:var(--bg-raise); }
.seg a.on { background:var(--cactus-d); color:#08150c; font-weight:600; }

.app-chart {
  position:relative; height:420px; width:100%;
  /* Room at the bottom for the library's attribution mark, which is required
     by its licence and which it positions inside its own container. Without
     the padding it is drawn ON TOP of the candles in the bottom-left corner. */
  padding-bottom:22px;
}
#tv-attr-logo { opacity:.45; }
.app-coverage { margin:10px 0 0; line-height:1.55; }

/* `display` is set explicitly here, so it BEATS the `hidden` attribute --
   `[hidden] { display: none }` is a UA default rule and loses to any author
   rule with the same or higher specificity. Without the guard below, the
   tooltip was permanently visible as an empty box under the chart, which is
   how it shipped the first time. */
.chart-tip[hidden] { display:none; }
.chart-tip {
  position:absolute; z-index:10; pointer-events:none;
  display:flex; flex-direction:column; gap:2px;
  padding:8px 10px; border-radius:var(--r);
  background:var(--bg); border:1px solid var(--line);
  font-size:11px; font-family:var(--mono); color:var(--ink);
  box-shadow:0 6px 20px rgba(0,0,0,.45);
}
.chart-tip span { display:flex; align-items:center; gap:6px;
                  color:var(--ink-dim); }
.chart-tip b { color:var(--ink); font-weight:600; }
.chart-tip .tip-warn { color:var(--sand); }

/* Book structure */
.app-stats { display:grid; gap:12px 28px; margin:0;
             grid-template-columns:repeat(auto-fit, minmax(260px, 1fr)); }
.app-stats > div { min-width:0; }
.app-stats dt { font-size:11px; text-transform:uppercase; letter-spacing:.06em;
                color:var(--ink-faint); margin-bottom:3px; }
.app-stats dd { margin:0; font-size:13px; color:var(--ink);
                overflow-wrap:anywhere; }

/* Status text. Always a word, never a colour on its own. */
.warn-text { color:var(--sand); }
.bad-text  { color:var(--clay); }
.warn-line { color:var(--sand); font-size:13px; margin:0 0 6px; }

/* Table view: what makes the chart readable without colour or JavaScript. */
.app-table-wrap { overflow-x:auto; }
.app-table { width:100%; border-collapse:collapse; font-size:12px; }
.app-table th, .app-table td {
  text-align:left; padding:6px 10px; border-bottom:1px solid var(--line-soft);
  white-space:nowrap;
}
.app-table th { color:var(--ink-faint); font-weight:500; font-size:11px;
                text-transform:uppercase; letter-spacing:.05em; }
/* A thin row is dimmed AND labelled "thin" in its own cell -- the dimming is
   a hint, the word is the information. */
.app-table .thin-row td { color:var(--ink-faint); }

@media (max-width: 720px) {
  .app-head { flex-direction:column; }
  .app-search { margin-left:0; width:100%; }
  .app-chart { height:320px; }
}

/* Chart chrome: the readout, the pane key, and the toolbar buttons.
   ------------------------------------------------------------------------ */
:root { --floor: #7a8a6a; }

.app-chart-tall { height:560px; }

.seg button {
  padding:5px 12px; font-size:12px; color:var(--ink-dim);
  background:transparent; border:0; border-right:1px solid var(--line);
  font-family:var(--mono); cursor:pointer;
}
.seg button:last-child { border-right:0; }
.seg button:hover { color:var(--ink); background:var(--bg-raise); }
.seg button[aria-pressed="true"] { background:var(--cactus-d); color:#08150c;
                                   font-weight:600; }

/* On a three-pane chart the tooltip alone is not enough: the reader is often
   looking at the spread pane while the number they want is on the price one. */
.chart-readout {
  display:flex; flex-wrap:wrap; gap:4px 16px; min-height:18px;
  margin:0 0 6px; font-family:var(--mono); font-size:11px;
  color:var(--ink-dim);
}
.chart-readout b { color:var(--ink); font-weight:600; }
.chart-readout span { display:flex; align-items:center; gap:5px; }
.chart-readout .tip-warn { color:var(--sand); }

.pane-key { margin:12px 0 0; padding:0 0 0 18px; font-size:12px;
            color:var(--ink-dim); line-height:1.6; }
.pane-key li { margin-bottom:4px; }
.pane-key b { color:var(--ink); font-weight:600; }

@media (max-width: 720px) { .app-chart-tall { height:440px; } }

/* The app's own navigation, under the site nav.
   ------------------------------------------------------------------------ */
.app-nav {
  display:flex; gap:2px; align-items:center; flex-wrap:wrap;
  margin:0 0 18px; padding:4px; border:1px solid var(--line);
  border-radius:var(--r); background:var(--bg-raise);
}
.app-nav a {
  padding:6px 12px; font-size:13px; color:var(--ink-dim);
  text-decoration:none; border-radius:6px; white-space:nowrap;
}
.app-nav a:hover { color:var(--ink); background:var(--panel); }
.app-nav a.on { background:var(--cactus-d); color:#08150c; font-weight:600; }
.app-nav .spacer { flex:1; }

.good-text { color:var(--cactus); }
.big-fig { font-size:22px; letter-spacing:-.02em; }
.app-stats dd.big-fig { margin-bottom:2px; }

@media (max-width: 720px) {
  .app-nav { gap:0; }
  .app-nav a { padding:6px 9px; font-size:12px; }
}

/* The board's table ------------------------------------------------------
   Five columns rather than eight, because the two that matter -- the queue on
   each side -- were the ones being clipped off the right edge at 1440px, and
   a comparison whose second half is behind a scrollbar is not a comparison.

   Below 720px the table stops being a table. `overflow-x:auto` on the wrapper
   keeps the PAGE from scrolling sideways, which is the rule, but on a 390px
   phone it left Item, Bid and a sliver of Ask visible -- everything this page
   exists to show was off-screen. So each row becomes a labelled card, using
   the `data-label` on every cell. No JavaScript: the labels are in the markup,
   so a row reads the same to a screen reader in either mode.
   ------------------------------------------------------------------------ */
.board-alt { display:block; font-size:11px; margin-top:1px; }
/* One flex item, so the phone layout does not space-between the number
   away from its own order count. */
.board-q { white-space:nowrap; }

@media (max-width: 720px) {
  .board-table thead { display:none; }
  .board-table, .board-table tbody, .board-table tr, .board-table td {
    display:block; width:100%;
  }
  .board-table tr {
    border-bottom:1px solid var(--line);
    padding:10px 0 8px;
  }
  .board-table td {
    border:none; padding:2px 0;
    display:flex; gap:10px; justify-content:space-between; align-items:baseline;
    /* Wrap rather than clip. Without this the value side was cut mid-word --
       "1 orde" -- which is worse than a second line, because a truncated
       order count reads as a different number. */
    flex-wrap:wrap;
  }
  .board-table td > :not(::before) { min-width:0; }
  .board-table td::before {
    content:attr(data-label);
    color:var(--ink-faint); font-size:11px; text-transform:uppercase;
    letter-spacing:.06em; flex:0 0 auto;
  }
  /* The item name is the card's heading, so it carries no label and sits on
     its own line rather than in a two-column row with an empty left half. */
  .board-table td[data-label="Item"],
  .board-table td[data-label="Output"],
  .board-table td[data-label="Time"],
  .board-table td[data-label="Strategy"] { display:block; margin-bottom:4px; }
  .board-table td[data-label="Item"]::before,
  .board-table td[data-label="Output"]::before,
  .board-table td[data-label="Time"]::before,
  .board-table td[data-label="Strategy"]::before { content:none; }

  /* The empty-state row spans the whole table and is a sentence, not a
     value. As a flex item with a `::before` it could not wrap and overflowed
     its own scroll container by 507px -- measured, on the Signals page with
     no live signals, which is the page's ordinary state between signals. */
  .board-table td[colspan] {
    display:block; white-space:normal; padding:8px 0;
  }
  .board-table td[colspan]::before { content:none; }
  /* Stays on its own line on a phone: inline, it competed with the figure
     it annotates for the same 390px and pushed it out of view. */
  .board-table .board-alt { display:block; width:100%; text-align:right; }

  /* One stat per row on a phone, for the same reason the table cells wrap
     rather than clip. Two 163px tiles could not hold a nine-digit figure --
     measured, 956,503,462 rendered as "956,503,46", which is not a truncated
     number so much as a different and much smaller one. Stacking cannot clip
     whatever the value turns out to be, which is the property worth having;
     a font size tuned to today's longest number is not. */
  .stat-row { grid-template-columns: 1fr; }
}

/* The flips page ---------------------------------------------------------
   The budget bar is the only input on the dashboard, so it is deliberately
   plain: preset amounts as links (shareable, back-button-able, no JavaScript)
   plus a number field for anything else. `.btn.small.on` marks the active
   preset, which needs to be legible without relying on colour alone -- hence
   the border as well as the fill.
   ------------------------------------------------------------------------ */
.budget-bar {
  display:flex; flex-wrap:wrap; align-items:center; gap:8px;
  margin:0 0 10px;
}
.btn.small {
  padding:5px 10px; font-size:12px; border-radius:6px;
  background:transparent; color:var(--ink-dim);
  border:1px solid var(--line); box-shadow:none;
}
.btn.small:hover {
  background:var(--bg-raise); color:var(--ink);
  border-color:var(--cactus); transform:none; box-shadow:none;
}
.btn.small.on {
  background:var(--bg-raise); color:var(--ink);
  border-color:var(--cactus); font-weight:600;
}
.budget-bar input[type="number"] {
  width:13ch; padding:5px 8px; font-size:12px; font-family:var(--mono);
  background:var(--bg); color:var(--ink);
  border:1px solid var(--line); border-radius:6px;
}

/* The refused register. A definition list because that is what it is: a term
   and the recorded reason it is not traded. The reasons are long on purpose --
   one of these entries lost money before it was demoted. */
.refused { margin:10px 0 0; }
.refused dt {
  margin:14px 0 3px; font-size:12px; color:var(--ink);
  display:flex; gap:8px; align-items:baseline; flex-wrap:wrap;
}
.refused dt span { font-size:11px; text-transform:uppercase; letter-spacing:.06em; }
.refused dd { margin:0; padding:0 0 0 2px; max-width:74ch; }

@media (max-width: 720px) {
  .budget-bar { gap:6px; }
  .budget-bar input[type="number"] { width:11ch; }
}

/* The export line sits under a table, so it needs separating from it without
   looking like another row. */
.export-line { margin:10px 0 0; padding-top:8px; border-top:1px solid var(--line-soft); }
.export-line a { font-family:var(--mono); font-size:12px; }

/* A form that sits inline with other controls rather than stacking. */
.inline-form { display:inline; margin:0; }
.budget-bar input[type="text"] {
  padding:5px 8px; font-size:12px; font-family:var(--mono);
  background:var(--bg); color:var(--ink);
  border:1px solid var(--line); border-radius:6px;
}
.budget-bar input[name="item"] { width:20ch; }
.budget-bar input[name="note"] { width:22ch; }
@media (max-width: 720px) {
  .budget-bar input[name="item"], .budget-bar input[name="note"] { width:100%; }
}

.budget-bar select {
  padding:5px 8px; font-size:12px;
  background:var(--bg); color:var(--ink);
  border:1px solid var(--line); border-radius:6px;
}
.budget-bar input[name="threshold"] { width:12ch; }
@media (max-width: 720px) {
  .budget-bar select, .budget-bar input[name="threshold"] { width:100%; }
}
