/* ─────────────────────────────────────────────────────────────────────────
   JNEXT — www.jnext.es landing page

   The design rule is the machine's own rule: on a ZX Spectrum, colour was
   applied per 8x8 attribute cell, not per pixel. So the layout is a grid of
   cells, each carrying exactly one INK/PAPER pair from the bright palette;
   borders are 1px and hard, radii are zero, and nothing is soft-shadowed or
   tweened — the hardware could not do any of that. The one gradient on the
   page is the rainbow flash stripe, because that one it *could* do.

   Hovering or focusing a cell swaps INK and PAPER instantly: INVERSE 1.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  /* Field. Black with a blue cast, the way a CRT actually showed it. */
  --paper:      #0E0E14;
  --paper-cell: #15151E;
  --ink:        #E6E7EF;
  --ink-dim:    #8B8DA0;
  --rule:       #2A2B38;

  /* The eight BRIGHT attribute colours, at their real values.
     Blue is stripe-only: #0000FF on black is unreadable as text. */
  --zx-red:     #FF0000;
  --zx-magenta: #FF00FF;
  --zx-green:   #00FF00;
  --zx-cyan:    #00FFFF;
  --zx-yellow:  #FFFF00;
  --zx-blue:    #0000FF;
  --zx-white:   #FFFFFF;

  --accent: var(--ink);          /* per-cell INK, overridden by modifiers */

  --cell: 8px;                   /* the attribute cell: every gap is a multiple */
  --gap:  calc(var(--cell) * 2);
  --max:  1216px;                /* 152 cells */
  --edge: 56px;                  /* width of the loading-stripe border */
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 17px;
  line-height: 1.6;
  font-variant-ligatures: none;
  overflow-x: hidden;
}

/* Screen memory: a fine 8px attribute grid, with every eighth line — the
   character cell boundary — drawn a shade stronger. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right,  rgba(230,231,239,.055) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(230,231,239,.055) 1px, transparent 1px),
    linear-gradient(to right,  rgba(230,231,239,.022) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(230,231,239,.022) 1px, transparent 1px);
  background-size: 64px 64px, 64px 64px, 8px 8px, 8px 8px;
}

/* The tape-loading border: irregular red/cyan bands, the way the pilot tone
   painted the edges of the screen while a program came in off cassette. */
