/* =============================================================================
   OtaLAN – site stylesheet (v2)
   -----------------------------------------------------------------------------
   Plain CSS: no preprocessor, no framework, no build step. Every page in en/
   and fi/ links to this one file.

   HOW THE FILE IS ORGANISED (top to bottom)
     1. Fonts            – self-hosted @font-face rules
     2. Animated props   – @property registrations that let gradients animate
     3. Design tokens    – colours, fonts, spacing scale, layout widths
     4. Base / reset     – element defaults
     5. Background       – the changing gradient and the wireframe canvas layer
     6. Type, buttons, header, hero, ticker
     7. Page sections    – facts, rows, photos, FAQ, map, logos, CTA
     8. Sub-page pieces  – page header, LAN specs, arcade tiles/dialogs,
                           tournaments, schedule, organizers, footer
     9. Motion / print   – reveal-on-scroll, reduced-motion, print, short screens

   TO RE-THEME THE SITE, EDIT THE VARIABLES IN SECTION 3 (":root"). Almost every
   colour and spacing value below is read from those variables.
   ============================================================================= */


/* -----------------------------------------------------------------------------
   1. FONTS
   All fonts live in assets/fonts/ and are loaded from there (nothing is fetched
   from Google), which is faster and avoids sending visitor IPs to a third party.
   `font-display: swap` shows fallback text immediately and swaps the real font
   in when it has loaded, so text is never invisible while waiting.
   The url() paths are relative to THIS css file (assets/), so "fonts/x" means
   assets/fonts/x. Relative paths are what let the site work from file:// too.
   ----------------------------------------------------------------------------- */

/* Rajdhani: squared technical face for headings, nav, buttons and labels.
   Three separate files, one per weight, because Rajdhani is not a variable font. */
@font-face { font-family: "Rajdhani"; src: url("fonts/rajdhani-500.woff2") format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "Rajdhani"; src: url("fonts/rajdhani-600.woff2") format("woff2"); font-weight: 600; font-display: swap; }
@font-face { font-family: "Rajdhani"; src: url("fonts/rajdhani-700.woff2") format("woff2"); font-weight: 700; font-display: swap; }

/* Inter: body text. One variable-font file covers every weight from 400 to 700. */
@font-face { font-family: "Inter"; src: url("fonts/inter.woff2") format("woff2"); font-weight: 400 700; font-display: swap; }


/* -----------------------------------------------------------------------------
   2. ANIMATED CUSTOM PROPERTIES
   Browsers cannot normally animate a CSS variable, because they do not know its
   type. @property tells the browser "--ang is an angle", "--x1 is a percentage",
   and so on, so `@keyframes` can smoothly interpolate them. The background
   gradient (section 5) uses these to slowly rotate and drift.
   `inherits: false` keeps them local to the element that uses them.
   If a browser does not support @property, the gradient simply stays still at
   the initial values below, so nothing breaks.
   ----------------------------------------------------------------------------- */
@property --ang { syntax: "<angle>"; inherits: false; initial-value: 135deg; }   /* direction of the base gradient  */
@property --x1 { syntax: "<percentage>"; inherits: false; initial-value: 12%; }  /* pink glow, horizontal position  */
@property --y1 { syntax: "<percentage>"; inherits: false; initial-value: 8%; }   /* pink glow, vertical position    */
@property --x2 { syntax: "<percentage>"; inherits: false; initial-value: 92%; }  /* cyan glow, horizontal position  */
@property --y2 { syntax: "<percentage>"; inherits: false; initial-value: 88%; }  /* cyan glow, vertical position    */


/* -----------------------------------------------------------------------------
   3. DESIGN TOKENS
   The single place to change the look of the whole site.
   ----------------------------------------------------------------------------- */
:root {
  /* ---- Colours (all chosen for high contrast on the dark background) ---- */
  --ink: #06031c;         /* darkest indigo: header, ticker bar, footer          */
  --bg: #0f0838;          /* page base colour (the gradient is drawn over it)    */
  --bg-raised: #1c1266;   /* slightly lighter surface, used for hovered rows     */
  --line: rgba(255, 255, 255, .3);  /* hairline borders and dividers             */
  --text: #ffffff;        /* body text: pure white on dark for maximum contrast  */
  --muted: #e4e0ff;       /* secondary text; about 13:1 contrast on --bg         */
  --pink: #f52ba6;        /* brand pink: "LAN" in the logo, countdown, accents   */
  --pink-btn: #d2138a;    /* button fill: a deeper pink so WHITE text passes AA  */
  --pink-soft: #ff8ad6;   /* lighter pink for small text (labels, list markers)  */
  --cyan: #3be3ff;        /* links and secondary (outlined) buttons              */
  --gold: #ffd166;        /* dates and eyebrow labels only                       */
  --on-pink: #0a0426;     /* dark text used ON cyan (outlined button hover)      */

  /* ---- Font stacks: first name is the real font, the rest are fallbacks ---- */
  --display: "Rajdhani", system-ui, sans-serif;   /* titles, big numbers (the OTALAN logo is an image) */
  --heading: "Rajdhani", system-ui, sans-serif;               /* headings, nav, buttons    */
  --body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;  /* paragraphs        */

  /* ---- Spacing scale: use these instead of ad-hoc pixel values ----
     --s1 = 4px ... --s9 = 96px (at the default 16px root font size).           */
  --s1: .25rem; --s2: .5rem; --s3: .75rem; --s4: 1rem; --s5: 1.5rem;
  --s6: 2rem; --s7: 3rem; --s8: 4rem; --s9: 6rem;

  /* ---- Layout ---- */
  --wrap: 1040px;         /* maximum width of the content column                 */
  --header-h: 68px;       /* height of the sticky header (used for offsets)      */
}


