/* ============================================================================
   leetdom.com — the house style
   ============================================================================

   "Leetest, evolved." Dave ran leetest.com from ~2006-2009; Leetdom is its
   successor and is meant to read as the same site after twenty years of
   modernising, not as a remake of it.

   The DNA below is taken from the REAL 2007 stylesheet and homepage, pulled
   from the Wayback Machine (css.css @ 20071012, homepage @ 20070209), not from
   memory. Worth writing down, because the previous brief in docs/ got several
   of these wrong and a future reader will otherwise "fix" this back:

     body{background-color:#336699}     the page was a DEEP BLUE FIELD, not white
     .table{border:3px solid #00aaee;   white panels floating on that blue, edged
            background:#ffffff}         in bright cyan. THE signature of the site.
     b{color:#336699}                   headings were bold blue "Something:" lines
     a:visited{color:#666666}           grey, not purple
     font-family:"Arial"                Arial, not Verdana
     bgcolor="#88ccff"                  the "Todays Game:" feature row
     #eeeeee / #cccccc                  alternating "Previous Games" rows
     a:hover{background-color:#ffffff}  hover inverted to a white ground

   What that becomes here: the deep blue survives as the page ground, the white
   boxes survive as content cards, and #00aaee survives as the accent that edges
   them. The 3px border becomes a 2px top rule (a border on all four sides at
   modern card sizes reads as a 2007 table; on one side it reads as a deliberate
   accent). Bold blue "Heading:" lines survive verbatim as section labels,
   including the colon, because that punctuation is genuinely distinctive.

   What does NOT survive: 11px type, fixed table layout, centre tags, and the
   assumption that a page is 1024px wide.

   No build step, no framework, no runtime CDN. Two self-hosted variable fonts
   (88KB total) and plain CSS, per docs/SITE-2007-BRIEF.md's rules of engagement.
   ========================================================================== */

/* ------------------------------------------------------------------ fonts -- */
/* Variable fonts: ONE file each covers every weight we use, which is why there
   are two @font-face rules here and not seven. Both are SIL OFL. */
