/* SPOT - spotevent.be
 *
 * Shared by the Dutch homepage (/) and the English one (/en/), and by
 * /blog, /support and /privacy for the nav, menu and footer. Only the
 * copy differs between the two homepages, so everything visual lives
 * here once. Change a rule and both languages move together, which is
 * the whole reason this is not inlined per page.
 *
 * The site is dark-only on purpose. SPOT is a going-out app and the app
 * itself opens dark; a light variant would be a second design to keep
 * true, not a courtesy.
 */

:root {
  --neon: #d8ff00;
  --ink: #0a0a09;
  --text: #ffffff;
  --muted: rgba(255, 255, 255, 0.62);
  --faint: rgba(255, 255, 255, 0.42);
  --line: rgba(255, 255, 255, 0.12);
  --line-soft: rgba(255, 255, 255, 0.07);
  --panel: #121210;

  /* The device row is three columns: a bubble column, the phone, a
     bubble column. Sized from the viewport *width* rather than its
     height, because width is what has to hold all three - the page
     scrolls, so height is no longer the scarce dimension. Retune the
     phone here and the bubbles follow it. */
  --phone-w: clamp(235px, 26vw, 330px);
  /* The bezel is 0.026 of the body width on all four sides, so solving
       (W - 0.052W) / (H - 0.052W) = 0.462
     - 0.462 being the ratio of the screenshots (1242x2688) - gives
     W = 0.4753H. Put 0.462 on the body instead and the screen comes out
     at 0.449, which is enough for object-fit to shave the sides. */
  --phone-h: calc(var(--phone-w) / 0.4753);
  /* Wide enough for a quote *plus* the 50px the arrow needs beside it -
     the arrow is reserved out of this column rather than allowed to
     hang into the gap, where the phone paints over it. */
  --bubble-col: clamp(175px, 21vw, 300px);
  --stage-gap: clamp(8px, 1.4vw, 22px);
  --gutter: 48px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--ink);
  color: var(--text);
  font-family: 'Geist', system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--neon); }
:focus-visible { outline: 2px solid var(--neon); outline-offset: 3px; border-radius: 4px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* Kicker above a heading. The homepage stopped using it, /support and
   /privacy still do. */
.eyebrow { color: var(--neon); margin: 0 0 14px; }

/* ── Frame + spotlight ──────────────────────────────────────────────
   The spotlight the product is named after. Painted from JS straight
   into these two layers so following the cursor never re-renders
   anything.

   The three layers are fixed, not absolute: the homepage scrolls now,
   and a beam anchored to a document several screens tall would sit
   nowhere near the cursor by the time you reached the features. */
.frame {
  position: relative;
  width: 100%;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--ink);
}
.beam, .vignette, .grain { position: fixed; inset: 0; pointer-events: none; }
.beam { transition: background 0.4s cubic-bezier(.2, .8, .3, 1); }
.grain {
  opacity: 0.35; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}
.page {
  position: relative;
  display: flex; flex-direction: column;
  flex: 1; min-height: 0;
}

/* ── Nav ────────────────────────────────────────────────────────── */
.nav {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 20px;
  padding: clamp(20px, 2.2vw, 30px) var(--gutter);
}
/* The mark plus the wordmark, not the domain: the app on the store and
   the site have to read as the same product at a glance. */
.brand {
  display: inline-flex; align-items: center; gap: 10px;
  color: var(--text); text-decoration: none;
  font-family: 'Bricolage Grotesque', 'Geist', sans-serif;
  font-weight: 700; font-size: clamp(22px, 1.9vw, 27px);
  letter-spacing: -0.03em;
}
.brand a { color: inherit; text-decoration: none; }
.brand img { width: clamp(23px, 2vw, 29px); height: auto; display: block; }

.navlinks {
  display: flex; align-items: center;
  gap: clamp(26px, 2.4vw, 40px); justify-self: center;
}
/* Hover draws a line, it does not brighten the label. The border is
   already there in transparent, so nothing shifts when it appears. The
   page you are on keeps that line, and its label at full strength. */
