/* IRONMATION.
   The one rule: nothing overshoots. Steps and linear rates only, never a spring. */

@import url("https://fonts.googleapis.com/css2?family=Antonio:wght@400;600;700&family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap");

:root {
  --iron-900: #0b0d0e;
  --iron-800: #121517;
  --iron-700: #191d20;
  --iron-650: #1e2327;
  --scribe: #2c3338;
  --scribe-lit: #3d464d;
  --steel: #939ea6;
  /* Measured, not picked by eye: #6b757c came out at 4.14:1 on the cast ground, and every use of
     this token is 10px to 12px text, which needs 4.5:1. Lightened until it cleared it. */
  --steel-dim: #848e96;
  --steel-lit: #e4eaee;
  /* HEAT AND MONEY ONLY. Ember is not the brand colour and must not become it again.
     The mark is teal, so a page built on orange fights its own logo, and orange in this category is
     already HubSpot's (#ff7a59). Ember now means exactly two things: iron that is hot, and a row
     that costs money. Both are true wherever it appears. */
  --ember: #e2561d;
  --ember-lo: #a83a11;

  /* The brand. Lifted from the mark's #127c71, which is drawn for a light app bar and sits at 2.6:1
     on this ground. This is the same hue at a luminance that clears AA on cast graphite. */
  --patina: #2ec4ad;
  --patina-lo: #17a08f;
  --accent: var(--patina);

  --display: "Antonio", "Arial Narrow", sans-serif;
  --body: "IBM Plex Sans", system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, monospace;

  --gutter: clamp(1.25rem, 4vw, 4rem);
  --measure: 1240px;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 72px; --s9: 112px;

  /* The index. A CNC table does not ease in. */
  --index: steps(9, end);
  --rate: cubic-bezier(0.2, 0, 0, 1);
}

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

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--iron-900);
  color: var(--steel);
  font-family: var(--body);
  font-size: clamp(0.95rem, 0.9rem + 0.2vw, 1.0625rem);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  /* Every number on this page is monospaced or tabular. A figure that changes width as it changes
     value is the one thing a datasheet may not do. */
  font-variant-numeric: tabular-nums;
  overflow-x: hidden;
}

/* ── the scribe grid ─────────────────────────────────────────────
   Drawing paper. Two rules at different weights so the page reads as something measured against a
   surface rather than floating on a flat fill. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    linear-gradient(var(--scribe) 1px, transparent 1px),
    linear-gradient(90deg, var(--scribe) 1px, transparent 1px),
    linear-gradient(rgba(44, 51, 56, 0.45) 1px, transparent 1px),
    linear-gradient(90deg, rgba(44, 51, 56, 0.45) 1px, transparent 1px);
  background-size: 120px 120px, 120px 120px, 24px 24px, 24px 24px;
  opacity: 0.5;
  mask-image: radial-gradient(ellipse 120% 80% at 50% 0%, #000 20%, transparent 78%);
}

/* Cast grain. Iron is not a flat fill. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

main, header, footer { position: relative; z-index: 2; }

.wrap { width: 100%; max-width: var(--measure); margin: 0 auto; padding-inline: var(--gutter); }

/* ── type ──────────────────────────────────────────────────────── */

h1, h2, h3 {
  font-family: var(--display);
  color: var(--steel-lit);
  font-weight: 700;
  line-height: 0.92;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  margin: 0;
}

h1 { font-size: clamp(3.2rem, 1.8rem + 7vw, 8.5rem); }
h2 { font-size: clamp(2.2rem, 1.4rem + 3.6vw, 4.5rem); }
h3 { font-size: clamp(1.25rem, 1.1rem + 0.7vw, 1.6rem); line-height: 1.05; }

p { margin: 0 0 var(--s4); max-width: 62ch; }
a { color: inherit; }

.lede { font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.3rem); color: var(--steel-lit); }
.mono { font-family: var(--mono); }

/* A section's drawing-sheet reference. */
.sheet-ref {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--steel-dim);
  margin-bottom: var(--s5);
}
.sheet-ref::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--scribe);
}
.sheet-ref b { color: var(--accent); font-weight: 600; }

/* ── dimension callouts ───────────────────────────────────────────
   A hairline with tick ends and a measurement. Every one of these states something true; a callout
   with an invented number would break the whole conceit. */
.dim {
  position: absolute;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--steel-dim);
  white-space: nowrap;
  pointer-events: none;
}
.dim--h { border-top: 1px solid var(--scribe-lit); padding-top: 5px; }
.dim--h::before, .dim--h::after {
  content: "";
  position: absolute;
  top: -4px;
  width: 1px;
  height: 9px;
  background: var(--scribe-lit);
}
.dim--h::before { left: 0; }
.dim--h::after { right: 0; }

