/* ════════════════════════════════════════════════════════════
   TSSA — THE SINGLE SOURCE OF TRUTH FOR DESIGN TOKENS
   ────────────────────────────────────────────────────────────
   Loaded by BOTH layers of the site:
     · public/index-public.html   → <link rel="stylesheet" href="/tssa-theme.css">
     · app/layout.tsx (Next.js)   → <link rel="stylesheet" href="/tssa-theme.css">

   Before this file existed, the two layers each carried their own :root block
   and had already drifted — --bg was #090806 in one and #0A0907 in the other,
   with --t1, --t2, --b2 and --b3 all slightly out of step. Every colour had to
   be fixed twice, and a theme switcher on top of that would have meant fixing
   it twice forever.

   RULE: no other file defines a design token. Consume them, never redeclare.
   ════════════════════════════════════════════════════════════ */

:root {
  /* ── layout ── */
  --nav-h: 76px;   /* the nav grew to give the top row room to breathe;
                       .hero padding, .mega and the sticky tab bar all key off this */
  --radius: 8px;
  --radius-lg: 12px;

  /* ── type ── */
  --serif: 'Cormorant Garamond', Georgia, serif;
  --sans:  'Inter', -apple-system, system-ui, sans-serif;

  /* ── gold: the brand constant ── */
  --gold:     #C9A84C;
  --gold-l:   #E8D08A;
  --gold-d:   #9A7A30;
  --gold-dim: #5A4820;

  /* ── grounds ── */
  --bg:  #090806;
  --bg2: #0E0C09;
  --bg3: #13110D;
  --bg4: #1F1D17;

  /* ── surfaces (both naming families — the app used --surf*, the
        static homepage used --s*. Both are kept so neither layer
        needed rewriting when this file landed.) ── */
  --s1:    #181510;
  --s2:    #1E1B14;
  --s3:    #242018;
  --surf:  #1A1814;
  --surf2: #252220;

  /* ── royal green: the South African register under the gold.
        Present as surfaces from the start; the themes below lean on
        it harder. ── */
  --royal:      #0E3B2E;
  --royal-deep: #072019;
  --royal-lift: #16563F;

  /* ── borders ── */
  --b1: rgba(201,168,76,0.10);
  --b2: rgba(201,168,76,0.21);
  --b3: rgba(201,168,76,0.38);

  /* ── text ── */
  --t1: #F2EDE3;
  --t2: rgba(242,237,227,0.64);
  --t3: rgba(242,237,227,0.35);
  --t4: rgba(242,237,227,0.17);

  /* ── semantic (separate from the accent, on purpose) ── */
  --green: #3D8B5F;
  --red:   #C04540;
  --amber: #D9A441;

  /* ── portal section colours ── */
  --col-ops:   #C9A84C;
  --col-cat:   #5DCAA5;
  --col-net:   #85B7EB;
  --col-docs:  #F0997B;
  --col-admin: #AFA9EC;

  /* ── theme transition. Only the properties that actually change,
        so we are not animating layout. ── */
  --theme-ease: 900ms cubic-bezier(.4,0,.2,1);
}

/* ════════════════════════════════════════════════════════════
   THE THREE HOURS
   Each maps to one of the anthems in /audio. When a track changes,
   the theme changes with it — the palette follows the music rather
   than needing its own control.

     morning  ← anthem-1-cape-town-halogen.mp3
     midday   ← anthem-2-cape-town-lights.mp3
     golden   ← anthem-3-golden-sunset-soiree.mp3

   Three more are CHOSEN rather than automatic, and each carries its
   own display face as well as its own palette:

     indigo   · city lights after dark    · Playfair Display
     karoo    · red earth and dust        · Marcellus
     ocean    · deep Indian Ocean teal    · Marcellus

   Applied by stamping data-theme on <html>. Absent = the default
   above, unchanged from what the site has always looked like.
   ════════════════════════════════════════════════════════════ */

/* ── MORNING · cool, clear, first light off the Atlantic ───────
   Green-blue cast, gold cooled toward pale brass, highest clarity. */
:root[data-theme="morning"] {
  --bg:  #060C0A;
  --bg2: #0A1210;
  --bg3: #0F1815;
  --bg4: #17231F;
  --s1:    #131C18;
  --s2:    #18231E;
  --s3:    #1E2B25;
  --surf:  #141D19;
  --surf2: #1D2823;

  --gold:     #D6BC72;
  --gold-l:   #F0DDA6;
  --gold-d:   #A68F45;
  --gold-dim: #5E5430;

  --b1: rgba(214,188,114,0.10);
  --b2: rgba(214,188,114,0.22);
  --b3: rgba(214,188,114,0.40);

  --t1: #F4F2EA;
  --t2: rgba(244,242,234,0.66);
  --t3: rgba(244,242,234,0.37);
  --t4: rgba(244,242,234,0.18);
}

/* ── MIDDAY · full saturation, deepest contrast, the royal green
   at its most present. This is the brand at its most confident. */