/* -----------------------------------------------------------------------------
   4. BASE / RESET
   ----------------------------------------------------------------------------- */

/* border-box makes width include padding and border, which keeps layout maths sane */
*, *::before, *::after { box-sizing: border-box; }

/* `scroll-padding-top` stops in-page anchor jumps (like #where) from hiding the
   target underneath the sticky header. `scroll-behavior: smooth` animates them. */
html { background: var(--bg); scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + 16px); -webkit-text-size-adjust: 100%; }

/* `overflow-x: hidden` guarantees no accidental sideways scrollbar (for example
   from the oversized ticker track). Body has no background of its own: the
   animated gradient in section 5 provides it. */
body { margin: 0; color: var(--text); font: 400 1.0625rem/1.65 var(--body); overflow-x: hidden; }

/* Images never overflow their container; `display: block` removes the small gap
   browsers leave under inline images. */
img { max-width: 100%; height: auto; display: block; }

a { color: var(--cyan); text-underline-offset: .18em; }
a:hover { color: #fff; }

/* Clear keyboard focus ring for accessibility; never remove it without a replacement. */
:focus-visible { outline: 3px solid var(--cyan); outline-offset: 3px; border-radius: 2px; }

p { margin: 0 0 var(--s4); }
h1, h2, h3 { margin: 0; font-weight: 700; line-height: 1.1; }
ul { padding: 0; margin: 0; }
strong { font-weight: 600; }

/* .wrap = the centred content column. `min()` keeps a 24px gutter on each side
   on narrow screens, and caps the width at --wrap on wide ones. */
.wrap { width: min(var(--wrap), 100% - 48px); margin-inline: auto; }

/* "Skip to content" link for keyboard and screen-reader users. It sits off-screen
   until it receives focus (first Tab press), then slides into view. */
.skip { position: absolute; left: var(--s4); top: -80px; z-index: 200; background: var(--gold); color: #000; padding: var(--s3) var(--s4); font-weight: 600; }
.skip:focus { top: var(--s4); }


/* -----------------------------------------------------------------------------
   5. BACKGROUND: changing gradient + wireframe canvas
   Three layers, back to front:
     z-index -2  body::before   the animated gradient (this file)
     z-index -1  #wire canvas   moving wireframes (drawn by assets/wire.js)
     z-index  1  main / footer  the real page content
   ----------------------------------------------------------------------------- */

/* The gradient is a fixed, full-window pseudo-element so it does not scroll.
   It stacks three gradients: a pink glow, a cyan glow, and a base linear gradient
   whose angle (--ang) slowly rotates. Two animations run together:
     sweep  – rotates the base gradient once every 70 s
     blobs  – drifts both glows toward the middle and back, every 32 s
   `pointer-events: none` lets clicks pass straight through it. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(55vmax 55vmax at var(--x1) var(--y1), rgba(245, 43, 166, .30), transparent 65%),
    radial-gradient(60vmax 60vmax at var(--x2) var(--y2), rgba(59, 227, 255, .22), transparent 65%),
    linear-gradient(var(--ang), #0a0429 0%, #1a0f66 45%, #0b0a52 70%, #0a0429 100%);
  animation: sweep 70s linear infinite, blobs 32s ease-in-out infinite alternate;
}
/* 135deg -> 495deg is exactly one full turn (360deg) so the loop is seamless. */
@keyframes sweep { to { --ang: 495deg; } }
@keyframes blobs { to { --x1: 38%; --y1: 34%; --x2: 62%; --y2: 62%; } }

/* The canvas that wire.js draws on. Fixed to the window, behind the content. */
#wire { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; }

/* Give content its own stacking layer above the background layers. */
main, .site-footer { position: relative; z-index: 1; }


/* -----------------------------------------------------------------------------
   6a. TYPE
   ----------------------------------------------------------------------------- */

/* Small uppercase label above a page title, e.g. "THE ARCADE". */
.eyebrow { font: 600 1rem/1 var(--heading); letter-spacing: .16em; text-transform: uppercase; color: var(--gold); margin: 0 0 var(--s4); }

/* clamp(min, preferred, max): the size grows with the window but never leaves
   the given range, so headings scale smoothly without media queries. */
h2 { font: 700 clamp(1.9rem, 3.4vw, 2.6rem)/1.1 var(--heading); letter-spacing: .01em; text-transform: uppercase; }
h3 { font: 700 1.35rem/1.2 var(--heading); letter-spacing: .02em; }

/* Intro paragraph. `max-width: 46ch` keeps lines a readable length. */
.lead { font-size: clamp(1.125rem, 1.6vw, 1.3rem); line-height: 1.6; max-width: 46ch; }

/* Long-form text column with a comfortable line length (about 62 characters). */
.prose { max-width: 62ch; }
.prose ul { padding-left: 1.2em; }
.prose li { margin-bottom: var(--s2); }

/* Pink bullet/number markers on lists. */
li::marker { color: var(--pink-soft); }
.muted { color: var(--muted); }


/* -----------------------------------------------------------------------------
   6b. BUTTONS
   .btn        = solid deep-pink button, WHITE text (readable, passes AA contrast)
   .btn--ghost = outlined cyan button for secondary actions
   ----------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: var(--s3);
  font: 700 1.15rem/1 var(--heading); letter-spacing: .08em; text-transform: uppercase; text-decoration: none;
  padding: 1rem 1.6rem; border-radius: 4px; border: 2px solid var(--pink-btn); background: var(--pink-btn); color: #fff; cursor: pointer;
  transition: background .15s, border-color .15s, transform .15s;
}
/* Hover: a darker pink and a 2px lift. White text stays readable on the darker fill. */
.btn:hover { background: #a80e6d; border-color: #a80e6d; color: #fff; transform: translateY(-2px); }

/* Ghost button: semi-transparent dark fill so it stays legible over the gradient. */
.btn--ghost { background: rgba(6, 3, 28, .55); border-color: var(--cyan); color: var(--cyan); }
.btn--ghost:hover { background: var(--cyan); border-color: var(--cyan); color: var(--on-pink); }

/* A row of buttons that wraps onto several lines on small screens. */
.btn-row { display: flex; flex-wrap: wrap; gap: var(--s4); }


/* -----------------------------------------------------------------------------
   6c. HEADER AND NAVIGATION
   ----------------------------------------------------------------------------- */

/* Sticky: stays at the top of the window while scrolling. z-index 100 keeps it
   above everything except the "skip" link. */
.site-header { position: sticky; top: 0; z-index: 100; background: var(--ink); }

/* The thin colour line under the header. It is a gradient three times wider than
   the bar, and the `slide` animation moves it sideways so the colours flow. */
.site-header::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: linear-gradient(90deg, var(--pink), var(--cyan), var(--gold), var(--pink)); background-size: 300% 100%; animation: slide 10s linear infinite;
}
@keyframes slide { to { background-position: -300% 0; } }

.site-header .wrap { display: flex; align-items: center; min-height: var(--header-h); gap: var(--s5); }

/* Small logo in the header/footer: white "OTA" with pink "LAN" (the <span>). */
.wordmark { display: inline-block; line-height: 0; text-decoration: none; }
.wordmark img { height: 1.1rem; width: auto; display: block; }
.wordmark:hover { color: var(--text); }

/* Desktop navigation: a horizontal list pushed to the right by `margin-left: auto`. */
.nav { margin-left: auto; }
.nav ul { display: flex; align-items: center; gap: var(--s5); list-style: none; }
.nav a { font: 600 1.15rem/1 var(--heading); letter-spacing: .08em; text-transform: uppercase; color: var(--text); text-decoration: none; padding: var(--s3) 0; border-bottom: 3px solid transparent; }
.nav a:hover { border-color: var(--cyan); }                 /* cyan underline on hover      */
.nav a[aria-current="page"] { border-color: var(--pink); }  /* pink underline = current page */

/* The FI/EN language switch, styled as a small gold outlined button. */
.nav .lang { color: var(--gold); border: 1px solid var(--gold); padding: var(--s2) var(--s3); border-radius: 3px; font-size: 1rem; }
.nav .lang:hover { background: var(--gold); color: #000; border-color: var(--gold); }

/* The "Menu" button only appears on small screens (see the media query below). */
.nav-toggle { display: none; margin-left: auto; background: none; color: var(--text); border: 1px solid var(--line); font: 600 1.1rem/1 var(--heading); letter-spacing: .08em; text-transform: uppercase; padding: var(--s3) var(--s4); border-radius: 3px; cursor: pointer; }

/* Below 960px the nav collapses into a dropdown panel under the header.
   site.js toggles the `.open` class to show or hide it. */
@media (max-width: 960px) {
  .nav-toggle { display: block; }
  .nav { position: absolute; top: 100%; left: 0; right: 0; margin: 0; background: var(--ink); border-bottom: 1px solid var(--line); display: none; }
  .nav.open { display: block; }
  .nav ul { flex-direction: column; align-items: stretch; gap: 0; padding: var(--s3) 24px var(--s5); }
  .nav li a { display: block; padding: var(--s4) 0; border-bottom: 1px solid var(--line); }
  .nav li:last-child a { margin-top: var(--s4); text-align: center; border: 1px solid var(--gold); }
}


/* -----------------------------------------------------------------------------
   6d. HERO (home page only)
   The hero is exactly one window tall (minus the header), so the ticker that
   follows it is docked at the bottom edge of the first screen. The perspective
   grid "floor" is NOT CSS: wire.js draws it on the canvas in the empty space
   between the countdown and the bottom of this section. The element carries the
   attribute data-wire-floor so the script can find it.
   ----------------------------------------------------------------------------- */
/* `100svh` = the "small viewport height", the window height with browser toolbars
   showing, so the hero never ends up taller than what is visible on phones.
   The 3px is the header's colour line. Flex column lets content stack from the top. */
.hero { position: relative; min-height: calc(100svh - var(--header-h) - 3px); display: flex; flex-direction: column; padding: clamp(1.25rem, 5vh, 4rem) 0 var(--s7); }

/* Docked ticker: `position: sticky; bottom: 0` pins the bar to the bottom edge of
   the window while its natural place (below the hero) is still off-screen.
   Once you scroll down to its natural place it just scrolls away like normal. */
.ticker--dock { position: sticky; bottom: 0; z-index: 50; }

/* The big OTALAN wordmark. Its size is the SMALLER of 16% of window width and 17%
   of window height, so it also shrinks on short windows and keeps the whole hero
   (including the countdown) inside the first screen. */
.hero__mark { font-size: clamp(3.6rem, min(16vw, 17vh), 11rem); line-height: 0; margin: 0 0 clamp(.75rem, 2.5vh, 2rem); filter: drop-shadow(0 0 30px rgba(245, 43, 166, .3)); }
.hero__mark img { height: .85em; width: auto; max-width: 100%; display: block; }
.hero__when { font: 700 clamp(1.5rem, 3.6vw, 2.4rem)/1.1 var(--display); color: var(--gold); margin: 0 0 var(--s2); text-transform: uppercase; }   /* the event dates */
.hero__where { font: 600 1.25rem/1.2 var(--heading); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin: 0 0 clamp(.75rem, 2.5vh, 2rem); } /* the venue      */
/*.hero .lead { margin-bottom: clamp(1rem, 3vh, 2rem); }*/
.hero .button {
   border: none;
   margin-left: -3%;
   margin-top: -3%;
   margin-bottom: -1.5%;
   width: 75%;
}
@media (max-width: 720px) {
   .hero .button {
      border: none;
      margin-top: -6%;
      margin-bottom: -3%;
      width: 100%;
   }
}

/* ---- Countdown ----
   Markup (in the page): .countdown > label + .countdown__digits > units + separators.
   site.js fills the numbers every second. `tabular-nums` makes every digit the
   same width so the numbers do not jiggle as they change. */
.countdown { margin: clamp(1.25rem, 4vh, 3rem) 0 clamp(.75rem, 2.5vh, 2rem); }
.countdown__label { font: 600 1.25rem/1.2 var(--heading); letter-spacing: .16em; text-transform: uppercase; color: var(--muted); margin: 0 0 var(--s3); }
.countdown__digits { display: flex; align-items: flex-start; gap: var(--s4); font-variant-numeric: tabular-nums; }
.countdown__unit { display: grid; gap: var(--s1); }   /* number on top, its label (DAYS...) below */
.countdown__unit b { font: 700 clamp(2.6rem, 8vw, 4.6rem)/1 var(--heading); color: var(--pink); min-width: 2ch; }  /* the number, brand pink */
.countdown__unit span { font: 600 .95rem/1 var(--heading); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.countdown__sep { font: 700 clamp(2.6rem, 8vw, 4.6rem)/1 var(--heading); color: rgba(255, 255, 255, .4); }        /* the ":" between units */
.countdown__live { font: 700 1.6rem/1.2 var(--heading); color: var(--pink); }   /* message shown once the event starts */
@media (max-width: 720px) { .countdown__digits { gap: var(--s3); } }


/* -----------------------------------------------------------------------------
   6e. TICKER (the scrolling list of console names)
   Markup: .ticker > .ticker__track > two identical <ul> lists side by side.
   The track is animated from 0 to -50% of its own width; because the second list
   is an exact copy of the first, the jump back to 0 is invisible and the scroll
   looks endless.
   ----------------------------------------------------------------------------- */
.ticker { background: rgba(6, 3, 28, .92); border-block: 1px solid var(--line); overflow: hidden; }
.ticker__track { display: flex; width: max-content; animation: ticker 70s linear infinite; }
.ticker:hover .ticker__track { animation-play-state: paused; }   /* pause when hovered so it can be read */
.ticker ul { display: flex; list-style: none; flex: none; }
.ticker li { font: 600 .85rem/1.15 var(--heading); letter-spacing: .1em; text-transform: uppercase; color: var(--text); padding: var(--s4) 0; display: flex; align-items: center; justify-content: center; text-align: center; position: relative; flex: none; width: 12rem; }
/* Every item is the same fixed-width slot with its content centred, and the pink dot sits on the slot's right edge, so the dots are exactly one slot apart no matter how wide each logo is. Logos are normalised to white silhouettes so they read as one set. */
.ticker li img { height: 2.2rem; width: auto; max-width: 8.5rem; object-fit: contain; filter: brightness(0) invert(1); opacity: .9; }
.ticker li > span { max-width: 8.5rem; }
/* The little rotated pink square between items is a pseudo-element, not markup. */
.ticker li::after { content: ""; position: absolute; right: -3.5px; top: 50%; width: 7px; height: 7px; margin-top: -3.5px; background: var(--pink); transform: rotate(45deg); }
@keyframes ticker { to { transform: translateX(-50%); } }


/* -----------------------------------------------------------------------------
   7. PAGE SECTIONS
   ----------------------------------------------------------------------------- */

/* A .section is one horizontal band of the page. Consecutive sections get a
   hairline between them (`.section + .section`). */
.section { padding: var(--s9) 0; }
.section + .section { border-top: 1px solid var(--line); }
.section--tight { padding: var(--s8) 0; }          /* a band with less vertical padding */
.section__head { margin-bottom: var(--s7); }       /* spacing under a section heading   */

/* .split = two-column layout: heading/label on the left (5 parts), content on
   the right (7 parts). `minmax(0, ...)` stops long content from stretching the
   column. Collapses to one column below 860px. */
.split { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--s8); align-items: start; }
@media (max-width: 860px) { .split { grid-template-columns: 1fr; gap: var(--s6); } .section { padding: var(--s8) 0; } }

/* ---- Facts row: four big numbers with captions ("0 EUR", "60", "20+", "3") ---- */
.facts { display: grid; grid-template-columns: repeat(4, 1fr); list-style: none; }
.facts li { padding: var(--s5) var(--s5) var(--s5) 0; margin-right: var(--s5); border-right: 1px solid var(--line); }  /* vertical dividers */
.facts li:last-child { border-right: 0; margin-right: 0; }
.facts b { display: block; font: 700 clamp(2.2rem, 4.5vw, 3.4rem)/1 var(--display); margin-bottom: var(--s3); }
.facts span { color: var(--muted); font-size: 1rem; line-height: 1.45; display: block; max-width: 20ch; }
/* On phones: a 2x2 grid with horizontal dividers instead. */
@media (max-width: 760px) {
  .facts { grid-template-columns: 1fr 1fr; }
  .facts li { border-right: 0; border-bottom: 1px solid var(--line); margin: 0; padding: var(--s5) var(--s4) var(--s5) 0; }
}

/* ---- Numbered link rows ("01 LAN", "02 ARCADE", ...) on the home page ---- */
.rows { list-style: none; border-top: 1px solid var(--line); }
/* Each row is one big link laid out as three columns: number | text | arrow. */
.rows a { display: grid; grid-template-columns: 4rem 1fr auto; gap: var(--s4); align-items: baseline; padding: var(--s5) var(--s3); text-decoration: none; color: var(--text); border-bottom: 1px solid var(--line); transition: background .15s, padding .15s; }
.rows a:hover { background: var(--bg-raised); padding-left: var(--s5); }   /* highlight and nudge on hover */
.rows .n { font: 700 1.1rem/1 var(--heading); color: var(--pink-soft); letter-spacing: .1em; }
.rows h3 { font-size: 1.9rem; text-transform: uppercase; margin-bottom: var(--s1); }
.rows p { margin: 0; color: var(--muted); }
.rows .arrow { font: 400 1.6rem/1 var(--body); color: var(--cyan); transition: transform .15s; }
.rows a:hover .arrow { transform: translateX(6px); }                       /* arrow slides right */

/* ---- Photos ----
   RULE: images sit side by side and are never layered on top of each other. */
.photo-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
.photo-pair img { width: 100%; aspect-ratio: 1; object-fit: cover; border: 1px solid var(--line); }

/* The three zoom-blur game frames. The filter brightens them because the source
   art has a dark vignette baked in. */
.screens { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s4); }
.screens img { width: 100%; aspect-ratio: 6 / 5; object-fit: cover; border: 1px solid var(--line); filter: brightness(1.35) saturate(1.25) contrast(1.05); }
@media (max-width: 640px) { .screens { grid-template-columns: 1fr; } .screens img { aspect-ratio: 16 / 9; } }

/* ---- FAQ accordion (native <details>/<summary>: works without JavaScript) ----
   The "+" and "–" indicators are drawn with ::after and swap when the item is open. */
.faq details { border-bottom: 1px solid var(--line); }
.faq details:first-child { border-top: 1px solid var(--line); }
.faq summary { list-style: none; cursor: pointer; padding: var(--s5) var(--s6) var(--s5) 0; font: 700 1.3rem/1.3 var(--heading); letter-spacing: .02em; position: relative; }
.faq summary::-webkit-details-marker { display: none; }     /* hide Safari's default triangle */
.faq summary::after { content: "+"; position: absolute; right: 0; top: var(--s5); font: 400 1.6rem/1 var(--body); color: var(--cyan); }
.faq details[open] summary::after { content: "–"; }
.faq details > div { padding: 0 0 var(--s5); max-width: 58ch; color: var(--muted); }
.faq details > div p:last-child { margin: 0; }

/* ---- Location: address and embedded map ---- */
address { font-style: normal; font: 600 1.6rem/1.3 var(--heading); letter-spacing: .03em; margin-bottom: var(--s5); }
/* The OpenStreetMap embed is normally bright white. `invert` + `hue-rotate` turns
   it into a dark map that matches the site (hue-rotate 180deg undoes the colour
   flip that invert causes, so roads and water keep sensible colours). */
.map { width: 100%; height: 340px; border: 1px solid var(--line); background: var(--ink); filter: invert(.92) hue-rotate(180deg) saturate(.6) brightness(1); }
.map-links { display: flex; flex-wrap: wrap; gap: var(--s5); margin-top: var(--s4); }

/* ---- Organizer logos (home page strip) ----
   Each logo sits in a box with a FIXED aspect ratio; the image is scaled to fit
   with max-width/max-height + object-fit: contain, so a logo is ALWAYS shown in
   full and never cropped. */
.org-strip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s5); }
.org-strip a { position: relative; display: block; min-width: 0; aspect-ratio: 4 / 3; border: 1px solid var(--line); background: rgba(6, 3, 28, .7); transition: border-color .15s, background .15s; }
.org-strip a:hover { border-color: var(--cyan); background: var(--bg-raised); }
.org-strip img { position: absolute; inset: var(--s5); width: calc(100% - 2 * var(--s5)); height: calc(100% - 2 * var(--s5)); max-width: none; object-fit: contain; }
@media (max-width: 640px) { .org-strip { grid-template-columns: minmax(0, 1fr); } .org-strip a { aspect-ratio: 16 / 9; } }