/* ── buttons ───────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 48px;
  padding: 0 var(--s5);
  border: 1px solid var(--scribe-lit);
  background: transparent;
  color: var(--steel-lit);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  /* Chamfer, not a radius. Iron gets a bevelled corner from a tool, not a rounded one. */
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  transition: background-color 120ms var(--rate), color 120ms var(--rate), border-color 120ms var(--rate);
}
.btn:hover { background: var(--iron-700); border-color: var(--steel-dim); }
.btn:active { background: var(--iron-800); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* The primary action is the BRAND colour, not the heat colour. Named --heat from when ember was
   the accent; kept so the markup did not need rewriting, but it is teal and it should stay teal. */
.btn--heat {
  background: var(--patina);
  border-color: var(--patina);
  color: #05201c;
  font-weight: 600;
}
.btn--heat:hover { background: #43d6bf; border-color: #43d6bf; }
.btn--heat:active { background: var(--patina-lo); border-color: var(--patina-lo); }

/* ── nav ───────────────────────────────────────────────────────── */

.nav {
  position: sticky;
  top: 0;
  z-index: 40;
  border-bottom: 1px solid var(--scribe);
  background: rgba(11, 13, 14, 0.86);
  backdrop-filter: blur(10px);
}
.nav__in {
  display: flex;
  align-items: center;
  gap: var(--s5);
  height: 60px;
}
.nav__mark {
  display: inline-flex;
  align-items: center;
  gap: var(--s3);
  font-family: var(--display);
  font-size: 22px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--steel-lit);
  text-decoration: none;
}
/* The mark keeps its own geometry. Ironmation chamfers everything else, but a logo is not subject
   to a page's styling rules: rounding it off or cutting its corners to match would be redrawing the
   brand to fit a stylesheet. It is the one rounded thing here and that is correct. */
.nav__fe {
  flex: 0 0 auto;
  display: block;
  /* text-transform CASCADES INTO SVG <text>. The wordmark beside this is uppercase, and that was
     inherited by the mark, which rendered iron's symbol as "FE". The symbol is "Fe" and the tile is
     a periodic-table tile, so the wrong case is not a styling preference, it is a factual error
     printed on the logo. */
  text-transform: none;
  letter-spacing: normal;
}
.nav__links { display: none; gap: var(--s5); margin-left: auto; }
@media (min-width: 900px) { .nav__links { display: flex; } }
.nav__links a {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--steel);
  text-decoration: none;
  padding: var(--s2) 0;
  border-bottom: 1px solid transparent;
  transition: color 120ms var(--rate), border-color 120ms var(--rate);
}
.nav__links a:hover { color: var(--steel-lit); border-bottom-color: var(--accent); }
.nav__cta { margin-left: auto; }
@media (min-width: 900px) { .nav__cta { margin-left: 0; } }

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

.hero { padding: clamp(3rem, 8vw, 7rem) 0 var(--s9); position: relative; }
.hero__grid { display: grid; gap: var(--s8); }
@media (min-width: 1000px) {
  .hero__grid { grid-template-columns: 1.15fr 0.85fr; align-items: center; gap: var(--s7); }
}

.hero__kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid rgba(46, 196, 173, 0.38);
  padding: 5px var(--s3);
  margin-bottom: var(--s5);
}

.hero h1 { margin-bottom: var(--s5); }
/* The counter-claim, set in the cooled colour so the two words are materials, not decoration. */
.hero h1 s { text-decoration: none; color: var(--steel-dim); position: relative; }
.hero h1 s::after {
  content: "";
  position: absolute;
  left: -2%;
  right: -2%;
  top: 52%;
  height: 5px;
  background: var(--ember);
  transform: scaleX(0);
  transform-origin: left;
  animation: strike 420ms var(--rate) 700ms forwards;
}
@keyframes strike { to { transform: scaleX(1); } }

.hero__sub { max-width: 54ch; margin-bottom: var(--s6); }
.hero__acts { display: flex; flex-wrap: wrap; gap: var(--s3); margin-bottom: var(--s6); }

.hero__facts {
  display: grid;
  /* Two columns, fixed — not auto-fit. There are four facts, so the only counts that come out even
     are four, two and one. Four never fits: the widest label needs 173px, four of those plus gaps is
     695px, and the hero's text column tops out near 612px at the 1240px measure. Left to itself
     auto-fit therefore picks three, which lays out 3 + 1 and leaves a hole the width of two tiles.
     A hole is worse than the squeeze it replaced, so the count is stated rather than derived.
     The floor is the widest label's own width; below it the longest label bleeds out of its tile. */
  grid-template-columns: repeat(2, minmax(173px, max-content));
  gap: 1px;
  background: var(--scribe);
  border: 1px solid var(--scribe);
  width: max-content;
  max-width: 100%;
}
/* Two tiles plus the gap need 347px of content box, which a 20px-gutter phone runs out of at about
   387px wide. Below that the pair would be squeezed under the floor and the longest label would
   bleed again, so the strip goes single file instead. One column of four is still even. */
@media (max-width: 419px) {
  .hero__facts { grid-template-columns: minmax(173px, max-content); }
}

