/* ==========================================================================
   ROYALTY CASINO PRAGUE — "Neon Royale"
   60% warm marble white · 30% royal-blue neon atmosphere · 10% gold

   The blue family is the old violet family hue-rotated in OKLCH to the hue of
   the brand #2E3C90, with every colour's lightness held. That is why the dark
   grounds are still exactly as dark as they were and the cove glows still read
   as two light sources — only the hue moved. Gold, the felt green, the
   roulette red/black/green and the card cream are NOT part of that rotation:
   they are object colours, not theme colours, and are left verbatim.

   Token names below are historical (--neon-violet, --neon-magenta). They are
   kept because build_neon.py and site-neon.js reference them; the values are
   blue.
   ========================================================================== */

/* --- display face ----------------------------------------------------------
   Collingar DEMO — self-hosted, because it is not on Google Fonts.

   ⚠️ This is NOT the file as downloaded. The demo cut ships 102 glyphs, ASCII
   only, which cannot set a word of Czech. `scripts/extend_collingar.py` adds 32:
   the full Czech set in both cases, plus an em and en dash. They are not drawn
   from scratch — the demo does ship four properly drawn marks, and every Czech
   diacritic is one of them transformed:

       acute    = grave (U+0060) mirrored horizontally
       caron    = asciicircum (U+005E) flipped vertically
       ring     = o scaled to 30%
       d/t tick = quotesingle set beside the ascender (Czech sets ď/ť that way)

   So the accents carry the typeface's own stroke contrast rather than geometry
   drawn to look approximately right. í additionally drops the tittle: an acute
   laid over a dot does not merge, it punches a white notch through by the
   nonzero fill rule.

   Re-run the script against a new cut rather than hand-editing this file.

   `unicode-range` is deliberate. Without it the browser would still fall back
   per glyph, but it would download the file for any heading — including a
   Russian one, where not a single character can land. Pinning the range makes
   the request itself conditional; verified live, the RU pages never fetch it.

   Cyrillic is the one gap that cannot be closed this way — 46 letterforms is
   type design, not transformation — so RU headings take Prata. See the locale
   block at the foot of this file.

   LICENCE: free for personal use only. Commercial use needs the paid family
   from saridezra@gmail.com / Creative Fabrica. See fonts/Collingar-DEMO-LICENSE.txt.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Collingar DEMO";
  src: url("fonts/collingar-demo-ext.woff2") format("woff2"),
       url("fonts/collingar-demo-ext.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0020-007E, U+00A0, U+00C1, U+00C9, U+00CD, U+00D3, U+00DA,
                 U+00DD, U+00E1, U+00E9, U+00ED, U+00F3, U+00FA, U+00FD,
                 U+010C-010F, U+011A-011B, U+0147-0148, U+0158-0159,
                 U+0160-0161, U+0164-0165, U+016E-016F, U+017D-017E,
                 U+2013-2014;
}

:root {
  /* Core */
  --bg-base: #F3EFE6;        /* warm marble white */
  --bg-dark: #0F1632;        /* deep blue-black, sparingly */
  --neon-violet: #2E3C90;    /* BRAND BLUE — dominant LED cove glow */
  --neon-blue: #3945D6;      /* secondary cove, one step brighter */
  --neon-magenta: #8FA0E8;   /* bar signage accent — bar section only */
  --scrub-accent: #8FA0E8;   /* 2nd tone of the two-tone headlines in the
                                scroll-scrub sections. Light enough to hold
                                6.4:1 on the dark blue room; the brand blue
                                itself would vanish there. */
  --gold: #C9A227;           /* stanchions, chips, script wordmark */
  --gold-deep: #A8861D;      /* gold on light bg (AA-safe hairlines/hover) */
  --felt-green: #1F5C4A;     /* table felt accent */

  /* Rug motif palette — footer strip only, since the section dividers moved to
     the gold deco rule. Kept because .rug-strip still uses all three. */
  --rug-blue: #5B7FA6;
  --rug-coral: #D89A96;
  --rug-ochre: #C9A66B;

  /* Text */
  --ink: #171E36;            /* on light */
  --ink-soft: #3D4564;       /* secondary on light */
  --ink-inverse: #F8F5EE;    /* on dark/violet */
  --ink-inverse-dim: #C3CBE4;

  /* Type */
  --sans: "Poppins", system-ui, sans-serif;
  --script: "Playfair Display", serif;
  --body: "Inter", system-ui, sans-serif;
  /* Headings. Collingar now covers English AND Czech outright; Prata stands
     behind it for Cyrillic only — same Didone genre, same single 400 weight, so
     nothing can trigger a synthetic bold. Georgia is the offline net. */
  --display: "Collingar DEMO", "Prata", Georgia, serif;

  /* Roulette object colours — the wheel's own red/black/green, previously
     inline in wheel_svg() only. Tokenised because the game controls now take
     the colour of the bet they place, so the pill and the pocket have to be
     one value, not two that drift. */
  --roul-red: #A81E32;
  --roul-black: #141118;
  --roul-green: #1F7A4D;

  /* Shape & rhythm */
  --radius: 6px;
  --radius-sm: 4px;
  --container: 1200px;
  --hairline-gold: 1px solid rgba(168, 134, 29, 0.45);
  --hairline-light: 1px solid rgba(23, 30, 54, 0.14);
  --hairline-inverse: 1px solid rgba(248, 245, 238, 0.18);
}

/* --- reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; scrollbar-gutter: stable; }
body {
  margin: 0;
  background: var(--bg-base);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, video { max-width: 100%; display: block; }
a { color: inherit; }
/* Headings are Collingar. It has one weight, so any `font-weight` above 400
   would be faked by the browser — and a smeared outline is the one thing that
   kills a high-contrast Didone. `font-synthesis-weight: none` refuses the fake
   outright; every heading rule below sets 400 to match. */
h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 400;
  font-synthesis-weight: none;
  line-height: 1.15;
  margin: 0 0 0.5em;
}
/* Czech needs no rule here any more — the extended cut sets it natively. Only
   Cyrillic is scoped, and it is scoped by `--display`'s own fallback, so the
   two-serif-in-one-word problem cannot come back for a language nobody
   anticipated. See the locale block at the foot of this file. */
p { margin: 0 0 1em; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.visually-hidden:focus {
  width: auto; height: auto; clip: auto; z-index: 200;
  background: var(--bg-dark); color: var(--ink-inverse); padding: 12px 20px;
}

.container { max-width: var(--container); margin: 0 auto; padding: 0 24px; }
.section { padding: 96px 0; position: relative; }

/* --- typography helpers ---------------------------------------------------- */
.eyebrow {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--neon-violet);
  margin: 0 0 14px;
}
.eyebrow--script {
  font-family: var(--script);
  font-style: italic;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0.02em;
  font-size: 22px;
}
h2 { font-size: clamp(32px, 4.7vw, 50px); font-weight: 400; letter-spacing: -0.005em; }
h3 { font-size: 23px; font-weight: 400; }
/* Collingar sets a shade smaller on the line than Poppins did at the same px —
   display serif, narrower fitting. The bump above puts the optical size back,
   and it holds for all three languages now that they are all on a Didone. */
.lead { font-size: 19px; color: var(--ink-soft); max-width: 640px; }
.sec-head { max-width: 720px; margin-bottom: 48px; }

