/* GamerGrove — die öffentlichen Seiten.
 *
 * Dieselbe Marke wie in der App: der dunkle Raum, das goldene Licht, die
 * Pixelkante als Spiele-Signal. Die Farben sind die Werte aus
 * lib/core/theme/gg_color_schemes.dart, nicht ähnliche.
 *
 * Kein Framework, keine CDN-Schrift. Beides wäre ein weiterer Empfänger in der
 * Datenschutzerklärung für nichts.
 */

:root {
  --raum: #0b1614;
  --raum-tief: #060f0d;
  --flaeche: #12211d;
  --flaeche-hoch: #182c26;
  --kante: #334741;
  --gold: #f2a63c;
  --gold-tief: #4a3418;
  --text: #e8e5dc;
  --text-leise: #9eaca6;

  --schrift-display: "BricolageGrotesque", ui-sans-serif, system-ui, sans-serif;
  --schrift-text: "IBMPlexSans", ui-sans-serif, system-ui, sans-serif;

  /* Eine Leiter, keine willkürlichen Größen. */
  --t-klein: 0.8125rem;
  --t-basis: clamp(1rem, 0.95rem + 0.25vw, 1.0625rem);
  --t-gross: clamp(1.25rem, 1.1rem + 0.7vw, 1.5rem);
  --t-titel: clamp(2.25rem, 1.4rem + 4vw, 4.5rem);

  --raster: 4px; /* Alles ist ein Vielfaches davon — daher die Pixelkante. */
  --s1: calc(var(--raster) * 2);
  --s2: calc(var(--raster) * 4);
  --s3: calc(var(--raster) * 6);
  --s4: calc(var(--raster) * 12);
  --s5: clamp(var(--s4), 4vw + 1rem, calc(var(--raster) * 24));

  --rund: 12px;
  /* Eine Lesespalte, keine Bühne: die Seite wird überwiegend von einem
   * geteilten Link auf einem Telefon geöffnet, und am Schreibtisch soll sie
   * dieselbe Seite sein und nicht eine zweite, breitgezogene. */
  --breite: 46rem;
}