/* ---- Call-to-action band at the bottom of a page ---- */
.cta { background: rgba(6, 3, 28, .8); }
.cta h2 { max-width: 16ch; margin-bottom: var(--s5); }
.cta p { color: var(--muted); max-width: 52ch; margin-bottom: var(--s6); }
.cta .button {
   border: none;
   width: 100%;
}


/* -----------------------------------------------------------------------------
   8a. SUB-PAGE HEADER (title block at the top of every page except home)
   ----------------------------------------------------------------------------- */
.page-head { padding: var(--s8) 0 var(--s7); border-bottom: 1px solid var(--line); }
.page-head h1 { font: 700 clamp(2.4rem, 7vw, 4.6rem)/1 var(--display); text-transform: uppercase; margin: 0 0 var(--s5); }


/* -----------------------------------------------------------------------------
   8b. LAN PAGE: specification list (<dl> with four big figures)
   ----------------------------------------------------------------------------- */
.specs { display: grid; grid-template-columns: repeat(4, 1fr); margin: 0; }
.specs div { padding: var(--s5) var(--s5) var(--s5) 0; margin-right: var(--s5); border-right: 1px solid var(--line); }
.specs div:last-child { border: 0; }
.specs dt { font: 700 clamp(2rem, 4vw, 3rem)/1 var(--display); margin-bottom: var(--s3); }   /* the figure, e.g. "1 GBIT" */
.specs dd { margin: 0; color: var(--muted); font-size: 1rem; line-height: 1.45; }             /* its description           */
@media (max-width: 760px) { .specs { grid-template-columns: 1fr 1fr; } .specs div { border: 0; border-bottom: 1px solid var(--line); margin: 0; } }