.navlinks a {
  color: var(--muted); text-decoration: none;
  font-size: clamp(15px, 1.15vw, 17.5px);
  padding-bottom: 3px;
  border-bottom: 1.5px solid transparent;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.navlinks a:hover { border-bottom-color: var(--neon); }
.navlinks a[aria-current="page"] { color: var(--text); border-bottom-color: var(--neon); }

.navright { display: flex; align-items: center; gap: 14px; justify-self: end; }

/* Two links, not a select: the alternate language is a real URL that
   search engines and the browser's back button both understand. */
.langswitch {
  display: flex; align-items: center; gap: 6px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
}
.langswitch a { color: var(--faint); text-decoration: none; }
.langswitch a:hover { color: var(--text); }
.langswitch .current { color: var(--neon); }
.langswitch .sep { color: var(--line); }

.pill {
  display: inline-flex; align-items: center; gap: 8px;
  border: none; border-radius: 99px;
  padding: clamp(11px, 1.1vw, 14px) clamp(22px, 2vw, 30px);
  background: var(--neon); color: var(--ink);
  font-family: inherit; font-size: clamp(15px, 1.15vw, 17px); font-weight: 700;
  letter-spacing: -0.01em; text-decoration: none; cursor: pointer;
  box-shadow: 0 0 24px rgba(216, 255, 0, 0.28);
  transition: transform 0.35s cubic-bezier(.2, .8, .3, 1), box-shadow 0.35s ease;
}
.pill:hover { transform: translateY(-2px); box-shadow: 0 0 40px rgba(216, 255, 0, 0.5); }

/* Hamburger. Hidden until the nav stops fitting; the panel it opens is
   the same list of links, so nothing is menu-only. */
.burger {
  display: none;
  width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--line); border-radius: 10px;
  background: transparent; cursor: pointer;
  align-items: center; justify-content: center;
}
.burger span {
  display: block; position: relative;
  width: 17px; height: 1.5px; background: var(--text);
}
.burger span::before, .burger span::after {
  content: ''; position: absolute; left: 0;
  width: 17px; height: 1.5px; background: var(--text);
}
.burger span::before { top: -5.5px; }
.burger span::after  { top: 5.5px; }

.menu {
  display: none;
  padding: 6px var(--gutter) 20px;
  border-bottom: 1px solid var(--line);
}
.menu[data-open="true"] { display: block; }
.menu a {
  display: block; padding: 13px 0;
  color: var(--text); text-decoration: none; font-size: 17px;
  border-top: 1px solid var(--line-soft);
}
.menu a:first-child { border-top: none; }

/* ── Hero ───────────────────────────────────────────────────────────
   One centred column: claim, then the device. The old three-column
   version squeezed both text blocks into a few words per line as soon
   as the phone grew, and put the store buttons off to one side where a
   visitor scanning down the middle never met them. */
.hero {
  display: flex; flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(30px, 5vw, 58px);
  padding: clamp(20px, 4vw, 44px) var(--gutter) clamp(40px, 5vw, 64px);
}
.claim { width: 100%; max-width: 660px; }

.hero h1 {
  font-family: 'Bricolage Grotesque', 'Geist', sans-serif;
  font-weight: 700;
  font-size: clamp(56px, 9.5vw, 124px);
  line-height: 0.94;
  letter-spacing: -0.045em;
  font-variation-settings: '"opsz" 96';
  margin: 0;
  text-wrap: balance;
}
.hero h1 em {
  font-style: normal; color: var(--text);
  text-shadow: 0 0 60px rgba(216, 255, 0, 0.35);
}
.tagline {
  font-size: clamp(16px, 1.45vw, 20px);
  line-height: 1.5; color: var(--muted);
  margin: clamp(18px, 2vw, 26px) auto 0;
  max-width: 30ch;
  text-wrap: balance;
}
.free {
  margin: 16px 0 0;
  font-size: 13px; color: var(--faint);
}

/* The invite chip. Only drawn when the visitor came in on /i/<id>, so a
   personal link visibly did something. We never name the inviter: that
   would mean a public endpoint turning any user id into a name. */
.invited {
  display: none;
  margin: 0 0 20px;
  padding: 7px 15px; border-radius: 99px;
  border: 1px solid rgba(216, 255, 0, 0.28);
  background: rgba(216, 255, 0, 0.08);
  color: var(--neon); font-size: 13.5px; font-weight: 600;
}
.invited[data-show="true"] { display: inline-block; }