:root[data-theme="midday"] {
  --bg:  #040807;
  --bg2: #08100D;
  --bg3: #0C1712;
  --bg4: #142019;
  --s1:    #0F1A15;
  --s2:    #14221B;
  --s3:    #1A2B22;
  --surf:  #101B16;
  --surf2: #1A2620;

  --gold:     #C9A84C;
  --gold-l:   #EDD48D;
  --gold-d:   #92742D;
  --gold-dim: #52431D;

  --b1: rgba(201,168,76,0.11);
  --b2: rgba(201,168,76,0.24);
  --b3: rgba(201,168,76,0.44);

  --t1: #F5F1E7;
  --t2: rgba(245,241,231,0.66);
  --t3: rgba(245,241,231,0.36);
  --t4: rgba(245,241,231,0.17);
}

/* ── GOLDEN HOUR · warm, amber-rich, the last light on the
   Twelve Apostles. Green pulled back, gold pushed forward. */
:root[data-theme="golden"] {
  --bg:  #0A0805;
  --bg2: #100C07;
  --bg3: #17110A;
  --bg4: #231A10;
  --s1:    #1C150C;
  --s2:    #241B10;
  --s3:    #2C2114;
  --surf:  #1B1409;
  --surf2: #261C11;

  --gold:     #DCB457;
  --gold-l:   #FFE1A0;
  --gold-d:   #A98336;
  --gold-dim: #614A22;

  --royal:      #123D2C;
  --royal-deep: #0A2118;

  --b1: rgba(220,180,87,0.11);
  --b2: rgba(220,180,87,0.24);
  --b3: rgba(220,180,87,0.44);

  --t1: #F8F1E2;
  --t2: rgba(248,241,226,0.66);
  --t3: rgba(248,241,226,0.36);
  --t4: rgba(248,241,226,0.17);
}


/* ══════════════════════════════════════════════════════════════
   THE CHOSEN PALETTES — indigo, karoo, ocean.

   The three above arrive with their anthem. These three are picked, never
   automatic, and they stay put when the track changes: Rudy asked for theme
   switching to be independent of the music.

   Each one also changes the TYPE, not just the colour. A palette swap that
   leaves the lettering identical reads as a filter over the same page; giving
   each its own serif makes it feel like a different edition of the same
   magazine. The sans stays Inter throughout so body copy never loses its
   legibility — only the display face moves.
   ══════════════════════════════════════════════════════════════ */

/* ── INDIGO · city lights after dark. Cool blue-violet, silvered gold,
      Playfair's high contrast for a night-edition feel. ── */
:root[data-theme="indigo"] {
  --bg:  #05060C;
  --bg2: #090B14;
  --bg3: #0E111C;
  --bg4: #161A29;
  --s1:    #10131F;
  --s2:    #161A28;
  --s3:    #1D2231;
  --surf:  #0F121D;
  --surf2: #181D2B;

  --gold:     #C9BE8C;
  --gold-l:   #EFE7C4;
  --gold-d:   #988E64;
  --gold-dim: #55503A;

  --royal:      #1B2A5E;
  --royal-deep: #0C1430;

  --b1: rgba(201,190,140,0.10);
  --b2: rgba(201,190,140,0.22);
  --b3: rgba(201,190,140,0.42);

  --t1: #F2F2F8;
  --t2: rgba(242,242,248,0.66);
  --t3: rgba(242,242,248,0.36);
  --t4: rgba(242,242,248,0.17);

  --serif: 'Playfair Display', 'Cormorant Garamond', Georgia, serif;
}

/* ── KAROO · red earth and dust. Warm terracotta ground, copper gold,
      Marcellus for something carved and inscriptional. ── */
:root[data-theme="karoo"] {
  --bg:  #0C0705;
  --bg2: #130C08;
  --bg3: #1B120C;
  --bg4: #281B12;
  --s1:    #1F150E;
  --s2:    #281B12;
  --s3:    #322217;
  --surf:  #1D140D;
  --surf2: #2A1D14;

  --gold:     #D89A5B;
  --gold-l:   #F7CE9C;
  --gold-d:   #A2703C;
  --gold-dim: #5E4124;

  --royal:      #4A2A1C;
  --royal-deep: #2A1610;

  --b1: rgba(216,154,91,0.11);
  --b2: rgba(216,154,91,0.24);
  --b3: rgba(216,154,91,0.44);

  --t1: #FBEFE2;
  --t2: rgba(251,239,226,0.66);
  --t3: rgba(251,239,226,0.36);
  --t4: rgba(251,239,226,0.17);

  --serif: 'Marcellus', 'Cormorant Garamond', Georgia, serif;
}

/* ── OCEAN · the Indian Ocean off Durban. Deep teal, sea-glass gold,
      Cormorant kept but the ground pulled cold and green. ── */
:root[data-theme="ocean"] {
  --bg:  #03090A;
  --bg2: #061012;
  --bg3: #0A181A;
  --bg4: #112527;
  --s1:    #0C1C1E;
  --s2:    #112527;
  --s3:    #172F32;
  --surf:  #0B1A1C;
  --surf2: #142A2C;

  --gold:     #9FC9B8;
  --gold-l:   #D6EFE4;
  --gold-d:   #6E9686;
  --gold-dim: #3D554C;

  --royal:      #0C3A38;
  --royal-deep: #051F1E;

  --b1: rgba(159,201,184,0.10);
  --b2: rgba(159,201,184,0.22);
  --b3: rgba(159,201,184,0.42);

  --t1: #EDF7F4;
  --t2: rgba(237,247,244,0.66);
  --t3: rgba(237,247,244,0.36);
  --t4: rgba(237,247,244,0.17);

  --serif: 'Marcellus', 'Cormorant Garamond', Georgia, serif;
}