@font-face {
  font-family: "BricolageGrotesque";
  src: url("fonts/BricolageGrotesque-Bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "IBMPlexSans";
  src: url("fonts/IBMPlexSans-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "IBMPlexSans";
  src: url("fonts/IBMPlexSans-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}

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

body {
  margin: 0;
  background: var(--raum);
  color: var(--text);
  font-family: var(--schrift-text);
  font-size: var(--t-basis);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Das Licht fällt von oben in den Raum. Ein Verlauf, kein Bild: er kostet
 * nichts und verschiebt nichts. */
body::before {
  content: "";
  position: fixed;
  inset: 0 0 auto 0;
  height: 60vh;
  background: radial-gradient(
    120% 100% at 50% -20%,
    rgb(242 166 60 / 14%),
    rgb(242 166 60 / 0%) 60%
  );
  pointer-events: none;
  z-index: 0;
}

/* Das Punktraster, das dem Zeiger folgt.
 *
 * Das Raster liegt fest im Hintergrund; der Zeiger trägt eine Fackel — eine
 * Maske um `--mx`/`--my`, die nur dort Punkte sichtbar macht. Kein Canvas und
 * keine Animationsschleife: JavaScript setzt zwei Werte, den Rest macht der
 * Browser beim Zeichnen ohnehin.
 *
 * Ohne JavaScript bleibt die Fackel in der Mitte stehen, ohne Zeigergerät
 * bleibt sie ganz aus (siehe site.js).
 */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: radial-gradient(
    circle at center,
    var(--gold) 0 1.5px,
    transparent 1.6px
  );
  background-size: 26px 26px;
  opacity: 0.55;
  -webkit-mask-image: radial-gradient(
    180px circle at var(--mx, 50%) var(--my, 38%),
    #000 0%,
    rgb(0 0 0 / 30%) 48%,
    transparent 74%
  );
  mask-image: radial-gradient(
    180px circle at var(--mx, 50%) var(--my, 38%),
    #000 0%,
    rgb(0 0 0 / 30%) 48%,
    transparent 74%
  );
}

main,
header.seitenkopf,
footer.seitenfuss {
  position: relative;
  z-index: 1;
}

a {
  color: var(--gold);
  text-underline-offset: 0.2em;
}
a:hover {
  color: #ffd68f;
}
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 2px;
}

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

.huelle {
  width: min(100% - var(--s2) * 2, var(--breite));
  margin-inline: auto;
}

/* ---------------------------------------------------------------- Kopfzeile */

.seitenkopf {
  padding: var(--s2) 0;
}
/* Vorher ein nachgebauter Bogen aus zwei CSS-Kanten in Schriftgröße — auf
 * einer Seite, die sonst niemand kennt, war die Marke damit das Kleinste im
 * Bild. Jetzt das echte Symbol der App und eine Größe, die man sieht. */
.marke {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  font-family: var(--schrift-display);
  font-size: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
  letter-spacing: 0.04em;
  color: var(--text);
  text-decoration: none;
}
.marke:hover {
  color: var(--gold);
}
.marke img {
  width: clamp(32px, 2rem + 1vw, 44px);
  height: auto;
  border-radius: 8px;
  /* Pixelkunst: keine Weichzeichnung beim Skalieren. */
  image-rendering: pixelated;
}

/* ------------------------------------------------------------------- Poster */

.poster {
  padding: var(--s4) 0 var(--s3);
}
.poster h1 {
  font-family: var(--schrift-display);
  font-size: var(--t-titel);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 var(--s1);
  text-wrap: balance;
}
.kuratorenzeile {
  font-size: var(--t-klein);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0 0 var(--s2);
}
.beschreibung {
  max-width: 46ch;
  color: var(--text-leise);
  font-size: var(--t-gross);
  margin: 0 0 var(--s3);
  text-wrap: pretty;
}

/* Die Coverwand. Dichter als ein Kartenraster, mit einem Anfang: das erste
 * Cover nimmt zwei Spalten und zwei Zeilen.
 *
 * Das gilt aber nur, wenn genug Cover da sind, um die zweite Zeile zu füllen.
 * Bei acht Spielen stand sonst ein großes Cover neben einer Reihe kleiner und
 * darunter ein Loch in der Größe des großen. Wie viele es sind, weiß nur der
 * Generator — deshalb entscheidet der über die Klasse und nicht das CSS. */
.wand {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: var(--raster);
}
.wand img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: 2px;
  background: var(--flaeche);
}
.wand--gross > :first-child {
  grid-column: span 2;
  grid-row: span 2;
}
/* Wenige Cover: dafür größer, und die Wand hört auf, wo die Reihe aufhört. */
/* Auf dem Telefon drei Spalten statt zwei: bei zwei war die Wand vier Reihen
 * hoch und schob alles andere unter den Rand. Eine Wand soll man auf einmal
 * sehen. */
.wand--klein {
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
}
@media (min-width: 40rem) {
  .wand--gross {
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  }
  .wand--klein {
    grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  }
}

/* Die Pixelkante: eine Linie aus Quadraten statt einer durchgezogenen. */
.pixelkante {
  height: var(--raster);
  border: 0;
  margin: var(--s4) 0;
  background: repeating-linear-gradient(
    to right,
    var(--kante) 0 var(--raster),
    transparent var(--raster) calc(var(--raster) * 2)
  );
}

/* ------------------------------------------------------------------ Artikel */

/* Ein Lesetext, keine Kachelwand: die Spalte bleibt in der Breite, in der man
 * eine Zeile noch mit dem Auge zurückfindet (45–75 Zeichen). */
.liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s3);
}
.eintrag {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: var(--s2);
  align-items: start;
}
@media (min-width: 40rem) {
  .eintrag {
    grid-template-columns: 132px 1fr;
    gap: var(--s3);
  }
  /* Rhythmus statt gleichmäßiger Kacheln — aber klein genug, dass es wie
   * Absicht aussieht und nicht wie ein verrutschter Eintrag. */
  .eintrag:nth-child(3n) {
    margin-inline-start: var(--s2);
  }
}
.eintrag img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: var(--rund);
  background: var(--flaeche);
  border: 1px solid var(--kante);
}
.eintrag .platz {
  font-family: var(--schrift-display);
  color: var(--gold);
  font-size: var(--t-klein);
  font-feature-settings: "tnum";
}
.eintrag h2 {
  font-family: var(--schrift-display);
  font-size: var(--t-gross);
  line-height: 1.2;
  margin: var(--raster) 0 var(--s1);
  text-wrap: balance;
}
.merkmale {
  margin: 0;
  color: var(--text-leise);
  font-size: var(--t-klein);
}
.merkmale span + span::before {
  content: " · ";
  color: var(--kante);
}