/* ── Store buttons ────────────────────────────────────────────────── */
.stores {
  display: flex; flex-wrap: wrap; gap: 12px;
  justify-content: center;
  margin-top: clamp(24px, 3vw, 36px);
}
.store {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 11px 20px 11px 18px; border-radius: 14px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.05);
  color: var(--text); text-decoration: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.store:hover { border-color: var(--neon); background: rgba(216, 255, 0, 0.07); }
.store svg { width: 22px; height: 22px; flex-shrink: 0; fill: currentColor; }
.store span { text-align: left; }
.store b {
  display: block; font-size: 15px; font-weight: 600;
  letter-spacing: -0.01em; line-height: 1.2;
}
.store i {
  display: block; font-style: normal;
  font-size: 11px; color: var(--faint); letter-spacing: 0.04em;
}

/* ── Phone + bubbles ───────────────────────────────────────────────
   Three real grid columns rather than bubbles absolutely positioned
   over the device. A grid cannot overlap the phone at any width, which
   is what the absolute version did as soon as the screen got narrow -
   the quotes ended up lying on the screenshot they were pointing at. */
.stage {
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(14px, 2vw, 22px);
  width: 100%;
}
.device {
  display: grid;
  grid-template-columns: var(--bubble-col) var(--phone-w) var(--bubble-col);
  gap: var(--stage-gap);
  justify-content: center;
}

/* One set of claims per side, and it stays put while the screenshot
   changes: they describe the app, not the screen on show. */
.col { display: flex; }
.bubbles { display: flex; flex-direction: column; gap: 14px; width: 100%; }
/* Staggered against the phone's height, so the four boxes read as four
   separate notes rather than one list. */
.col.left .bubbles {
  align-items: flex-end; justify-content: space-between;
  padding: calc(var(--phone-h) * 0.12) 50px calc(var(--phone-h) * 0.28) 0;
}
.col.right .bubbles {
  align-items: flex-start; justify-content: space-between;
  padding: calc(var(--phone-h) * 0.26) 0 calc(var(--phone-h) * 0.16) 50px;
}

.bubble {
  position: relative; margin: 0; max-width: 100%;
  padding: 12px 17px; border-radius: 16px;
  border: 1.5px solid var(--neon);
  background: rgba(10, 10, 9, 0.92);
  box-shadow: 0 0 26px rgba(216, 255, 0, 0.2);
  font-size: clamp(13px, 1.05vw, 16.5px);
  line-height: 1.3; font-weight: 500;
  text-align: left;
}

/* The hand-drawn arrow, as a mask rather than an inline <svg>: it is
   the same shape sixteen times over two language files, and a quote is
   then one line of markup instead of six. */
.bubble::after {
  content: '';
  position: absolute; top: 50%;
  width: 44px; height: 35px;
  background: var(--neon);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 42 34' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 7C16 9 26 14 35 25'/%3E%3Cpath d='M27 25l9 1.5-3.5-9'/%3E%3C/svg%3E") no-repeat center / contain;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 42 34' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 7C16 9 26 14 35 25'/%3E%3Cpath d='M27 25l9 1.5-3.5-9'/%3E%3C/svg%3E") no-repeat center / contain;
}
.col.left  .bubble::after { left: 100%;  margin-left: 6px;  transform: translateY(-50%); }
.col.right .bubble::after { right: 100%; margin-right: 6px; transform: translateY(-50%) scaleX(-1); }

/* The device body. Every measurement inside is in em, and font-size is
   the device width, so 1em = the width of the phone and the whole thing
   scales from one variable. Nothing here contains text - if that ever
   changes, reset font-size on the child.
   Proportions are an iPhone 11 Pro Max, which is what the screenshots
   were taken on: 1242x2688, a notch about half the width of the screen,
   volume pair on the left and the side button lower down on the right.
   No overflow:hidden - the buttons stick out past the body. The screen
   does its own clipping. */
