/* The landing page's own sections: the hero, the size table, the four notes on how it works, the
   platform cards, and the one entrance animation. Loaded only by the home page — the 404 wears
   `styles.css` alone.

   Everything here assumes the tokens and the rails defined there. The two grids on this page break
   out of the content column's padding so their dividers meet the rails: inside the frame, the page
   is one continuous sheet of graph paper with things drawn on it. */

/* --- hero --- */

.hero {
  position: relative;
}
.hero > .container {
  padding-block: clamp(40px, 7vw, 88px) var(--band-pad);
}

/* Graph paper, over the hero only. Two gradients, no image.
   Scoped to the hero rather than fixed behind the whole page: a fixed layer is masked against
   the viewport, so it would reappear at the top of the window at every scroll position — the
   texture has to belong to the fold, not follow the reader down the page. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--rule) 1px, transparent 1px),
    linear-gradient(to bottom, var(--rule) 1px, transparent 1px);
  background-size: var(--grid-step) var(--grid-step);
  opacity: 0.45;
  -webkit-mask-image: radial-gradient(115% 100% at 50% 0%, #000 0%, transparent 78%);
  mask-image: radial-gradient(115% 100% at 50% 0%, #000 0%, transparent 78%);
}

/* Deliberately not 1fr 1fr: the copy leads, the size table answers. An even split would make the
   page read as two things of equal weight, which is not the argument. */
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}

/* Square, like everything else. A rounded pill was the one shape on the page that came from
   somewhere other than the grid. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-stretch: 85%;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--body);
  border: 1px solid var(--rule-strong);
  padding: 0.35rem 0.7rem;
  background: var(--panel);
}
/* The tag is uppercased; a version number is not. `v0.1.0` is how the tag is written
   everywhere else, and `V0.1.0` is not a version anyone would type. */
.version {
  text-transform: none;
}
/* The only thing on the page that animates forever, so it is also the only thing that could
   keep the renderer from ever going idle. The ring is a pseudo-element scaled by `transform`
   rather than a growing `box-shadow`: spread is not a compositor property, and animating it
   repaints on the main thread every frame for as long as the tab is open. */
.pulse {
  position: relative;
  width: 6px;
  height: 6px;
  background: var(--accent);
}
.pulse::after {
  content: '';
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--accent) 45%, transparent);
  animation: pulse 2.4s ease-out infinite;
}
@keyframes pulse {
  0% {
    transform: scale(1);
    opacity: 0.9;
  }
  70%,
  100% {
    transform: scale(3);
    opacity: 0;
  }
}

/* The width axis pushed to its expanded end. This is the one place the page raises its voice. */
.wordmark {
  margin: 1.4rem 0 0;
  font-weight: 700;
  font-stretch: 116%;
  font-size: clamp(2.8rem, 7.6vw, 5.4rem);
  line-height: 0.9;
  letter-spacing: -0.035em;
}
.wordmark span {
  display: block;
}

.claim {
  margin-top: 1.3rem;
  font-size: clamp(1.125rem, 2.1vw, 1.5rem);
  font-weight: 500;
  font-stretch: 96%;
  line-height: 1.3;
  letter-spacing: -0.015em;
  max-width: 26ch;
  /* Two short lines of equal length rather than a long one and an orphan. */
  text-wrap: balance;
}

.cta {
  margin-top: 2.2rem;
}

.cta-meta {
  margin-top: 0.9rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-stretch: 85%;
  color: var(--muted);
  letter-spacing: 0.02em;
}

/* --- the size table --- */

/* Flat, bordered, square: the same sheet as the rest of the page, ruled off. A shadow would make
   it the one object floating above a design whose whole vocabulary is lines. */
.spec-card {
  background: var(--panel);
  border: 1px solid var(--rule);
  padding: clamp(22px, 3vw, 32px);
}

.spec {
  list-style: none;
  margin-top: 1.1rem;
}

/* Areas are named rather than left to auto-placement. The DOM order is label, bar, value —
   the reading order — while the drawing order is label and value on one line with the bar
   beneath them, and auto-placement would put the bar between the two. */
.spec-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    'label value'
    'bar   bar';
  align-items: baseline;
  column-gap: 1rem;
  row-gap: 0.55rem;
  padding: 0.95rem 0 1.05rem;
}

.spec-label {
  grid-area: label;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-stretch: 82%;
  color: var(--body);
  letter-spacing: 0.01em;
  overflow-wrap: anywhere;
}

.spec-value {
  grid-area: value;
  font-family: var(--mono);
  font-size: 1.125rem;
  font-stretch: 90%;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
/* The Windows number rides alongside the macOS one, sized down so the row still has one subject. */
.spec-alt {
  margin-left: 0.5rem;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--muted);
}

.spec-unit {
  font-size: 0.625rem;
  font-weight: 400;
  color: var(--muted);
  margin-left: 0.3rem;
}

/* A visible track under every fill, so a short bar reads as "little of the whole" rather than
   as a stray rule. Without it the 100% row is indistinguishable from a row separator, which is
   exactly the comparison the table exists to make. */