.hero__fact { background: var(--iron-900); padding: var(--s3) var(--s4); }
.hero__fact dt {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--steel-dim);
  margin-bottom: 2px;
}
.hero__fact dd {
  margin: 0;
  font-family: var(--display);
  font-size: 26px;
  color: var(--steel-lit);
  line-height: 1;
}
/* One line, always. A value that wraps makes the strip three lines tall on one cell and one on the
   rest, and a datasheet with a ragged row is a datasheet nobody trusts the numbers in. */
.hero__fact dd { white-space: nowrap; }
.hero__fact dt { white-space: nowrap; }

/* ── steel tread plate ────────────────────────────────────────────
   Tear plate: the raised anti-slip pattern on a steel floor or a truck bed. Four dashes per tile in
   alternating diagonal pairs, which is how the real thing is rolled.

   EMBOSSED, not printed. Each dash is drawn twice, once in near-black offset down by 1.6px and once
   in white at low alpha in place, so the light reads as coming from above and the raised edge has a
   shadow under it. A single flat stroke gives you a diagonal hatch, which is a texture; the two
   passes are what make it a surface with things standing proud of it.

   Sized in one place. The tile is 48px and the pattern is scale-sensitive: much smaller and it
   turns into noise, much larger and the page looks like it is wearing a costume. */
.tread {
  background-color: #101416;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48'%3E%3Cg fill='none' stroke-width='3.4' stroke-linecap='round'%3E%3Cg stroke='%23000' stroke-opacity='.55' transform='translate(0 1.6)'%3E%3Cpath d='M8 20 20 8M28 40 40 28M28 8 40 20M8 40 20 28'/%3E%3C/g%3E%3Cg stroke='%23fff' stroke-opacity='.085'%3E%3Cpath d='M8 20 20 8M28 40 40 28M28 8 40 20M8 40 20 28'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-size: 48px 48px;
}

/* ── the billet ───────────────────────────────────────────────────
   A forged Fe ingot, CSS only. Chamfered ends, knurling along the top edge, ember through the
   middle cooling to graphite at the extremities. It cools on load and reheats on hover, which is
   the only "alive" thing on the page and is therefore the thing people remember. */
.billet-stage { position: relative; display: grid; place-items: center; min-height: 340px; }

/* The plate the billet rests on. Inset from the stage so the plate has visible edges rather than
   bleeding to the column boundary, and masked at the bottom so it fades into the ground instead of
   ending on a hard line that would read as a mistake. */
.billet-stage::before {
  content: "";
  position: absolute;
  inset: 6% 2% 10%;
  background-color: #101416;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48'%3E%3Cg fill='none' stroke-width='3.4' stroke-linecap='round'%3E%3Cg stroke='%23000' stroke-opacity='.55' transform='translate(0 1.6)'%3E%3Cpath d='M8 20 20 8M28 40 40 28M28 8 40 20M8 40 20 28'/%3E%3C/g%3E%3Cg stroke='%23fff' stroke-opacity='.085'%3E%3Cpath d='M8 20 20 8M28 40 40 28M28 8 40 20M8 40 20 28'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-size: 48px 48px;
  border: 1px solid #232a2e;
  mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
}

.billet {
  position: relative;
  width: min(360px, 82%);
  aspect-ratio: 5 / 3;
  background:
    linear-gradient(180deg, #2a2f33 0%, #16191c 42%, #0e1113 100%);
  clip-path: polygon(14% 0, 86% 0, 100% 26%, 100% 74%, 86% 100%, 14% 100%, 0 74%, 0 26%);
  display: grid;
  place-items: center;
  isolation: isolate;
  box-shadow: 0 40px 80px -30px #000;
}

/* Knurling. A machined grip pattern, not a texture image. */
.billet::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 26%;
  background-image: repeating-linear-gradient(
    115deg,
    rgba(255, 255, 255, 0.055) 0 2px,
    transparent 2px 5px
  );
  z-index: 1;
}

/* The heat. Sits under the glyph and animates its own opacity. */
.billet::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 62% 54% at 50% 58%, var(--ember) 0%, var(--ember-lo) 34%, transparent 72%);
  filter: blur(14px);
  opacity: 0.95;
  z-index: 0;
  animation: cool 2.6s var(--rate) 500ms forwards;
}
@keyframes cool { to { opacity: 0.34; } }
.billet:hover::after { opacity: 0.95; transition: opacity 260ms var(--rate); }

.billet__fe {
  position: relative;
  z-index: 2;
  font-family: var(--display);
  font-size: clamp(4.5rem, 13vw, 7.5rem);
  font-weight: 700;
  line-height: 1;
  color: #f6e7dd;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.5);
}
.billet__no {
  position: absolute;
  right: 9%;
  bottom: 12%;
  z-index: 2;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  color: rgba(246, 231, 221, 0.55);
}


.billet-stage .dim--mass { top: 34px; right: 6%; text-align: right; }