/* -----------------------------------------------------------------------------
   8c. ARCADE PAGE
   Filter chips, a grid of console tiles, and a <dialog> pop-up per console.
   Behaviour (filtering, opening dialogs) lives in site.js.
   ----------------------------------------------------------------------------- */

/* Filter chips: rounded pill buttons. aria-pressed="true" marks the active one. */
.chips { display: flex; flex-wrap: wrap; gap: var(--s3); margin-bottom: var(--s6); }
.chip { font: 600 1.05rem/1 var(--heading); letter-spacing: .1em; text-transform: uppercase; padding: .7rem 1.1rem; border: 1px solid var(--line); background: rgba(6, 3, 28, .6); color: var(--text); border-radius: 999px; cursor: pointer; transition: background .15s, border-color .15s; }
.chip:hover { border-color: var(--cyan); }
.chip[aria-pressed="true"] { background: var(--pink-btn); border-color: var(--pink-btn); color: #fff; }

/* Tile grid: as many 230px-minimum columns as fit; they stretch to fill the row. */
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s5); list-style: none; }

/* A tile is a <button> (so it is keyboard-accessible) restyled to look like a card. */
.tile { display: block; width: 100%; text-align: left; background: none; border: 0; padding: 0; color: var(--text); cursor: pointer; font: inherit; }
/* `display: block` matters: the photo wrapper is a <span>, which is inline by
   default and would ignore aspect-ratio. */