/* --- buttons ---------------------------------------------------------------
   Gold fill on dark sections, gold outline on light. Flat, 1px hairline,
   no drop shadows — marble/glass material language. */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--sans); font-weight: 600; font-size: 15px;
  letter-spacing: 0.04em;
  padding: 14px 28px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
  border: 1px solid transparent;
}
.btn svg { width: 18px; height: 18px; flex: none; }
.btn--gold { /* on dark */
  background: var(--gold);
  color: var(--bg-dark);
  border-color: rgba(248, 245, 238, 0.25);
}
.btn--gold:hover { background: #DDB53A; }
/* Primary CTA in brand blue. It is the brighter cove tone, not #2E3C90: the
   hero scrim bottoms out near the brand blue itself, and a button the colour of
   the ground behind it stops being a button. 7.0:1 with white. */
.btn--blue { /* on dark */
  background: var(--neon-blue);
  color: #fff;
  border-color: rgba(248, 245, 238, 0.28);
}
.btn--blue:hover { background: #4E5AE6; }
/* Solid white — the one CTA weight that outranks gold. Used by the game
   controls, where the buttons carry table colours and the primary action has
   to sit above all of them. */
.btn--white {
  background: var(--ink-inverse);
  color: var(--ink);
  border-color: rgba(23, 30, 54, 0.14);
}
.btn--white:hover { background: #fff; }
.btn--outline { /* on light */
  background: transparent;
  color: var(--ink);
  border-color: var(--gold-deep);
}
.btn--outline:hover { background: rgba(201, 162, 39, 0.12); }
.btn--ghost-dark { /* secondary on dark */
  background: transparent;
  color: var(--ink-inverse);
  border-color: rgba(248, 245, 238, 0.4);
}
.btn--ghost-dark:hover { border-color: var(--gold); color: var(--gold); }
/* Glass secondary, for use over moving footage (the hero). The hairline is a
   masked gradient ring rather than a flat border, so the edge catches light at
   top and bottom and disappears at the sides — reads as a glass lip, not a box.
   NB: the standard `mask` is declared alongside `-webkit-mask`; with only the
   webkit one, Firefox skips the cut-out and paints a solid slab. */
.btn--glass {
  background: rgba(255, 255, 255, 0.01);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: var(--ink-inverse);
  border: none;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.1);
  position: relative;
  overflow: hidden;
}
.btn--glass::before {
  content: ""; position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1.4px;
  background: linear-gradient(180deg,
    rgba(255, 255, 255, 0.45) 0%, rgba(255, 255, 255, 0.15) 20%,
    rgba(255, 255, 255, 0) 40%, rgba(255, 255, 255, 0) 60%,
    rgba(255, 255, 255, 0.15) 80%, rgba(255, 255, 255, 0.45) 100%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.btn--glass:hover { color: var(--gold); }

/* --- header ---------------------------------------------------------------- */
.site-header {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  padding: 14px 0;
  transition: background-color 0.25s ease, box-shadow 0.25s ease, padding 0.25s ease;
  color: var(--ink-inverse);
}
.site-header.scrolled, .site-header.nav-open, .site-header.solid {
  background: rgba(243, 239, 230, 0.96);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: var(--ink);
  border-bottom: var(--hairline-gold);
  padding: 10px 0;
}
.header-inner { display: flex; align-items: center; gap: 28px; }
.brand { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; }
/* The crown, as a mask rather than an <img>.
   The header flips from transparent-over-video to opaque marble on scroll, so a
   white crown has to become a blue one at the same moment the gold script does.
   Masking one alpha PNG and painting it with background-color makes that a
   colour swap in CSS — the same swap the wordmark already does — instead of two
   PNGs and a display toggle. It also means the mark follows the palette tokens
   if the blue is ever re-rotated again.
   1.3547 is the crown's own aspect ratio, measured off the artwork. */
.brand__mark {
  flex: none;
  width: calc(34px * 1.3547);
  height: 34px;
  background-color: var(--ink-inverse);
  -webkit-mask: url("img/logo-crown-white.png") center / contain no-repeat;
  mask: url("img/logo-crown-white.png") center / contain no-repeat;
  transition: background-color 0.25s ease;
}
/* The header carries the mark alone. With no wordmark beside it to lend scale
   the crown has to hold the corner by itself, hence 34px against the footer
   lockup's 23px — at the old 26px it read as an orphaned bullet. */
.site-header.scrolled .brand__mark,
.site-header.solid .brand__mark { background-color: var(--neon-violet); }
.brand__script {
  font-family: var(--script); font-style: italic; font-weight: 600;
  font-size: 21px; letter-spacing: 0.01em;
  transform: translateY(1px);
  /* Light blue on the dark header, brand blue on the marble one. #2E3C90 on the
     hero would sink into the scrim; #8FA0E8 is the tone already carried for
     exactly this problem in the scrub sections. */
  color: var(--scrub-accent);
  transition: color 0.25s ease;
}
.site-header.scrolled .brand__script, .site-header.solid .brand__script { color: var(--neon-violet); }
.nav { display: flex; gap: 26px; margin-left: auto; }
.nav a {
  font-family: var(--sans); font-weight: 500; font-size: 14px;
  letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none;
  padding: 6px 2px; border-bottom: 2px solid transparent;
  transition: border-color 0.2s ease;
}
.nav a:hover { border-bottom-color: var(--gold); }
.header-right { display: flex; align-items: center; gap: 18px; }
.lang-switch { display: flex; gap: 6px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; }
.lang-switch a { text-decoration: none; opacity: 0.6; padding: 2px 3px; }
.lang-switch a:hover { opacity: 1; }
.lang-switch a[aria-current="true"] { opacity: 1; color: var(--gold); font-weight: 600; }
.site-header.scrolled .lang-switch a[aria-current="true"],
.site-header.solid .lang-switch a[aria-current="true"] { color: var(--gold-deep); }
.header-call {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--sans); font-weight: 600; font-size: 14px;
  text-decoration: none;
}
.header-call svg { width: 16px; height: 16px; }
.nav-toggle {
  display: none; background: none; border: 0; color: inherit;
  width: 42px; height: 42px; cursor: pointer; padding: 8px;
}
.nav-toggle svg { width: 100%; height: 100%; }

/* --- hero ------------------------------------------------------------------ */
.hero {
  position: relative; min-height: 100svh;
  display: flex; align-items: flex-end;
  color: var(--ink-inverse);
  overflow: hidden;
  background: var(--bg-dark);
}
.hero__media { position: absolute; inset: 0; }
.hero__media video, .hero__media img.hero__poster {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
/* Populated frame → stronger violet scrim across the bottom two-thirds */
.hero__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg,
      rgba(15, 22, 50, 0.30) 0%,
      rgba(15, 22, 50, 0.10) 22%,
      rgba(25, 34, 88, 0.42) 55%,
      rgba(15, 22, 50, 0.88) 100%);
}
.hero__inner { position: relative; width: 100%; padding-bottom: 96px; padding-top: 160px; }
.hero__kicker {
  font-family: var(--sans); font-size: 13px; font-weight: 600;
  letter-spacing: 0.3em; text-transform: uppercase;
  color: var(--ink-inverse-dim); margin-bottom: 18px;
}
.hero__lockup { margin: 0 0 10px; }
.hero__lockup .block {
  display: block;
  font-family: var(--display); font-weight: 400;
  font-size: clamp(44px, 9vw, 108px);
  letter-spacing: 0.22em; text-indent: 0.22em; /* optically re-center wide tracking */
  line-height: 1;
}
.hero__lockup .script {
  display: block;
  font-family: var(--display); font-style: normal; font-weight: 400;
  font-size: clamp(38px, 7vw, 84px);
  color: var(--gold);
  margin-top: 2px;
  line-height: 1.1;
}
.hero__tagline { max-width: 560px; font-size: 18px; color: var(--ink-inverse-dim); margin-bottom: 30px; }
.hero__cta { display: flex; gap: 14px; flex-wrap: wrap; }

/* --- hero: 3D scroll --------------------------------------------------------
   site-neon.js writes a single 0→1 progress value into --hp; every depth
   offset below is derived from it, so the whole feel is tunable here without
   touching JS. At --hp:0 the section renders exactly as the static version,
   apart from the 1.08 background scale (see below).

   Depth reads from three cues stacked together:
     1. the footage drifts DOWN while the copy rises  → opposed motion
     2. each copy line rises at its own rate          → separation
     3. the copy block tilts away on rotateX          → the actual 3D part
   Text is vector, so it stays razor-sharp through the tilt; the 720p clip
   deliberately gets NO translateZ, which would force an upscale and soften it. */
.hero {
  perspective: 1200px;
  perspective-origin: 50% 60%;
  --hp: 0;
}
/* 1.08 buys ~4vh of bleed on every edge so the drift can never expose one. */
.hero__media {
  transform: scale(1.08) translateY(calc(var(--hp) * 4vh));
  will-change: transform;
}
/* Deepens the scrim as the section leaves — invisible at rest (opacity 0). */
.hero__scrim::after {
  content: ""; position: absolute; inset: 0;
  background: var(--bg-dark);
  opacity: calc(var(--hp) * 0.45);
}
.hero__inner {
  transform: translateY(calc(var(--hp) * -5vh)) rotateX(calc(var(--hp) * 7deg));
  transform-origin: 50% 100%;
  opacity: calc(1 - var(--hp) * 1.15);
  will-change: transform, opacity;
}
.hero__kicker  { transform: translateY(calc(var(--hp) * -1.5vh)); }
.hero__lockup  { transform: translateY(calc(var(--hp) * -3vh)); }
.hero__tagline { transform: translateY(calc(var(--hp) * -4.5vh)); }
.hero__cta     { transform: translateY(calc(var(--hp) * -6vh)); }

/* Bloom on the lockup so it holds against the moving footage. */
.hero__lockup .block {
  text-shadow: 0 0 80px rgba(248, 245, 238, 0.30), 0 0 160px rgba(248, 245, 238, 0.10);
}
.hero__lockup .script {
  text-shadow: 0 0 60px rgba(201, 162, 39, 0.45), 0 0 130px rgba(201, 162, 39, 0.18);
}

/* --- trust bar --------------------------------------------------------------
   Thin strip under the hero, gold hairline top & bottom. */
.trust-bar {
  border-top: var(--hairline-gold);
  border-bottom: var(--hairline-gold);
  background: var(--bg-base);
  padding: 14px 0;
}
.trust-bar__inner {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 12px 34px;
  font-family: var(--sans); font-size: 14px;
}
.trust-item { display: inline-flex; align-items: center; gap: 9px; color: var(--ink-soft); }
.trust-item strong { color: var(--ink); font-weight: 600; }
.trust-item svg { width: 17px; height: 17px; color: var(--gold-deep); flex: none; }
.trust-badge {
  font-weight: 700; font-size: 13px; letter-spacing: 0.02em;
  border: 1.5px solid var(--ink);
  border-radius: var(--radius-sm);
  padding: 3px 9px;
}

/* --- rug divider ------------------------------------------------------------
   Watercolor-blob motif of the signature rug, 10–15% opacity, CSS/SVG only. */
/* --- deco rule (section break) ------------------------------------------------
   Brand-blue hairline in from both edges, broken by the Royalty Casino lockup,
   so the rules read as a fade of the same blue the mark is painted in. The
   trust bar, header and mobile nav keep --hairline-gold; only this rule moved.
   Both
   halves are pure horizontal gradients, so they are unaffected by however wide
   the viewport is; the mark is a fixed size and never scales. That is the whole
   reason this is markup instead of one full-bleed stretched SVG — the original
   divider stretched its artwork to the viewport, which only amorphous shapes
   can survive.
   The rules are capped so the mark still reads as the centre of something on
   an ultra-wide screen rather than as a hairline crossing the whole page. */
.deco-rule {
  display: flex; align-items: center; justify-content: center;
  gap: 40px; height: 168px; padding: 0 24px;
}
.deco-rule__line {
  flex: 1 1 auto; height: 1px; max-width: 420px;
  background: linear-gradient(90deg,
    rgba(46, 60, 144, 0) 0%, rgba(46, 60, 144, 0.55) 60%, rgba(46, 60, 144, 0.75) 100%);
}
.deco-rule__line--r { transform: scaleX(-1); }
/* The centre mark is the brand lockup painted as a MASK rather than placed as
   an image: the PNG carries artwork in its alpha channel only, and the visible
   colour comes from `currentColor`. One asset therefore works on either ground
   — brand blue on the marble sections, white if it ever sits on blue — and a
   palette change never means re-exporting the artwork. Aspect is 1.1579, so
   the width is pinned to the height to keep the crown from squashing.
   Swap the two url()s for royalty-divider-crown.png (aspect 1.4638) to run the
   crown on its own without the wordmark. */
.deco-rule__mark {
  flex: 0 0 auto; display: block;
  width: 111px; height: 96px;
  color: var(--neon-violet);
}
/* Guarded: without mask support `background: currentColor` alone would paint a
   solid blue rectangle, which is a far worse failure than no mark at all. */
@supports (mask-image: url("#m")) or (-webkit-mask-image: url("#m")) {
  .deco-rule__mark {
    background: currentColor;
    -webkit-mask: url("img/brand/royalty-divider.png") center / contain no-repeat;
    mask: url("img/brand/royalty-divider.png") center / contain no-repeat;
  }
}

/* --- games bento ------------------------------------------------------------ */
.bento {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 300px;
  gap: 18px;
}
.game-card {
  position: relative; overflow: hidden;
  border-radius: var(--radius);
  border: var(--hairline-light);
  color: var(--ink-inverse);
  text-decoration: none;
  background: var(--bg-dark);
}
.game-card--tall { grid-row: span 2; }
.game-card--wide { grid-column: span 2; }
/* The feature tile is the bento's closing band: full container width, and two
   rows rather than one so the box lands at ~1152x618 (1.86:1) instead of
   ~1152x300 (3.84:1). It carried a 4:3 room shot until 2026-08-22 and now
   carries the 16:9 poker macro, which is why the crop bias went back to centre
   — at 1.78:1 against a 1.86:1 box there is only a few per cent to give up, and
   the subject is dead centre. cover is kept rather than contain: it fills the
   tile and never distorts, it only decides how much is cropped. */
.game-card--feature { grid-row: span 2; }
.game-card--feature .game-card__media img { object-position: center 50%; }
.game-card__media { position: absolute; inset: 0; }
.game-card__media img, .game-card__media video {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 0.5s ease;
}
.game-card:hover .game-card__media img,
.game-card:hover .game-card__media video { transform: scale(1.035); }
.game-card__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(185deg, rgba(15, 22, 50, 0) 32%, rgba(15, 22, 50, 0.55) 68%, rgba(15, 22, 50, 0.92) 100%);
}
.game-card__body { position: absolute; inset: auto 0 0 0; padding: 24px 26px; }
.game-card__body h3 {
  display: inline-block;
  font-size: 24px; margin-bottom: 6px;
  border-bottom: 2px solid transparent;
  transition: border-color 0.25s ease;
  padding-bottom: 3px;
}
.game-card:hover .game-card__body h3 { border-bottom-color: var(--gold); }
.game-card__body p { font-size: 15px; color: var(--ink-inverse-dim); margin: 0; max-width: 46ch; }
/* Outbound link inside a tile. The tiles are <article>, not <a>, so a real link
   here is valid markup rather than a nested-anchor problem. It sits on the
   card's dark scrim, so it takes gold like the other accents on that ground. */
.game-card__link {
  display: inline-flex; align-items: center; gap: 2px;
  margin: 12px 0 0;
  font-family: var(--sans); font-weight: 600; font-size: 13.5px;
  color: var(--gold);
  text-decoration: none;
  border-bottom: 1px solid rgba(201, 162, 39, 0.45);
  padding-bottom: 2px;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.game-card__link:hover { color: #DDB53A; border-bottom-color: #DDB53A; }
.game-card__link:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.game-card__tag {
  position: absolute; top: 18px; left: 20px;
  font-family: var(--script); font-style: italic; font-weight: 500;
  font-size: 19px; color: var(--gold);
  text-shadow: 0 1px 14px rgba(15, 22, 50, 0.8);
}

/* --- the floor (editorial) --------------------------------------------------- */
.floor__grid {
  display: grid; grid-template-columns: minmax(320px, 5fr) 7fr;
  gap: 56px; align-items: center;
}
/* The wide floor shot and the poker clip, side by side. The split is a ratio,
   not a breakpoint: the shot keeps its native 3:2 and the reel column is a
   fixed fraction of it, so the reel resolves to ~9:16 at every width and never
   needs a media query to stay portrait. */
.floor__media {
  display: grid; grid-template-columns: minmax(0, 8fr) minmax(0, 3fr);
  gap: 14px; align-items: stretch;
}
.floor__media figure {
  margin: 0; position: relative; background: var(--bg-dark);
  border-radius: var(--radius); overflow: hidden; border: var(--hairline-light);
}
/* The shot alone sets the row height, and it sets it from its own width via
   aspect-ratio — so the pair is laid out correctly before either file has
   loaded, and neither one can resize the other on arrival.
   Getting this backwards is a real bug and it hides until the images land: with
   the reel's poster in normal flow, *it* became the tallest item, the row grew
   to the poster's natural height, and the shot's aspect-ratio then resolved
   against that stretched height instead of its track width — computing ~60px
   wider than its own column and sliding under the reel. Taking the reel's
   contents out of flow removes it from the row-height calculation entirely. */
.floor__shot { aspect-ratio: 680 / 453; }   /* the source's own ratio — no crop */
.floor__reel > img, .floor__reel > video { position: absolute; inset: 0; }
.floor__media img, .floor__media video {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* The busy-floor plate, full container width between the editorial pair above
   and the detail strip below. It takes the same figure treatment as both of its
   neighbours so the three read as one section rather than three inserts.
   16/9 rather than the file's own 4:3: at ~1152px the native ratio is an 864px
   slab that pushes the strip off screen entirely, and the crop it costs is the
   ceiling, which is the least interesting band in the frame. Hence the bias
   below centre — it holds the chandelier and every table. */
.floor__plate {
  margin: 56px 0 0; position: relative; aspect-ratio: 16 / 9;
  border-radius: var(--radius); border: var(--hairline-light);
  overflow: hidden; background: var(--bg-dark);
}
.floor__plate img {
  width: 100%; height: 100%; object-fit: cover;
  object-position: center 58%; display: block;
}
.floor__features { display: flex; flex-wrap: wrap; gap: 10px 22px; margin-top: 26px; }
.floor__feature {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--sans); font-size: 14px; font-weight: 500; color: var(--ink-soft);
}
.floor__feature svg { width: 17px; height: 17px; color: var(--neon-violet); flex: none; }
.gallery-strip {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 14px; margin-top: 56px;
}
.gallery-strip figure {
  margin: 0; border-radius: var(--radius); overflow: hidden;
  border: var(--hairline-light); aspect-ratio: 3 / 4; background: var(--bg-dark);
  position: relative;
}
.gallery-strip figure:nth-child(2n) { transform: translateY(20px); }
.gallery-strip img,
.gallery-strip video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The clip sits on top of its own poster: the still is the fallback for
   reduced motion and for a refused autoplay, so it must be underneath rather
   than replaced. */
.floor__reel video { z-index: 1; }
@media (prefers-reduced-motion: reduce) {
  .floor__reel video { display: none; }
}

/* --- Bar ---------------------------------------------------------------
   The ONLY section that uses magenta — reads as a distinct sub-brand. */
.bar { background: var(--bg-dark); color: var(--ink-inverse); overflow: hidden; }
.bar .eyebrow { color: var(--neon-magenta); }
.bar__grid {
  display: grid; grid-template-columns: 7fr 5fr;
  gap: 56px; align-items: center;
}
/* Box is reserved at the clip's 16:9 before any media loads. The poster
   (3:2) and the clip (16:9) disagree, and Chrome sizes a <video> from its
   poster until playback starts — without this the section collapsed 66px
   the moment the clip autoplayed on scroll-in. */
.bar__media {
  position: relative; border-radius: var(--radius); overflow: hidden;
  aspect-ratio: 16 / 9; background: var(--bg-dark);
}
.bar__media video, .bar__media img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.bar__media img { display: none; } /* fallback — shown only when the clip is hidden */
.bar__media::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  border: 1px solid rgba(248, 245, 238, 0.35); border-radius: var(--radius);
}
.bar__script {
  font-family: var(--display); font-style: normal; font-weight: 400;
  font-size: clamp(36px, 4.9vw, 56px);
  color: var(--neon-magenta);
  text-shadow: 0 0 26px rgba(143, 160, 232, 0.45);
  margin-bottom: 12px; line-height: 1.1;
}
.bar__text p { color: var(--ink-inverse-dim); }
.bar__features { display: grid; gap: 12px; margin: 26px 0 0; padding: 0; list-style: none; }
.bar__features li { display: flex; align-items: center; gap: 12px; font-size: 15px; }
.bar__features svg { width: 18px; height: 18px; color: var(--neon-magenta); flex: none; }
.bar__shelf { margin: 18px 0 0; border-radius: var(--radius); overflow: hidden; opacity: 0.92; }

/* --- visit (hours & location) ------------------------------------------------ */
.visit__card {
  background: var(--bg-dark); color: var(--ink-inverse);
  border-radius: var(--radius);
  padding: 56px;
  display: grid; grid-template-columns: 5fr 4fr 5fr; gap: 44px;
  align-items: start;
}
.visit__card .eyebrow { color: var(--ink-inverse-dim); }
.visit__card h2 { color: var(--ink-inverse); font-size: clamp(26px, 3vw, 36px); }
.info-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 20px; }
.info-row { display: flex; gap: 14px; }
.info-row__icon { color: var(--gold); flex: none; margin-top: 3px; }
.info-row__icon svg { width: 19px; height: 19px; }
.info-row__label {
  display: block; font-family: var(--sans); font-size: 12px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-inverse-dim);
  margin-bottom: 3px;
}
.info-row__value { font-size: 15px; }
.info-row__value a { color: var(--ink-inverse); text-decoration-color: var(--gold); }
.visit__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }
.visit__map { border-radius: var(--radius); overflow: hidden; min-height: 300px; }
.visit__map iframe { width: 100%; height: 100%; min-height: 300px; border: 0; display: block; }
/* Same reservation as .bar__media — poster and clip are both 3:4 and the box
   is fixed before either loads, so scroll-in playback cannot reflow the card. */
