/* ============================================================================
   Vibe: The Substation
   Industrial basement club. Brutalist sans, black + single neon accent,
   distressed textures, lineup-heavy layout.

   Built for venue-anchored DJ nights — the lineup IS the product, so this
   vibe gives lineup its own oversized block instead of burying it.

   Color story: near-black concrete + ONE neon accent (per-event overridable
   via --event-accent). Texture comes from layered radial gradients that fake
   concrete dust without an image.
   ============================================================================ */

/* Top-level body selectors need to match the specificity of body.landing-dark
   (which app.css loads alongside this vibe) — hence body.vibe-substation rather
   than bare .vibe-substation here.

   Palette variables live here so they cascade down to .event-substation and
   below. The theme_overrides system from lib/vibe_templates.php emits a later
   <style> block targeting body.vibe-substation that overrides these defaults
   when a promoter customizes their palette. */
body.vibe-substation {
  --sub-bg:   #050505;
  --sub-card: #161616;
  --sub-ink:  #f5f5f5;

  background:
    linear-gradient(180deg, rgba(5,5,5,0.78) 0%, rgba(5,5,5,0.55) 45%, rgba(5,5,5,0.95) 100%),
    url('/assets/uploads/studio/substation-bg.webp') center top / cover no-repeat fixed,
    var(--sub-bg);
  color: var(--sub-ink);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
}

/* Subtle concrete-dust noise across the whole page — pure CSS, no asset. */
body.vibe-substation::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(ellipse at 20% 10%, rgba(255,255,255,0.025) 0%, transparent 50%),
    radial-gradient(ellipse at 80% 90%, rgba(255,255,255,0.02) 0%, transparent 50%),
    repeating-linear-gradient(
      0deg,
      transparent 0px,
      transparent 2px,
      rgba(255,255,255,0.008) 2px,
      rgba(255,255,255,0.008) 3px
    );
}

/* Override the global header to feel like a steel rail across the top. */
.vibe-substation .site-header {
  background: rgba(0,0,0,0.85) !important;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  position: relative;
  z-index: 10;
}

.vibe-substation main {
  position: relative;
  z-index: 1;
}

/* Wrapper — kills card-style margins around the content */
.vibe-substation .event-substation {
  --event-accent: #00ff9d;
  color: var(--sub-ink);
  padding: 0 0 var(--s-16, 80px);
  min-height: calc(100vh - 80px);
}

/* ---------- Hero strip ---------- */
.vibe-substation .sub-hero {
  border-top: 1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  background:
    linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(255,255,255,0.015) 100%);
  padding: var(--s-8) 0 var(--s-6);
}

.vibe-substation .sub-hero__date {
  font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace;
  font-size: 12px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--event-accent);
  margin-bottom: var(--s-3);
}