.tile__img { display: block; aspect-ratio: 1; overflow: hidden; background: #000; border: 1px solid var(--line); }
.tile__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s ease; }
.tile:hover .tile__img img { transform: scale(1.05); }                                   /* gentle zoom on hover     */
.tile:hover .tile__img, .tile:focus-visible .tile__img { border-color: var(--pink); }    /* pink frame on hover/focus */
.tile__brand { display: block; font: 700 .95rem/1 var(--heading); letter-spacing: .14em; text-transform: uppercase; color: var(--pink-soft); margin: var(--s3) 0 var(--s1); }
.tile__name { display: block; font: 700 1.2rem/1.2 var(--heading); letter-spacing: .02em; }
/* The game-highlights teaser under the name is clamped to two lines with "...". */
.tile__more { display: -webkit-box; margin-top: var(--s2); font-size: .92rem; color: var(--muted); -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* Filtering hides tiles by setting the `hidden` attribute; this makes it stick
   even though other rules set `display` on these elements. */
.tile[hidden], li[hidden] { display: none; }

/* ---- Console detail pop-up (<dialog>) ----
   Native <dialog> gives us focus trapping, Esc-to-close and a backdrop for free. */
dialog.sys { border: 1px solid var(--line); background: var(--bg); color: var(--text); padding: 0; width: min(920px, calc(100% - 32px)); max-height: calc(100dvh - 32px); overflow: auto; box-shadow: 0 30px 80px rgba(0, 0, 0, .7); }
dialog.sys::backdrop { background: rgba(4, 2, 18, .88); backdrop-filter: blur(3px); }   /* dim + blur the page behind */
.sys__inner { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }   /* photo | text */
.sys__img { background: #000; }
.sys__img img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 1; }
.sys__body { padding: var(--s7) var(--s6) var(--s6); }
.sys__body h3 { font-size: 1.9rem; margin: 0 0 var(--s4); text-transform: uppercase; }
.sys__body h4 { font: 700 .95rem/1 var(--heading); letter-spacing: .14em; text-transform: uppercase; color: var(--gold); margin: var(--s5) 0 var(--s2); }  /* "GAME HIGHLIGHTS" etc. */
.sys__body ul { padding-left: 1.2em; color: var(--muted); }
.sys__body li { margin-bottom: var(--s1); }
.sys__close { position: absolute; top: var(--s3); right: var(--s3); background: var(--ink); border: 1px solid var(--line); color: var(--text); font: 600 1rem/1 var(--heading); letter-spacing: .1em; text-transform: uppercase; padding: var(--s3) var(--s4); cursor: pointer; border-radius: 3px; }
.sys__close:hover { border-color: var(--cyan); }
/* Phones: stack the photo above the text. */
@media (max-width: 720px) { .sys__inner { grid-template-columns: 1fr; } .sys__img img { aspect-ratio: 16 / 10; } .sys__body { padding: var(--s5); } }

/* Two equal columns (rhythm games | home computers) under the tile grid. */
.cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s8); }
@media (max-width: 760px) { .cols { grid-template-columns: 1fr; gap: var(--s6); } }
/* Simple ruled list. */
.plain { list-style: none; border-top: 1px solid var(--line); }
.plain li { padding: var(--s3) 0; border-bottom: 1px solid var(--line); }


