/* =========================================================
   SPLASH PAGE — Tour Dates · poster-style dates edition
   Orientation logic:
     - Landscape (wide):  square graphic LEFT, dates RIGHT
     - Portrait  (tall):  square graphic TOP,  dates BELOW
   ========================================================= */

:root {
  /* Paper base — neutral gray, no warm/cream tint */
  --paper-fallback: #e4e4e4;
  --ink:            #14110d;
  --ink-dim:        #6b5e4f;
  --accent:         #c8391e;
  --accent-deep:    #8e2413;
  --rule:           rgba(20, 17, 13, 0.18);
  --rule-strong:    rgba(20, 17, 13, 0.40);

  /* Typography */
  /* One typeface, one variable, used everywhere. */
  --font-display: "NeueHaasGroteskDisplayPro", Helvetica,  sans-serif;

  --pad: clamp(1.25rem, 3vw, 3rem);



    --cookie_bg: rgba(0, 0, 0, 0);
    --anti_d_padding: 20px;
    --anti_d_padding_right: 20px;
    --text-shadows: rgba(0, 0, 0, 0.3);
    --text-shadows-heavy: rgba(0, 0, 0, 0.6);
  


  /* Landscape-layout reservation: dates column never shrinks below
     this width. Used in 2 places (panel flex-basis and graphic's
     width constraint). Change here to change both. Needs to be wide
     enough for the longest venue string at the landscape font-size. */
  --dates-min: 420px;
}

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

/* Body grows to content height naturally. The paper layer is a fixed
   pseudo below, so it covers the viewport regardless of scroll depth. */

body {
  /* Prevent any stray wide element (cookie banner, etc.) from introducing
     horizontal scroll, which can visually offset everything inside. */
  overflow-x: hidden;

  /* Paper texture is on body::before (fixed pseudo-layer) rather than
     a body background, so it's pinned to the viewport and never scrolls.
     This fallback colour shows for the single frame before ::before paints. */
  background-color: var(--paper-fallback);

  color: var(--ink);
  font-family: var(--font-display);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---------- Paper-texture background layer ----------
   Fixed to the viewport so it stays covered at all times, never scrolls,
   and doesn't rely on `background-attachment: fixed` (which has iOS
   quirks). z-index: -1 places it behind content but above body's
   fallback background-color.

   GLITCH: the `bg-glitch` animation runs on a long 12s loop with most
   keyframes sitting at a neutral resting state. Glitch hits are clustered
   at a few percentages so the effect reads as "occasional malfunction"
   rather than constant chaos. Each hit combines:
     - translate()    → hard horizontal/vertical jumps (tracking error)
     - drop-shadow()  → RGB-split flashes (chromatic aberration)
     - brightness()   → exposure flicks
   Paired with grayscale(1) so the drop-shadow colours read as chromatic
   fringe rather than a full colour shift. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  background-image: url('./images/Texturelabs_Paper_312L.jpg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
  filter: grayscale(1);
  animation: bg-glitch 12s steps(40) infinite;
  will-change: filter;
}

@keyframes bg-glitch {
  /* Long stretches of calm... */
  0%,  7%,
  12%, 26%,
  31%, 48%,
  54%, 71%,
  76%, 92%,
  97%, 100% {
    filter: grayscale(1) brightness(1);
  }
  /* ...punctured by glitch hits. No translation — paper stays put,
     only brightness pulses and chromatic aberration flashes. */
  8%  { filter: grayscale(1) brightness(1.15) drop-shadow(3px 0 0 rgba(255, 0, 80, 0.6)) drop-shadow(-3px 0 0 rgba(0, 200, 255, 0.6)); }
  9%  { filter: grayscale(1) brightness(0.85); }
  10% { filter: grayscale(1) brightness(1); }
  11% { filter: grayscale(1) brightness(1.08); }

  27% { filter: grayscale(1) drop-shadow(-4px 0 0 rgba(0, 255, 200, 0.5)) drop-shadow(4px 0 0 rgba(255, 50, 100, 0.5)); }
  28% { filter: grayscale(1) brightness(1.2); }
  29% { filter: grayscale(1); }

  49% { filter: grayscale(1) brightness(0.7); }
  50% { filter: grayscale(1) drop-shadow(5px 0 0 rgba(255, 0, 120, 0.55)) drop-shadow(-5px 0 0 rgba(0, 180, 255, 0.55)); }
  51% { filter: grayscale(1) brightness(1.3); }
  52% { filter: grayscale(1); }

  72% { filter: grayscale(1) brightness(1.1); }
  73% { filter: grayscale(1) drop-shadow(3px 0 0 rgba(255, 40, 80, 0.5)) drop-shadow(-3px 0 0 rgba(40, 220, 255, 0.5)); }
  74% { filter: grayscale(1); }

  93% { filter: grayscale(1) brightness(0.8) drop-shadow(-4px 0 0 rgba(255, 0, 100, 0.6)); }
  94% { filter: grayscale(1) brightness(1.15); }
  95% { filter: grayscale(1); }
}