.spec-bars {
  grid-area: bar;
  display: grid;
  gap: 3px;
}
.spec-track {
  display: block;
  height: 5px;
  background: var(--rule);
}
/* Scaled, not resized: a transform animates on the compositor, a width animates the layout. */
.spec-track i {
  display: block;
  height: 100%;
  width: var(--w);
  background: var(--ink);
  transform-origin: left;
  animation: grow 0.85s cubic-bezier(0.2, 0.75, 0.2, 1) both;
  animation-delay: calc(0.45s + var(--i) * 0.1s);
}
/* The second platform reads as the same measurement in a quieter voice, not as a second subject. */
.spec-track.is-alt i {
  background: var(--rule-strong);
  animation-delay: calc(0.52s + var(--i) * 0.1s);
}

/* The key. Two tones with nothing naming them is a chart that has to be guessed at. */
.spec-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.1rem;
  margin-top: 1rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  font-stretch: 82%;
  color: var(--muted);
}
.spec-key {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}
.spec-key i {
  display: block;
  width: 14px;
  height: 5px;
  background: var(--ink);
}
.spec-key.is-alt i {
  background: var(--rule-strong);
}
@keyframes grow {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

/* One row is the answer to the question the table asks. It is the only thing on the page
   painted in the brand colour. */
.is-highlight .spec-track i {
  background: var(--accent);
}
.is-highlight .spec-value {
  color: var(--accent-text);
}
.is-highlight .spec-label {
  color: var(--ink);
}

.spec-note {
  margin-top: 1.3rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--rule);
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--muted);
}

/* --- bands --- */

.band {
  border-top: 1px solid var(--rule);
}
.band > .container {
  padding-block: var(--band-pad);
}
.band-last {
  border-bottom: 1px solid var(--rule);
}

/* Every heading on this page is a datasheet caption: index, label, then a hairline that runs out to
   the edge of whatever holds it. The line is the heading's own, so no section has to draw one. */
.section-head,
.spec-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-stretch: 85%;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.section-head::after,
.spec-head::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--rule);
}

/* Only the band headings: the size table's caption sets its own spacing inside the card. */
.section-head {
  margin-bottom: clamp(24px, 3.5vw, 40px);
}

/* Both grids on the page share one construction: 1px gaps over a ruled ground, pulled out to the
   rails so their dividers are the same lines the frame is made of. No element draws a border. */
.cards,
.dl-grid {
  display: grid;
  gap: 1px;
  background: var(--rule);
  border-block: 1px solid var(--rule);
  margin-inline: calc(var(--gutter) * -1);
}

/* Two by two, not auto-fit: there are exactly four cards, and a third column leaves the grid
   with a hole in it at most desktop widths. */
.cards {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.card {
  background: var(--panel);
  padding: clamp(24px, 3.2vw, 38px);
}
.card h3 {
  font-size: 1.0625rem;
  font-weight: 600;
  font-stretch: 98%;
  letter-spacing: -0.012em;
  line-height: 1.3;
}
.card p {
  margin-top: 0.7rem;
  color: var(--body);
  font-size: 0.9375rem;
}

/* --- downloads --- */

.dl-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* A column, so the button sits on one line across all three cards however long the note above it
   runs — `margin-top: auto` on the button is what holds that line. */
.dl-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.35rem;
  background: var(--panel);
  padding: clamp(32px, 4vw, 52px) clamp(20px, 2.6vw, 34px);
  transition: background 0.16s;
}
.dl-card.is-available:hover {
  background: var(--accent-wash);
}

.dl-art {
  width: 96px;
  margin-bottom: 1.5rem;
  color: var(--ink);
}
.dl-art svg {
  display: block;
  width: 100%;
  height: auto;
}
.art-line {
  stroke: currentColor;
  stroke-width: 1;
  fill: none;
}
.art-fill {
  fill: currentColor;
}
.art-accent {
  fill: var(--accent);
}

.dl-os {
  font-size: 1.125rem;
  font-weight: 600;
  font-stretch: 100%;
  letter-spacing: -0.01em;
}
.dl-arch {
  font-family: var(--mono);
  font-size: 0.625rem;
  font-stretch: 85%;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
/* The bottom margin, not the button's top one: `margin-top: auto` below is what lines the three
   buttons up, and anything it has to compete with wins over it and breaks that line. */
.dl-note {
  margin: 0.6rem 0 1.9rem;
  color: var(--body);
  font-size: 0.875rem;
  line-height: 1.55;
  max-width: 32ch;
  text-wrap: pretty;
}

.dl-get {
  margin-top: auto;
}

/* A planned platform is visibly not a control: the drawing is dimmed and the button's place holds
   a ruled label, not something pressable that does nothing. */
.is-planned .dl-art,
.is-planned .dl-os {
  color: var(--muted);
}
.is-planned .dl-os {
  font-weight: 500;
}
.btn-off {
  border: 1px dashed var(--rule-strong);
  color: var(--muted);
}

/* --- motion ---
   One orchestrated entrance on load, ordered by --i, and nothing that fires on scroll. The page
   is short enough to be read in one pass; content that animates as you reach it just delays it. */

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.rise {
  animation: rise 0.72s cubic-bezier(0.2, 0.75, 0.2, 1) both;
  animation-delay: calc(var(--i) * 0.085s);
}

/* --- narrow --- */

@media (max-width: 900px) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Below this the columns are narrower than the text they hold, and the prose starts breaking
   every second word. Both grids collapse at the same width so the page keeps one rhythm. */
@media (max-width: 720px) {
  .cards,
  .dl-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .dl-card {
    padding-block: clamp(28px, 7vw, 40px);
  }
}