/* -----------------------------------------------------------------------------
   8d. TOURNAMENTS AND SCHEDULE
   ----------------------------------------------------------------------------- */

/* Gold outlined banner, e.g. "November line-up coming soon". The diamond before
   the text is a ::before pseudo-element. */
.notice { display: flex; gap: var(--s4); align-items: center; border: 1px solid var(--gold); background: rgba(6, 3, 28, .6); padding: var(--s4) var(--s5); color: var(--gold); font: 700 1.2rem/1.3 var(--heading); letter-spacing: .06em; text-transform: uppercase; margin-bottom: var(--s7); }
.notice::before { content: ""; flex: none; width: 10px; height: 10px; background: var(--gold); transform: rotate(45deg); }

/* Tournament accordion (.acc): title on the left, date and +/– on the right. */
.acc details { border-bottom: 1px solid var(--line); }
.acc details:first-child { border-top: 1px solid var(--line); }
.acc summary { list-style: none; cursor: pointer; display: flex; gap: var(--s4); align-items: baseline; padding: var(--s5) 0; }
.acc summary::-webkit-details-marker { display: none; }
.acc summary h3 { flex: 1; font-size: 1.6rem; text-transform: uppercase; }   /* flex:1 pushes date and +/- to the far right */
.acc summary::after { content: "+"; font: 400 1.6rem/1 var(--body); color: var(--cyan); width: 1ch; text-align: center; }
.acc details[open] summary::after { content: "–"; }
.acc summary time { font: 700 1.05rem/1 var(--heading); letter-spacing: .1em; text-transform: uppercase; color: var(--gold); }
.acc summary:hover h3 { color: var(--cyan); }
.acc .body { padding: 0 0 var(--s6); max-width: 64ch; color: var(--muted); }