/* The type swap needs the same easing as the colour, or the headline snaps
   while the background glides. Font-family cannot transition, so we soften the
   moment with a brief opacity dip on display text only. */
@media (prefers-reduced-motion: no-preference) {
  html[data-theme] h1, html[data-theme] h2, html[data-theme] h3 {
    animation: tssa-type-settle .42s ease-out;
  }
}
@keyframes tssa-type-settle {
  from { opacity: .55; letter-spacing: .012em; }
  to   { opacity: 1;   letter-spacing: inherit; }
}

/* ── The crossfade. Scoped to colour properties only so we never
   animate layout, and disabled outright for anyone who has asked
   for reduced motion. ── */
@media (prefers-reduced-motion: no-preference) {
  html[data-theme] body,
  html[data-theme] nav,
  html[data-theme] footer,
  html[data-theme] section,
  html[data-theme] .b5 {
    transition:
      background-color var(--theme-ease),
      border-color     var(--theme-ease),
      color            var(--theme-ease);
  }
}

/* ══════════════════════════════════════════════════════════════
   TSSA SIGNATURE VISUAL LAYER  ·  shared by the app, public site & games
   ──────────────────────────────────────────────────────────────
   Dark elegance, but alive: soft neon glow on type, gradient depth on
   surfaces, gentle pulse on headers. Tasteful — low intensity, and every
   motion honours prefers-reduced-motion. Added 19 Aug 2026 per Rudy's
   "150%" brief. Use the utility classes anywhere in either layer.
   ══════════════════════════════════════════════════════════════ */

/* Glow tokens — bold tier (Rudy: "push it bolder"), still elegant not Vegas */
:root{
  --glow-w: 0 0 16px rgba(242,237,227,.34), 0 0 4px rgba(242,237,227,.20);
  --glow-w-soft: 0 0 10px rgba(242,237,227,.22);
  --glow-g: 0 0 24px rgba(201,168,76,.58), 0 0 7px rgba(201,168,76,.44);
  --glow-g-soft: 0 0 14px rgba(201,168,76,.36);
  /* signature dark gradients for surfaces — a touch more gold warmth */
  --grad-surf:  linear-gradient(157deg, color-mix(in srgb, var(--surf) 84%, var(--gold) 6%), var(--bg2) 96%);
  --grad-panel: linear-gradient(157deg, var(--surf2), var(--surf) 68%);
  --grad-gold:  linear-gradient(135deg, var(--gold-l), var(--gold-d));
}

/* Text glow utilities */
.glow-w{ text-shadow: var(--glow-w); }
.glow-w-soft{ text-shadow: var(--glow-w-soft); }
.glow-g{ text-shadow: var(--glow-g); }
.glow-g-soft{ text-shadow: var(--glow-g-soft); }



/* ── The waving flag's canvas ─────────────────────────────────
   Rendered by the MARKUP now (SiteFooter.tsx and index-public.html) and
   painted by /tssa-flag.js. These base styles live here rather than being
   applied by that script, so the canvas is correctly positioned on the very
   first paint instead of flashing as a full-width block before the script
   catches it. The footer itself still gets position/isolation from the
   script, because only it knows whether it has anything to draw. */