.visit__photo {
  position: relative; border-radius: var(--radius); overflow: hidden;
  aspect-ratio: 3 / 4; background: var(--bg-dark);
}
.visit__photo video, .visit__photo img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.visit__photo img { display: none; } /* fallback — shown only when the clip is hidden */
/* The visit card carries a still rather than a clip: there is no <video> in
   this box at all, so the image is the content and opts out of the fallback
   rule above. Nothing to autoplay, nothing to hide under reduced motion. */
.visit__photo--still img { display: block; }

/* --- responsible gambling ------------------------------------------------------ */
.responsible { background: rgba(62, 79, 174, 0.07); }
/* 7fr/5fr since 2026-08-22. It was 5fr/7fr when the right column held a
   five-item tips list; that list is gone and three helpline links do not need
   the wider track, while the lead and the venue note do. */
.responsible__grid { display: grid; grid-template-columns: 7fr 5fr; gap: 56px; }
.responsible h2 { color: var(--neon-violet); }
.rg-note {
  border-left: 3px solid var(--neon-violet);
  padding: 14px 20px; font-size: 15px; color: var(--ink-soft);
  background: rgba(243, 239, 230, 0.7); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
/* The global 1.15 on headings is set for display lines that do not wrap. This
   one always does — "If you or someone you know needs help" and its Russian
   equivalent are both too long for the column at any sane split — and at 1.15
   the descenders of line one sit on the ascenders of line two. */
.responsible__col h3 { line-height: 1.28; }
.rg-help { display: flex; flex-direction: column; gap: 8px; font-size: 15px; }
.rg-help a { color: var(--neon-violet); font-weight: 500; }

/* --- footer -------------------------------------------------------------------- */
.rug-strip {
  height: 5px;
  background: linear-gradient(90deg,
    var(--rug-blue) 0 22%, var(--rug-coral) 22% 40%, var(--rug-ochre) 40% 58%,
    var(--rug-blue) 58% 76%, var(--rug-coral) 76% 100%);
}
.site-footer { background: var(--bg-dark); color: var(--ink-inverse-dim); padding: 72px 0 36px; font-size: 15px; }
.footer-top {
  display: grid; grid-template-columns: 5fr 3fr 4fr; gap: 44px;
  padding-bottom: 44px; border-bottom: var(--hairline-inverse);
}
/* Footer lockup, one step down from the header's. The ground is dark here and
   never changes, so the mark stays white and the wordmark keeps the light blue
   it has on the transparent header — no scrolled-state swap needed. */
.site-footer .brand__mark { width: calc(23px * 1.3547); height: 23px; }
.site-footer .brand__script { font-size: 19px; }
.footer-about p { margin-top: 16px; max-width: 44ch; }
/* Column labels are micro-caps at 12px — the same role as .eyebrow, and the
   same reason to stay on Poppins: Collingar's hairlines thin out to nothing at
   that size on a dark ground. They are h4 for outline order, not for display. */
.footer-col h4 {
  font-family: var(--sans); font-weight: 600;
  font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--ink-inverse); margin-bottom: 16px;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.footer-col a { text-decoration: none; }
.footer-col a:hover { color: var(--gold); }
.footer-social { display: flex; gap: 14px; margin-top: 18px; }
.footer-social a { color: var(--ink-inverse-dim); }
.footer-social a:hover { color: var(--gold); }
.footer-social svg { width: 20px; height: 20px; }
.footer-rg {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin: 32px 0 14px; color: var(--ink-inverse);
}
.footer-rg .trust-badge { border-color: var(--ink-inverse); }
/* The MF warning is statutory and the law asks for "viditelne a zretelne", so
   it does NOT get the 0.75 opacity the license line carries. */
.footer-mf { flex: 1 1 100%; font-size: 13px; line-height: 1.55; }
.footer-mf strong { font-weight: 600; }
.mf-gloss { display: block; opacity: 0.72; }
.license-line { font-size: 13px; opacity: 0.75; max-width: 74ch; }
.footer-bottom {
  display: flex; flex-wrap: wrap; gap: 12px 28px; justify-content: space-between;
  margin-top: 28px; padding-top: 24px; border-top: var(--hairline-inverse);
  font-size: 13px;
}
.footer-legal { display: flex; gap: 22px; }
.footer-legal a { text-decoration: none; }
.footer-legal a:hover { color: var(--gold); }
.footer-meta { margin: 0; }

/* --- age advisory ---------------------------------------------------------------- */
/* A bottom-anchored notice, not a cover. It has no backdrop to blur, which is
   the point: the old full-viewport blur rasterised the whole viewport on the
   frame that had to present. Fixed positioning also means it shifts nothing
   when it arrives, so the reveal costs no CLS. */
.age-gate {
  position: fixed; inset: auto 0 0 0; z-index: 300;
  display: flex; justify-content: center;
  padding: 0 16px calc(16px + env(safe-area-inset-bottom)) 16px;
  pointer-events: none;
}
.age-gate[hidden] { display: none; }
.age-gate__card {
  background: var(--bg-base); color: var(--ink);
  border-radius: var(--radius); border-top: 4px solid var(--gold);
  box-shadow: 0 18px 48px rgba(8, 10, 16, 0.42);
  max-width: 480px; padding: 24px 28px; text-align: center;
  pointer-events: auto;
  /* Never animate from exactly 0 -- an element at opacity 0 is not an LCP
     candidate, which is the classic cause of a Lighthouse NO_LCP result. */
  opacity: 0.01; transform: translateY(16px);
  transition: opacity 0.32s ease, transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
.age-gate.is-in .age-gate__card { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .age-gate__card { transition: none; opacity: 1; transform: none; }
}
.age-gate__card h2 { font-size: 20px; margin: 0 0 8px; }
.age-gate__sub { color: var(--ink-soft); font-size: 15px; margin: 0; }
.age-gate__mf { margin: 12px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--ink); }
.age-gate__mf .mf-gloss { display: block; color: var(--ink-soft); }
.age-gate__actions { display: flex; gap: 12px; justify-content: center; margin-top: 20px; flex-wrap: wrap; }
.age-gate__no {
  background: none; border: 0; color: var(--ink-soft);
  font-family: var(--sans); font-size: 14px; cursor: pointer; text-decoration: underline;
}

/* On a phone the notice should read as a strip, not a second page. */
@media (max-width: 560px) {
  .age-gate { padding-left: 10px; padding-right: 10px; }
  .age-gate__card { padding: 18px 20px; }
  .age-gate__card h2 { font-size: 17px; }
  .age-gate__sub { font-size: 14px; }
  .age-gate__mf { margin-top: 10px; font-size: 11.5px; }
  .age-gate__actions { margin-top: 14px; gap: 8px; }
  .age-gate--blocked .age-gate__card { padding: 32px 24px; }
}

/* The one lock that survives: the visitor said they are under 18. This is the
   old full-screen treatment, kept for the only case that earns it. */
.age-gate--blocked {
  inset: 0; align-items: center; padding: 24px;
  background: rgba(15, 22, 50, 0.94);
  pointer-events: auto;
}
.age-gate--blocked .age-gate__card { padding: 44px 40px; opacity: 1; transform: none; }
.age-gate--blocked .age-gate__card:focus { outline: none; }
.age-gate--blocked .age-gate__actions { display: none; }

/* --- legal pages ------------------------------------------------------------------ */
.legal-body { color: var(--ink-soft); font-size: 16px; line-height: 1.7; }
.legal-body h2 { font-size: 22px; margin-top: 1.6em; color: var(--ink); }
.legal-body a { color: var(--neon-violet); }

/* --- reveal ------------------------------------------------------------------------ */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal[data-d="2"] { transition-delay: 0.1s; }
.reveal[data-d="3"] { transition-delay: 0.2s; }
.reveal.in { opacity: 1; transform: none; }

/* --- reduced motion ------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .game-card__media img, .game-card__media video { transition: none; }
  video[data-motion] { display: none; }  /* posters take over */
  .bar__media img { display: block; }
  .visit__photo img { display: block; }
  /* hero 3D scroll off — flat, exactly the pre-parallax composition */
  .hero { perspective: none; }
  .hero__media, .hero__inner,
  .hero__kicker, .hero__lockup, .hero__tagline, .hero__cta { transform: none; }
  .hero__inner { opacity: 1; }
  .hero__scrim::after { opacity: 0; }
}