/* ---------------------------------------------------------------- Aufruf */

.aufruf {
  background: var(--flaeche);
  border: 1px solid var(--kante);
  border-radius: var(--rund);
  padding: var(--s3);
  margin: var(--s4) 0;
  display: grid;
  gap: var(--s2);
  justify-items: start;
}
.aufruf h2 {
  font-family: var(--schrift-display);
  font-size: var(--t-gross);
  margin: 0;
}
.aufruf p {
  margin: 0;
  color: var(--text-leise);
  max-width: 52ch;
}
/* Das offizielle Abzeichen, unveraendert. Googles Markenrichtlinien lassen
 * nicht zu, das Dreieck in einen eigenen Knopf zu setzen oder umzufaerben —
 * und der Wiedererkennungswert, um den es geht, steckt ohnehin genau in der
 * unveraenderten Form. */
.play-abzeichen {
  display: inline-block;
  line-height: 0;
}
.play-abzeichen img {
  height: 60px;
  width: auto;
  transition: transform 120ms ease;
}
.play-abzeichen:hover img {
  transform: translateY(-2px);
}

.knopf {
  display: inline-block;
  background: var(--gold);
  color: #2a1a0a;
  font-weight: 600;
  text-decoration: none;
  padding: var(--s1) var(--s3);
  border-radius: 999px;
  transition: transform 120ms ease, background-color 120ms ease;
}
.knopf:hover {
  background: #ffd68f;
  color: #2a1a0a;
  transform: translateY(-1px);
}
.knopf:active {
  transform: translateY(0);
}

/* -------------------------------------------------------------- Fußzeile */

.seitenfuss {
  border-top: 1px solid var(--kante);
  margin-top: var(--s4);
  padding: var(--s3) 0 var(--s4);
  color: var(--text-leise);
  font-size: var(--t-klein);
}
.seitenfuss nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin-bottom: var(--s1);
}

/* ------------------------------------------------------------------ Prosa */

/* Kein eigenes max-width: `--breite` ist bereits eine Lesespalte, und ein
 * zweites Maß auf demselben Element hätte den Text gegenüber allem darüber
 * eingerückt. */