@media (prefers-reduced-motion: reduce) {
  .billet::after { animation: none; opacity: 0.5; }
  .hero h1 s::after { animation: none; transform: scaleX(1); }
}

/* ── generic section ───────────────────────────────────────────── */

.sec { padding: var(--s9) 0; border-top: 1px solid var(--scribe); }
.sec__head { margin-bottom: var(--s7); }
.sec__head h2 { margin-bottom: var(--s4); }
.sec__head p { color: var(--steel); }

/* ── the credit meter ──────────────────────────────────────────── */

.meter { display: grid; gap: var(--s6); }
@media (min-width: 940px) { .meter { grid-template-columns: 1fr 1fr; gap: var(--s8); align-items: start; } }

.drain {
  border: 1px solid var(--scribe);
  background: var(--iron-800);
  padding: var(--s5);
}
.drain__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4); padding: var(--s3) 0; border-bottom: 1px solid var(--scribe); }
.drain__row:last-child { border-bottom: 0; }
.drain__row dt { font-family: var(--mono); font-size: 12px; color: var(--steel); }
.drain__row dd { margin: 0; font-family: var(--mono); font-size: 13px; color: var(--steel-lit); text-align: right; }
.drain__row dd b { color: var(--ember); font-weight: 600; }
.drain__note { font-size: 12px; color: var(--steel-dim); margin: var(--s4) 0 0; }

/* ── comparison, the spec sheet ─────────────────────────────────── */

.sheet { border: 1px solid var(--scribe); background: var(--iron-800); overflow-x: auto; }
.sheet table { width: 100%; min-width: 780px; border-collapse: collapse; }

.sheet th, .sheet td {
  padding: var(--s3) var(--s4);
  text-align: left;
  border-bottom: 1px solid var(--scribe);
  vertical-align: top;
  font-size: 13px;
}
/* NOT sticky, and it must not be made sticky again without changing the wrapper first.
   
   `.sheet` needs `overflow-x: auto` so a 780px table scrolls inside its own box on a phone rather
   than scrolling the whole page. That makes `.sheet` the scroll container, so a sticky header
   sticks relative to IT, and the box scrolls off the top of the page with the header still glued
   inside. Measured: the header ended up 302px above the viewport while claiming to be pinned at 60.
   Fifteen rows do not need a pinned header enough to give up the horizontal scroll. */