.vibe-substation .sub-hero__title {
  font-family: 'Archivo Black', 'Inter', sans-serif;
  font-weight: 900;
  /* Matched ceiling with Sub Bass — ~14-char titles fit on one line in the
     1120px container. Longer titles wrap evenly via text-wrap: balance. */
  font-size: clamp(44px, 7.5vw, 100px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  margin: 0;
  color: #f5f5f5;
  text-transform: uppercase;
  text-wrap: balance;
  /* RAVE signature: chromatic RGB-split + neon-green glow, with a periodic
     glitch jitter. Pure text-shadow so no markup/pseudo-element is needed. */
  text-shadow:
     0.028em 0 0 rgba(255, 42, 120, 0.75),
    -0.028em 0 0 rgba(0, 240, 255, 0.75),
     0 0 18px rgba(0, 255, 157, 0.55);
  animation: sub-glitch 3.8s steps(1) infinite;
  will-change: transform, text-shadow;
}
@keyframes sub-glitch {
  0%, 90%, 100% {
    text-shadow: 0.028em 0 0 rgba(255,42,120,.75), -0.028em 0 0 rgba(0,240,255,.75), 0 0 18px rgba(0,255,157,.55);
    transform: translate(0, 0) skewX(0);
  }
  91% { text-shadow: -0.06em 0 0 rgba(255,42,120,.95), 0.06em 0 0 rgba(0,240,255,.95), 0 0 30px rgba(0,255,157,.85); transform: translate(3px,-1px) skewX(-3deg); }
  93% { text-shadow: 0.07em 0 0 rgba(255,42,120,.95), -0.07em 0 0 rgba(0,240,255,.95), 0 0 30px rgba(0,255,157,.85); transform: translate(-3px, 1px) skewX(2deg); }
  95% { transform: translate(2px, 0) skewX(0); }
  97% { transform: translate(-1px, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .vibe-substation .sub-hero__title { animation: none; }
}

.vibe-substation .sub-hero__venue {
  margin-top: var(--s-5);
  font-size: 16px;
  letter-spacing: 0.04em;
  color: #b8b8b8;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  align-items: baseline;
}

.vibe-substation .sub-hero__venue strong {
  color: #f5f5f5;
  font-weight: 600;
}

.vibe-substation .sub-hero__presented {
  font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #8a8a8a;
  margin-top: var(--s-3);
}

.vibe-substation .sub-hero__presented a {
  color: var(--event-accent);
  text-decoration: none;
}

.vibe-substation .sub-hero__presented a:hover {
  text-decoration: underline;
}

/* ---------- Two-column body grid ---------- */
.vibe-substation .sub-grid {
  display: grid;
  /* minmax(0, Xfr) prevents grid intrinsic-content blowout on Android Chrome */
  grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
  gap: var(--s-10);
  padding: var(--s-8) 0;
  align-items: start;
}

/* ---------- Flyer ---------- */
.vibe-substation .sub-flyer {
  position: relative;
  border: 1px solid rgba(255,255,255,0.08);
  background: #0c0c0c;
}

.vibe-substation .sub-flyer img {
  display: block;
  width: 100%;
  height: auto;
}

.vibe-substation .sub-flyer--empty {
  aspect-ratio: 4/5;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255,255,255,0.15);
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

/* Corner-marks on the flyer — like a contact-sheet crop frame */
.vibe-substation .sub-flyer::before,
.vibe-substation .sub-flyer::after {
  content: '';
  position: absolute;
  width: 18px;
  height: 18px;
  border: 1px solid var(--event-accent);
  pointer-events: none;
}
.vibe-substation .sub-flyer::before { top: -6px; left: -6px;  border-right: 0; border-bottom: 0; }
.vibe-substation .sub-flyer::after  { bottom: -6px; right: -6px; border-left: 0; border-top: 0; }

/* [flyerframe-20260918] AN EMPTY FRAME IS WHAT SENT BACK2BASSICS CLICKING. Deleting the flyer
   image on the canvas removes only the <img>; the .sub-flyer frame stayed behind as a bordered
   zero-height line with accent corner marks, the left column went lopsided, and the promoter
   kept deleting and switching layouts trying to make it look right. When the frame has no image
   (live page: <img> removed; studio: <img> tombstoned with data-vfx-del) the frame goes with it.
   .sub-flyer--empty is a different state -- no flyer uploaded yet -- and keeps its placeholder. */
.vibe-substation .sub-flyer:not(.sub-flyer--empty):not(:has(img)),
.vibe-substation .sub-flyer:not(.sub-flyer--empty):has(> img[data-vfx-del="1"]) {
  display: none;
}
/* ...and the block under it drops the top margin that was spacing it off the flyer, so About
   sits level with Lineup. !important because the partial sets that margin inline. `+ *` rather
   than `+ .sub-block` because a section swap can rename the block (e.g. hdl-block). */
.vibe-substation .sub-flyer:not(.sub-flyer--empty):not(:has(img)) + *,
.vibe-substation .sub-flyer:not(.sub-flyer--empty):has(> img[data-vfx-del="1"]) + * {
  margin-top: 0 !important;
}
/* And when the frame itself was deleted (not just the image), About becomes the column's
   first child with that same inline margin -- so whatever leads the left column sits flush
   with the top of Lineup on the right. */
.vibe-substation .sub-grid > div > :first-child {
  margin-top: 0 !important;
}

/* ---------- Section block (lineup, about, blurb, tickets) ---------- */
.vibe-substation .sub-block {
  border: 1px solid rgba(255,255,255,0.08);
  background: rgba(255,255,255,0.015);
  padding: var(--s-6) var(--s-6);
  margin-bottom: var(--s-6);
}

.vibe-substation .sub-block__label {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--event-accent);
  margin-bottom: var(--s-4);
  display: flex;
  align-items: center;
  gap: 10px;
}

.vibe-substation .sub-block__label::after {
  content: '';
  flex: 1;
  height: 1px;
  background: rgba(255,255,255,0.08);
}

/* ---------- Lineup — the headline section ---------- */
.vibe-substation .sub-lineup-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-2);
}