canvas.tssa-flag{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: .13;
  -webkit-mask-image: radial-gradient(120% 130% at 50% 45%, #000 18%, transparent 78%);
  mask-image: radial-gradient(120% 130% at 50% 45%, #000 18%, transparent 78%);
}
footer:has(> canvas.tssa-flag){ position: relative; isolation: isolate; }
footer > canvas.tssa-flag ~ *{ position: relative; z-index: 1; }

/* ══════════════════════════════════════════════════════════════
   CONTRAST INK — the fix for "text hidden by the background imaging".

   Rudy's rule, in his words: bright backs get dark text, dark backs get light
   text, each with the slight neon glow.

   THE PART A GLOW ALONE CANNOT DO. Over a photograph the background is not one
   brightness — Table Mountain is dark rock against bright sky in the same
   frame. A gold glow on light text vanishes the moment it crosses the sky.
   So every ink class carries TWO shadows:

     · a HALO in the opposite luminance, which separates the letterform from
       whatever is actually behind it, and
     · the neon GLOW, which is the look.

   The halo is what makes it readable; the glow is what makes it ours.
   ══════════════════════════════════════════════════════════════ */
:root{
  /* Halos — deliberately tight and multi-pass, so they read as a clean edge
     rather than a blur smeared behind the words. */
  --halo-d: 0 1px 2px rgba(0,0,0,.92), 0 0 8px rgba(0,0,0,.72), 0 0 18px rgba(0,0,0,.46);
  --halo-l: 0 1px 2px rgba(255,255,255,.90), 0 0 8px rgba(255,255,255,.66), 0 0 16px rgba(255,255,255,.40);

  /* Ink colours. --ink-dark is not pure black: against a bright photo a warm
     near-black sits with the brand better and reads softer. */
  --ink-light: #F6F1E6;
  --ink-dark:  #14100A;
}

/* LIGHT TEXT on a dark ground or dark imagery — dark halo + gold glow. */
.ink-light,
.over-media .ink-light{
  color: var(--ink-light);
  text-shadow: var(--halo-d), var(--glow-g-soft);
}

/* DARK TEXT on a bright ground or bright imagery — light halo + white glow. */
.ink-dark{
  color: var(--ink-dark);
  text-shadow: var(--halo-l), var(--glow-w-soft);
}

/* Stronger variants for display type sitting directly on a photograph. */
.ink-light-strong{
  color: var(--ink-light);
  text-shadow: var(--halo-d), var(--glow-g);
}
.ink-dark-strong{
  color: var(--ink-dark);
  text-shadow: var(--halo-l), var(--glow-w);
}

/* ── The automatic case ───────────────────────────────────────
   Put .over-media on ANY element that has a photograph behind it and every
   piece of text inside becomes legible without tagging each one. This is the
   blanket Rudy asked for — "anywhere on the site" — without turning the whole
   site glowy, because it only applies where imagery actually is.

   Buttons, chips and inputs are excluded: they carry their own solid grounds,
   and a halo on a form control looks like a rendering fault. */
/* .landmark-tile ALREADY carries a dark scrim, and that alone was the earlier
   answer to this problem. A scrim handles the average brightness; the halo
   handles the exception — a white cloud or a bright sea landing exactly behind
   a line of text. Belt and braces, which is what Rudy described. Listing it
   here means all seven pages that use the tile inherit the treatment without
   being touched individually. */
.landmark-tile h1, .landmark-tile h2, .landmark-tile h3,
.landmark-tile h4, .landmark-tile p, .landmark-tile span,
.landmark-tile li, .landmark-tile em, .landmark-tile strong,
.over-media,
.over-media h1, .over-media h2, .over-media h3,
.over-media h4, .over-media h5, .over-media h6,
.over-media p,  .over-media span, .over-media li,
.over-media em, .over-media strong, .over-media a{
  text-shadow: var(--halo-d), var(--glow-g-soft);
}
.landmark-tile button, .landmark-tile input, .landmark-tile select,
.landmark-tile .chip, .landmark-tile [class*="chip"], .landmark-tile [class*="badge"],
.over-media button, .over-media input, .over-media select,
.over-media textarea, .over-media .chip, .over-media .btn,
.over-media [class*="chip"], .over-media [class*="btn"]{
  text-shadow: none;
}

/* The same surface, but the imagery behind it is BRIGHT. */
.over-media-light,
.over-media-light h1, .over-media-light h2, .over-media-light h3,
.over-media-light h4, .over-media-light h5, .over-media-light h6,
.over-media-light p,  .over-media-light span, .over-media-light li,
.over-media-light em, .over-media-light strong, .over-media-light a{
  color: var(--ink-dark);
  text-shadow: var(--halo-l), var(--glow-w-soft);
}
.over-media-light button, .over-media-light input, .over-media-light select,
.over-media-light textarea, .over-media-light .chip, .over-media-light .btn{
  color: inherit;
  text-shadow: none;
}

/* A scrim for the worst case: small copy over a busy photograph, where no
   amount of halo is enough and the honest answer is to darken the plate. */
.media-scrim{ position: relative; }
.media-scrim::before{
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6,5,4,.10) 0%, rgba(6,5,4,.62) 62%, rgba(6,5,4,.86) 100%);
  pointer-events: none;
  z-index: 0;
}
.media-scrim > *{ position: relative; z-index: 1; }

@media (prefers-reduced-transparency: reduce){
  .media-scrim::before{ background: rgba(6,5,4,.78); }
}

/* Gentle header pulse — breathes the glow in and out */
@keyframes tssa-glow-pulse{
  0%,100%{ text-shadow: var(--glow-g-soft); }
  50%    { text-shadow: var(--glow-g); }
}
.glow-pulse{ animation: tssa-glow-pulse 3.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce){
  .glow-pulse{ animation: none; text-shadow: var(--glow-g-soft); }
}

/* Gradient surface helpers */
.grad-surf{ background: var(--grad-surf); }
.grad-panel{ background: var(--grad-panel); }

/* Count-up figures start hidden-ish until the script lifts them, so the
   run-up reads as intentional rather than a flash of the final value. */
[data-countup]{ font-variant-numeric: tabular-nums; }

/* Landmark tile — drop an SA landmark photo behind any tile with a GUARANTEED
   dark scrim so overlaid text is always readable (this is also the systemic
   answer to "text lost behind background imaging"). Set --img to the photo.
   Usage: <div class="landmark-tile" style="--img:url('/destinations/capetown.jpg')"> */
.landmark-tile{ position:relative; overflow:hidden; border-radius:var(--radius-lg);
  border:1px solid var(--b1); isolation:isolate; }
.landmark-tile::before{ content:''; position:absolute; inset:0; z-index:-2;
  background-image:var(--img); background-size:cover; background-position:center;
  transform:scale(1.02); transition:transform 6s ease; }