/* The "Rules" call-out inside an opened tournament: pink bar on the left. */
.rule { border-left: 3px solid var(--pink); padding: var(--s2) 0 var(--s2) var(--s4); margin: var(--s4) 0 0; color: var(--text); }
.rule b { display: block; font: 700 .95rem/1 var(--heading); letter-spacing: .14em; text-transform: uppercase; color: var(--pink-soft); margin-bottom: var(--s2); }
.rule ul { padding-left: 1.2em; margin: var(--s2) 0 0; }
.subhead { margin: var(--s8) 0 var(--s4); }
.subhead + p { color: var(--muted); }

/* ---- Schedule: the three upcoming days (placeholders until times are known) ---- */
.days { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s5); }
.days time { font: 700 1rem/1.4 var(--heading); letter-spacing: .06em; color: var(--cyan); }
.day { border-top: 3px solid var(--pink); padding-top: var(--s4); }   /* pink bar marks the top of each day */
.day h3 { text-transform: uppercase; margin-bottom: var(--s1); }
.day p { color: var(--muted); margin: 0; }
.day .tba { display: inline-block; font: 700 1.1rem/1 var(--heading); letter-spacing: .12em; color: var(--gold); }
.day ul { list-style: none; margin-top: var(--s4); }
.day li { display: grid; grid-template-columns: 3.2rem 1fr; gap: var(--s3); padding: var(--s2) 0; border-bottom: 1px solid var(--line); font-size: 1rem; line-height: 1.4; }  /* time | what */
@media (max-width: 760px) { .days { grid-template-columns: 1fr; } }