/* ---------- Scanline sweep ----------
   A faint vertical bar that sweeps left-to-right slowly. Separate
   pseudo-layer so it composites independently of the paper glitch. */
html::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    to right,
    transparent 0%,
    transparent 48%,
    rgba(255, 255, 255, 0.04) 49%,
    rgba(0, 0, 0, 0.08) 50%,
    rgba(255, 255, 255, 0.04) 51%,
    transparent 52%,
    transparent 100%
  );
  background-size: 300vw 100%;
  animation: scanline-sweep 6s linear infinite;
  mix-blend-mode: overlay;
}

@keyframes scanline-sweep {
  0%   { background-position: -200vw 0; }
  100% { background-position:  100vw 0; }
}

/* ---------- Film-grain overlay ----------
   A fixed pseudo-layer of fractal noise that translates in discrete
   steps to flicker like real film grain. Blended via `overlay` so
   light pixels brighten slightly and dark pixels darken — adds motion
   and texture without tinting the paper. Oversized (inset: -20%) so
   translation never reveals the edge. */
body::after {
  content: '';
  position: fixed;
  inset: -20%;
  pointer-events: none;
  z-index: 10;

  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 240px 240px;

  opacity: 0.18;
  mix-blend-mode: overlay;
  animation: grain 1.2s steps(6) infinite;
  will-change: transform;
}

@keyframes grain {
  0%   { transform: translate(0,     0);    }
  17%  { transform: translate(-3%,  -4%);   }
  33%  { transform: translate(4%,   -2%);   }
  50%  { transform: translate(-2%,   5%);   }
  67%  { transform: translate(3%,    3%);   }
  83%  { transform: translate(-5%,  -3%);   }
  100% { transform: translate(0,     0);    }
}

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

/* ---------- Image flicker & shift ----------
   Two paired animations per cutout:
     - Opacity animations (warm-up, subtle-flicker) run on the inner <image>
     - Transform animations (warm-up-shift, subtle-shift) run on the <g>
       wrapper so they compose with the image's baked-in SVG rotation
   Delays are shared across both via --warm-delay custom property,
   which inherits from .cutout-g down to its <image> child. */

@keyframes warm-up {
  0%   { opacity: 0;    }
  /* First faint blip after a pause */
  10%  { opacity: 0.2;  }
  13%  { opacity: 0;    }
  /* Second flash, slightly stronger */
  27%  { opacity: 0.4;  }
  30%  { opacity: 0.05; }
  /* Getting quicker and brighter */
  42%  { opacity: 0.6;  }
  44%  { opacity: 0.15; }
  51%  { opacity: 0.78; }
  54%  { opacity: 0.3;  }
  /* Rapid settle-in */
  61%  { opacity: 0.92; }
  63%  { opacity: 0.5;  }
  68%  { opacity: 1;    }
  71%  { opacity: 0.7;  }
  76%  { opacity: 1;    }
  79%  { opacity: 0.85; }
  84%  { opacity: 1;    }
  87%  { opacity: 0.93; }
  92%  { opacity: 1;    }
  95%  { opacity: 0.97; }
  100% { opacity: 1;    }
}

@keyframes subtle-flicker {
  0%, 100% { opacity: 1;    }
  /* Only two dips per 9s loop. Shallower so they read as ambient
     imperfection rather than a strobe. */
  23%      { opacity: 0.82; }
  27%      { opacity: 1;    }
  71%      { opacity: 0.86; }
  75%      { opacity: 1;    }
}

/* Wrapper <g>: no animation on the wrapper — cutouts sit still.
   The inner <image> handles all opacity flicker + filter toggling. */
.graphic__art .cutout-g {
  transform-box: fill-box;
  transform-origin: center;
}

/* Glitch-dup pulse: flips the SVG #glitch-dup filter on for the duration
   of each flicker dip, off between. Same percentages as subtle-flicker
   so the RGB-split appears exactly when the opacity drops. `steps(1)`
   prevents the browser from tweening the filter URL, which wouldn't
   work anyway — it toggles cleanly on/off. */