.phone {
  position: relative;
  width: var(--phone-w); height: var(--phone-h);
  font-size: var(--phone-w);
  border-radius: 0.128em;
  background:
    linear-gradient(145deg,
      #55554d 0%, #2b2b26 14%, #1b1b18 38%,
      #1b1b18 62%, #2f2f29 86%, #504f48 100%);
  box-shadow:
    0 30px 70px rgba(0, 0, 0, 0.65),
    0 0 90px rgba(216, 255, 0, 0.10);
}

/* Volume up and down. One element plus a shadow copy of itself. */
.phone::before {
  content: '';
  position: absolute; z-index: 0;
  left: -0.0075em; top: 0.205em;
  width: 0.0085em; height: 0.055em;
  background: #3d3d36;
  border-radius: 0.005em 0 0 0.005em;
  box-shadow: 0 0.078em 0 #3d3d36;
}
/* Side button, right, lower and longer than the volume keys. */
.phone::after {
  content: '';
  position: absolute; z-index: 0;
  right: -0.0075em; top: 0.25em;
  width: 0.0085em; height: 0.092em;
  background: #3d3d36;
  border-radius: 0 0.005em 0.005em 0;
}
/* The screen: inset by the bezel, and the thing that actually clips the
   screenshot. The inner radius is smaller than the body's by exactly
   the bezel, which is what keeps the two curves concentric. */
.slides {
  position: absolute;
  inset: 0.026em;
  border-radius: 0.102em;
  overflow: hidden;
  background: #000;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.85);
}

/* The notch. The screenshots were taken on a notched phone, so their
   status bar already leaves this gap free - the time sits left of it and
   the battery right of it. Drawing it back in is what makes the frame
   read as a device instead of a rounded rectangle. */
.slides::after {
  content: '';
  position: absolute; z-index: 3;
  top: 0; left: 50%; transform: translateX(-50%);
  width: 0.46em; height: 0.072em;
  background: #000;
  border-radius: 0 0 0.05em 0.05em;
}
/* Real 1242x2688 exports, downscaled to 800 wide, so the image and the
   frame share an aspect ratio and nothing is letterboxed or cropped.
   The one marked active in the markup carries a plain src and renders
   whether or not site.js ever runs; the rest are hydrated on demand. */
.slide {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  opacity: 0;
  transition: opacity 0.35s ease;
  /* .phone sets font-size to its own width so its em measurements work.
     Anything inside that can fall back to text - alt text, a label - has
     to opt out of that, or it renders 300px tall. */
  font-size: 14px;
}
.slide[data-active="true"] { opacity: 1; }

/* Hit areas laid over the tab bar that is already drawn in every
   screenshot, in the same five columns the app uses. Clicking one swaps
   the screenshot for that tab - and each screenshot already has its own
   tab lit, so the bar lights up by itself. The middle column is the
   compose button and is a spacer, not a target: there is no
   create-an-event screenshot to show yet. */
.tabs {
  position: absolute; z-index: 4;
  left: 0; right: 0; bottom: 0; height: 12.2%;
  display: grid; grid-template-columns: repeat(5, 1fr);
  font-size: 14px;
}
.tabs button {
  position: relative;
  border: 0; padding: 0; background: none; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.tabs button::after {
  content: '';
  position: absolute; inset: 10% 6% 20%;
  border-radius: 10px;
  border: 1.5px solid transparent;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.tabs button:hover::after,
.tabs button:focus-visible::after {
  border-color: rgba(216, 255, 0, 0.6);
  background: rgba(216, 255, 0, 0.1);
}

/* Told, not discovered: nothing about a screenshot suggests it is
   clickable. Hidden wherever the tab strip itself is. */
.phone-hint {
  margin: 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--faint); text-align: center;
}

/* Organisers are a different audience than the people downloading the
   app, so they get one quiet line here instead of a second call to
   action competing with the store buttons. */
.organiser {
  margin: 0; text-align: center;
  font-size: 14.5px; line-height: 1.5; color: var(--muted);
}
.organiser a {
  color: var(--neon); text-decoration: none;
  border-bottom: 1px solid rgba(216, 255, 0, 0.45); padding-bottom: 1px;
}

/* ── Features ──────────────────────────────────────────────────────
   Numbered because the three are read as a sequence: find something,
   make something, see who is coming. */
.features {
  width: 100%; max-width: 1240px; margin: 0 auto;
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(26px, 4vw, 56px);
  padding: clamp(32px, 4.5vw, 54px) var(--gutter);
  border-top: 1px solid var(--line);
}
.feature { display: flex; gap: 14px; }
.feature .n {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px; letter-spacing: 0.1em; color: var(--neon); min-width: 22px;
}
.feature h3 { margin: 0 0 6px; font-size: 17px; font-weight: 600; letter-spacing: -0.01em; }
.feature p  { margin: 0; font-size: 14px; line-height: 1.5; color: var(--muted); }

/* ── Footer ────────────────────────────────────────────────────────── */
.footer {
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: 20px;
  padding: 16px var(--gutter);
  border-top: 1px solid var(--line);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11.5px; letter-spacing: 0.04em; color: var(--muted);
}
.follow { display: flex; align-items: center; gap: 10px; }
.follow > span { opacity: 0.55; }
.social {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: var(--muted); text-decoration: none; font-size: 11px;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.social:hover { color: var(--neon); border-color: var(--neon); }
.copy { display: flex; align-items: center; gap: 8px; justify-content: center; }
.copy img { width: 14px; height: auto; display: block; }
.legal { display: flex; gap: 16px; justify-content: flex-end; flex-wrap: wrap; }
.legal a {
  color: rgba(255, 255, 255, 0.7); text-decoration: none;
  border-bottom: 1px solid rgba(216, 255, 0, 0.5); padding-bottom: 1px;
}

/* ── Narrower ────────────────────────────────────────────────────── */
@media (max-width: 1100px) {
  :root { --gutter: 26px; }
  .navlinks { display: none; }
  .burger { display: inline-flex; }
  /* Brand and controls only. The middle track held the links that just
     went into the menu, and an empty `auto` track still eats gap. */
  .nav { grid-template-columns: 1fr auto; }

  .features { grid-template-columns: 1fr; gap: 20px; }
  .footer {
    grid-template-columns: 1fr; justify-items: center;
    text-align: center; gap: 14px;
  }
  .legal { justify-content: center; }
}

/* ── Phone ─────────────────────────────────────────────────────────
   The claims stay beside the device rather than moving on top of it,
   so the phone is what gives way: its width is whatever is left after
   two bubble columns and the gutters have taken their share. */
@media (max-width: 700px) {
  :root {
    --gutter: 14px;
    --bubble-col: clamp(82px, 23vw, 116px);
    --stage-gap: 6px;
    --phone-w: min(300px, calc(100vw - 2 * var(--gutter) - 2 * var(--bubble-col) - 2 * var(--stage-gap)));
  }
  .hero h1 { font-size: clamp(52px, 15vw, 76px); }
  /* Brand, button and burger is already the whole width of a phone. Both
     languages stay one tap away in the menu. */
  .langswitch { display: none; }

  /* A column this narrow breaks "organisatoren" whatever you do, so let
     the browser hyphenate it rather than leave a ragged hole. */
  .bubble {
    font-size: 10.5px; padding: 6px 9px; border-radius: 11px;
    line-height: 1.35; hyphens: auto;
  }
  /* No room for an arrow between a bubble and the screen at this width,
     and a shorter phone leaves no room for the stagger either. */
  .bubble::after { display: none; }
  .col.left .bubbles, .col.right .bubbles {
    justify-content: space-around;
    padding: calc(var(--phone-h) * 0.06) 0;
  }
}

/* Where the tab strip is worth offering. Below 700px the phone drops to
   about 180px wide and a tab is a 36px target on a picture too small to
   read the difference; above it the phone is 235px or more, so a tab is
   roughly 47x60 - fine for a cursor.

   Do NOT raise this back towards 900px. A 1280 screen at Windows' 150%
   scaling reports an 853px viewport, so a cutoff up there silently kills
   the control on an ordinary laptop.

   The second condition is deliberately both halves: a touch-primary
   device (a tablet at 820px) has no cursor to hover with, but a laptop
   with a touchscreen still reports pointer:fine and keeps the strip. */
@media (max-width: 700px), (hover: none) and (pointer: coarse) {
  .tabs, .phone-hint { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