/* --- breakpoints: 1440 is the container cap; 1024 / 768 / 375 below ------------------ */
@media (max-width: 1024px) {
  .section { padding: 72px 0; }
  .floor__grid, .bar__grid, .responsible__grid { grid-template-columns: 1fr; gap: 36px; }
  .visit__card { grid-template-columns: 1fr 1fr; padding: 40px; }
  .visit__photo { display: none; }
  .gallery-strip { grid-template-columns: repeat(4, 1fr); }
}

@media (max-width: 768px) {
  body { font-size: 16px; }
  .nav {
    display: none;
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; gap: 0;
    background: rgba(243, 239, 230, 0.98); color: var(--ink);
    border-bottom: var(--hairline-gold);
    padding: 12px 24px 20px;
  }
  .nav.open { display: flex; }
  .nav a { padding: 13px 2px; border-bottom: var(--hairline-light); }
  .nav-toggle { display: block; }
  .header-call span { display: none; }
  .bento { grid-template-columns: 1fr; grid-auto-rows: 260px; }
  /* The single explicit column above is only half the story: a tile still
     asking for `grid-column: span 2` reaches past it and makes the grid open
     an *implicit* second column, sized by grid-auto-columns (auto). With no
     content to widen it that column resolved to ~2px, and the two tiles that
     auto-placed into it — Blackjack and Poker — became invisible slivers.
     Every column span has to come back to 1 wherever the column count does. */
  .game-card--wide { grid-column: span 1; }
  .game-card--tall { grid-row: span 1; }
  .game-card--tall.game-card--video { grid-row: span 2; } /* keep video tiles roomy */
  /* One column here, so a single 260px row is already ~4:3 and the room shot
     is landscape, which is the shape the band's photo wants. Keeping the
     desktop two-row span would make the box portrait and crop the width in
     half instead. */
  .game-card--feature { grid-row: span 1; }
  .visit__card { grid-template-columns: 1fr; padding: 32px 24px; }
  /* One narrow column here, so the plate goes back to the file's own 4:3 and
     nothing is cropped at all. margin matches the strip's below it. */
  .floor__plate { aspect-ratio: 680 / 510; margin-top: 36px; }
  .floor__plate img { object-position: center 50%; }
  .gallery-strip { grid-template-columns: repeat(2, 1fr); }
  .gallery-strip figure:nth-child(2n) { transform: none; }
  /* Two columns and an odd number of cells leaves one stranded in its own row
     next to a hole. Both strips are even today, so this is a guard rather than
     an active rule: any strip that ends up odd puts its last cell full width
     and lands it as a banner instead of an orphan. */
  .gallery-strip figure:last-child:nth-child(odd) {
    grid-column: span 2; aspect-ratio: 16 / 10;
  }
  .hero__inner { padding-bottom: 72px; }
  .section { padding: 60px 0; }
  .deco-rule { height: 122px; gap: 24px; }
  .deco-rule__mark { width: 84px; height: 72px; }
  .footer-top { grid-template-columns: 1fr; gap: 32px; }
}

@media (max-width: 480px) {
  .container { padding: 0 18px; }
  .header-inner { gap: 14px; }
  .header-call { display: none; }
  .trust-bar__inner { gap: 10px 20px; font-size: 13px; }
  .hero__lockup .block { letter-spacing: 0.14em; text-indent: 0.14em; }
  .visit__actions .btn { width: 100%; justify-content: center; }
}

/* ==========================================================================
   GAMES SHOWCASE — full-bleed equipment carousel between trust bar and bento
   Three product renders occupy centre / left / right slots. Roles live in
   data-role on each figure and every dimension of the move is expressed
   here, so the JS only ever relabels roles — the feel is tuned in one place.
   Section colour rides on --sc-bg, which the controller rewrites; because
   background-color resolves through that custom property it still animates.
   ========================================================================== */
.showcase {
  position: relative;
  overflow: hidden;
  height: 100svh;
  min-height: 620px;
  isolation: isolate;
  color: var(--ink-inverse);
  background-color: var(--sc-bg, var(--neon-violet));
  transition: background-color 650ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* film grain, matching the density used on the hero scrim */
.showcase::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  opacity: 0.4;
  background-repeat: repeat;
  background-size: 200px 200px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E");
}

/* Oversized ghost word the renders overlap. Poppins 700 rather than a fourth
   webfont family loaded for a single word. */
.showcase__ghost {
  position: absolute;
  inset-inline: 0;
  top: 16%;
  z-index: 2;
  margin: 0;
  text-align: center;
  white-space: nowrap;
  user-select: none;
  pointer-events: none;
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(72px, 20vw, 320px);
  line-height: 1;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  color: var(--ink-inverse);
}

.showcase__stage { position: absolute; inset: 0; z-index: 3; }

/* The centre slot is height:92% scaled 1.68 about its own centre, so the
   rendered box runs from -0.313 to 1.233 of the section height. The renders
   carry matching transparent padding — see hero-royale/README.md — which is
   what keeps the object inside the visible band at that scale. */
.showcase__item {
  position: absolute;
  margin: 0;
  aspect-ratio: 0.6 / 1;
  will-change: transform, filter, opacity;
  transition:
    transform 650ms cubic-bezier(0.4, 0, 0.2, 1),
    filter    650ms cubic-bezier(0.4, 0, 0.2, 1),
    opacity   650ms cubic-bezier(0.4, 0, 0.2, 1),
    left      650ms cubic-bezier(0.4, 0, 0.2, 1),
    bottom    650ms cubic-bezier(0.4, 0, 0.2, 1),
    height    650ms cubic-bezier(0.4, 0, 0.2, 1);
}
.showcase__item img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: bottom center;
}

.showcase__item[data-role="center"] {
  left: 50%; bottom: 0; height: 92%;
  transform: translateX(-50%) scale(1.68);
  filter: none; opacity: 1; z-index: 20;
}
.showcase__item[data-role="left"],
.showcase__item[data-role="right"] {
  bottom: 12%; height: 28%;
  transform: translateX(-50%) scale(1);
  filter: blur(2px); opacity: 0.85; z-index: 10;
}
.showcase__item[data-role="left"]  { left: 30%; }
.showcase__item[data-role="right"] { left: 70%; }
.showcase__item[data-role="back"] {
  left: 50%; bottom: 12%; height: 22%;
  transform: translateX(-50%) scale(1);
  filter: blur(4px); opacity: 1; z-index: 5;
}

.showcase__panel {
  position: absolute;
  left: clamp(18px, 5vw, 96px);
  bottom: clamp(24px, 6vh, 80px);
  z-index: 60;
  max-width: 340px;
}
.showcase__slide-copy[hidden] { display: none; }
.showcase__copy h2 {
  margin: 0 0 0.5em;
  font-size: clamp(19px, 2.2vw, 26px);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-inverse);
}
.showcase__copy p {
  margin: 0 0 1.3em;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink-inverse-dim);
}

.showcase__nav { display: flex; gap: 12px; }
.showcase__btn {
  width: 60px; height: 60px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border: 2px solid rgba(248, 245, 238, 0.9);
  border-radius: 50%;
  background: transparent;
  color: var(--ink-inverse);
  cursor: pointer;
  transition: transform 150ms, background-color 150ms;
}
.showcase__btn svg { width: 24px; height: 24px; }
.showcase__btn:hover { transform: scale(1.08); background-color: rgba(248, 245, 238, 0.14); }
.showcase__btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

.showcase__cta {
  position: absolute;
  right: clamp(18px, 4vw, 64px);
  bottom: clamp(24px, 6vh, 80px);
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(17px, 2.6vw, 40px);
  line-height: 1;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-inverse);
  opacity: 0.95;
  transition: opacity 200ms;
}
.showcase__cta:hover { opacity: 1; }
.showcase__cta svg { width: 0.7em; height: 0.7em; }
.showcase__cta:focus-visible { outline: 2px solid var(--gold); outline-offset: 4px; }

@media (max-width: 900px) {
  .showcase__cta { font-size: clamp(16px, 4vw, 26px); }
}

@media (max-width: 639px) {
  .showcase { min-height: 560px; }
  .showcase__ghost { top: 14%; font-size: clamp(60px, 24vw, 150px); }
  .showcase__item[data-role="center"] {
    height: 60%; bottom: 22%;
    transform: translateX(-50%) scale(1.25);
  }
  .showcase__item[data-role="left"],
  .showcase__item[data-role="right"] { height: 16%; bottom: 32%; }
  .showcase__item[data-role="left"]  { left: 20%; }
  .showcase__item[data-role="right"] { left: 80%; }
  .showcase__copy p { display: none; }
  .showcase__btn { width: 48px; height: 48px; }
  .showcase__btn svg { width: 20px; height: 20px; }
}