.vibe-substation .sub-lineup-list li {
  font-family: 'Archivo Black', 'Inter', sans-serif;
  font-weight: 900;
  font-size: clamp(22px, 3.5vw, 36px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #f5f5f5;
  padding: var(--s-2) 0;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  display: flex;
  align-items: baseline;
  gap: var(--s-4);
}

.vibe-substation .sub-lineup-list li::before {
  content: counter(lineup, decimal-leading-zero);
  counter-increment: lineup;
  font-family: 'JetBrains Mono', monospace;
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.1em;
  color: var(--event-accent);
  min-width: 32px;
}

.vibe-substation .sub-lineup-list { counter-reset: lineup; }
.vibe-substation .sub-lineup-list li:last-child { border-bottom: 0; }

/* If lineup came in as comma/dot-separated single string, render as wrapping block */
.vibe-substation .sub-lineup-flat {
  font-family: 'Archivo Black', 'Inter', sans-serif;
  font-weight: 900;
  font-size: clamp(20px, 3vw, 30px);
  line-height: 1.15;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  color: #f5f5f5;
  word-break: break-word;
}

/* ---------- About paragraph ---------- */
.vibe-substation .sub-about {
  color: #d0d0d0;
  font-size: 16px;
  line-height: 1.65;
  white-space: pre-line;
  margin: 0;
  overflow-wrap: anywhere;
  word-break: break-word;
}
.vibe-substation .sub-about a { overflow-wrap: anywhere; word-break: break-all; }

/* [blurbswap-20260817] The blurb's HOST block is deliberately bare — .sub-blurb
   below draws its own panel, so a .sub-block frame around it would be a box
   inside a box. This lived inline in _partials/vibes/substation.php until an
   inline style turned out to beat every swapped-in look (see the note there).

   Identified by what it CONTAINS, not by a modifier class: the Vibe Studio's
   applyLook() preserves any class it does not recognise as a vibe class, so a
   modifier would survive the swap and keep suppressing the new look. `.sub-block`
   is the slot class, so it IS swapped away — which is exactly the behaviour
   wanted here. Swap back and vkOrig restores it, and so does this rule. */
.vibe-substation .sub-block:has(> .sub-blurb) {
  background: transparent;
  border: 0;
  padding: 0;
}

/* ---------- Promoter blurb ---------- */
.vibe-substation .sub-blurb {
  border-left: 3px solid var(--event-accent);
  padding: var(--s-4) var(--s-5);
  background: rgba(255,255,255,0.025);
  color: #c0c0c0;
  font-style: italic;
  font-size: 15px;
  line-height: 1.65;
  white-space: pre-line;
}

/* ---------- Tickets block ---------- */
.vibe-substation .sub-ticket-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--s-4) 0;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  gap: var(--s-4);
}
.vibe-substation .sub-ticket-row:last-of-type { border-bottom: 0; }

.vibe-substation .sub-ticket-name {
  font-family: 'Archivo Black', sans-serif;
  font-size: 16px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: #f5f5f5;
}
.vibe-substation .sub-ticket-meta {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  color: #888;
  margin-top: 4px;
  letter-spacing: 0.04em;
}
.vibe-substation .sub-ticket-price {
  color: var(--event-accent);
}

.vibe-substation .sub-ticket-low {
  display: inline-block;
  padding: 2px 8px;
  border: 1px solid var(--event-accent);
  color: var(--event-accent);
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  margin-left: 6px;
  text-transform: uppercase;
}

.vibe-substation .sub-soldout {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #ff3333;
  border: 1px solid #ff3333;
  padding: 4px 10px;
}

/* Quantity dropdown — brutalist */
/* [derivedinputs-20260820] DERIVED, not enumerated.
   This rule used to list input[type=text], input[type=email], input[type=number]
   and nothing else. The buyer checkout form also has input[type=tel] (Phone) and,
   on the standard vibe, a Name field carrying NO type attribute at all — so
   input[type=text] never matched it either. Both fell through to the browser
   default and rendered as WHITE BOXES on a dark event page, measured live on
   standard / substation / sub_bass / junglist / headliner.
   The exclusion list is the derivation: style every text-entry control, and name
   only the controls that must keep native chrome. A field type added to the form
   later is covered from the moment it exists. */