.landmark-tile:hover::before{ transform:scale(1.1); }
.landmark-tile::after{ content:''; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(157deg, rgba(9,8,6,.62), rgba(9,8,6,.86) 70%, rgba(9,8,6,.95)); }
.landmark-tile > *{ position:relative; }
@media (prefers-reduced-motion: reduce){ .landmark-tile:hover::before{ transform:scale(1.02); } }

/* ══════════════════════════════════════════════════════════════
   GOLD & SILVER FLAKES — the site-wide backdrop.

   Rudy: "gold & silver tiny neon flakes as part of the solid backdrops on all
   site pages … similar to the RAH concept but not so upfront — keep and lock
   to the backgrounds as a means to light-up darker spaces."

   CSS ONLY, DELIBERATELY. The obvious build is a canvas of particles, and it
   would be the wrong one: every page would need a script, it would repaint
   continuously, and it would cost battery on a phone for decoration. Layered
   radial-gradients give the same effect for nothing — the browser paints them
   once and the only work is a slow transform.

   LOCKED TO THE BACKGROUND, three ways:
     · position:fixed on a ::before that belongs to <body>, so it never scrolls
       into content or joins a stacking context it shouldn't;
     · pointer-events:none, so it can never eat a click;
     · z-index:0 with page content lifted to 1 — flakes cannot cover anything.

   Opacity is the whole design. At .5 this looks like dirt on the screen; the
   values below are tuned to read as a faint shimmer you notice only when the
   section behind is dark, which is exactly the "light up darker spaces" brief.
   ══════════════════════════════════════════════════════════════ */
:root{
  --flake-gold:   rgba(201,168,76,.55);
  --flake-gold-2: rgba(232,208,138,.40);
  --flake-silver: rgba(214,222,232,.34);
  --flake-op:     .30;    /* master dimmer — the one number to tune */
  --flake-drift:  90s;
}

body::before{
  content:'';
  position:fixed;
  inset:-25%;                      /* oversized so the drift never shows an edge */
  z-index:0;
  pointer-events:none;
  opacity:var(--flake-op);
  background-image:
    /* gold, larger and sparser */
    radial-gradient(1.6px 1.6px at 12% 18%, var(--flake-gold) 50%, transparent 51%),
    radial-gradient(1.4px 1.4px at 78% 8%,  var(--flake-gold-2) 50%, transparent 51%),
    radial-gradient(1.8px 1.8px at 43% 62%, var(--flake-gold) 50%, transparent 51%),
    radial-gradient(1.3px 1.3px at 88% 71%, var(--flake-gold-2) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 26% 88%, var(--flake-gold) 50%, transparent 51%),
    /* silver, smaller and denser — this is what reads as "flakes" not "dots" */
    radial-gradient(1px 1px at 61% 31%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1px 1px at 8%  54%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1.1px 1.1px at 95% 43%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1px 1px at 35% 12%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1px 1px at 70% 92%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 18% 74%, var(--flake-silver) 50%, transparent 51%);
  /* Two sizes so the pattern never visibly repeats. */
  background-size: 340px 340px, 340px 340px, 340px 340px, 340px 340px, 340px 340px,
                   210px 210px, 210px 210px, 210px 210px, 210px 210px, 210px 210px, 210px 210px;
  animation: tssa-flake-drift var(--flake-drift) linear infinite;
}

/* A second, slower plane at half opacity gives depth — flakes nearer and
   further away — without a second element's worth of paint cost. */