/* Reduced motion: the carousel still works, it just cuts between states. */
@media (prefers-reduced-motion: reduce) {
  .showcase,
  .showcase__item,
  .showcase__btn,
  .showcase__cta { transition: none; }
}

/* ==========================================================================
   SPOTLIGHT HERO — two plates of one table, revealed by a pointer-following
   light. Port of ../hero-spotlight.

   The mask is declared here as a radial-gradient driven by --sx/--sy, so the
   controller's only per-frame job is writing two custom properties. The
   earlier canvas approach (draw gradient, toDataURL, hand the PNG to
   mask-image) cost ~9.2ms of main thread and a ~290KB data URI per frame at
   desktop sizes — for the identical result.
   ========================================================================== */
.hero-spot {
  position: relative;
  height: 100svh;
  min-height: 600px;
  overflow: hidden;
  isolation: isolate;
  background: #05070A;
  color: var(--ink-inverse);
}

.hero-spot__base,
.hero-spot__reveal {
  position: absolute;
  inset: 0;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  pointer-events: none;
}
/* Hand-off to the cardfall below. The photo used to stop dead against the
   next section's violet, which read as a hard horizontal seam; this fades the
   plate down to the hero's own base colour, and the cardfall starts from that
   same value, so the two sections meet on one colour instead of an edge.
   z-index 3 keeps it above the photo layers (1, 2) and below the copy (5). */
.hero-spot::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 38%;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(5, 7, 10, 0) 0%,
    rgba(5, 7, 10, 0.45) 42%,
    rgba(5, 7, 10, 0.85) 74%,
    #05070A 100%
  );
}

.hero-spot__base { z-index: 1; background-image: url("img/hero/table-dark.webp"); }
.hero-spot__reveal {
  z-index: 2;
  background-image: url("img/hero/table-lit.webp");
  --sr: 260px;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-image: radial-gradient(circle var(--sr) at var(--sx, -999px) var(--sy, -999px),
    rgba(0,0,0,1) 0%, rgba(0,0,0,1) 40%, rgba(0,0,0,.75) 60%,
    rgba(0,0,0,.4) 75%, rgba(0,0,0,.12) 88%, rgba(0,0,0,0) 100%);
  mask-image: radial-gradient(circle var(--sr) at var(--sx, -999px) var(--sy, -999px),
    rgba(0,0,0,1) 0%, rgba(0,0,0,1) 40%, rgba(0,0,0,.75) 60%,
    rgba(0,0,0,.4) 75%, rgba(0,0,0,.12) 88%, rgba(0,0,0,0) 100%);
}

.hero-spot__head {
  position: absolute;
  top: 14%;
  left: 0;
  right: 0;
  z-index: 5;
  padding: 0 20px;
  text-align: center;
  pointer-events: none;
}
.hero-spot__kicker {
  margin: 0 0 14px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(248, 245, 238, 0.75);
}
.hero-spot__lockup { margin: 0; line-height: 0.95; }
.hero-spot__lockup .script,
.hero-spot__lockup .block { display: block; }
/* Both lines now sit in one voice. The old lockup got its contrast from two
   families (Playfair italic over Poppins); Collingar carries the whole masthead
   instead, and the tracking is reset to suit it — -0.05em was a fix for
   Poppins' loose fitting and would lock the serifs together here. */
.hero-spot__lockup .script,
.hero-spot__lockup .block {
  font-family: var(--display);
  font-style: normal;
  font-weight: 400;
  font-size: clamp(44px, 8.5vw, 116px);
  letter-spacing: -0.018em;
}
.hero-spot__lockup .block { margin-top: -0.04em; }

.hero-spot__left {
  position: absolute;
  bottom: 56px;
  left: clamp(20px, 4vw, 56px);
  z-index: 5;
  max-width: 280px;
}
.hero-spot__left p {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.6;
  color: rgba(248, 245, 238, 0.8);
}

.hero-spot__right {
  position: absolute;
  bottom: 40px;
  left: 20px;
  right: 20px;
  z-index: 5;
}
.hero-spot__right p {
  margin: 0 0 18px;
  font-size: 0.9rem;
  line-height: 1.6;
  color: rgba(248, 245, 238, 0.8);
}
.hero-spot__cta { display: flex; flex-wrap: wrap; gap: 12px; }

@media (min-width: 641px) {
  .hero-spot__right {
    left: auto;
    right: clamp(20px, 4vw, 56px);
    bottom: 84px;
    max-width: 300px;
  }
}
@media (max-width: 640px) {
  .hero-spot__left { display: none; }
  .hero-spot__reveal { --sr: 170px; }
  .hero-spot__head { top: 12%; }
  .hero-spot__cta .btn { flex: 1 1 auto; justify-content: center; }
}