.prosa {
  padding-block: var(--s3);
}
.prosa h1,
.prosa h2,
.prosa h3 {
  font-family: var(--schrift-display);
  line-height: 1.2;
}
.prosa h1 {
  font-size: var(--t-titel);
  letter-spacing: -0.02em;
}
.prosa h2 {
  font-size: var(--t-gross);
  margin-top: var(--s3);
}
.prosa code {
  background: var(--flaeche-hoch);
  padding: 0.1em 0.35em;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ------------------------------------------------------- Startseite */

/* Die Startseite darf breiter sein als eine Lesespalte: sie zeigt ein Gerät
 * neben dem Text, und das braucht Platz. Die Sammlungsseiten bleiben schmal. */
.huelle--breit {
  width: min(100% - var(--s2) * 2, 72rem);
}

.buehne {
  display: grid;
  gap: var(--s4);
  align-items: center;
  padding: var(--s3) 0 var(--s4);
}
@media (min-width: 56rem) {
  .buehne {
    grid-template-columns: 1fr 22rem;
  }
}
.buehne h1 {
  font-family: var(--schrift-display);
  font-size: var(--t-titel);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 var(--s2);
  text-wrap: balance;
}

/* Das Gerät steht leicht gedreht und trägt das Licht, das im Raum ohnehin von
 * oben kommt — dieselbe Geste wie der Verlauf hinter der Seite.
 *
 * Dahinter das Schachraster, mit dem die App ihre Top 3 unterlegt: dieselbe
 * geordnete Rasterung wie im Symbol und im Play-Banner. Es wird nach außen
 * weggeblendet, damit es eine Lichtfläche bleibt und keine Kachelwand wird. */
.geraet {
  position: relative;
  justify-self: center;
  max-width: 24rem;
  /* Das Schachraster liegt als **Hintergrund des Behälters**, nicht als
   * Pseudoelement mit z-index. Ein Hintergrund ist definitionsgemäß hinter dem
   * Inhalt — damit gibt es keine Stapelreihenfolge, die schiefgehen kann.
   *
   * Der Weg hierher war teuer: erst eine Einzugsanimation auf dem Bild, dann
   * eine Aufblende auf einem Pseudoelement daneben — beide ließen Chrome das
   * Telefonbild überhaupt nicht mehr zeichnen. Nachgewiesen durch Ausschluss.
   * Ein Effekt, der das wichtigste Bild der Seite verschwinden lässt, ist die
   * Sekunde Politur nicht wert; Bewegung liefern das Magnetraster und der
   * Auftritt beim Scrollen, wo ein Ausfall nichts versteckt. */
  padding: 9% 11%;
  background:
    radial-gradient(58% 46% at 50% 46%, rgb(242 166 60 / 20%), transparent 72%),
    repeating-conic-gradient(
      rgb(242 166 60 / 10%) 0% 25%,
      transparent 0% 50%
    ) 0 0 / 8px 8px;
  /* Weit genug, dass das Telefon selbst vollständig im deckenden Teil liegt —
   * die Maske soll das Raster ausblenden, nicht das Gerät beschneiden. */
  -webkit-mask-image: radial-gradient(78% 72% at 50% 50%, #000 62%, transparent 96%);
  mask-image: radial-gradient(78% 72% at 50% 50%, #000 62%, transparent 96%);
}
.geraet img {
  display: block;
  width: 100%;
  border-radius: 20px;
  border: 1px solid var(--kante);
  box-shadow: 0 30px 60px -20px rgb(0 0 0 / 70%),
              0 0 90px -30px rgb(242 166 60 / 45%);
  transform: rotate(-2.5deg);
}

.merkmalsblock {
  display: grid;
  gap: var(--s4);
  padding-block: var(--s3);
}
@media (min-width: 52rem) {
  .merkmalsblock {
    grid-template-columns: 1fr 14rem;
    align-items: center;
  }
  /* Jeder zweite Block spiegelt sich, damit die Seite nicht zur Liste wird.
   * Die Spalten müssen mitspiegeln — nur die Reihenfolge zu tauschen, setzte
   * das Bild in die 1fr-Spalte und blies es auf Bildschirmbreite auf. */
  .merkmalsblock:nth-child(even) {
    grid-template-columns: 14rem 1fr;
  }
  .merkmalsblock:nth-child(even) > :first-child {
    order: 2;
  }
}
.merkmalsblock h2 {
  font-family: var(--schrift-display);
  font-size: var(--t-gross);
  margin: 0 0 var(--s1);
}
.merkmalsblock p {
  margin: 0;
  color: var(--text-leise);
  max-width: 46ch;
}
/* Ein Telefonbild ist 9:20 hoch. In voller Höhe macht es aus jedem Block eine
 * Seite für sich; oben beschnitten steht genau der Teil da, um den es geht. */
.merkmalsblock img {
  display: block;
  width: 100%;
  aspect-ratio: 9 / 14;
  object-fit: cover;
  object-position: top;
  border-radius: 16px;
  border: 1px solid var(--kante);
  box-shadow: 0 20px 40px -20px rgb(0 0 0 / 70%);
}

/* Coverstapel: die Vorschau einer geteilten Liste in der Übersicht. */
.stapel {
  display: flex;
  gap: 2px;
}
.stapel img {
  width: 44px;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: 2px;
  background: var(--flaeche);
}
.listenzeile {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s1);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--outline, var(--kante));
}
.listenzeile h2 {
  font-family: var(--schrift-display);
  font-size: var(--t-gross);
  margin: 0;
}
.listenzeile a {
  text-decoration: none;
}
.listenzeile a:hover h2 {
  color: var(--gold);
}

/* ------------------------------------------------------------- Bewegung
 *
 * Nur Transform und Opazität, und nur beim ersten Erscheinen. Wer
 * "Bewegung reduzieren" gesetzt hat, sieht den Endzustand sofort — der Block
 * unten schaltet die Startwerte gar nicht erst ein. */
@media (prefers-reduced-motion: no-preference) {
  .auftritt {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 600ms var(--ease, cubic-bezier(0.16, 1, 0.3, 1)),
                transform 600ms var(--ease, cubic-bezier(0.16, 1, 0.3, 1));
  }
  .auftritt.da {
    opacity: 1;
    transform: none;
  }
}