body::after{
  content:'';
  position:fixed;
  inset:-25%;
  z-index:0;
  pointer-events:none;
  opacity:calc(var(--flake-op) * .55);
  background-image:
    radial-gradient(2.2px 2.2px at 55% 25%, var(--flake-gold) 50%, transparent 51%),
    radial-gradient(2px 2px   at 15% 66%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(2.4px 2.4px at 84% 84%, var(--flake-gold-2) 50%, transparent 51%);
  background-size: 520px 520px, 520px 520px, 520px 520px;
  animation: tssa-flake-drift calc(var(--flake-drift) * 1.9) linear infinite reverse;
}

@keyframes tssa-flake-drift{
  from{ transform: translate3d(0,0,0) }
  to  { transform: translate3d(-210px,-340px,0) }
}

/* Content sits above the flakes. Without this the shimmer would wash over
   text on any element that has no background of its own. */
body > *{ position:relative; z-index:1 }

@media (prefers-reduced-motion: reduce){
  body::before, body::after{ animation:none }
}

/* Someone reading a long page of text does not need shimmer behind it. */
@media print{
  body::before, body::after{ display:none }
}

/* Per-page dimmer. Add .flakes-soft to a page that is already busy, or
   .flakes-off where they would compete — the RAH globe, for instance, makes
   its own starfield and does not need ours behind it. */
.flakes-soft{ --flake-op:.16 }
.flakes-off::before, .flakes-off::after{ display:none }

/* ══════════════════════════════════════════════════════════════
   DIMENSIONAL TABS & BUTTONS.

   Rudy: "small colour breaks in the text on certain tab bars to attract more
   attention, make them 3D and enlarge actual buttons/tabs to look as if it can
   do something."

   The last clause is the real brief. A flat chip that happens to be clickable
   reads as a label; the job is to make the affordance obvious BEFORE the
   cursor arrives. Three things do that, and they are cheap:

     1. A LIT TOP EDGE. Real objects catch light on the surface facing up. One
        inset highlight does more for "this is a raised thing" than any amount
        of drop shadow.
     2. A GROUND SHADOW that shortens on press. The tab moves 1px down and the
        shadow tightens — the whole illusion is that pairing, not the shadow.
     3. SIZE. Genuinely bigger targets, which is also the accessibility win:
        44px is the recommended minimum touch target and most of these chips
        were nowhere near it.

   The colour break is a single accent character before the label rather than
   colouring the whole word — a two-tone label is harder to read, and the eye
   is caught by the DIFFERENCE, so one mark does the work.
   ══════════════════════════════════════════════════════════════ */
:root{
  --d3-lift:   0 1px 0 rgba(255,255,255,.09) inset,
               0 -1px 0 rgba(0,0,0,.34) inset;
  --d3-ground: 0 2px 5px -1px rgba(0,0,0,.55), 0 6px 16px -8px rgba(0,0,0,.7);
  --d3-press:  0 1px 2px -1px rgba(0,0,0,.6);
  --d3-glow:   0 0 16px -4px rgba(201,168,76,.55);
}

.d3{
  position:relative;
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  min-height:40px; padding:11px 20px;      /* was ~8px 14px on most chips */
  border-radius:99px; cursor:pointer;
  font-family:inherit; font-size:11.5px; font-weight:500;
  letter-spacing:.1em; text-transform:uppercase;
  color:var(--t2);
  background:linear-gradient(180deg, var(--s3) 0%, var(--s1) 100%);
  border:1px solid var(--b2);
  box-shadow:var(--d3-lift), var(--d3-ground);
  transition:transform .14s ease, box-shadow .14s ease,
             color .18s ease, border-color .18s ease;
}
.d3:hover{
  color:var(--t1);
  border-color:var(--b3);
  transform:translateY(-1px);
  box-shadow:var(--d3-lift), var(--d3-ground), var(--d3-glow);
}
/* Press: down and tight. The shadow shortening is what sells it. */
.d3:active{
  transform:translateY(1px);
  box-shadow:var(--d3-lift), var(--d3-press);
}
.d3:focus-visible{
  outline:2px solid var(--gold);
  outline-offset:2px;
}

/* The colour break — one accent mark, not a two-tone word. */
.d3::before{
  content:'';
  width:5px; height:5px; border-radius:50%;
  background:var(--gold-d);
  box-shadow:0 0 6px rgba(201,168,76,.5);
  transition:background .18s, box-shadow .18s, transform .18s;
}
.d3:hover::before{
  background:var(--gold-l);
  box-shadow:0 0 10px rgba(232,208,138,.85);
  transform:scale(1.25);
}

/* Selected: gold, and it sits DOWN — a pressed key stays pressed. */
.d3.on{
  color:var(--bg);
  background:linear-gradient(180deg, var(--gold-l) 0%, var(--gold) 100%);
  border-color:var(--gold);
  box-shadow:0 1px 0 rgba(255,255,255,.35) inset, var(--d3-press);
  transform:translateY(1px);
}
.d3.on::before{ background:rgba(9,8,6,.55); box-shadow:none }
.d3.on:hover{ transform:translateY(1px) }

/* Primary — the one button on a screen that should pull the eye. */
.d3-primary{
  color:var(--bg);
  background:linear-gradient(180deg, var(--gold-l) 0%, var(--gold) 55%, var(--gold-d) 100%);
  border-color:var(--gold);
  box-shadow:0 1px 0 rgba(255,255,255,.45) inset, var(--d3-ground), var(--d3-glow);
}
.d3-primary::before{ background:rgba(9,8,6,.5); box-shadow:none }
.d3-primary:hover{ color:var(--bg) }

/* Sparkle — opt-in, for the one or two controls worth drawing an eye to.
   Deliberately NOT on every button: if everything sparkles, nothing does. */
.d3-spark::after{
  content:'✦';
  position:absolute; top:-3px; right:6px;
  font-size:9px; color:var(--gold-l);
  text-shadow:0 0 8px rgba(232,208,138,.9);
  animation:tssa-spark 2.6s ease-in-out infinite;
}
@keyframes tssa-spark{
  0%,100%{ opacity:.25; transform:scale(.8) rotate(0deg) }
  50%    { opacity:1;   transform:scale(1.15) rotate(20deg) }
}

/* A bar of them. Scrolls rather than wraps, because a tab row that reflows to
   two lines stops reading as a row. */
.d3-bar{
  display:flex; align-items:center; gap:9px;
  overflow-x:auto; scrollbar-width:none;
  padding:4px 2px 10px;
}
.d3-bar::-webkit-scrollbar{ display:none }
.d3-bar .d3{ flex:none }

@media (prefers-reduced-motion: reduce){
  .d3, .d3:hover, .d3:active{ transform:none }
  .d3-spark::after{ animation:none; opacity:.7 }
}

/* ── ADOPTION ─────────────────────────────────────────────────
   The existing bars inherit the treatment without every call site being
   rewritten. Applied to the CLASSES, so both layers pick it up: .gt is the
   Hidden Gems tab row on the homepage, .rf-chip the catalogue filters, and
   .nb-city the Build-My-Night city switch.

   Deliberately NOT applied to .b5-tab (the Radio/Themes switch) — it sits
   inside a small panel where a 40px raised pill would dominate the player it
   belongs to. Scale is part of the design, not a setting to max out. */
.gt, .rf-chip{
  min-height:38px; padding:10px 18px;
  background:linear-gradient(180deg, var(--s3) 0%, var(--s1) 100%);
  box-shadow:var(--d3-lift), var(--d3-ground);
  transition:transform .14s ease, box-shadow .14s ease,
             color .18s ease, border-color .18s ease;
}
.gt:hover:not(.on), .rf-chip:hover:not(.on){
  transform:translateY(-1px);
  box-shadow:var(--d3-lift), var(--d3-ground), var(--d3-glow);
}
.gt:active, .rf-chip:active{
  transform:translateY(1px);
  box-shadow:var(--d3-lift), var(--d3-press);
}
.gt.on, .rf-chip.on{
  background:linear-gradient(180deg, var(--gold-l) 0%, var(--gold) 100%);
  box-shadow:0 1px 0 rgba(255,255,255,.35) inset, var(--d3-press);
  transform:translateY(1px);
}

/* ── FLAKES ON A SOLID PANEL ──────────────────────────────────
   The body-level flakes are FIXED and sit at z-index 0. Any section that
   paints its own solid background covers them — which is why they showed in
   some places and not others, and exactly the panels Rudy wants them on are
   the solid ones.

   .flaked gives a panel its OWN flake layer inside its background. Same
   gradients, same drift, but scoped to the element so it travels with the
   panel and cannot leak over neighbours.

   The panel must establish a stacking context (position + isolation) or the
   layer escapes; and its children need lifting, or the shimmer washes over the
   text sitting on it. */
.flaked{ position:relative; isolation:isolate; }
.flaked::before{
  content:'';
  position:absolute; inset:0;
  z-index:0;
  pointer-events:none;
  opacity:calc(var(--flake-op) * 1.15);   /* a touch brighter — a lit panel is darker than the page */
  background-image:
    radial-gradient(1.6px 1.6px at 12% 18%, var(--flake-gold) 50%, transparent 51%),
    radial-gradient(1.4px 1.4px at 78% 8%,  var(--flake-gold-2) 50%, transparent 51%),
    radial-gradient(1.8px 1.8px at 43% 62%, var(--flake-gold) 50%, transparent 51%),
    radial-gradient(1.3px 1.3px at 88% 71%, var(--flake-gold-2) 50%, transparent 51%),
    radial-gradient(1px 1px at 61% 31%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1px 1px at 8%  54%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1.1px 1.1px at 95% 43%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1px 1px at 35% 12%, var(--flake-silver) 50%, transparent 51%);
  background-size: 340px 340px, 340px 340px, 340px 340px, 340px 340px,
                   210px 210px, 210px 210px, 210px 210px, 210px 210px;
  animation: tssa-flake-drift calc(var(--flake-drift) * 1.35) linear infinite;
  border-radius:inherit;
}
.flaked > *{ position:relative; z-index:1 }
@media (prefers-reduced-motion: reduce){ .flaked::before{ animation:none } }

/* Search fields and other plain solid inputs — the same shimmer, dialled well
   down. A field is for reading what you typed, so this is barely there. */
.flaked-field{ position:relative; isolation:isolate; }
.flaked-field::before{
  content:'';
  position:absolute; inset:0; z-index:0; pointer-events:none;
  opacity:calc(var(--flake-op) * .5);
  background-image:
    radial-gradient(1px 1px at 20% 40%, var(--flake-gold-2) 50%, transparent 51%),
    radial-gradient(1px 1px at 72% 65%, var(--flake-silver) 50%, transparent 51%),
    radial-gradient(1px 1px at 45% 20%, var(--flake-silver) 50%, transparent 51%);
  background-size:120px 120px,120px 120px,120px 120px;
  border-radius:inherit;
}
.flaked-field > *{ position:relative; z-index:1 }

/* ══════════════════════════════════════════════════════════════
   COLLAPSIBLE PANEL HEADERS — What We Offer, Access Portals.

   Rudy: "centre text for these headers and enhance font and enlarge size with
   a flashing visual effect and a 3D drop-down on one click (if mouse hovers,
   pop-up click to drop text in this section)."

   Both panels default to minimised now, which means these bars ARE the page
   until someone opens them. A 20px left-aligned label was fine as a divider
   above open content; as the only thing on screen it has to carry the section.

   The "flashing" is a slow breathing glow rather than a blink. A hard flash on
   a heading is an accessibility problem — WCAG draws the line at three flashes
   a second and anything that pulses hard is unpleasant long before that. This
   reads as "alive" without ever crossing into strobing.

   Both layers share this file, so the React <Section> and the vanilla twin get
   it together and cannot drift.
   ══════════════════════════════════════════════════════════════ */
.csec-bar{
  justify-content:center;          /* centred, per the brief */
  position:relative;
  gap:14px;
  padding-top:26px; padding-bottom:22px;
  transition:transform .18s ease;
}
.csec-title{
  font-size:clamp(26px, 3.4vw, 40px) !important;
  letter-spacing:.005em;
  color:var(--t1);
  text-shadow:var(--glow-g-soft);
  animation:csec-breathe 3.6s ease-in-out infinite;
}
@keyframes csec-breathe{
  0%,100%{ text-shadow:var(--glow-g-soft); opacity:.92 }
  50%    { text-shadow:var(--glow-g);      opacity:1 }
}

/* The rule becomes a pair of wings either side of a centred title. */
.csec-rule{
  width:44px !important; height:2px !important;
  background:linear-gradient(90deg, transparent, var(--gold)) !important;
}
.csec-bar::after{
  content:'';
  width:44px; height:2px; border-radius:99px;
  background:linear-gradient(90deg, var(--gold), transparent);
}
/* The chevron was pushed right by margin-left:auto — centred layout needs it
   in the flow instead. */
.csec-chev{ margin-left:0 !important; font-size:20px !important }

/* ── The hover hint ───────────────────────────────────────────
   "If mouse hovers, pop-up click to drop text in this section." A collapsed
   bar does not explain itself; this says what a click will do, and only to
   people using a pointer. */
.csec-bar::before{
  content:'Click to open this section';
  position:absolute;
  top:-2px; left:50%;
  transform:translate(-50%, -6px);
  padding:4px 12px; border-radius:99px;
  font-family:var(--sans);
  font-size:9.5px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--gold-l);
  background:rgba(16,14,10,.94);
  border:1px solid rgba(201,168,76,.34);
  box-shadow:0 6px 18px -8px rgba(0,0,0,.9);
  opacity:0; pointer-events:none;
  transition:opacity .18s ease, transform .18s ease;
  white-space:nowrap;
}
.csec-bar:hover::before{ opacity:1; transform:translate(-50%, -14px) }
.csec.open .csec-bar::before{ content:'Click to close this section' }