.sheet thead th {
  background: var(--iron-700);
  border-bottom: 1px solid var(--scribe-lit);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--steel);
  white-space: nowrap;
}
.sheet thead th:first-child { width: 24%; }
.sheet thead th.is-us { color: var(--accent); background: #10201e; }

.sheet tbody th {
  font-family: var(--body);
  font-weight: 600;
  color: var(--steel-lit);
  font-size: 13px;
}
.sheet tbody th small { display: block; font-weight: 400; color: var(--steel-dim); font-size: 11px; line-height: 1.4; margin-top: 2px; }
.sheet td { font-family: var(--mono); color: var(--steel); font-size: 12.5px; line-height: 1.5; }
.sheet td.is-us { background: rgba(46, 196, 173, 0.05); color: var(--steel-lit); border-left: 1px solid rgba(46, 196, 173, 0.24); border-right: 1px solid rgba(46, 196, 173, 0.24); }
.sheet tbody tr:hover td { background: var(--iron-700); }
.sheet tbody tr:hover td.is-us { background: rgba(46, 196, 173, 0.09); }

.sheet__foot {
  padding: var(--s4);
  border-top: 1px solid var(--scribe-lit);
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.7;
  color: var(--steel-dim);
}
.sheet__foot a { color: var(--steel); }

/* ── worked example ────────────────────────────────────────────── */

.calc { display: grid; gap: 1px; background: var(--scribe); border: 1px solid var(--scribe); }
@media (min-width: 820px) { .calc { grid-template-columns: repeat(3, 1fr); } }
.calc__col { background: var(--iron-800); padding: var(--s5); }
.calc__col--us { background: #0e1a19; }
.calc__who { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--steel-dim); margin-bottom: var(--s3); }
.calc__col--us .calc__who { color: var(--accent); }
.calc__fig { font-family: var(--display); font-size: clamp(2.4rem, 5vw, 3.4rem); line-height: 1; color: var(--steel-lit); margin-bottom: var(--s2); }
.calc__col--us .calc__fig { color: var(--accent); }
.calc__unit { font-family: var(--mono); font-size: 12px; color: var(--steel-dim); margin-bottom: var(--s4); }
.calc__body { font-size: 13px; color: var(--steel); margin: 0; }

/* ── lanes ─────────────────────────────────────────────────────── */

.lanes { display: grid; gap: 1px; background: var(--scribe); border: 1px solid var(--scribe); }
/* A row of cards that does not divide by the column count leaves a hole where a card should be, and
   these strips do not divide: eleven lanes into three, three into two. The last card takes whatever
   track is spare so the block ends flush.

   Written as "is the last card ALSO at a position that leaves a gap", not as a hardcoded span,
   because the counts change whenever a lane is added and a span tuned to today's eleven becomes the
   thing that breaks tomorrow's twelve.

   Two constraints that are easy to get wrong here. The two-column rule is bounded at the TOP as well
   as the bottom: both queries are min-width, so an unbounded `odd` rule stays live at three columns
   and drags a three-lane strip into 1 + 1 + 2. And neither rule exists at one column, where `span`
   does not clamp — it invents an implicit second column and pushes the strip off the side of a
   phone. */
@media (min-width: 760px) and (max-width: 1079.98px) {
  .lanes { grid-template-columns: repeat(2, 1fr); }
  .lanes:not(.lanes--pair) > .lane:last-child:nth-child(odd) { grid-column: span 2; }
}
@media (min-width: 1080px) {
  .lanes { grid-template-columns: repeat(3, 1fr); }
  .lanes:not(.lanes--pair) > .lane:last-child:nth-child(3n + 2) { grid-column: span 2; }
  .lanes:not(.lanes--pair) > .lane:last-child:nth-child(3n + 1) { grid-column: span 3; }
}
/* A strip of exactly two. Two equal halves rather than a third and two thirds, which is what the
   spare-track rule above would give it — full, but lopsided, and these two are peers.
   Excluded via `:not()` on the rules themselves rather than by overriding them afterwards: an
   override loses, because `:last-child:nth-child(…)` carries one more class than a plain
   `:last-child` and quietly outranks it. */
@media (min-width: 760px) {
  .lanes--pair { grid-template-columns: repeat(2, 1fr); }
}
.lane { background: var(--iron-800); padding: var(--s5); min-height: 172px; display: flex; flex-direction: column; gap: var(--s2); }
.lane__no { font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em; color: var(--steel-dim); }
.lane h3 { color: var(--steel-lit); }
.lane p { font-size: 13.5px; margin: 0; color: var(--steel); }
.lane__cost { margin-top: auto; padding-top: var(--s3); font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; }
.lane__cost--free { color: var(--accent); }
.lane__cost--paid { color: var(--ember); }

/* ── the refusals ──────────────────────────────────────────────── */

.wont { display: grid; gap: var(--s5); }
@media (min-width: 860px) { .wont { grid-template-columns: repeat(2, 1fr); gap: var(--s6); } }
.wont__item { border-left: 2px solid var(--scribe-lit); padding-left: var(--s4); }
.wont__item h3 { font-size: 1.1rem; margin-bottom: var(--s2); color: var(--steel-lit); }
.wont__item p { font-size: 13.5px; margin: 0; }

/* ── close ─────────────────────────────────────────────────────── */

.close { text-align: center; padding: var(--s9) 0; border-top: 1px solid var(--scribe); }
.close h2 { margin-bottom: var(--s5); }
.close p { margin-inline: auto; }
.close__acts { display: flex; flex-wrap: wrap; gap: var(--s3); justify-content: center; margin-top: var(--s6); }

.code {
  display: inline-block;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--steel-lit);
  background: var(--iron-800);
  border: 1px solid var(--scribe);
  padding: var(--s3) var(--s4);
  text-align: left;
  margin-top: var(--s5);
}
.code span { color: var(--steel-dim); }

.foot { border-top: 1px solid var(--scribe); padding: var(--s6) 0; position: relative; }
/* A strip of plate along the top edge of the footer, the way a floor meets a wall. Ten pixels, so
   it reads as an edge rather than as another panel competing with the content above it. */
.foot::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 10px;
  background-color: #101416;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48'%3E%3Cg fill='none' stroke-width='3.4' stroke-linecap='round'%3E%3Cg stroke='%23000' stroke-opacity='.55' transform='translate(0 1.6)'%3E%3Cpath d='M8 20 20 8M28 40 40 28M28 8 40 20M8 40 20 28'/%3E%3C/g%3E%3Cg stroke='%23fff' stroke-opacity='.085'%3E%3Cpath d='M8 20 20 8M28 40 40 28M28 8 40 20M8 40 20 28'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-size: 48px 48px;
}
.foot__in { display: flex; flex-wrap: wrap; gap: var(--s4); align-items: center; justify-content: space-between; }
.foot p { font-family: var(--mono); font-size: 11px; color: var(--steel-dim); margin: 0; }

/* ── reveal on scroll ──────────────────────────────────────────────
   The indexed move: opacity plus a short travel, stepped rather than eased, so blocks land on the
   grid like a table indexing to position. */
/* Scoped to .js, which the inline script in <head> sets before first paint.
   
   Unscoped, this hid every block by default and left JavaScript to reveal them, so with JS off the
   page rendered with nine of its ten sections invisible. That is the single worst thing a landing
   page can do: it is blank to anyone with scripts blocked and to any crawler that does not run
   them. The class is set in the head rather than at the end of the body so there is no frame where
   the content is visible and then hidden. */