.vibe-substation select,
.vibe-substation input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]):not([type=reset]):not([type=file]):not([type=range]):not([type=color]):not([type=image]) {
  background: #0a0a0a;
  border: 1px solid rgba(255,255,255,0.18);
  color: #f5f5f5;
  padding: 10px 12px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 14px;
  border-radius: 0;
  outline: none;
  width: 100%;
}

.vibe-substation select:focus,
.vibe-substation input:focus {
  border-color: var(--event-accent);
  box-shadow: 0 0 0 1px var(--event-accent);
}

.vibe-substation select[name^="qty"] {
  width: 80px;
  text-align: center;
}

.vibe-substation .sub-form-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s-3);
  margin-top: var(--s-5);
}

.vibe-substation label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #999;
  display: block;
  margin-bottom: 6px;
}

.vibe-substation .sub-help {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  color: #777;
  letter-spacing: 0.06em;
  margin-top: var(--s-2);
}

/* Buy button — slab, neon, brutalist */
.vibe-substation .sub-buy-btn {
  display: block;
  width: 100%;
  margin-top: var(--s-5);
  padding: 20px 24px;
  background: var(--event-accent);
  color: #050505;
  border: 0;
  border-radius: 0;
  font-family: 'Archivo Black', sans-serif;
  font-weight: 900;
  font-size: 18px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform 0.1s, box-shadow 0.15s;
  box-shadow: 0 0 24px -8px var(--event-accent);
}
.vibe-substation .sub-buy-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 0 36px -6px var(--event-accent);
}
.vibe-substation .sub-buy-btn:active {
  transform: translateY(0);
}

.vibe-substation .sub-secure {
  text-align: center;
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #666;
  margin-top: var(--s-3);
}

/* Sales-closed / sold-out alert strip */
.vibe-substation .sub-alert {
  padding: var(--s-4);
  border: 1px solid var(--event-accent);
  color: var(--event-accent);
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
}

/* Discount-code row */
.vibe-substation .sub-disc-field {
  margin-top: var(--s-5);
}
.vibe-substation .sub-disc-field input {
  text-transform: uppercase;
}

/* ---------- Hover polish ---------- */
.vibe-substation .sub-block {
  transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
.vibe-substation .sub-block:hover {
  border-color: var(--event-accent);
  box-shadow: 0 0 28px -8px var(--event-accent);
  transform: translateY(-2px);
}

.vibe-substation .sub-flyer {
  transition: transform 0.25s, box-shadow 0.25s;
}
.vibe-substation .sub-flyer:hover {
  transform: scale(1.012);
  box-shadow: 0 0 40px -10px var(--event-accent);
}

/* ---------- Mobile — stack ---------- */
@media (max-width: 860px) {
  .vibe-substation .sub-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-6);
    padding: var(--s-5) 0;
  }
  .vibe-substation .sub-hero {
    padding: var(--s-6) 0 var(--s-4);
  }
  .vibe-substation .sub-form-row {
    grid-template-columns: minmax(0, 1fr);
  }
  .vibe-substation .sub-lineup-list li {
    font-size: 22px;
  }
  .vibe-substation .sub-ticket-row {
    flex-wrap: wrap;
  }
}

/* Long-URL overflow guard: break long unbreakable strings (URLs, code)
   inside .about and .blurb containers so they don't blow past the box's
   right border. Added 2026-06-03 after Baytek's Junglist event showed a
   Google Sheets URL overflowing. See [[buzzhive-vibe-android-fix]]. */
.vibe-substation .sub-about,
.vibe-substation .sub-blurb {
  overflow-wrap: anywhere;
  word-wrap: break-word;
}

/* Mobile-overflow guard added 2026-06-03:
   Flyer images naturally render at their source dimensions (often 1080-1500px
   wide). At narrow viewports the grid track can't stop them blowing out
   unless we explicitly cap the container at 100% of its parent. Caught on
   Baytek's iPhone 16 Pro — horizontal scroll resulted in the page appearing
   to "load horizontally but not vertically." */
.vibe-substation .sub-flyer {
  max-width: 100%;
}
.vibe-substation .sub-flyer img {
  max-width: 100%;
}