/* ── The 3D drop ──────────────────────────────────────────────
   The bar presses down as the panel drops, so the click has a physical
   result rather than content simply appearing underneath. */
.csec-bar:active{ transform:translateY(2px) }
.csec.open .csec-bar{
  transform:translateY(1px);
  text-shadow:none;
}
/* THE 3D DROP RIDES ON THE BODY, NEVER ON THE WRAP.

   It used to run on .csec-wrap — the element that also owns the open/close
   HEIGHT. When that animation stalled it held the wrap at its `from` keyframe
   (opacity 0) and the whole section rendered as a bar with nothing under it.
   Rudy found it as "Discover has no data" and "What we offer disappears".

   The wrap does height. The body does the flourish. Two elements, two jobs,
   and a stalled flourish can no longer hide the content. */
.csec-body{ transform-origin:top center }
.csec.open .csec-body{ animation:csec-drop .34s cubic-bezier(.2,.8,.25,1) both }
@keyframes csec-drop{
  from{ opacity:0; transform:perspective(900px) rotateX(-7deg) translateY(-10px) }
  to  { opacity:1; transform:perspective(900px) rotateX(0)     translateY(0) }
}

/* Touch has no hover, so the hint would never show — and reduced motion should
   not get a breathing heading. */
@media (hover:none){ .csec-bar::before{ display:none } }
@media (prefers-reduced-motion:reduce){
  .csec-title{ animation:none }
  .csec.open .csec-body{ animation:none }
  .csec-bar, .csec-bar:active{ transform:none }
}