/* ---- Schedule: the timeline of last time's event (four day columns) ---- */
.timeline { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s5); }
.timeline .day h3 { font-size: 1.25rem; }
.timeline ul { list-style: none; margin-top: var(--s4); }
.timeline li { display: grid; grid-template-columns: 3.2rem 1fr; gap: var(--s3); padding: var(--s2) 0; border-bottom: 1px solid var(--line); font-size: 1rem; line-height: 1.4; }  /* time | what */
.timeline time { font: 700 1rem/1.4 var(--heading); letter-spacing: .06em; color: var(--cyan); }
@media (max-width: 960px) { .timeline { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .timeline { grid-template-columns: 1fr; } }


/* -----------------------------------------------------------------------------
   8e. ORGANIZERS PAGE
   One row per organization: logo box on the left, description on the right.
   ----------------------------------------------------------------------------- */
.org { display: grid; grid-template-columns: 220px 1fr; gap: var(--s7); padding: var(--s7) 0; border-bottom: 1px solid var(--line); align-items: start; }
.org:first-of-type { border-top: 1px solid var(--line); }
/* Same never-crop rule as the home strip: fixed square box, image fitted inside. */
.org__logo { display: flex; align-items: center; justify-content: center; aspect-ratio: 1; }
.org__logo img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.org h2 { margin-bottom: var(--s4); }
/* The row of "Website / Telegram / ..." links. `.prose` is included in the
   selector so it out-ranks `.prose ul` (which would add left padding). */
.prose .org__links { padding-left: 0; display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5); list-style: none; margin-top: var(--s5); font: 600 1.1rem/1 var(--heading); letter-spacing: .08em; text-transform: uppercase; }
@media (max-width: 720px) { .org { grid-template-columns: 1fr; gap: var(--s5); } .org__logo { max-width: 200px; } }


/* -----------------------------------------------------------------------------
   8f. FOOTER
   ----------------------------------------------------------------------------- */
.site-footer { background: var(--ink); border-top: 1px solid var(--line); padding: var(--s8) 0 var(--s7); }
.site-footer .wrap { display: grid; grid-template-columns: 1fr auto; gap: var(--s7); align-items: start; }   /* brand | links */
.site-footer .wordmark img { height: 1.7rem; }
.site-footer p { color: var(--muted); margin: var(--s3) 0 0; font-size: .95rem; }
.site-footer nav ul { list-style: none; display: grid; gap: var(--s2); }
.site-footer nav a { font: 600 1.1rem/1.2 var(--heading); letter-spacing: .08em; text-transform: uppercase; text-decoration: none; color: var(--text); }
.site-footer nav a:hover { color: var(--cyan); }
@media (max-width: 640px) { .site-footer .wrap { grid-template-columns: 1fr; gap: var(--s5); } }


/* -----------------------------------------------------------------------------
   9a. REVEAL ON SCROLL
   site.js adds the class `js` to <html> as soon as it runs, and adds `.in` to
   each `.reveal` element when it scrolls into view. Elements start at 60%
   opacity (NOT invisible) and 18px low, then ease to full opacity and position.
   Because they never start at 0% opacity, the content stays readable even if the
   script fails or the page is printed or previewed by a tool that does not scroll.
   The `.js` prefix means none of this applies when JavaScript is off.
   ----------------------------------------------------------------------------- */
.js .reveal { opacity: .6; transform: translateY(18px); transition: opacity .6s ease, transform .6s ease; }
.js .reveal.in { opacity: 1; transform: none; }


/* -----------------------------------------------------------------------------
   9b. REDUCED MOTION
   Respect the operating-system setting "reduce motion": switch off every
   continuous animation and transition. (wire.js reads the same setting and draws
   still frames instead of animating.) The ticker becomes a manually scrollable
   strip instead of an auto-scrolling one.
   ----------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  body::before, .site-header::after, .ticker__track { animation: none; }
  .ticker { overflow-x: auto; }
  .js .reveal { opacity: 1; transform: none; transition: none; }
  .btn, .tile__img img, .rows a, .rows .arrow { transition: none; }
}


/* -----------------------------------------------------------------------------
   9c. PRINT
   Printed pages: drop the decorative layers and the header, and use black text
   on white paper so it is legible and saves ink.
   ----------------------------------------------------------------------------- */
@media print { #wire, .ticker, .site-header { display: none; } body { background: #fff; color: #000; } body::before { display: none; } }


/* -----------------------------------------------------------------------------
   9d. SHORT DESKTOP WINDOWS
   On windows that are wide but not tall (for example a 1366x650 laptop browser),
   tighten the home hero so the whole countdown still fits above the docked
   ticker without being covered by it. Phones are excluded (min-width: 900px)
   because they have their own stacked layout.
   ----------------------------------------------------------------------------- */
@media (max-height: 760px) and (min-width: 900px) {
  .hero { padding-top: 1rem; }
  .hero__mark { font-size: clamp(3.6rem, min(16vw, 15vh), 11rem); }
  .hero__where { margin-bottom: .5rem; }
  .hero .lead { max-width: 64ch; font-size: 1.05rem; margin-bottom: 1rem; }
  .countdown { margin-top: 1rem; }
  .countdown__label { margin-bottom: .4rem; }
  .countdown__unit b, .countdown__sep { font-size: 2.4rem; }
}