.js .rise { opacity: 0; transform: translateY(14px); }
.js .rise.in {
  animation: index-in 420ms var(--index) forwards;
}
@keyframes index-in { to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .js .rise { opacity: 1; transform: none; }
  .js .rise.in { animation: none; }
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--patina); color: #05201c; padding: var(--s3) var(--s4);
  font-family: var(--mono); font-size: 12px;
}
.skip:focus { left: var(--s4); top: var(--s4); }

/* The billet reheats while the page is being scrolled and cools when it stops. Transition, not a
   keyframe, so an interrupted scroll cools from wherever it got to rather than snapping. */
.billet.is-hot::after { opacity: 0.9; transition: opacity 180ms var(--rate); }
.billet::after { transition: opacity 900ms var(--rate); }
@media (prefers-reduced-motion: reduce) { .billet.is-hot::after { opacity: 0.5; } }

/* ══ APP WINDOWS ═══════════════════════════════════════════════════
   The product, at work, rather than a logo standing in for it.

   These are CSS recreations of the real surfaces, not screenshots: a screenshot is a moment and the
   thing worth showing here is a SEQUENCE, cells going from empty to running to answered while a
   cost ticks. They mirror the real app's geometry (32px rows, 56px gutter, the same status tints)
   so what you see here is what you get when you clone it.

   Everything is driven by animation-delay off one shared timeline. No JavaScript: the hero of a
   page about a local-first tool should not need a runtime to draw itself. */

.win {
  border: 1px solid var(--scribe-lit);
  background: var(--iron-800);
  box-shadow: 0 30px 70px -34px #000;
  overflow: hidden;
  font-family: var(--body);
}

.win__bar {
  display: flex;
  align-items: center;
  gap: var(--s2);
  height: 34px;
  padding: 0 var(--s3);
  background: #0f1214;
  border-bottom: 1px solid var(--scribe);
}
.win__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--scribe-lit); flex: 0 0 auto; }
.win__title {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--steel);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.win__title b { color: var(--steel-lit); font-weight: 600; }
.win__right { margin-left: auto; font-family: var(--mono); font-size: 10px; color: var(--steel-dim); white-space: nowrap; }

/* The run strip. In the app this is a live band under the toolbar while a run is going. */
.win__run {
  display: flex;
  align-items: center;
  gap: var(--s2);
  height: 26px;
  padding: 0 var(--s3);
  background: #101a19;
  border-bottom: 1px solid var(--scribe);
  font-family: var(--mono);
  font-size: 10px;
  color: var(--accent);
}
.win__bar-track { flex: 1 1 auto; height: 3px; background: var(--iron-700); overflow: hidden; }
.win__bar-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  animation: fill 6s linear infinite;
}
@keyframes fill { 0% { width: 4%; } 82% { width: 100%; } 100% { width: 100%; } }

/* ── the grid ──────────────────────────────────────────────────── */
.g { font-size: 11px; }
.g__row { display: grid; grid-template-columns: 30px 1.15fr 1fr 1fr 0.72fr; align-items: stretch; }
.g__row + .g__row { border-top: 1px solid #1c2124; }

.g__h {
  background: var(--iron-700);
  border-bottom: 1px solid var(--scribe);
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--steel);
}
.g__h .g__c { display: flex; align-items: center; gap: 5px; height: 26px; }
.g__h i { font-style: normal; color: var(--steel-dim); font-size: 10px; }

.g__c {
  display: flex;
  align-items: center;
  height: 30px;
  padding: 0 8px;
  border-right: 1px solid #1c2124;
  color: var(--steel-lit);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.g__n {
  justify-content: flex-end;
  background: #0f1214;
  color: var(--steel-dim);
  font-family: var(--mono);
  font-size: 9.5px;
  padding-right: 7px;
}

/* A cell the engine is filling in. Three states on one timeline: waiting, working, answered.
   The value is a child that fades in at the moment the band clears, which is exactly how the real
   grid behaves. */
.g__c--work { position: relative; color: var(--steel-dim); }
.g__c--work::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(46, 196, 173, 0.1);
  border-left: 2px solid var(--accent);
  animation: working 6s var(--rate) infinite;
}
@keyframes working {
  0%, 8% { opacity: 0; }
  9%, 34% { opacity: 1; }
  40%, 100% { opacity: 0; }
}
/* STACKED, not side by side. Both were in normal flow, so during the crossfade the placeholder and
   the value sat next to each other and the cell briefly read "queuedIndustrial". Absolutely
   positioned they occupy the same box, and the handover is a real dissolve. The timings no longer
   overlap either: the placeholder is gone at 36% and the value starts at 38%. */