/* ══════════════════════════════════════════════════════════════
   FLOATING CONTROLS FADE AT THE FOOTER.

   Rudy: "as a user enters the footer, public and in all portals, any floating
   buttons need to fade away so footer text is all clearly visible."

   The footer is the densest text on the site — company registration, VAT,
   contact addresses, legal links — and it is exactly where four floating
   things pile up: the sticky CTA, the WhatsApp button, the ARIA badge and the
   sound dock. Reading a VAT number around a floating button is miserable.

   ONE CLASS ON <body>, driven by an IntersectionObserver in
   /tssa-footer-fade.js, so both layers behave identically and neither has to
   know what floats on the other.

   They fade and lift slightly rather than vanishing — a control that blinks
   out reads as a bug. pointer-events goes with the opacity so a faded button
   cannot be clicked by accident while invisible.
   ══════════════════════════════════════════════════════════════ */
body.at-footer .sticky-cta,
body.at-footer .wa-float,
body.at-footer .ai-float,
body.at-footer #soundDock,
body.at-footer .tssa-guide-btn,
body.at-footer .slb-dock,
body.at-footer [data-float]{
  opacity:0 !important;
  transform:translateY(10px) !important;
  pointer-events:none !important;
  transition:opacity .3s ease, transform .3s ease !important;
}

/* An open panel is a deliberate act — someone reading the radio list or
   mid-conversation with ARIA should not have it yanked away because they
   scrolled. Only the collapsed, decorative state fades. */
body.at-footer #soundDock.open,
body.at-footer .ai-drawer.open{
  opacity:1 !important;
  transform:none !important;
  pointer-events:auto !important;
}

@media (prefers-reduced-motion: reduce){
  body.at-footer .sticky-cta,
  body.at-footer .wa-float,
  body.at-footer .ai-float,
  body.at-footer #soundDock,
  body.at-footer [data-float]{ transition:none !important }
}