/* Entry choreography — mirrors the prototype's timing. */
@keyframes heroReveal {
  0% { opacity: 0; transform: translateY(28px); filter: blur(12px); }
  100% { opacity: 1; transform: translateY(0); filter: blur(0); }
}
@keyframes heroFadeUp {
  0% { opacity: 0; transform: translateY(20px); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes heroZoom {
  0% { transform: scale(1.12); }
  100% { transform: scale(1); }
}
.hero-anim {
  opacity: 0;
  animation-fill-mode: forwards;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.hero-reveal { animation-name: heroReveal; animation-duration: 1.1s; }
.hero-fade { animation-name: heroFadeUp; animation-duration: 1s; }
.hero-spot__zoom { animation: heroZoom 1.8s cubic-bezier(0.16, 1, 0.3, 1) forwards; }

@media (prefers-reduced-motion: reduce) {
  .hero-anim, .hero-spot__zoom { animation: none; opacity: 1; }
}

/* ==========================================================================
   ROULETTE SIMULATOR — dark felt panel on the light section ground
   Free demo: no stakes, no balance, nothing to win. The wheel is drawn as
   SVG sectors at build time from one pocket-order list, so the graphic and
   the win check cannot drift apart.
   ========================================================================== */
.roul {
  display: grid;
  grid-template-columns: minmax(280px, 440px) 1fr;
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
  padding: clamp(28px, 4vw, 56px);
  border-radius: 18px;
  background: radial-gradient(120% 120% at 20% 0%, #1B2449 0%, var(--bg-dark) 60%);
  color: var(--ink-inverse);
  border: var(--hairline-inverse);
}

.roul__stage { position: relative; width: 100%; aspect-ratio: 1; margin: 0 auto; }
.roul__svg { width: 100%; height: 100%; display: block; }
.roul__flat { position: absolute; inset: 0; }

/* The WebGL bowl mounts here and, once it reports a live context, the script
   sets .is-3d on .roul — which retires the flat wheel and shows the drag hint.
   Until then (no WebGL, reduced motion, script never runs) the SVG plays. */
.roul__gl { position: absolute; inset: 0; display: none; touch-action: pan-y; }
.roul.is-3d .roul__flat { display: none; }
.roul.is-3d .roul__gl { display: block; }

.roul__wheelcol { min-width: 0; }
.roul__drag {
  margin: 14px 0 0;
  text-align: center;
  font-size: 0.8rem;
  color: var(--ink-inverse-dim);
}

/* Rotating the wheel by -index*(360/37) brings that pocket under the pointer. */
.roul__wheel {
  width: 100%;
  height: 100%;
  transform: rotate(0deg);
  will-change: transform;
  transition: transform 5s cubic-bezier(0.12, 0.68, 0.1, 1);
}

/* The ball rides its own layer, orbiting the other way and coming to rest at
   the top — which is where the winning pocket has just arrived. */
.roul__orbit {
  position: absolute;
  inset: 0;
  transform: rotate(0deg);
  will-change: transform;
  transition: transform 5s cubic-bezier(0.15, 0.72, 0.12, 1);
}
.roul__ball {
  position: absolute;
  top: 5.5%;
  left: 50%;
  width: 4.4%;
  aspect-ratio: 1;
  margin-left: -2.2%;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, #fff 0%, #E6E1D5 45%, #A8A296 100%);
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.55);
}

.roul__pointer {
  position: absolute;
  top: -2px;
  left: 50%;
  z-index: 3;
  width: 0;
  height: 0;
  margin-left: -11px;
  border-left: 11px solid transparent;
  border-right: 11px solid transparent;
  border-top: 20px solid var(--gold);
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5));
}

.roul__panel { min-width: 0; }
.roul__lead {
  margin: 0 0 22px;
  color: var(--ink-inverse-dim);
  font-size: 1rem;
  line-height: 1.65;
}

.roul__bets { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 22px; }
.roul__bet {
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid rgba(248, 245, 238, 0.28);
  background: rgba(248, 245, 238, 0.06);
  color: var(--ink-inverse);
  font-family: var(--sans);
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 150ms, border-color 150ms, transform 150ms,
              box-shadow 150ms;
}
.roul__bet:hover { transform: translateY(-1px); }
.roul__bet:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

/* --- bet chips take the colour of the bet -----------------------------------
   Each pill is painted as the thing it backs: the red pill is the wheel's red,
   the black pill its black, and the four even-money bets that have no colour on
   the layout (odd / even / 1–18 / 19–36) are white. The hexes come from the
   --roul-* tokens, which is the same value wheel_svg() paints the pockets with,
   so pill and pocket can never drift apart.

   Selection can no longer be "fill it gold" — every pill is already filled. It
   is a gold ring around the pill instead, which reads on all three grounds and
   leaves the bet's own colour intact. */
.roul__bet[data-bet="red"] {
  background: var(--roul-red);
  border-color: rgba(248, 245, 238, 0.3);
  color: #fff;
}
.roul__bet[data-bet="red"]:hover { background: #C0263C; }
.roul__bet[data-bet="black"] {
  background: var(--roul-black);
  border-color: rgba(248, 245, 238, 0.42);
  color: #fff;
}
.roul__bet[data-bet="black"]:hover { background: #241F2B; }
.roul__bet[data-bet="odd"],
.roul__bet[data-bet="even"],
.roul__bet[data-bet="low"],
.roul__bet[data-bet="high"] {
  background: var(--ink-inverse);
  border-color: rgba(23, 30, 54, 0.16);
  color: var(--ink);
  font-weight: 600;
}
.roul__bet[data-bet="odd"]:hover,
.roul__bet[data-bet="even"]:hover,
.roul__bet[data-bet="low"]:hover,
.roul__bet[data-bet="high"]:hover { background: #fff; }

.roul__bet[aria-pressed="true"] {
  box-shadow: 0 0 0 2px var(--gold), 0 4px 14px rgba(0, 0, 0, 0.35);
  transform: translateY(-1px);
}

.roul__spin { margin-bottom: 20px; }
/* aria-disabled, not disabled — see the note in site-neon.js. The button keeps
   focus and stays in the tab order while the ball is running. */
.roul__spin[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }

.roul__result {
  margin: 0 0 6px;
  min-height: 1.6em;
  font-family: var(--sans);
  font-size: 1.05rem;
  font-weight: 600;
}
.roul__result .num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2em;
  padding: 0.15em 0.45em;
  margin-right: 0.5em;
  border-radius: 6px;
  font-variant-numeric: tabular-nums;
}
.num--red { background: #A81E32; }
.num--black { background: #141118; border: 1px solid rgba(248, 245, 238, 0.25); }
.num--green { background: #1F7A4D; }

.roul__tally { margin: 0 0 18px; font-size: 0.85rem; color: var(--ink-inverse-dim); }

.roul__history { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 20px; }
.roul__history-label {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(248, 245, 238, 0.55);
}
.roul__history-list { display: flex; gap: 6px; flex-wrap: wrap; }
.roul__history-list .num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.9em;
  padding: 0.1em 0.35em;
  border-radius: 5px;
  font-size: 0.8rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.roul__note {
  margin: 0;
  padding-top: 16px;
  border-top: var(--hairline-inverse);
  font-size: 0.78rem;
  line-height: 1.5;
  color: rgba(248, 245, 238, 0.6);
}

@media (max-width: 860px) {
  .roul { grid-template-columns: 1fr; }
  .roul__stage { max-width: 380px; }
}
@media (max-width: 480px) {
  .roul__spin { width: 100%; justify-content: center; }
}

/* The wheel still resolves a number and reports it; it just stops turning. */
@media (prefers-reduced-motion: reduce) {
  .roul__wheel,
  .roul__orbit,
  .roul__bet { transition: none; }
}

/* ==========================================================================
   CARDFALL — scroll-scrubbed field of levitating cards
   A tall section with a sticky viewport-height stage. Scroll position inside
   the section drives the scene; see the cardfall block in site-neon.js.
   ========================================================================== */
.cardfall {
  position: relative;
  height: 240vh;                 /* the scrub distance — not visible height */
  background: var(--bg-dark);
}
/* The gradient lives on the sticky child, not the section. Two reasons: on a
   240vh box the bloom would stretch over the whole scroll and never be seen,
   and the sticky's top edge is exactly where the hero ends — so starting it
   on the hero's own #05070A is what makes the seam disappear. Once pinned it
   reads as a vignette, which suits the falling cards. */
.cardfall__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  background:
    radial-gradient(115% 80% at 50% 58%, #202A59 0%, rgba(32, 42, 89, 0) 72%),
    /* The hero's darks are cool — the blue rim light off the felt — so the
       first stops stay blue-black and only then warm into the house violet.
       Jumping straight to violet is what made the two sections read as two
       different palettes butted together. */
    linear-gradient(to bottom,
      #05070A 0%,
      #090B16 11%,
      #080B1D 24%,
      #0B112A 42%,
      var(--bg-dark) 66%,
      var(--bg-dark) 100%);
}
@supports (height: 100svh) {
  .cardfall__sticky { height: 100svh; }
}
.cardfall__gl { position: absolute; inset: 0; }
.cardfall__copy {
  position: relative;
  z-index: 1;
  text-align: center;
  padding: clamp(28px, 5vw, 56px) clamp(20px, 5vw, 48px);
  color: var(--ink-inverse);
  pointer-events: none;
}
/* Near-white card faces drift straight behind near-white display type. This
   scrim keeps the line readable whatever passes under it, without putting an
   opaque slab on the scene. */
.cardfall__copy::before {
  content: "";
  position: absolute;
  inset: -8% -6%;
  z-index: -1;
  background: radial-gradient(60% 60% at 50% 50%, rgba(8, 13, 30, 0.82) 0%, rgba(8, 13, 30, 0.55) 45%, transparent 78%);
}
/* The section's own ground is dark, so the eyebrow takes gold rather than the
   violet it wears on the light marble sections. */
.cardfall__copy .eyebrow { color: var(--scrub-accent); }
.cardfall__line {
  margin: 0;
  font-family: var(--display);
  font-style: normal;
  font-size: clamp(2.3rem, 7.3vw, 4.8rem);
  line-height: 1.04;
  letter-spacing: -0.01em;
  text-shadow: 0 6px 28px rgba(5, 8, 20, 0.75);
}
.cardfall__line--2 { color: var(--scrub-accent); }
.cardfall__sub {
  margin: 26px 0 0;
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-inverse-dim);
  text-shadow: 0 2px 12px rgba(5, 8, 20, 0.8);
}

/* The tall height is the DEFAULT, not something JS grows into. Gating it on a
   class added after a dynamic import meant the common path — module loads
   fine — was the path that reflowed: the section gained 1.4 screens under a
   visitor who was already inside it. It is only collapsed when we know at
   paint (media query) or synchronously at script time (.is-flat, set before
   the visitor can scroll this far) that there will be no cards at all. */
.cardfall.is-flat { height: 100vh; }
@supports (height: 100svh) {
  .cardfall.is-flat { height: 100svh; }
}
@media (prefers-reduced-motion: reduce) {
  .cardfall { height: 100vh; }
  .cardfall__sticky { position: relative; }
}

/* The instruction only makes sense when something is actually falling. */
.cardfall__sub { visibility: hidden; }
.cardfall.is-live .cardfall__sub { visibility: visible; }

/* ==========================================================================
   GALLERY3D — infinite field of photographs travelling toward the camera
   Same shape as the cardfall above: a tall section with a sticky viewport
   stage. Unlike the cardfall this one is not scrubbed — scroll feeds velocity
   into the field and it keeps drifting on its own. The extra height is dwell
   time, so the section cannot be crossed in a single flick.
   ========================================================================== */
/* The whole backdrop lives HERE, on the 180vh box, and not on the sticky child.
   That one placement is what makes the section blend at all:

   - A sticky child is pinned, so a gradient painted on it never moves relative
     to the viewport. The entire section would show one frozen wash, and its top
     and bottom edges would land on the marble of #roulette and #games as two
     hard lines. Painted here instead, the pinned 100vh window slides ACROSS a
     180vh gradient, so the sky changes on its own as the section is read —
     no JS, no scroll listener.
   - Both ends of the ramp are var(--bg-base), which is exactly what the
     neighbouring sections are sitting on, so the seams are marble on marble and
     there is nothing to see. Same trick the cardfall documents above: start on
     the neighbour's own colour and the join disappears.

   The renderer is created with alpha: true, so all of this shows through the
   canvas and the photographs fly over it. */
.gallery3d {
  position: relative;
  height: 180vh;                 /* dwell distance — not visible height */
  background:
    /* Grain, on top. A violet ramp this deep bands visibly on a wide-gamut
       display — stepping is the failure mode of big dark gradients, and a few
       percent of noise is what breaks the steps up. It also stops the sky
       reading as a flat fill. */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E"),
    /* Three cove glows, held low. These are lighting on a dark room, not
       colour blocking — push the alphas up and the section turns candy. */
    radial-gradient(64% 42% at 22% 30%, rgba(62, 79, 174, 0.30) 0%, rgba(62, 79, 174, 0) 100%),
    radial-gradient(58% 40% at 82% 66%, rgba(57, 69, 214, 0.24) 0%, rgba(57, 69, 214, 0) 100%),
    /* A single warm note low down, so the field is not uniformly cold. */
    radial-gradient(70% 26% at 50% 88%, rgba(201, 162, 39, 0.10) 0%, rgba(201, 162, 39, 0) 100%),
    /* Vignette — under the canvas, so it deepens the sky and never dims a
       photograph. */
    radial-gradient(115% 62% at 50% 50%, rgba(5, 8, 22, 0) 42%, rgba(5, 8, 22, 0.55) 100%),
    /* The ramp. Marble at both ends = invisible seams. */
    linear-gradient(to bottom,
      var(--bg-base) 0%,
      #36416A 12%,
      #0E1433 26%,
      var(--bg-dark) 44%,
      var(--bg-dark) 58%,
      #0E1433 74%,
      #36416A 88%,
      var(--bg-base) 100%);
  background-repeat: repeat, no-repeat, no-repeat, no-repeat, no-repeat, no-repeat;
}
.gallery3d__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: transparent;       /* see the note on .gallery3d */
}
@supports (height: 100svh) {
  .gallery3d__sticky { height: 100svh; }
}
.gallery3d__gl { position: absolute; inset: 0; }

/* The copy sits at the foot of the stage rather than dead centre — the middle
   is where the pictures arrive at full size, and a title parked there is
   permanently behind a photograph. */
.gallery3d__copy {
  position: relative;
  z-index: 1;
  align-self: end;
  text-align: center;
  margin-bottom: clamp(36px, 8vh, 88px);
  padding: clamp(18px, 3vw, 30px) clamp(20px, 5vw, 44px);
  color: var(--ink-inverse);
  pointer-events: none;
}
.gallery3d__copy::before {
  content: "";
  position: absolute;
  inset: -10% -6%;
  z-index: -1;
  background: radial-gradient(62% 62% at 50% 50%, rgba(7, 11, 28, 0.8) 0%, rgba(7, 11, 28, 0.5) 46%, transparent 78%);
}
.gallery3d__copy .eyebrow { color: var(--scrub-accent); }
.gallery3d__copy h2 {
  margin: 0;
  font-family: var(--display);
  font-style: normal;
  font-size: clamp(2rem, 5.5vw, 3.6rem);
  line-height: 1.06;
  text-shadow: 0 6px 28px rgba(5, 8, 20, 0.75);
}
.gallery3d__sub {
  margin: 18px 0 0;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-inverse-dim);
  text-shadow: 0 2px 12px rgba(5, 8, 20, 0.8);
}
/* Same rule as the cardfall's: an instruction to keep scrolling is a lie
   until there is something on screen responding to it. */
.gallery3d__sub { visibility: hidden; }
.gallery3d.is-live .gallery3d__sub { visibility: visible; }

/* The tall box is the DEFAULT and is only ever collapsed — never grown by JS.
   See the note under .cardfall.is-flat: gating height on a class added after a
   dynamic import means the success path is the one that reflows the page,
   under a visitor who is already standing inside the section. */
.gallery3d.is-flat { height: auto; }
/* The scrolling sky is a scrolling effect. Collapsed to one screen there is
   nothing to travel across, and the marble ends of the ramp would land directly
   under the light copy — white type on #F3EFE6. So the flat section drops the
   ramp and keeps only the glows over a solid dark ground. */
.gallery3d.is-flat,
.gallery3d.is-flat .gallery3d__sticky { background-image: none; }
.gallery3d.is-flat {
  background-color: var(--bg-dark);
  background-image:
    radial-gradient(64% 46% at 22% 26%, rgba(62, 79, 174, 0.32) 0%, rgba(62, 79, 174, 0) 100%),
    radial-gradient(58% 44% at 82% 74%, rgba(57, 69, 214, 0.24) 0%, rgba(57, 69, 214, 0) 100%);
}
.gallery3d.is-flat .gallery3d__sticky {
  position: relative;
  height: auto;
  padding: clamp(56px, 9vw, 104px) 0;
}
.gallery3d.is-flat .gallery3d__gl { display: none; }
.gallery3d.is-flat .gallery3d__copy {
  align-self: auto;
  margin-bottom: 0;
}

/* The flat fallback: the same pictures, standing still. Hidden until we know
   the scene will not run, so the common path never pays to lay it out. */
.gallery3d__flat { display: none; }
.gallery3d.is-flat .gallery3d__flat {
  display: grid;
  width: min(var(--container), calc(100% - 2 * clamp(20px, 5vw, 48px)));
  margin: clamp(32px, 5vw, 56px) auto 0;
}
/* .gallery-strip is already a 4-up of 3:4 portrait cells, which is the shape
   these photographs are. Only the ground changes on the dark section. */
.gallery3d__flat.gallery-strip { margin-top: clamp(32px, 5vw, 56px); }
.gallery3d__flat.gallery-strip figure {
  border: var(--hairline-inverse);
  background: #080B1B;
}
@media (max-width: 760px) {
  .gallery3d__flat.gallery-strip { grid-template-columns: repeat(2, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  .gallery3d { height: auto; }
  /* Same reasoning as .is-flat: one screen, so no ramp and no marble ends. */
  .gallery3d, .gallery3d__sticky { background-image: none; }
  .gallery3d {
    background-color: var(--bg-dark);
    background-image:
      radial-gradient(64% 46% at 22% 26%, rgba(62, 79, 174, 0.32) 0%, rgba(62, 79, 174, 0) 100%),
      radial-gradient(58% 44% at 82% 74%, rgba(57, 69, 214, 0.24) 0%, rgba(57, 69, 214, 0) 100%);
  }
  .gallery3d__sticky {
    position: relative;
    height: auto;
    padding: clamp(56px, 9vw, 104px) 0;
  }
  .gallery3d__gl { display: none; }
  .gallery3d__copy { align-self: auto; margin-bottom: 0; }
  .gallery3d__flat {
    display: grid;
    width: min(var(--container), calc(100% - 2 * clamp(20px, 5vw, 48px)));
    margin: clamp(32px, 5vw, 56px) auto 0;
  }
}

/* ==========================================================================
   REVIEWS — three columns of real Google reviews drifting upward
   Ported from a React component that animated translateY to -50% on an
   infinite linear repeat. That is a keyframe, so this is a keyframe: no
   library, no runtime, and the compositor does all of it off the main thread.
   ========================================================================== */
.rv { background: var(--bg-base); overflow: hidden; }
.rv-head { text-align: center; max-width: 620px; margin-inline: auto; }
.rv-lead {
  margin: 14px 0 0; color: var(--ink-soft); font-size: 15px;
}
/* The .rv-score badge (Google G + 4.3 + stars + review count) and the per-card
   .rv-stars row were removed on 2026-08-22 — the section shows unrated quotes
   now. Their rules are gone with them rather than left dangling; the schema
   side of that change is documented on jsonld_casino() in build.py. */

/* The mask is what sells the loop: cards dissolve at both ends instead of
   being sliced by the overflow edge, so there is no visible entry or exit. */
.rv-cols {
  display: flex; justify-content: center; gap: 22px;
  margin-top: 44px; max-height: 720px; overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent);
}
.rv-col { width: min(340px, 100%); flex: none; }
/* Columns two and three appear as the viewport can hold them, matching the
   original's hidden md:block / hidden lg:block. */
.rv-col--md, .rv-col--lg { display: none; }
@media (min-width: 768px)  { .rv-col--md { display: block; } }
@media (min-width: 1024px) { .rv-col--lg { display: block; } }

.rv-track {
  display: flex; flex-direction: column; gap: 22px;
  /* The track holds the six cards twice and travels exactly one half its own
     height, so the copy that arrives is pixel-identical to the one leaving and
     the wrap is invisible. Any other distance and the seam jumps. */
  animation: rv-drift var(--rv-dur, 40s) linear infinite;
  will-change: transform;
}
@keyframes rv-drift {
  from { transform: translateY(0); }
  to   { transform: translateY(-50%); }
}
/* Hovering holds the column still so a quote can actually be read. */
.rv-cols:hover .rv-track { animation-play-state: paused; }

.rv-card {
  margin: 0; padding: 26px 26px 22px;
  border: var(--hairline-light); border-radius: 14px;
  background: #fff;
  box-shadow: 0 10px 30px -18px rgba(23, 30, 54, 0.5);
}
/* margin-top was 12px to clear the star row above. The quote is the card's
   first child now, so that gap only made the card top-heavy against its 22px
   bottom padding. */
.rv-quote {
  margin: 0; font-size: 15.5px; line-height: 1.62; color: var(--ink);
}
.rv-quote::before { content: "\201C"; }
.rv-quote::after  { content: "\201D"; }
.rv-by { display: flex; align-items: center; gap: 12px; margin-top: 20px; }
.rv-avatar {
  width: 38px; height: 38px; flex: none; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--sans); font-weight: 600; font-size: 15px; color: #fff;
}
/* Monograms, not stock faces — see the note in build_neon.py's reviews(). */
.rv-avatar--1 { background: linear-gradient(140deg, #2E3C90, #3945D6); }
.rv-avatar--2 { background: linear-gradient(140deg, #1F5C4A, #2E8168); }
.rv-avatar--3 { background: linear-gradient(140deg, #A8861D, #C9A227); }
.rv-who { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
.rv-who strong { font-family: var(--sans); font-size: 14.5px; font-weight: 600; }
.rv-who span { font-size: 12.5px; color: var(--ink-soft); }
.rv-more { text-align: center; margin-top: 34px; }
.rv-more a { color: var(--gold-deep); font-weight: 600; }

@media (max-width: 480px) {
  .rv-cols { max-height: 560px; }
  .rv-card { padding: 22px 20px 18px; }
}

/* Reduced motion: stop the drift and let the column stand as a plain stack.
   The mask goes too — faded-out edges on something that will never move are
   just unreadable text. */
@media (prefers-reduced-motion: reduce) {
  .rv-track { animation: none; }
  .rv-cols {
    max-height: none; overflow: visible;
    -webkit-mask-image: none; mask-image: none;
    align-items: start;
  }
  /* The doubled list only exists to make the loop seamless. Standing still it
     would just print every review twice. */
  .rv-track .rv-card:nth-child(n + 7) { display: none; }
}

/* ==========================================================================
   BLACKJACK — same dark felt panel as the roulette, same no-stakes rule
   ========================================================================== */
.bj {
  display: grid;
  grid-template-columns: 1fr minmax(280px, 420px);
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
  padding: clamp(28px, 4vw, 56px);
  border-radius: 18px;
  background: radial-gradient(120% 120% at 80% 0%, #1B2449 0%, var(--bg-dark) 60%);
  color: var(--ink-inverse);
  border: var(--hairline-inverse);
}

.bj__felt {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 4vw, 36px);
  padding: clamp(20px, 3vw, 32px);
  border-radius: 14px;
  /* Table baize, kept deep rather than bright — this is a floor, not a felt swatch. */
  background:
    radial-gradient(90% 120% at 50% 0%, rgba(255, 255, 255, 0.06) 0%, transparent 60%),
    linear-gradient(180deg, #123829 0%, #0C271C 100%);
  border: 1px solid rgba(201, 162, 39, 0.22);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.bj__tablecol { min-width: 0; }
.bj__row { min-width: 0; }
/* Lives outside the felt so it survives the swap to the 3D table. */
.bj__scores {
  display: flex;
  justify-content: center;
  gap: clamp(20px, 5vw, 44px);
  margin: 14px 0 0;
}
.bj__score { display: inline-flex; align-items: center; gap: 9px; }
.bj__who {
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(248, 245, 238, 0.62);
}
.bj__total {
  min-width: 30px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(248, 245, 238, 0.12);
  border: 1px solid rgba(248, 245, 238, 0.2);
  font-size: 0.78rem;
  font-weight: 600;
  text-align: center;
}
.bj__hand {
  display: flex;
  gap: 10px;
  min-height: 104px;
  flex-wrap: wrap;
}

/* --- the cards --- */
.bjcard {
  width: 72px;
  aspect-ratio: 5 / 7;
  perspective: 700px;
  /* --i is the card's position in the hand; it staggers the deal. */
  animation: bj-deal 420ms cubic-bezier(0.16, 0.82, 0.24, 1) backwards;
  animation-delay: calc(var(--i, 0) * 110ms);
}
.bjcard__inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 480ms cubic-bezier(0.3, 0.7, 0.2, 1);
}
.bjcard.is-down .bjcard__inner { transform: rotateY(180deg); }

.bjcard__face,
.bjcard__back {
  position: absolute;
  inset: 0;
  border-radius: 7px;
  backface-visibility: hidden;
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.45);
}
.bjcard__face {
  background: linear-gradient(160deg, #FFFDF8 0%, #F2EDE2 100%);
  border: 1px solid rgba(20, 17, 24, 0.18);
  color: #141118;
}
.bjcard__face.is-red { color: #B01E33; }
.bjcard__back {
  transform: rotateY(180deg);
  background:
    repeating-linear-gradient(45deg, rgba(201, 162, 39, 0.22) 0 2px, transparent 2px 7px),
    repeating-linear-gradient(-45deg, rgba(201, 162, 39, 0.22) 0 2px, transparent 2px 7px),
    linear-gradient(160deg, #212B5E 0%, #141B3D 100%);
  border: 1px solid rgba(201, 162, 39, 0.5);
  box-shadow: inset 0 0 0 3px rgba(201, 162, 39, 0.35), 0 6px 14px rgba(0, 0, 0, 0.45);
}

.bjcard__corner {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1;
  font-size: 0.72rem;
  font-weight: 600;
}
.bjcard__corner i { font-style: normal; font-size: 0.66rem; margin-top: 1px; }
.bjcard__corner--tl { top: 5px; left: 5px; }
.bjcard__corner--br { bottom: 5px; right: 5px; transform: rotate(180deg); }
.bjcard__pip {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 1.85rem;
  opacity: 0.92;
}

@keyframes bj-deal {
  from { opacity: 0; transform: translate(38px, -26px) rotate(9deg) scale(0.94); }
  to   { opacity: 1; transform: none; }
}

/* --- panel --- */
.bj__panel { min-width: 0; }
.bj__lead {
  margin: 0 0 22px;
  color: var(--ink-inverse-dim);
  font-size: 1rem;
  line-height: 1.65;
}
.bj__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 20px; }
.bj__btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; }

/* --- the three moves, colour-coded -----------------------------------------
   Deal is white: it is the only control live at the start of a hand, and white
   is the one weight that outranks the two coloured ones beside it. Card is the
   felt green — take another, stay in. Stand is red — the hand stops here. The
   pair reads before the label does, which is the point in three languages.

   Greens and reds are the site's existing object colours (--felt-green,
   --roul-red), not new ones. 7.8:1 and 7.3:1 with white. */
.bj__btn--green {
  background: var(--felt-green);
  border-color: rgba(248, 245, 238, 0.26);
  color: #fff;
}
.bj__btn--green:hover { background: #2A7360; }
.bj__btn--red {
  background: var(--roul-red);
  border-color: rgba(248, 245, 238, 0.26);
  color: #fff;
}
.bj__btn--red:hover { background: #C0263C; }
/* aria-disabled, not disabled — the buttons keep focus between hands, so the
   dimmed state must not also brighten on hover. */
.bj__btn--green[aria-disabled="true"]:hover { background: var(--felt-green); }
.bj__btn--red[aria-disabled="true"]:hover { background: var(--roul-red); }
.btn--white[aria-disabled="true"]:hover { background: var(--ink-inverse); }

/* Retained for the parked poker demo, which still deals with a ghost draw. */
.bj__btn--ghost {
  background: rgba(248, 245, 238, 0.1);
  border: 1px solid rgba(248, 245, 238, 0.24);
  color: var(--ink-inverse);
}
.bj__btn--ghost:hover { background: rgba(248, 245, 238, 0.18); }
.bj__btn--ghost[aria-disabled="true"]:hover { background: rgba(248, 245, 238, 0.1); }
.bj__result {
  min-height: 2.4em;
  margin: 0 0 14px;
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1.5;
}
.bj__tally { margin: 0 0 18px; font-size: 0.85rem; color: var(--ink-inverse-dim); }
.bj__note {
  margin: 0;
  padding-top: 16px;
  border-top: var(--hairline-inverse);
  font-size: 0.78rem;
  line-height: 1.5;
  color: rgba(248, 245, 238, 0.6);
}

@media (max-width: 860px) {
  .bj { grid-template-columns: 1fr; }
  .bj__felt { order: -1; }
}
@media (max-width: 480px) {
  .bjcard { width: 58px; }
  .bjcard__pip { font-size: 1.45rem; }
  .bj__hand { min-height: 84px; }
  .bj__actions .bj__btn { flex: 1 1 auto; justify-content: center; }
}

/* The hand still deals and still reports; the cards just appear in place. */
@media (prefers-reduced-motion: reduce) {
  .bjcard { animation: none; }
  .bjcard__inner { transition: none; }
}

/* ==========================================================================
   3D CARD TABLES — blackjack and poker
   The DOM felt is the baseline and stays playable on its own. When table3d.js
   reports a WebGL context the section gets .is-3d, which retires the flat
   felt and shows the canvas in the same box. Same contract as the roulette.
   ========================================================================== */
.bj__stage,
.pk__stage {
  position: relative;
  min-width: 0;
  border-radius: 14px;
  overflow: hidden;
}
.bj__gl,
.pk__gl {
  display: none;
  width: 100%;
  aspect-ratio: 4 / 3;
  /* The page keeps the vertical axis so a finger can still scroll past the
     table; the module only reads horizontal drag on touch. */
  touch-action: pan-y;
}
.bj.is-3d .bj__felt,
.pk.is-3d .pk__felt { display: none; }
.bj.is-3d .bj__gl,
.pk.is-3d .pk__gl { display: block; }

/* ==========================================================================
   POKER — five-card draw, same felt panel and same no-stakes rule
   ========================================================================== */
.pk {
  display: grid;
  grid-template-columns: 1fr minmax(280px, 420px);
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
  padding: clamp(28px, 4vw, 56px);
  border-radius: 18px;
  background: radial-gradient(120% 120% at 20% 0%, #1B2449 0%, var(--bg-dark) 60%);
  color: var(--ink-inverse);
  border: var(--hairline-inverse);
}
.pk__tablecol { min-width: 0; }
.pk__felt {
  padding: clamp(20px, 3vw, 32px);
  border-radius: 14px;
  background:
    radial-gradient(90% 120% at 50% 0%, rgba(255, 255, 255, 0.06) 0%, transparent 60%),
    linear-gradient(180deg, #123829 0%, #0C271C 100%);
  border: 1px solid rgba(201, 162, 39, 0.22);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.pk__hand { justify-content: center; }

/* A held card lifts and takes a gold edge, so the choice reads on the card
   itself and not only on the button below it. */
.bjcard.is-held .bjcard__inner { transform: translateY(-10px); }
.bjcard.is-held .bjcard__face {
  border-color: var(--gold);
  box-shadow: 0 10px 18px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(201, 162, 39, 0.75);
}

.pk__holds {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px;
  margin-top: 16px;
}
.pk__hold {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 4px;
  border-radius: 10px;
  border: 1px solid rgba(248, 245, 238, 0.22);
  background: rgba(248, 245, 238, 0.08);
  color: var(--ink-inverse);
  font-family: var(--sans);
  font-size: 0.72rem;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.pk__hold:hover { background: rgba(248, 245, 238, 0.16); }
.pk__hold:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.pk__hold.is-on {
  background: rgba(201, 162, 39, 0.9);
  border-color: var(--gold);
  color: #131623;
  transform: translateY(-2px);
}
.pk__hold[aria-disabled="true"] { opacity: 0.4; cursor: not-allowed; }
.pk__hold[aria-disabled="true"]:hover { background: rgba(248, 245, 238, 0.08); }
.pk__holdslot {
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  opacity: 0.7;
}
.pk__panel { min-width: 0; }

@media (max-width: 860px) {
  .pk { grid-template-columns: 1fr; }
  .pk__tablecol { order: -1; }
}
@media (max-width: 480px) {
  .pk__hold { font-size: 0.62rem; padding: 7px 2px; }
  .pk__holdtxt { display: none; }   /* the number and the gold state carry it */
}

/* ==========================================================================
   FAQ — native <details>/<summary>, no JS.
   The answers sit in the DOM whether or not the item is open, so crawlers read
   them and the FAQPage schema on the page is backed by copy a visitor can
   actually see. <summary> needs both `list-style: none` and the -webkit-
   marker reset: the two engines hide the default triangle by different means.
   ========================================================================== */
.faq__list {
  max-width: 860px; margin: 0 auto;
  border-top: var(--hairline-light);
}
.faq__item { border-bottom: var(--hairline-light); }
.faq__q {
  list-style: none; cursor: pointer;
  display: flex; align-items: baseline; gap: 20px;
  padding: 24px 4px;
  font-family: var(--sans); font-weight: 600;
  font-size: clamp(16px, 1.8vw, 19px); line-height: 1.35;
  color: var(--ink);
  transition: color 0.18s ease;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { color: var(--neon-violet); }
.faq__q:focus-visible {
  outline: 2px solid var(--gold-deep); outline-offset: 3px;
  border-radius: var(--radius-sm);
}
/* A sign rather than a chevron: it survives at any font size and needs no SVG. */
.faq__q::after {
  content: "+"; margin-left: auto; flex: none;
  font-family: var(--sans); font-weight: 400; font-size: 24px; line-height: 1;
  color: var(--gold-deep);
}
.faq__item[open] .faq__q::after { content: "\2212"; }
.faq__item[open] .faq__q { color: var(--neon-violet); }
.faq__a {
  padding: 0 4px 26px; max-width: 68ch;
  color: var(--ink-soft);
}
.faq__a a { color: var(--neon-violet); text-decoration-thickness: 1px; text-underline-offset: 3px; }
.faq__a a:hover { color: var(--gold-deep); }

@media (max-width: 640px) {
  .faq__q { padding: 20px 2px; gap: 14px; }
  .faq__a { padding: 0 2px 22px; }
}

/* ==========================================================================
   LOCALE TYPE FALLBACK — keep this block last
   ==========================================================================
   Czech is gone from here: the extended cut of Collingar draws it natively now,
   accents and all. What is left is Cyrillic, which cannot be reached the same
   way — 46 letterforms is type design, not a transformation of marks the font
   already has.

   So Russian headings take **Prata**. It is the closest available match rather
   than a generic serif: the same high-contrast Didone genre as Collingar, and
   crucially the same single 400 weight, so no rule anywhere can ask it for a
   bold it does not have. Playfair was the previous stand-in and sat visibly
   heavier and more bookish beside Collingar.

   Note `h1-h4` IS in the list below, even though `--display` already falls
   through to Prata on its own. Leaving it to the fallback looked safe and was
   not: five Russian headings carry characters BOTH faces have — the em dash,
   the digits in "24/7", the Latin in "Ultimate Texas Hold'em" — and those would
   be served by Collingar while the Cyrillic around them came from Prata. One
   line, two Didones. Naming the face outright drops Collingar from the Russian
   stack entirely, so a heading can only ever be set in one thing.

   If the paid Collingar family is ever licensed AND turns out to ship Cyrillic
   (unlikely — the foundry's "multilanguage" almost certainly means Latin-Ext),
   delete this block and drop Prata from the font request.
   ========================================================================== */
:lang(ru) h1, :lang(ru) h2, :lang(ru) h3,   /* h4 deliberately absent:
     the only two on the site are the footer micro-caps, which stay on
     Poppins in every language for the reason given at .footer-col h4. */
:lang(ru) .hero-spot__lockup .script,
:lang(ru) .hero-spot__lockup .block,
:lang(ru) .hero__lockup .script,
:lang(ru) .hero__lockup .block,
:lang(ru) .gallery3d__copy h2,
:lang(ru) .cardfall__line,
:lang(ru) .bar__script {
  font-family: "Prata", Georgia, serif;
  font-style: normal;
  font-weight: 400;
}


/* ==========================================================================
   TOPIC PAGES  (/{lang}/blackjack-prague, /ruleta-praha, /oteviraci-doba …)
   Added 2026-09-05. Scoped under .topic so nothing here can reach the
   homepage — these are new class names, not overrides of existing ones.
   ========================================================================== */
.topic { background: var(--bg-base); color: var(--ink); }
.topic .container { max-width: 900px; }

.topic__crumbs {
  font-size: 13px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: 18px;
}
.topic__crumbs a { color: var(--gold-deep); text-decoration: none; }
.topic__crumbs a:hover { text-decoration: underline; }
.topic__crumbs span[aria-hidden] { margin: 0 6px; opacity: .55; }

.topic__h1 {
  font-family: var(--display, var(--serif));
  font-size: clamp(34px, 6vw, 58px); line-height: 1.05;
  margin: 0 0 18px; color: var(--ink);
}
.topic__lead {
  font-size: clamp(17px, 2.1vw, 20px); line-height: 1.6;
  color: var(--ink-soft); max-width: 68ch; margin: 0 0 48px;
}

.topic__rows { display: grid; gap: 28px; margin-bottom: 56px; }
.topic__row {
  display: grid; grid-template-columns: 46px 1fr; gap: 18px;
  align-items: start;
  padding: 26px 28px;
  background: #fff;
  border: 1px solid rgba(23, 30, 54, .08);
  border-radius: var(--radius, 14px);
}
.topic__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(201, 162, 39, .12); color: var(--gold-deep);
}
.topic__icon svg { width: 21px; height: 21px; }
.topic__row h2 {
  font-size: 20px; margin: 4px 0 8px; color: var(--ink);
  font-family: var(--sans); font-weight: 600; letter-spacing: -.01em;
}
.topic__row p { margin: 0 0 8px; line-height: 1.68; color: var(--ink-soft); }
.topic__row p:last-child { margin-bottom: 0; }
.topic__inline { color: var(--gold-deep); font-weight: 600; text-decoration: none; }
.topic__inline:hover { text-decoration: underline; }

.topic__map { margin-bottom: 56px; }
.topic__map h2 { font-size: 22px; margin: 0 0 10px; }
.topic__map p { color: var(--ink-soft); line-height: 1.68; margin: 0 0 20px; max-width: 68ch; }
.topic__map iframe {
  width: 100%; height: 340px; border: 0;
  border-radius: var(--radius, 14px); display: block;
}

.topic__cta {
  background: var(--bg-dark); color: var(--ink-inverse);
  border-radius: var(--radius, 14px); padding: 40px 38px;
  margin-bottom: 48px;
}
.topic__cta h2 { font-size: clamp(22px, 3vw, 30px); margin: 0 0 10px; color: var(--ink-inverse); }
.topic__cta p { color: var(--ink-inverse-dim); line-height: 1.65; margin: 0 0 24px; max-width: 62ch; }
.topic__cta-btns { display: flex; flex-wrap: wrap; gap: 12px; }

.topic__more { border-top: 1px solid rgba(23, 30, 54, .1); padding-top: 26px; }
.topic__more-h {
  font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft); margin: 0 0 14px;
}
.topic__more-links { display: flex; flex-wrap: wrap; gap: 10px; }
.topic__more-link {
  display: inline-block; padding: 9px 16px;
  border: 1px solid rgba(23, 30, 54, .14); border-radius: 999px;
  font-size: 14px; color: var(--ink); text-decoration: none;
  transition: border-color .18s ease, background .18s ease;
}
.topic__more-link:hover {
  border-color: var(--gold-deep); background: rgba(201, 162, 39, .08);
}

@media (max-width: 640px) {
  .topic__row { grid-template-columns: 1fr; padding: 22px 20px; gap: 12px; }
  .topic__cta { padding: 30px 24px; }
  .topic__map iframe { height: 260px; }
}

/* Secondary link on the cash-games band. The primary now goes to our own poker
   page; pokerzive.cz stays, one step quieter, as the live-tables feed. */
.game-card__link--alt { opacity: .82; font-weight: 500; margin-left: 14px; }
@media (max-width: 640px) { .game-card__link--alt { margin-left: 0; display: block; margin-top: 6px; } }