.g__v, .g__wait {
  position: absolute;
  left: 8px;
  right: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.g__v { opacity: 0; color: var(--steel-lit); animation: landed 6s var(--rate) infinite; }
@keyframes landed {
  0%, 38% { opacity: 0; }
  46%, 100% { opacity: 1; }
}
.g__wait { color: var(--steel-dim); animation: waited 6s var(--rate) infinite; }
@keyframes waited { 0%, 30% { opacity: 1; } 36%, 100% { opacity: 0; } }

/* Stagger, so rows land one after another rather than as a block. */
.g__row:nth-child(2) .g__c--work::before, .g__row:nth-child(2) .g__v, .g__row:nth-child(2) .g__wait { animation-delay: 0ms; }
.g__row:nth-child(3) .g__c--work::before, .g__row:nth-child(3) .g__v, .g__row:nth-child(3) .g__wait { animation-delay: 260ms; }
.g__row:nth-child(4) .g__c--work::before, .g__row:nth-child(4) .g__v, .g__row:nth-child(4) .g__wait { animation-delay: 520ms; }
.g__row:nth-child(5) .g__c--work::before, .g__row:nth-child(5) .g__v, .g__row:nth-child(5) .g__wait { animation-delay: 780ms; }
.g__row:nth-child(6) .g__c--work::before, .g__row:nth-child(6) .g__v, .g__row:nth-child(6) .g__wait { animation-delay: 1040ms; }

.g__free { color: var(--accent); font-family: var(--mono); font-size: 10px; }
.g__paid { color: var(--ember); font-family: var(--mono); font-size: 10px; }

/* ── a confirm dialog, as the app draws it ─────────────────────── */
.dlg { border: 1px solid var(--scribe-lit); background: var(--iron-700); padding: var(--s4); }
.dlg__h { font-family: var(--body); font-size: 13px; font-weight: 600; color: var(--steel-lit); margin: 0 0 var(--s3); }
.dlg__line { display: flex; justify-content: space-between; gap: var(--s3); padding: 6px 0; border-bottom: 1px solid var(--scribe); font-family: var(--mono); font-size: 11px; }
.dlg__line:last-of-type { border-bottom: 0; }
.dlg__line span:first-child { color: var(--steel); }
.dlg__line span:last-child { color: var(--steel-lit); }
.dlg__total { color: var(--ember) !important; font-weight: 600; }
.dlg__acts { display: flex; gap: var(--s2); margin-top: var(--s4); }
.dlg__btn { font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; padding: 8px 12px; border: 1px solid var(--scribe-lit); color: var(--steel); }
.dlg__btn--go { background: var(--patina); border-color: var(--patina); color: #05201c; font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .win__bar-fill { animation: none; width: 72%; }
  .g__c--work::before { animation: none; opacity: 0; }
  .g__v { animation: none; opacity: 1; }
  .g__wait { animation: none; opacity: 0; }
}

/* ── sub-blocks inside one section ────────────────────────────────────
   SEC.04 is four arguments under one heading, so each needs a header smaller than `.sec__head` and
   bigger than nothing. Tighter top and bottom than a section head, because it is a step in a
   sequence rather than the start of one. */
.subsec { margin: var(--s7) 0 var(--s4); }
.subsec .sheet-ref { margin-bottom: var(--s3); }
/* Nothing but the margin. Every other property here was a re-declaration of what `h1, h2, h3`
   already sets, and two of them DISAGREED with it — a size running to 30px against the shared h3
   scale's 25.6px, and letter-spacing of -0.01em where every other heading on the page is +0.005em.
   Tighter and larger at once, which is why these read as a different typeface from the headings
   above them. They are the same headings; they now look it. */
.subsec h3 { margin: 0; }
.subsec p { margin: var(--s3) 0 0; max-width: 60ch; color: var(--steel); font-size: 14px; line-height: 1.6; }

/* Three narrow columns rather than five wide ones, so this table does not force a sideways scroll
   on a phone the way the comparison sheet legitimately does. */
.sheet--pairs table { min-width: 420px; }
/* The two right-hand columns are short and fixed, so the reason column takes the rest. Without this
   the $0.00 column stretches and leaves a void beside a two-character value. */
.sheet--pairs td { white-space: nowrap; width: 1%; font-family: var(--mono); }

/* A label row banding the comparison table into four groups. Full width, quiet, and heavier on top
   so it reads as the start of a block rather than a row of data. */
.sheet__band th {
  background: var(--iron-700);
  border-top: 1px solid var(--steel-dim);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--steel-dim);
  padding-top: var(--s3);
  padding-bottom: var(--s3);
}

/* Ceilings are money you CANNOT be charged, so they take the accent. Competitor prices in SEC.01
   keep ember, and the two lists rhyme against each other on purpose. */
.drain__row dd.is-free { color: var(--patina); font-weight: 600; }
.drain__row dt small { display: block; font-family: var(--sans); font-size: 10.5px; color: var(--steel-dim); margin-top: 2px; letter-spacing: 0; }

/* In SEC.05 the total is a saving, and ember is reserved for money that leaves. */
.dlg__total--save { color: var(--patina) !important; }