@font-face {
  font-family: 'Archivo';
  src: url('/fonts/archivo-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'InterVar';
  src: url('/fonts/inter-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ------------------------------------------------------------------ tokens -- */
:root {
  /* The blue field, in depth. --bg-deep is behind everything; --bg is the page;
     --bg-rise is the header/footer chrome. Three steps, not a gradient, so the
     page still looks right when a card is dragged across it. */
  --bg-deep:  #05121f;
  --bg:       #0a2137;
  --bg-rise:  #0d2a45;
  --bg-line:  #17456d;   /* hairlines ON the blue */

  --steel:    #336699;   /* the heritage blue, unchanged */
  --steel-dk: #1f5c94;   /* links on white: 4.9:1, where raw #336699 is 4.4:1 */
  --cyan:     #00a6ed;   /* the heritage accent (#00aaee), a shade truer on sRGB */
  --cyan-lt:  #7fd4ff;   /* links/accents ON the blue field */
  --cyan-dim: #0d3f60;

  /* Card surfaces. */
  --paper:    #ffffff;
  --paper-2:  #f6f9fc;   /* zebra A — descendant of #eeeeee */
  --paper-3:  #edf2f8;   /* zebra B — descendant of #cccccc, much softened */
  --feature:  #e6f5ff;   /* descendant of the #88ccff "Todays Game" row */

  --ink:      #0e1a26;
  --ink-2:    #46596e;   /* meta/secondary — 5.9:1 on white */
  --ink-3:    #6b7c90;   /* faintest usable — 4.6:1 on white */
  --rule:     #dde6ef;
  --rule-2:   #eaf0f6;

  --gold:     #b8801f;   /* review scores — 4.6:1 on white */
  --good:     #17794a;
  --bad:      #b3261e;

  --font-display: 'Archivo', ui-sans-serif, system-ui, 'Segoe UI', Arial, sans-serif;
  --font-body: 'InterVar', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --measure: 68ch;       /* reading width for long-form prose */
  --radius: 4px;         /* small. 2007 boxes had none; pills would read as SaaS */
  --gap: 18px;

  /* One shadow scale. Cards sit on a dark ground, so shadows are for lift off
     the blue, not for drama. */
  --lift-1: 0 1px 2px rgba(3, 12, 22, .28);
  --lift-2: 0 4px 14px rgba(3, 12, 22, .35);

  /* --- compatibility aliases -------------------------------------------------
     reviews.css was inherited wholesale from Wanderer along with the 1,941
     review pages it styles, and it reads eight token names that this palette
     renames. Aliasing them is deliberate rather than lazy: rewriting 446 lines
     of rules against markup emitted by a 1,079-line file nobody is refactoring
     today is how review pages quietly break. Every name below is READ by
     reviews.css — none are written here for new work, which should use the real
     tokens above. */
  --link:    var(--steel-dk);
  --muted:   var(--ink-2);
  --band:    var(--paper-2);
  --altA:    var(--paper-2);
  --altB:    var(--paper-3);
  /* --visited is read by reviews.css ONLY as the "cons" accent in pros/cons and
     the gold-button hover, never as a link colour (a:visited is set directly in
     base), so it maps to the negative red rather than to a purple. */
  --visited: var(--bad);
}

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

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

html { min-height: 100%; }

body {
  margin: 0;
  /* Short pages (a guide index, an empty tag) otherwise left the footer floating
     mid-screen with blue underneath it. The 2007 site set height:100% on html and
     body for the same reason; this is that, done with flexbox. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  /* Depth without noise: one very soft top-light so the masthead area reads as
     nearer than the foot of the page. No repeating texture — it costs bytes and
     shimmers on high-DPI phones. */
  background-image:
    radial-gradient(1200px 420px at 50% -180px, #16456e 0%, rgba(22, 69, 110, 0) 70%);
  background-repeat: no-repeat;
  background-attachment: fixed;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  font-synthesis-weight: none;
}

img { max-width: 100%; height: auto; }
svg { display: block; }

a { color: var(--steel-dk); text-underline-offset: 2px; }
a:visited { color: #5a5f6b; }          /* 2007 used grey, not purple. Kept. */
a:hover { color: var(--cyan); }

/* One focus style everywhere, high contrast on both light cards and blue ground. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--cyan);
  outline-offset: 2px;
  border-radius: 2px;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0 0 .5em;
  text-wrap: balance;
}

.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;
}

/* ------------------------------------------------------------------- shell -- */
.ld-page {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 20px;
}
/* The front page and the archive listings earn more width than an article does.
   2007 used 100% of the window; we use more of it than a blog would, but stop
   short of edge-to-edge because 21:9 monitors exist. */
.ld-page.wide { max-width: 1560px; }

main { padding: 26px 0 56px; flex: 1 0 auto; }

/* ------------------------------------------------------------------ header -- */
.ld-header {
  background: var(--bg-deep);
  border-bottom: 1px solid var(--bg-line);
}
.ld-header .ld-page {
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: 74px;
  padding-top: 12px;
  padding-bottom: 12px;
  flex-wrap: wrap;
}

/* The wordmark. The 2007 logo was a heavy blocky uppercase LEETEST in steel blue
   with a hard offset shadow — an extruded, stamped feel. Archivo 800 at tight
   tracking is the closest modern letterform; the offset shadow is kept because
   it is the most recognisable single thing about the old logo, but at 2px rather
   than the original's chunky 4px+, and in the deep blue rather than grey. */
.ld-logo {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(26px, 3.4vw, 34px);
  letter-spacing: -0.03em;
  text-transform: uppercase;
  color: #fff;
  text-decoration: none;
  line-height: 1;
  text-shadow: 2px 2px 0 var(--steel);
  white-space: nowrap;
}
.ld-logo:visited { color: #fff; }
/* The leading L in the heritage cyan — the one flourish, carried over from the
   old site's accent rather than invented. */
.ld-logo::first-letter { color: var(--cyan); }
.ld-logo:hover { color: #fff; text-shadow: 2px 2px 0 var(--cyan); }

.ld-tagline {
  margin: 0;
  color: #9fc0dc;
  font-size: 13px;
  line-height: 1.3;
  max-width: 30ch;
  border-left: 2px solid var(--bg-line);
  padding-left: 14px;
}

.ld-search {
  margin-left: auto;
  display: flex;
  align-items: stretch;
  gap: 0;
}
.ld-search input[type=search] {
  font: inherit;
  font-size: 14px;
  width: clamp(160px, 22vw, 280px);
  padding: 9px 12px;
  color: #eaf4ff;
  background: #071b2e;
  border: 1px solid var(--bg-line);
  border-right: 0;
  border-radius: var(--radius) 0 0 var(--radius);
}
.ld-search input[type=search]::placeholder { color: #6d8ba7; }
.ld-search input[type=search]:focus-visible { outline-offset: -1px; }
.ld-search button {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #04202f;
  background: var(--cyan);
  border: 1px solid var(--cyan);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 0 16px;
  cursor: pointer;
  transition: background .15s ease;
}
.ld-search button:hover { background: #35bcff; }

/* --------------------------------------------------------------------- nav -- */
/* 2007 had no nav bar at all — the sections were links inside boxes. This is the
   one piece of chrome with no direct ancestor, so it is built from the same
   parts as everything else: blue ground, cyan for the active state. */
.ld-nav {
  background: var(--bg-rise);
  border-bottom: 1px solid var(--bg-line);
  position: sticky;
  top: 0;
  z-index: 20;
}
.ld-nav .ld-page {
  display: flex;
  gap: 2px;
  overflow-x: auto;
  scrollbar-width: none;
}
.ld-nav .ld-page::-webkit-scrollbar { display: none; }
.ld-nav a {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: #bcd6ec;
  text-decoration: none;
  padding: 14px 16px;
  white-space: nowrap;
  border-bottom: 3px solid transparent;
  transition: color .15s ease, background .15s ease;
}
.ld-nav a:visited { color: #bcd6ec; }
.ld-nav a:hover { color: #fff; background: rgba(255, 255, 255, .06); }
.ld-nav a[aria-current] {
  color: #fff;
  border-bottom-color: var(--cyan);
  background: rgba(0, 166, 237, .1);
}

/* ------------------------------------------------------------------ footer -- */
/* The 2007 footer was a centred pipe-separated row ending in the exact moment
   the page was generated. Both are kept — the live timestamp is a genuine piece
   of the old site's character and costs nothing. */
.ld-footer {
  background: var(--bg-deep);
  border-top: 1px solid var(--bg-line);
  padding: 26px 0 34px;
  color: #8fb2ce;
  font-size: 13px;
}
.ld-footer .ld-page { text-align: center; }
.ld-footer p { margin: 0; }
.ld-footer a { color: #d6e9f8; text-decoration: none; }
.ld-footer a:visited { color: #d6e9f8; }
.ld-footer a:hover { color: var(--cyan-lt); text-decoration: underline; }
/* The separators are literal " | " characters in layout.js's markup (as they were
   in 2007), NOT generated content — adding ::before pipes here doubles them. The
   spacing below is all this rule may do. */
.ld-footer p > * { margin: 0 3px; }

/* ------------------------------------------------------------------- grid -- */
/* The 2007 front page was 33% / 34% / 33%. The centre column carried the
   headline content, so here it is genuinely wider rather than a hair wider, and
   the outer columns are sidebars in the editorial sense. */
.ld-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.7fr) minmax(0, 1fr);
  gap: var(--gap);
  align-items: start;
}
.ld-cols > * { min-width: 0; }

@media (max-width: 1100px) {
  .ld-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  /* The wide column leads, full width, with the two sidebars side by side under
     it — better than letting a 1.7fr column squeeze to half a screen. */
  .ld-col-wide { grid-column: 1 / -1; order: -1; }
}
@media (max-width: 720px) {
  .ld-cols { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------------------------ panel -- */
/* The 2007 `.table`: a white box on the blue, edged in #00aaee. Here the border
   is a hairline and the cyan is concentrated into a top rule — at these sizes a
   full 3px cyan frame reads as a table, one edge reads as design. */
.panel {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-top: 2px solid var(--cyan);
  border-radius: var(--radius);
  padding: 16px 16px 14px;
  margin: 0 0 var(--gap);
  box-shadow: var(--lift-1);
}
.panel:last-child { margin-bottom: 0; }

/* The bold "Something:" line. Kept, colon and all. */
.panel > h2,
.panel > h1 {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--steel-dk);
  margin: 0 0 12px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--rule);
}

/* The page's own title. On the front page this sits above the columns. */
/* The front page's <h1> says "Leetdom", directly under a masthead that already
   says LEETDOM. It has to stay in the markup (it is the page's only h1), but at
   full display size it reads as the logo printed twice — so it is set as a
   section rule instead: small, spaced, with the cyan hairline carrying the eye
   into the columns. */
main > .ld-page > h1:first-child {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #9fc0dc;
  margin: 2px 0 16px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--bg-line);
  display: flex;
  align-items: center;
  gap: 10px;
}
main > .ld-page > h1:first-child::after {
  content: '';
  flex: 1;
  height: 2px;
  background: linear-gradient(90deg, var(--cyan), rgba(0, 166, 237, 0));
}

/* -------------------------------------------------------------------- tiles -- */
.tiles { display: flex; flex-direction: column; gap: 2px; }

/* The core content unit since 2007: thumbnail left, bold linked title and a
   one-line blurb right. */
.tile {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 9px;
  border-radius: 3px;
  background: var(--paper);
  transition: background .14s ease;
}
/* The #eeeeee / #cccccc alternation, softened to the point where it organises
   the list without striping it like a spreadsheet. */
.tiles .tile:nth-child(odd) { background: var(--paper-2); }
.tile:hover { background: #e9f4fd; }

.tile .thumb {
  flex: 0 0 auto;
  width: 104px;
  height: 78px;
  border-radius: 3px;
  border: 1px solid var(--rule);
  overflow: hidden;
  background: var(--paper-3);
  display: grid;
  place-items: center;
  /* The no-image case is the NORMAL case: only 20,322 of 177,407 rows carry a
     screenshot. So this has to look chosen, not broken — see .thumb:not(:has(img))
     below for the lettered plate. */
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 30px;
  color: #97b2cc;
  line-height: 1;
}
.tile .thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  /* Screenshots are 100x75-ish and wildly inconsistent; cover crops rather than
     letterboxes, which keeps the row rhythm even. */
}
/* A lettered plate for the image-less majority. The tint is not random — it is
   set per-tile from the title in home.js via --plate, so a given game always
   gets the same colour and a column of tiles looks composed rather than noisy. */
.tile .thumb:not(:has(img)) {
  background:
    linear-gradient(140deg, hsl(var(--plate, 205) 42% 92%), hsl(var(--plate, 205) 38% 84%));
  color: hsl(var(--plate, 205) 45% 42%);
  border-color: hsl(var(--plate, 205) 35% 82%);
}

.tile > span:last-child { min-width: 0; display: block; }

.tile .name {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  margin-bottom: 3px;
  /* Long Flashpoint titles are common; two lines then ellipsis keeps rows even. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.tile .name:visited { color: var(--ink); }
.tile:hover .name { color: var(--steel-dk); text-decoration: underline; }

.tile .blurb {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  color: var(--ink-2);
  font-size: 13px;
  line-height: 1.4;
}

/* In the centre column there is room for two tiles abreast, which roughly halves
   the height of "Previous Games:" and stops the front page running a long way
   past its two sidebars. The sidebars keep the single-column list, which is the
   2007 shape and the right one at ~370px.

   The #eeeeee/#cccccc zebra is dropped HERE ONLY: alternating rows read as a
   list, but alternating cells in a 2-up grid read as a checkerboard. Inside the
   grid the tiles get a hairline instead, which does the same separating job. */
.ld-col-wide .tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(255px, 1fr));
  gap: 10px;
}
.ld-col-wide .tiles .tile,
.ld-col-wide .tiles .tile:nth-child(odd) {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 3px;
}
.ld-col-wide .tiles .tile:hover { background: #f2fafe; border-color: #b6e2fb; }
/* Today's Game is the page's headline and always spans. */
.ld-col-wide .tiles .tile.feature { grid-column: 1 / -1; }

/* The "Todays Game:" feature row. 2007 painted it #88ccff; this keeps the cyan
   cast but turns the row into an actual feature module — bigger art, bigger
   headline — because it is the single most important thing on the front page. */
.tile.feature {
  display: block;
  background: var(--feature);
  border: 1px solid #b6e2fb;
  border-radius: var(--radius);
  padding: 12px;
}
.tiles .tile.feature:nth-child(odd) { background: var(--feature); }
.tile.feature:hover { background: #dbeffd; }
.tile.feature .thumb {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  max-height: 270px;
  margin-bottom: 12px;
  font-size: 76px;
  border-color: #b6e2fb;
}
.tile.feature .name {
  font-size: clamp(20px, 2.4vw, 27px);
  font-weight: 800;
  letter-spacing: -0.02em;
  -webkit-line-clamp: 3;
  margin-bottom: 5px;
}
.tile.feature .blurb { font-size: 15px; -webkit-line-clamp: 3; color: #2c4a63; }

/* ----------------------------------------------------------------- toplist -- */
/* 2007's numbered <ol> of bold links. The numerals are the design here. */
.toplist { list-style: none; margin: 0; padding: 0; counter-reset: top; }
.toplist li {
  counter-increment: top;
  display: flex;
  align-items: baseline;
  gap: 11px;
  padding: 8px 4px;
  border-bottom: 1px solid var(--rule-2);
}
.toplist li:last-child { border-bottom: 0; }
.toplist li::before {
  content: counter(top);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 17px;
  color: var(--cyan);
  min-width: 1.4em;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.toplist a {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14.5px;
  color: var(--ink);
  text-decoration: none;
}
.toplist a:visited { color: var(--ink); }
.toplist a:hover { color: var(--steel-dk); text-decoration: underline; }

/* ----------------------------------------------------------------- catgrid -- */
/* The 2×2 Browse box: Action / Puzzle / Sport / All, centred and bold. Kept as a
   2-up grid of real tap targets. */
.catgrid {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.catgrid a {
  display: block;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13.5px;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--steel-dk);
  text-decoration: none;
  padding: 13px 8px;
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: 3px;
  transition: background .14s ease, color .14s ease, border-color .14s ease;
}
.catgrid a:visited { color: var(--steel-dk); }
.catgrid a:hover {
  background: var(--cyan);
  border-color: var(--cyan);
  color: #04202f;
}

/* -------------------------------------------------------------------- misc -- */
.meta { color: var(--ink-2); font-size: 12.5px; line-height: 1.45; }
.empty {
  color: var(--ink-3);
  font-size: 13.5px;
  margin: 0;
  padding: 14px 12px;
  background: var(--paper-2);
  border: 1px dashed var(--rule);
  border-radius: 3px;
}

/* Plain link lists inside panels (Latest Reviews, Guides & Wiki). */
.panel ul:not(.catgrid):not(.toplist):not(.chips) {
  list-style: none; margin: 0; padding: 0;
}
.panel ul:not(.catgrid):not(.toplist):not(.chips) > li {
  padding: 9px 2px;
  border-bottom: 1px solid var(--rule-2);
  font-size: 14px;
}
.panel ul:not(.catgrid):not(.toplist):not(.chips) > li:last-child { border-bottom: 0; }
.panel ul b, .panel ul a > b { font-weight: 700; }
.panel ul a {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}
.panel ul a:visited { color: var(--ink); }
.panel ul a:hover { color: var(--steel-dk); text-decoration: underline; }
.panel ul .meta { display: block; margin-top: 2px; }

/* ----------------------------------------------------------------- buttons -- */
/* Square-ish, not pills. The old site's geometry was rectangular throughout. */
.btn {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: #04202f;
  background: var(--cyan);
  border: 1px solid var(--cyan);
  border-radius: var(--radius);
  padding: 10px 18px;
  text-decoration: none;
  cursor: pointer;
  transition: background .15s ease, transform .1s ease;
}
.btn:visited { color: #04202f; }
.btn:hover { background: #35bcff; color: #04202f; }
.btn:active { transform: translateY(1px); }

.btn.ghost {
  background: transparent;
  color: var(--steel-dk);
  border-color: var(--rule);
}
.btn.ghost:visited { color: var(--steel-dk); }
.btn.ghost:hover { background: var(--paper-2); border-color: var(--steel-dk); color: var(--steel-dk); }

/* On the blue ground rather than inside a card. */
main > .ld-page > .btn.ghost,
.pager .btn.ghost {
  color: #d6e9f8;
  border-color: var(--bg-line);
  background: rgba(255, 255, 255, .04);
}
main > .ld-page > .btn.ghost:hover,
.pager .btn.ghost:hover { background: rgba(255, 255, 255, .1); color: #fff; }

/* ------------------------------------------------------------------ pager -- */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 22px 0 0;
  color: #9fc0dc;
  font-size: 13px;
}
.pager a {
  color: #d6e9f8;
  text-decoration: none;
  padding: 8px 13px;
  border: 1px solid var(--bg-line);
  border-radius: 3px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 13px;
  background: rgba(255, 255, 255, .04);
}
.pager a:visited { color: #d6e9f8; }
.pager a:hover { background: var(--cyan); border-color: var(--cyan); color: #04202f; }
.pager .on, .pager [aria-current] {
  background: var(--cyan);
  border-color: var(--cyan);
  color: #04202f;
}
.pager-pos, .pager-nums { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

/* ------------------------------------------------------------------- chips -- */
.chips { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; }
.chip, .chip-plain {
  display: inline-block;
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
  padding: 6px 10px;
  border-radius: 999px;   /* the one place a pill is right: tags read as tags */
  background: var(--paper-2);
  border: 1px solid var(--rule);
  color: var(--ink-2);
  text-decoration: none;
}
a.chip:visited { color: var(--ink-2); }
a.chip:hover { background: var(--cyan); border-color: var(--cyan); color: #04202f; }

/* --------------------------------------------------------------- card grid -- */
/* The archive listings: a responsive grid of the same tile unit, so a game looks
   identical whether it is on the front page or on page 400 of /games. */
.grid, .browse-tiles, .review-grid, .shot-grid, .chart-mini-grid, .related-list {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}
.grid > .tile, .browse-tiles > .tile {
  display: block;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 10px;
  box-shadow: var(--lift-1);
  transition: transform .14s ease, box-shadow .14s ease, border-color .14s ease;
}
.grid > .tile:nth-child(odd), .browse-tiles > .tile:nth-child(odd) { background: var(--paper); }
.grid > .tile:hover, .browse-tiles > .tile:hover {
  transform: translateY(-2px);
  box-shadow: var(--lift-2);
  border-color: #b6e2fb;
}
.grid > .tile .thumb, .browse-tiles > .tile .thumb {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  margin-bottom: 10px;
  font-size: 44px;
}

/* ------------------------------------------------------------- game player -- */
/* The Ruffle stage. Black, because everything else on a game page should get out
   of the way of the game. */
/* margin-inline:auto centres a capped stage. Portrait games cap out around 560px inside a
   1200px content column, and left-aligned they read as a rendering accident rather than a
   deliberate size. */
/* "Desktop" badge on a tile. The shelf was touch-only until 8 Sep 2026; now that it also
   carries games needing a keyboard, a phone visitor has to be able to see which is which
   BEFORE tapping in — being sent to a game you cannot play is the exact complaint that created
   this section in the first place. */
.pill-desktop {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper-3);
  color: var(--ink-2);
  font-family: var(--font-display);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  vertical-align: 2px;
}

.stagewrap { margin: 0 auto var(--gap); }
.stage {
  background: #000;
  border: 1px solid var(--bg-line);
  border-radius: var(--radius);
  overflow: hidden;
  display: grid;
  place-items: center;
  min-height: 320px;
}
.stage canvas, .stage ruffle-player, .stage object, .stage embed { max-width: 100%; }
.stagebar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
  color: #9fc0dc;
  font-size: 13px;
}

/* The embedded game frame on /games. The stage is a black box of a known shape
   (--stage-aspect comes from the game's manifest, e.g. "4 / 3"), because a game
   that gets handed a container of the wrong shape either letterboxes itself or,
   worse, crops its own HUD off the edge. */
.stagewrap { --stage-aspect: 4 / 3; }
.gameframe {
  display: block;
  width: 100%;
  aspect-ratio: var(--stage-aspect);
  border: 0;
  background: #000;
}

/* ---------------------------------------------------------------- maximise --
   The CSS half of the Maximise button. This exists because iOS Safari refuses
   requestFullscreen() on anything that is not a <video>: on an iPhone this rule
   IS fullscreen, and the native API adds only the hiding of the browser chrome
   where it is available.

   NOTE FOR ANYONE READING THIS AFTER A BUG REPORT: games.js and stage.js have
   emitted .is-max, body.stage-maxed and .stage-exit since the player was first
   written, but nothing ever styled them, so Maximise did nothing at all on iOS
   and only worked elsewhere by falling through to the native API. Added 7 Sep
   2026 along with the /games section. Do not delete these as unused — they are
   toggled from JavaScript, so no grep of the HTML will find them. */
.stagewrap.is-max {
  position: fixed;
  inset: 0;
  z-index: 1000;
  max-width: none !important;
  margin: 0;
  background: #000;
  display: grid;
  place-items: center;
}
.stagewrap.is-max .stage {
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 0;
  min-height: 0;
}
.stagewrap.is-max .gameframe { height: 100%; aspect-ratio: auto; }

/* Kill the scroll behind the overlay. Without this the page keeps scrolling
   under a fixed layer, so letting go of a drag inside a game jumps the page. */
body.stage-maxed { overflow: hidden; }

/* The way out. Fixed rather than inside .stagewrap so it cannot be covered by a
   game that paints to the edges, and above z-index 1000 for the same reason. It
   is the ONLY exit in the CSS-only case, so it is deliberately large enough to
   hit with a thumb and sits clear of the notch. */
.stage-exit {
  position: fixed;
  z-index: 1001;
  top: max(10px, env(safe-area-inset-top));
  right: max(10px, env(safe-area-inset-right));
  padding: 10px 14px;
  font: 600 14px/1 var(--font-display);
  color: #fff;
  background: rgba(5, 18, 31, .72);
  border: 1px solid var(--cyan);
  border-radius: var(--radius);
  cursor: pointer;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.stage-exit:hover { background: var(--cyan); color: #04202f; }
.stage-exit[hidden] { display: none; }

/* --------------------------------------------------------------- long-form -- */
/* Article bodies. Everything here is about the reading line: measure, rhythm,
   and enough contrast to read a 3,000-word review without strain. */
.prose, .review-body, .body {
  max-width: var(--measure);
  font-size: 17px;
  line-height: 1.68;
  color: #1a2836;
}
.prose > * + *, .review-body > * + *, .body > * + * { margin-top: 1.1em; }
.prose h2, .review-body h2, .body h2 {
  font-size: 25px;
  margin-top: 1.9em;
  padding-top: .55em;
  border-top: 1px solid var(--rule);
  letter-spacing: -0.02em;
}
.prose h3, .review-body h3, .body h3 { font-size: 19.5px; margin-top: 1.6em; }
.prose p, .review-body p, .body p { margin: 0; }
.prose ul, .prose ol, .review-body ul, .review-body ol { padding-left: 1.35em; margin: 0; }
.prose li + li, .review-body li + li { margin-top: .45em; }
.prose img, .review-body img {
  border-radius: var(--radius);
  border: 1px solid var(--rule);
  display: block;
}
.prose figure, .review-body figure { margin: 1.6em 0; }
.prose figcaption, .review-body figcaption, .shot-credit {
  font-size: 13px;
  color: var(--ink-3);
  margin-top: .55em;
  line-height: 1.45;
}
.prose blockquote, .review-body blockquote {
  margin: 1.6em 0;
  padding: 4px 0 4px 18px;
  border-left: 3px solid var(--cyan);
  color: #2b3d4f;
  font-size: 18px;
}
.prose code, .review-body code {
  font-family: var(--font-mono);
  font-size: .88em;
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: .1em .35em;
}
.lede, .summary, .verdict-lead {
  font-size: 19px;
  line-height: 1.55;
  color: #2b3d4f;
  max-width: var(--measure);
}

/* ------------------------------------------------------------------ about -- */
/* about.html is Dave's original page, carried across. It keeps its own shape;
   this only stops it clashing with the new chrome. */
.wrap { max-width: 900px; margin: 0 auto; }
.box {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-top: 2px solid var(--cyan);
  border-radius: var(--radius);
  padding: 18px;
  margin: 0 0 var(--gap);
  box-shadow: var(--lift-1);
}
.two { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--gap); }
.banner, .me { border-radius: var(--radius); }
.counter {
  font-family: var(--font-mono);
  background: var(--bg-deep);
  color: var(--cyan-lt);
  border-radius: 3px;
  padding: 3px 9px;
  display: inline-block;
}
.backhome { color: #d6e9f8; }

/* ------------------------------------------------------------------ mobile -- */
@media (max-width: 720px) {
  body { font-size: 15.5px; }
  main { padding: 18px 0 40px; }
  .ld-page { padding: 0 14px; }

  .ld-header .ld-page {
    min-height: 0;
    gap: 10px 14px;
    padding-top: 14px;
    padding-bottom: 14px;
  }
  /* The tagline is the first thing to go: on a phone the wordmark and the search
     box are what the header is for. */
  .ld-tagline { display: none; }
  .ld-search { margin-left: 0; width: 100%; }
  .ld-search input[type=search] { width: 100%; }

  .ld-nav a { padding: 13px 13px; font-size: 12.5px; }

  .panel { padding: 13px 13px 12px; }
  .tile { padding: 8px; gap: 10px; }
  .tile .thumb { width: 84px; height: 63px; font-size: 24px; }
  .tile .name { font-size: 14.5px; }
  .tile.feature .thumb { max-height: 220px; }

  .grid, .browse-tiles, .review-grid, .shot-grid, .related-list {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 10px;
  }

  .prose, .review-body, .body { font-size: 16.5px; }
  .prose h2, .review-body h2 { font-size: 21px; }

  /* Touch targets. */
  .ld-footer p > * { display: inline-block; padding: 4px 0; }
  .pager a { padding: 10px 14px; }
}

/* Respect people who have asked for less movement. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Printing a review should give you the review, not the furniture. */
@media print {
  body { background: #fff; }
  .ld-header, .ld-nav, .ld-footer, .ld-search, .pager { display: none; }
  .panel, .box { border: 1px solid #999; box-shadow: none; }
}

/* ================================================================ on the blue --
   THE SYSTEMATIC BUG THIS FIXES.

   Every colour in this site's components was chosen for a light surface, and
   most content does sit on one — .panel, .box, article.review, the cards. But
   several pages also render text STRAIGHT onto the page: the intro paragraph on
   /guides and /games, section headings on /reviews, sort and pager rows. On the
   old cream page those inherited --ink and were fine. On the blue field they are
   near-black on navy: present in the DOM, invisible to a reader.

   Rather than hunt them page by page (and miss the next one someone adds), the
   rule is structural: a direct child of main's page wrapper is ON THE BLUE and
   gets light type. Anything inside a card paints its own surface and keeps the
   dark ink it was designed for.
   ========================================================================== */
main > .ld-page > p,
main > .ld-page > ul,
main > .ld-page > ol,
main > .ld-page > dl,
main > .ld-page > .meta,
main > .ld-page > form,
main > .ld-page > div > .meta {
  color: #b9d5ec;
}
main > .ld-page > h2,
main > .ld-page > h3,
main > .ld-page > h4 {
  color: #ffffff;
}
main > .ld-page > p a,
main > .ld-page > ul a,
main > .ld-page > ol a,
main > .ld-page > .meta a,
main > .ld-page > div > .meta a {
  color: var(--cyan-lt);
}
main > .ld-page > p a:visited,
main > .ld-page > ul a:visited { color: #a8c4da; }
main > .ld-page > p a:hover,
main > .ld-page > ul a:hover { color: #fff; }

/* Search and sort controls on the archive pages sit on the blue too. */
main > .ld-page > form input[type=search],
main > .ld-page > form input[type=text] {
  font: inherit;
  font-size: 14px;
  padding: 9px 12px;
  color: #eaf4ff;
  background: #071b2e;
  border: 1px solid var(--bg-line);
  border-radius: var(--radius);
}
main > .ld-page > form input::placeholder { color: #6d8ba7; }

/* ---------------------------------------------------------- featured art -- */
/* Review key art is often a logo or a wide banner, so it is fitted rather than
   cropped — cropping would cut the game's title off. Fitting leaves bars, and
   bars against white read as a broken image; against a dark matte they read as a
   frame, which is what a cinema does for the same reason. */
.review-featured-art,
.review-featured-art img,
.review-head-art {
  background: #08192a;
}

/* ------------------------------------------------------- late corrections -- */

/* A content page's <h1> — a game title, a tag name — is NOT :first-child, because
   a breadcrumb precedes it. It therefore missed the rule above and fell back to
   --ink: a black headline on the blue field. Section index pages keep the small
   label treatment (they ARE first-child); everything else gets a real headline. */
main > .ld-page > h1 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(26px, 3.8vw, 40px);
  letter-spacing: -0.028em;
  color: #ffffff;
  margin: 2px 0 6px;
  text-shadow: 0 1px 0 rgba(0, 0, 0, .3);
}
/* ...restated because the selector above is equally specific and comes later. */
main > .ld-page > h1:first-child {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #9fc0dc;
  margin: 2px 0 16px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--bg-line);
  text-shadow: none;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The maximise icon, drawn rather than typed. It was U+26F6 (SQUARE FOUR
   CORNERS), which is in no subset we ship and has no fallback on a stock Linux
   box — it rendered as a tofu box next to the word. A 2px square cannot tofu. */
#stagemax::before {
  content: '';
  display: inline-block;
  width: 11px;
  height: 11px;
  border: 2px solid currentColor;
  border-radius: 1px;
  margin-right: 8px;
  vertical-align: -1px;
}

/* The section bar scrolls sideways on a phone rather than wrapping to two rows.
   Scrolling is right, but a nav item sliced mid-word at the screen edge reads as
   a bug, so the edge fades to say "there is more this way". */
@media (max-width: 720px) {
  .ld-nav { position: relative; }
  .ld-nav::after {
    content: '';
    position: absolute;
    top: 0; right: 0; bottom: 0;
    width: 34px;
    pointer-events: none;
    background: linear-gradient(90deg, rgba(13, 42, 69, 0), var(--bg-rise) 78%);
  }
  .ld-nav .ld-page { padding-right: 30px; }
}

/* ---------------------------------------------------------- tiles without art --
   .tile is a flex ROW because the archive's tiles are "thumbnail, then text".
   Guides and Films emit the same class with no thumbnail at all — just three
   stacked <p>: title, summary, meta. Flex turned those three paragraphs into
   three columns. On a wide screen that passed for a deliberate table; at 390px
   it was a title broken one word per line beside two other slivers.

   Written as :not(:has(.thumb)) rather than by giving the art-less tiles their
   own class, because the markup comes from three different files. The direction
   matters: if a browser does not understand :has, this whole rule is dropped and
   tiles stay exactly as they are today rather than collapsing. */
.tile:not(:has(.thumb)) {
  display: block;
  padding: 12px 11px;
}
.tile:not(:has(.thumb)) > p { margin: 0; }
.tile:not(:has(.thumb)) > p:first-child {
  font-family: var(--font-display);
  font-size: 15.5px;
  line-height: 1.25;
  letter-spacing: -0.012em;
  margin-bottom: 4px;
}
.tile:not(:has(.thumb)) > p:first-child a {
  color: var(--ink);
  text-decoration: none;
}
.tile:not(:has(.thumb)) > p:first-child a:visited { color: var(--ink); }
.tile:not(:has(.thumb)):hover > p:first-child a {
  color: var(--steel-dk);
  text-decoration: underline;
}
.tile:not(:has(.thumb)) > p:nth-child(2) {
  color: var(--ink-2);
  font-size: 14px;
  line-height: 1.5;
}
.tile:not(:has(.thumb)) > .meta { margin-top: 6px; font-size: 12.5px; color: var(--ink-3); }