@keyframes dup-pulse {
  0%, 22%, 28%, 70%, 76%, 100% {
    filter: none;
  }
  23%, 27%, 71%, 75% {
    filter: url(#glitch-dup);
  }
}

/* Inner <image>: handles the opacity part of the flicker.
   Shares the same --warm-delay via CSS custom-property inheritance. */
.graphic__art .cutout-g image {
  /* Multiply-blend so the dark cutouts sink into the paper texture
     rather than sitting flatly on top of it. */
  mix-blend-mode: multiply;
  animation-name:            warm-up,            subtle-flicker,                    dup-pulse;
  animation-duration:        3s,                 9s,                                9s;
  animation-iteration-count: 1,                  infinite,                          infinite;
  animation-fill-mode:       both,               none,                              none;
  animation-timing-function: ease-out,           linear,                            steps(1);
  animation-delay:           var(--warm-delay),  calc(var(--warm-delay) + 3s),      calc(var(--warm-delay) + 3s);
}

/* Single source of truth for per-cutout start delays. Both the
   transform on .cutout-g and the opacity on its <image> pick this
   up via inheritance, so they stay in perfect sync. */
.graphic__art .cutout-g:nth-child(1)  { --warm-delay: 0.3s; }
.graphic__art .cutout-g:nth-child(2)  { --warm-delay: 0.8s; }
.graphic__art .cutout-g:nth-child(3)  { --warm-delay: 0.1s; }
.graphic__art .cutout-g:nth-child(4)  { --warm-delay: 1.2s; }
.graphic__art .cutout-g:nth-child(5)  { --warm-delay: 0.5s; }
.graphic__art .cutout-g:nth-child(6)  { --warm-delay: 1.0s; }
.graphic__art .cutout-g:nth-child(7)  { --warm-delay: 0.2s; }
.graphic__art .cutout-g:nth-child(8)  { --warm-delay: 1.4s; }
.graphic__art .cutout-g:nth-child(9)  { --warm-delay: 0.7s; }
.graphic__art .cutout-g:nth-child(10) { --warm-delay: 0.9s; }

@media (prefers-reduced-motion: reduce) {
  .graphic__art .cutout-g,
  .graphic__art .cutout-g image {
    animation: none;
    opacity: 1;
    transform: none;
  }
}

a {
  color: inherit;
  text-decoration: none;
}

/* ---------- LAYOUT (landscape default) ---------- */

.splash {
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(1rem, 2vw, 2rem);
  padding: var(--pad);
}

/* ---------- GRAPHIC ---------- */

.graphic {
  /* Always sized by `width` so orientation flips don't leave stale
     height computations behind. The `min()` picks the tightest of:
       - width-bound (viewport - padding - gap - dates min width)
       - height-bound (viewport height - padding, since width===height)
     Wrapped in max() with 0 so a cramped viewport that would produce
     a negative calc doesn't collapse the element — layout stays sane. */
  width: max(0px, min(
    calc(100dvw - var(--pad) * 2 - 2rem - var(--dates-min)),
    calc(100dvh - var(--pad) * 2)
  ));
  aspect-ratio: 1 / 1;
  flex-shrink: 0;
}

.graphic__inner {
  /* Fill parent. Parent's aspect-ratio determines the shape. */
  width: 100%;
  aspect-ratio: 1 / 1;
  position: relative;
  overflow: hidden;
}

.graphic__art {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- DATES PANEL ---------- */

.dates-panel {
  flex: 0 0 var(--dates-min);
  mix-blend-mode: multiply;
}

.dates-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.date-row {
  /* No borders — whitespace does the separating. Rows have no
     padding/margin so the .dates-list `gap` is the only source of
     vertical spacing, which keeps it identical between rows. */
  transition: opacity 260ms ease;
}

/* Per-row hover rotation angles.
   These nth-child values provide a JS-free fallback so hover still
   tilts even without JavaScript. When JS is available, app.js
   overrides --row-rot per load with true randomness. */
.date-row:nth-child(1)  { --row-rot:  1.2deg; }
.date-row:nth-child(2)  { --row-rot: -0.8deg; }
.date-row:nth-child(3)  { --row-rot:  1.5deg; }
.date-row:nth-child(4)  { --row-rot: -1.3deg; }
.date-row:nth-child(5)  { --row-rot:  0.7deg; }
.date-row:nth-child(6)  { --row-rot: -1.6deg; }
.date-row:nth-child(7)  { --row-rot:  1.1deg; }
.date-row:nth-child(8)  { --row-rot: -0.9deg; }

.date-row__link {
  display: block;
  color: var(--ink);
  text-align: center;
  /* Default: no rotation. On hover/focus the row tilts to its random
     angle (set per-row by app.js as --row-rot), then eases back to 0
     on leave. Gives the dates an "at attention, then relax" feel. */
  transform: rotate(0deg);
  transform-origin: center center;
  transition: transform 320ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.date-row__link:hover,
.date-row__link:focus-visible {
  transform: rotate(var(--row-rot, 0deg));
}

.date-row__link:focus-visible {
  outline: 1px dashed var(--ink);
  outline-offset: 6px;
}

/* When a row is hovered or focused, dim the siblings.
   :focus-within covers keyboard navigation; the plain :hover covers
   mouse. The specific active row is re-raised back to opacity 1. */
.dates-list:hover .date-row,
.dates-list:focus-within .date-row {
  opacity: 0.22;
}
.dates-list:hover .date-row:hover,
.dates-list:focus-within .date-row:focus-within {
  opacity: 1;
}

/* Date and venue lines — heavy display type, flat size.
   No size hierarchy between date / venue — both read as one unit. */
.date-row__date,
.date-row__venue,
.date-row__city,
.date-row__place {
  display: block;
  font-family: var(--font-display);
  font-weight: 500;
  /* 18px base (portrait / mobile). Landscape desktop bumps to 24px
     via the media query below. */
  font-size: 18px;
  line-height: 0.7;
  letter-spacing: -0.05em;
  text-transform: uppercase;
  -webkit-font-smoothing: antialiased;
}

@media (orientation: landscape) and (min-width: 700px) {
  .date-row__date,
  .date-row__venue,
  .date-row__city,
  .date-row__place {
    font-size: 24px;
  }
}

/* Force venue on one line so every row has identical height,
   which in turn keeps the vertical gap between rows visually even. */
.date-row__venue {
  white-space: nowrap;
}

.date-row__date {
  margin-bottom: 0.2em;
}

/* City and place share the same line visually (display: inline) so the
   layout reads as one wrapped venue string, but remain separate spans
   so WP/ACF can drive them from separate fields. */
.date-row__city,
.date-row__place {
  display: inline;
}
.date-row__city::after {
  content: " ";
}

/* =========================================================
   PORTRAIT — graphic on top, dates below
   ========================================================= */

@media (orientation: portrait) {
  .splash {
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 0;
    min-height: auto;
    /* Extra top space to clear the fixed cookie banner. On mobile
       (<=640px) the banner is inline (see mobile override below),
       so the padding is reduced there — no longer reserving space
       for a floating element. */
    padding-top: clamp(8rem, 28vw, 12rem);
  }

  @media (max-width: 640px) {
    .splash {
      padding-top: var(--pad);
    }
  }

  .graphic {
    /* In portrait, width formula simplifies — no dates beside it,
       no gap to subtract. Explicitly re-declare aspect-ratio and
       margin reset so nothing from the landscape rule persists
       across orientation flips. */
    width: min(calc(100dvw - var(--pad) * 2), 32rem);
    aspect-ratio: 1 / 1;
    margin-left: auto;
    margin-right: auto;
    align-self: center;
  }

  .dates-panel {
    flex: 0 0 auto;
    max-width: min(calc(100dvw - (var(--pad) * 2)), 36rem);
    padding-bottom:50px;
  }
}

/* ---------- Motion preferences ---------- */

@media (prefers-reduced-motion: reduce) {
  .date-row__link {
    transition: none;
  }
}

/* ---------- Initial hidden state for GSAP reveal ---------- */
.js-enabled .graphic,
.js-enabled .date-row {
  opacity: 0;
}


.bordered { border:2px solid var(--ink); padding:10px; padding-bottom:4px; }
.note { display:inline-block; text-align:center !important; font-weight:500; font-size:0.7em; text-transform:uppercase; line-height:1.0em; margin-top:10px;}




.monotype {
    font-family: var(--font-display) !important;
    font-style: normal;
    font-stretch: normal;
    font-weight: 500;	
}

  #text_overlay {
    position: absolute;
    left: var(--anti_d_padding);
    top: var(--anti_d_padding);
    z-index: 10;
    color: var(--ink);
    text-transform: lowercase;
    opacity: 0;
    width: 300px;
  }

  .cky-notice-des *,
  .cky-dma-content-wrapper * {
    font-size: 12px !important;
  }

  .cky-tab-desc a {
    text-decoration: underline !important;
    color: var(--ink);
  }

  .cky-bar-text {
    text-align: left !important;
  }

  .cky-consent-bar .cky-btn-customize::after {
    display: none !important;
  }

  .cky-notice-des {
    font-size: 12px !important;
    line-height: 1 !important;
    font-family: var(--font-display) !important;
    font-weight: 500 !important;
  }

  .cky-tab-desc a,
  .cky-policy {
    text-decoration: underline !important;
    color: var(--ink) !important;
  }


  .cky-notice-group {
    justify-content: end !important;
  }

  /* Equal gap between all three buttons. Neutralise CookieYes's
     per-button margins and use flex gap instead. No forced display:flex
     on desktop — CookieYes handles the bar layout; we just fix the
     inter-button spacing. */
  .cky-notice-btn-wrapper .cky-btn {
    margin: 0 !important;
  }
  .cky-notice-btn-wrapper {
    gap: 14px !important;
  }

  /* On narrower viewports where the banner stacks vertically, force
     the buttons into their own flex row with breathing room above. */
  @media (max-width: 640px) {
    .cky-notice-btn-wrapper {
      display: flex !important;
      flex-wrap: wrap !important;
      justify-content: center !important;
      margin-top: 18px !important;
    }
  }

  .cky-btn {
    font-size: 9px !important;
    background-color: var(--cookie_bg) !important;
    color: var(--ink) !important;
    border: 2px solid var(--ink) !important;
    text-align: center !important;
    max-width: 30vw !important;
    border-radius: 0 !important;
    padding: 5px !important;
    /* font-family, font-weight, text-transform, letter-spacing, line-height
       now inherited from the unified rule above (cky-consent-bar,*). */
  }

  .cky-consent-title,
  .cky-bar-text,
  .cky-btn-readMore {
    font-size: 12px !important;
    line-height: 1 !important;
    text-align: center !important;
    background-color: var(--cookie_bg) !important;
    color: var(--ink) !important;
    /* letter-spacing set by body-prefixed block above */
  }

  .cky-consent-title {
    display: none;
  }

  .cky-consent-bar {
    background-color: var(--cookie_bg) !important;
    box-shadow: none !important;
    top: 10px !important;
    bottom: auto !important;
    left: 10px !important;
    right: 10px !important;
  }

  .cky-classic .cky-btn-settings:before {
    display: none !important;
  }

  .cky-consent-bar.cky-classic {
    width: auto !important;
  }

  .cky-notice .cky-title,
  .cky-notice-des {
    color: var(--ink) !important;
    text-align: center !important;
    /* font-size, line-height set by main .cky-notice-des rule above */
  }

  .cky-classic-bottom {
    /* Banner at top of page, not bottom. The class is still
       called "cky-classic-bottom" because that's what CookieYes
       emits — we override its position via !important. */
    top: 0 !important;
    bottom: auto !important;
    right: 0px !important;
    left: 0px !important;
    max-width: 100vw !important;
  }

  /* Mobile: let the banner flow inline with document instead of
     floating fixed at viewport top. Lets the user scroll the banner
     away with the rest of the page rather than eating permanent
     screen real estate. Also strips the portrait `padding-top`
     clearance since we no longer need to reserve space for a fixed
     banner. */
  @media (max-width: 640px) {
    .cky-consent-container,
    .cky-consent-bar,
    .cky-classic-bottom {
      position: static !important;
      top: auto !important;
    }
  }

  /* ============================================================
     COOKIE BANNER TYPOGRAPHY — MUST MATCH SIGN-UP STYLE
     Consolidated into ONE high-specificity block.
     Prefixed with `body` to beat any equal-specificity rule from
     CookieYes's own stylesheet (which loads after ours).
     ============================================================ */
  body .cky-consent-container,
  body .cky-consent-container *,
  body .cky-consent-bar,
  body .cky-consent-bar *,
  body .cky-notice,
  body .cky-notice *,
  body .cky-notice-des,
  body .cky-notice-des *,
  body .cky-notice-des p,
  body .cky-notice-des span,
  body .cky-btn,
  body .cky-consent-title,
  body .cky-bar-text,
  body .cky-btn-readMore,
  body .cky-policy {
    font-family: var(--font-display) !important;
    font-weight: 500 !important;
    text-transform: uppercase !important;
    letter-spacing: -0.05em !important;
    line-height: 1 !important;
  }

  /* Preserve existing banner frame styles (border, margin, padding)
     but DON'T let letter-spacing be reset to 0 by this block */
  .cky-consent-container .cky-consent-bar {
    border: 0px solid var(--ink) !important;
    text-align: center;
    margin: 10px;
    padding: 0px !important;
  }


.cky-consent-container {
    width: 100% !important;
  }

  .cky-notice .cky-title {
    display: none;
  }