/* Five cells in a three-column grid leaves a hole the width of a whole card in row two. A six-track
   grid divides evenly by both three and two, so three cards span two tracks each and the remaining
   two span three each, and the block ends flush. Below 820px `.calc` is a single column and none of
   this applies. */
@media (min-width: 820px) {
  .calc--five { grid-template-columns: repeat(6, 1fr); }
  .calc--five > * { grid-column: span 2; }
  .calc--five > :nth-child(4), .calc--five > :nth-child(5) { grid-column: span 3; }
}

/* ── how far down the page you are ──────────────────────────────────
   Ember, because it is the one thing on the page that tracks the reader's own effort, and ember is
   what this page uses for heat and money. Two pixels: a progress bar you notice is a progress bar
   competing with the thing it is measuring.

   Added by main.js, so it only exists when there is scroll animation for it to report on. It is
   scaled rather than sized — transform is the only property that animates on the compositor, and a
   bar that drives layout on every scroll frame is a scroll-jank generator. */
.prog {
  position: fixed;
  top: 0; left: 0;
  height: 2px;
  width: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: var(--ember);
  z-index: 60;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) { .prog { display: none; } }

/* ── what the motion starts from ────────────────────────────────────
   Hidden by the STYLESHEET and only under `.js`, exactly like `.rise` above, and for the same
   reason: with scripts off, or with the animation library blocked, or on a crawler, the page has to
   render whole. Doing this in JavaScript instead — which is what `gsap.from()` does — means the hero
   is invisible in the first paint for everyone, and stays invisible for anyone whose ticker never
   starts. A tab opened in the background is the ordinary case of that, not an exotic one.

   Reduced motion cancels it declaratively, so that path never depends on a script running either. */
.js .hero__kicker,
.js .hero h1,
.js .hero__sub,
.js .hero__acts,
.js .hero__fact,
.js .win,
.js .sheet tbody tr { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .js .hero__kicker,
  .js .hero h1,
  .js .hero__sub,
  .js .hero__acts,
  .js .hero__fact,
  .js .win,
  .js .sheet tbody tr { opacity: 1; }
}


/* ── let the text use the width it already has ──────────────────────
   `p { max-width: 62ch }` caps every paragraph, and `ch` scales with the font, so the smaller the
   type the narrower its column — the 11px source note was the tightest measure on the page at 409px
   with 727px empty beside it. These blocks span their container, where they already sit. */
.sec__head p,
.subsec p,
.drain__note,
.sheet__foot,
.lane p {
  max-width: none;
}

/* ── the word that costs you ────────────────────────────────────────
   Ember already means exactly two things on this page: iron that is hot, and money leaving. The
   phrase a headline is really about takes the money colour and a rule under it, the way you would
   ring a charge on a bill. Deliberately not the strike used on "clay" in the hero — a strike says
   removed, and this is not removed, it is the point being made.

   `text-decoration` rather than a positioned bar. A bar drawn with `::after` spans the element's
   whole box, so on a phrase that wraps — "the one you never make" does — it draws ONE rule across
   two lines of text instead of underlining each. The decoration follows the lines. */
.pain {
  font-style: normal;
  color: var(--ember);
  text-decoration: underline;
  text-decoration-color: rgba(226, 86, 29, 0.45);
  text-decoration-thickness: 0.055em;
  text-underline-offset: 0.12em;
}

/* ── headlines break where they run out of room, not where they were told ──
   Every one of these carried a hard <br>, so "Eleven kinds of column" sat on two lines inside a
   1,055px column it needed 595px of. The breaks are gone; `balance` splits what genuinely has to
   wrap into even lines rather than leaving one word stranded. */
h1, h2 { text-wrap: balance; }

/* ── the stat strip, four across when there is room for four ────────
   Both strips share this class and they sit in very different columns: the hero's is about 578px
   and the savings one is 1,055px. Pinning it to two put the wide one in a 2x2 with half the row
   empty; leaving it to auto-fit let the narrow one pick THREE, which lays four tiles out 3 + 1 and
   leaves a hole. Four tiles only divide evenly by four, two and one, so those are the only counts
   offered — and which one applies is a question about the strip's own column, not the viewport,
   which is why the wide one is tagged rather than detected. */
.hero__facts--wide { grid-template-columns: repeat(2, minmax(173px, max-content)); }
@media (min-width: 820px) {
  /* 4 x 173 + 3 gaps = 695px, and .wrap has that from about 820px up. */
  .hero__facts--wide { grid-template-columns: repeat(4, minmax(173px, max-content)); }
}

/* The alpha caveat under the install commands. Stated once, where somebody is deciding whether to
   run it, and set below the surrounding copy so it reads as a condition of installing rather than
   as a warning about the software. */
.alpha {
  max-width: 62ch;
  margin: var(--s5) auto 0;
  color: var(--steel-dim);
  font-size: 14px;
  line-height: 1.65;
  text-align: left;
}
.alpha code { font-family: var(--mono); font-size: 0.92em; color: var(--steel); }