.border-stripes {
  position: fixed;
  top: 0;
  width: var(--edge);
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: .3;
  background: repeating-linear-gradient(180deg,
    var(--zx-red)  0 3px,  var(--zx-cyan)  3px 7px,
    var(--zx-red)  7px 9px, var(--zx-cyan)  9px 16px,
    var(--zx-red) 16px 21px, var(--zx-cyan) 21px 24px,
    var(--zx-red) 24px 30px, var(--zx-cyan) 30px 33px,
    var(--zx-red) 33px 39px, var(--zx-cyan) 39px 42px,
    var(--zx-red) 42px 46px, var(--zx-cyan) 46px 53px);
}
.border-stripes--l { left: 0;  mask-image: linear-gradient(90deg, #000 0%, transparent 100%); }
.border-stripes--r { right: 0; mask-image: linear-gradient(270deg, #000 0%, transparent 100%); }

.skip {
  position: absolute;
  left: -9999px;
  z-index: 100;
  padding: 12px 16px;
  background: var(--zx-yellow);
  color: var(--paper);
  font-weight: 700;
  text-decoration: none;
}
.skip:focus { left: 8px; top: 8px; }

a { color: inherit; }

/* ── Hero ──────────────────────────────────────────────────────────────── */

main { position: relative; z-index: 1; }

.hero {
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(48px, 9vw, 104px) var(--gap) clamp(32px, 5vw, 56px);
}

.eyebrow {
  margin: 0 0 20px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .74rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.hero__h { margin: 0; }

.hero__name {
  display: block;
  font-size: clamp(2.2rem, 6.2vw, 4.7rem);
  font-weight: 800;
  font-stretch: 125%;
  line-height: .94;
  letter-spacing: .015em;
}

/* The subtitle carries the actual description, so it stays close to the
   wordmark in size — a caption underneath would bury the one sentence that
   says what this is. */
.hero__tag {
  display: block;
  margin-top: .3em;
  /* No max-width: the subtitle is one sentence and should sit on one line
     wherever it fits. It only wraps once the viewport is narrow enough that the
     font-size floor stops it shrinking any further, which is what should
     happen. Capping it here forced a break on wide screens that had room. */
  font-size: clamp(1.45rem, 3.5vw, 2.6rem);
  font-weight: 600;
  font-stretch: 112%;
  line-height: 1.1;
  letter-spacing: -.014em;
  color: #C9CBD9;
}

/* No measure cap: the paragraph wraps to whatever its container gives it,
   rather than stopping short and leaving a gap down the right. */
.hero__lede {
  margin: 28px 0 0;
  font-size: clamp(1.02rem, 1.5vw, 1.2rem);
  color: #C3C5D4;
}
.hero__lede em {
  font-style: normal;
  font-weight: 700;
  color: var(--zx-yellow);
}

.hero__cta {
  margin: 36px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap);
}

.btn {
  display: inline-block;
  padding: 14px 24px;
  border: 1px solid var(--rule);
  color: var(--ink);
  font-weight: 600;
  font-size: .96rem;
  text-decoration: none;
}
.btn:hover, .btn:focus-visible { border-color: var(--ink); }

/* The one filled control on the page. It uses plain INK on PAPER rather than a
   saturated accent: this is a link to a download, not a sales funnel. */
.btn--go {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.btn--go:hover, .btn--go:focus-visible {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--ink);
}

/* ── The attribute grid ────────────────────────────────────────────────── */

.grid {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 var(--gap) clamp(56px, 8vw, 96px);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
}

.cell {
  --accent: var(--ink);
  /* What the text becomes once the cell inverts. A cell declares an INK/PAPER
     pair, so both halves live together on the modifier. */
  --on-accent: var(--paper);
  position: relative;
  grid-column: span 4;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 24px;
  background: var(--paper-cell);
  border: 1px solid var(--rule);
  color: var(--ink);
  text-decoration: none;
  /* No transition: the machine inverted a cell in the time it took to
     rewrite one attribute byte. */
}
.cell--wide { grid-column: span 6; }

.cell:hover, .cell:focus-visible {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.cell:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.cell__eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .68rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
}
.cell:hover .cell__eyebrow,
.cell:focus-visible .cell__eyebrow { color: var(--on-accent); }

.cell__h {
  margin: 10px 0 0;
  font-size: 1.4rem;
  font-weight: 700;
  font-stretch: 112%;
  line-height: 1.1;
  letter-spacing: -.012em;
}
/* The release version and the project-status heading are deliberately the same
   optical weight: neither the good news nor the caveat gets to shout. */
.cell__h--big {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: clamp(1.8rem, 3.4vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -.03em;
}
.cell__h--lead {
  font-size: clamp(1.6rem, 2.9vw, 2.1rem);
  font-stretch: 118%;
}

.cell__p {
  margin: 12px 0 0;
  font-size: .93rem;
  color: var(--ink-dim);
}
.cell:hover .cell__p,
.cell:focus-visible .cell__p { color: var(--on-accent); }

.cell__go {
  margin-top: auto;
  padding-top: 22px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .78rem;
  font-weight: 700;
  color: var(--accent);
}
.cell__go::after { content: ' \2192'; }
.cell:hover .cell__go,
.cell:focus-visible .cell__go { color: var(--on-accent); }

.cell--yellow  { --accent: var(--zx-yellow); }
.cell--cyan    { --accent: var(--zx-cyan); }
.cell--green   { --accent: var(--zx-green); }
.cell--magenta { --accent: var(--zx-magenta); }
/* PAPER 2, INK 7 — red is the one bright colour dark enough to take white
   rather than black, and it is what the machine itself paired it with. */
.cell--red     { --accent: var(--zx-red); --on-accent: var(--zx-white); }
.cell--white   { --accent: var(--zx-white); }

/* ── Screenshot cells ──────────────────────────────────────────────────── */

.cell--shot {
  padding: 0;
  --accent: var(--zx-cyan);
}
.cell--shot:hover, .cell--shot:focus-within {
  background: var(--paper-cell);
  border-color: var(--accent);
  color: var(--ink);
}
.cell--shot img {
  display: block;
  width: 100%;
  height: auto;
  /* The files are the 320x256 captures scaled x4 by nearest neighbour, so every
     Next pixel is an exact 4x4 block in the file and no colour was invented.
     The cell is ~382px wide, which is 1.19x the native width: at that ratio
     `image-rendering: pixelated` has to double one source pixel in five and the
     result is visibly lumpy. Shipping 1280x1024 and letting the browser scale
     it DOWN instead means the default (smooth) filter averages whole blocks —
     hard edges stay hard, and they land antialiased on any cell width and on
     any display density. Hence no image-rendering here: the default is right. */
  border-bottom: 1px solid var(--rule);
}
.cell--shot figcaption {
  padding: 14px 18px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .72rem;
  line-height: 1.5;
  color: var(--ink-dim);
}
.cell--shot:hover figcaption {
  background: var(--accent);
  color: var(--on-accent);
}

/* ── Footer ────────────────────────────────────────────────────────────── */

.foot {
  position: relative;
  z-index: 1;
  border-top: 1px solid var(--rule);
}
.foot__in {
  max-width: var(--max);
  margin: 0 auto;
  padding: 32px var(--gap) 56px;
}
/* As with the hero lede: no measure cap and no text-wrap: balance — both hold
   the text back from the container edge. It fills the footer column and wraps
   where the column ends. */
.foot__note {
  margin: 0 0 18px;
  font-size: .84rem;
  color: var(--ink-dim);
}
.foot__meta {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 22px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .74rem;
  color: var(--ink-dim);
}
.foot__meta a { color: var(--ink); text-decoration-color: var(--rule); }
.foot__meta a:hover { text-decoration-color: currentColor; }

/* ── Raster paint ──────────────────────────────────────────────────────────
   The grid appears the way the display was drawn: cell by cell, in raster
   order, each one arriving whole. steps(1) — no fade, nothing tweens. */

@media (prefers-reduced-motion: no-preference) {
  .cell {
    animation: paint .01s steps(1, end) both;
    animation-delay: calc(var(--i, 0) * 55ms + 120ms);
  }
}
@keyframes paint {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ── Narrower screens ──────────────────────────────────────────────────── */

@media (max-width: 1000px) {
  .cell       { grid-column: span 6; }
  .cell--wide { grid-column: span 12; }
}

@media (max-width: 860px) {
  :root { --edge: 28px; }
  .border-stripes { opacity: .1; }
}

@media (max-width: 620px) {
  body { font-size: 16px; }
  .cell, .cell--wide { grid-column: span 12; }
  .border-stripes { display: none; }
  .hero, .grid, .foot__in { padding-left: 16px; padding-right: 16px; }
}
