@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Fraunces:opsz,wght@9..144,600;9..144,700&family=Oswald:wght@400;500;600&display=swap');

/* ============================================================
   Good Vibez SLO Quest — theme tokens
   Palette sampled directly from the official Good Vibez SLO flyer
   (goodvibezlive.com artwork). Forest green is the dark base: the
   poster's signage block and type are green ink on a seafoam sky,
   with sand and coral as the warm pops. Tokens are named by ROLE,
   not by colour, so a future shared stylesheet can serve both this
   page and San Jose from one file with only :root swapped.

   SLO         San Jose        role
   forest      navy            --base
   seafoam     sky blue        --accent-cool
   sand        gold            --accent-warm
   coral       orange-red      --accent-hot
   moss        green           --accent-leaf
   ============================================================ */
:root {
  --base: #1c4a2b;
  --base-deep: #102e1b;
  --cream: #f7f0d8;
  --cream-dark: rgba(247,240,216,.72);
  --accent-cool: #8accba;
  --accent-warm: #f2b45f;
  --accent-hot: #e98246;
  --accent-leaf: #548430;
  --line: rgba(247,240,216,.14);
  --line-on-cream: rgba(28,74,43,.14);
  --shadow: 0 24px 60px rgba(6,23,13,.45);
  --ink: var(--base);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--base);
  color: var(--cream);
  font-family: 'Oswald', sans-serif;
  -webkit-font-smoothing: antialiased;
}
.container { max-width: 1120px; margin: 0 auto; padding: 0 28px; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }

.skip-link { position: absolute; left: -999px; top: 0; background: var(--accent-warm); color: var(--base-deep); padding: 10px 16px; font-weight: 600; z-index: 999; }
.skip-link:focus { left: 12px; top: 12px; }

/* ---- header ---- */
.site-header { position: sticky; top: 0; z-index: 40; background: rgba(28,74,43,.92); backdrop-filter: blur(10px); border-bottom: 1px solid var(--line); }
.header-inner { display: flex; align-items: center; justify-content: space-between; padding: 16px 28px; }
.brand { display: flex; align-items: center; gap: 12px; }
/* Eye & I mark. Mirrored verbatim from styles.css because this page is
   standalone and does not load styles.css or site.js. The colors are the
   SITE palette on purpose: the logo does not change per event theme.
   Do not restyle these in the Good Vibez palette. */
.eye-mark { width: 34px; height: 34px; flex: none; }
.logo-eye .eye-lid { transform-box: fill-box; transform-origin: center;
  transform: scaleY(0); transition: transform .09s ease-in; }
.logo-eye.is-blinking .eye-lid { transform: scaleY(1); }
.logo-eye .eye-pupil { transform-box: fill-box; transform-origin: center;
  transition: transform .12s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .logo-eye .eye-lid { display: none; }
  .logo-eye .eye-pupil { transform: none !important; }
}
.brand-copy { display: flex; flex-direction: column; line-height: 1.15; }
.brand-copy strong { font-family: 'Bebas Neue'; font-size: 1.3rem; letter-spacing: .04em; color: var(--cream); }
.brand-copy span { font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; color: var(--accent-cool); }
.nav-links { display: flex; align-items: center; gap: 26px; font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; }
.nav-links a:not(.btn) { color: var(--cream-dark); transition: color .2s ease; }
.nav-links a:not(.btn):hover { color: var(--accent-warm); }
@media (max-width: 720px) { .nav-links { gap: 14px; font-size: .62rem; } }
@media (max-width: 560px) { .nav-links a:not(.btn) { display: none; } }

/* ---- buttons ---- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 15px 28px; border-radius: 999px; font-family: 'Oswald'; font-weight: 600; font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; border: 1px solid transparent; cursor: pointer; transition: transform .2s ease, box-shadow .2s ease, background .2s ease, border-color .2s ease; }
.btn-primary { background: linear-gradient(135deg, var(--accent-warm), var(--accent-hot)); color: var(--base-deep); box-shadow: 0 12px 28px rgba(233,130,70,.35); }
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 16px 34px rgba(233,130,70,.45); }
.btn-secondary { border-color: rgba(247,240,216,.35); background: rgba(247,240,216,.05); color: var(--cream); }
.btn-secondary:hover { border-color: var(--accent-cool); color: var(--accent-cool); }
.btn-small { padding: 10px 18px; font-size: .68rem; }
.section-light .btn-secondary { border-color: rgba(28,74,43,.28); background: rgba(28,74,43,.05); color: var(--base); }
.section-light .btn-secondary:hover { border-color: var(--base); }

/* ---- hero: the poster stands alone, the card rides up over it ---- */
.hero-art {
  /* Ratio matches gv-slo-quest-banner-wide.png as delivered (2172x724, 3:1),
     uncropped. Deliberately the same file the footer band uses -- same art in
     both places means one download, not two. */
  width: 100%; aspect-ratio: 2172 / 724;
  max-height: 92svh; min-height: 300px;
  background-color: #8accba;
  /* Two layers: the hero art on top, and the official wave-pattern tile
     beneath it, repeated horizontally. background-size:cover means the tile
     is normally fully hidden behind the hero image -- it only shows through
     as letterboxing on window shapes wider or taller than 3:1, where a
     flat fallback color would otherwise create a visible seam. Matches the
     poster's own sky pattern exactly since it is sampled from the same art. */
  background-image:
    url('images/gv_slo_quest/gv_slo_banner.webp'),
    url('images/gv_slo_quest/gv_slo_wave_tile.webp');
  background-size: cover, auto 100%;
  background-position: center, center;
  background-repeat: no-repeat, repeat-x;
}
/* Narrow OR portrait-ish windows get the 4:5 crop instead. Keying on aspect
   ratio as well as width means a half-screen vertical window on a big display
   switches too, not just phones -- that window is wide in pixels but the wide
   poster still ends up far too tall for it. */
@media (max-width: 900px), (max-aspect-ratio: 6 / 5) {
  .hero-art {
    aspect-ratio: 1080 / 1350;
    background-image: url('images/gv_slo_quest/gv_slo_mobile.webp');
  }
}
/* Very tall narrow phones keep the 9:16 crop, which fills them better still. */
.hero-art-alt { position: absolute; inset: 0; }

/* Without this the poster can read as a static flyer and people never scroll.
   Sits on its own forest-green pill so it stays legible wherever the artwork's bottom
   edge happens to land. */
/* Desktop hides this: see the media query below. */
.scroll-cue {
  position: absolute; left: 50%; bottom: 8%; transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 10px;
  padding: 11px 22px; border-radius: 999px;
  background: rgba(28,74,43,.92); border: 1px solid rgba(138,204,186,.4);
  color: var(--cream); font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; font-weight: 600;
  box-shadow: 0 10px 28px rgba(6,23,13,.45); z-index: 4;
  transition: background .2s ease, border-color .2s ease, color .2s ease;
}
.scroll-cue:hover, .scroll-cue:focus-visible { background: var(--base); border-color: var(--accent-warm); color: var(--accent-warm); }
.scroll-cue-arrow { font-size: .9rem; line-height: 1; }
@media (prefers-reduced-motion: no-preference) {
  .scroll-cue-arrow { animation: cuenudge 1.8s ease-in-out infinite; }
}
@keyframes cuenudge { 0%,100% { transform: translateY(0); } 50% { transform: translateY(4px); } }
/* On the tall mobile crop the poster's own artwork fills the bottom, so the
   cue sits clear of the artwork's own type instead: the SLO date banner and
   the goodvibezlive.com line bottom out around 80% down, then the poppy band
   starts. Sitting low keeps the cue off both. 24% from the
   bottom lands in that gap, under the hosted-by line and above the caps. */
/* The cue exists so a poster that fills the screen does not read as a static
   flyer. The wide hero is 3:1, so at 1440x900 roughly 330px of the next
   section is already visible and nothing needs prompting -- and the art's own
   date plaque sits at 89-95% of its height, leaving no clear spot for a pill
   that would not cover either the date or the skeleton. So it is hidden on
   the wide hero and kept on the tall mobile crop, which does fill the screen. */
@media (min-width: 901px) { .scroll-cue { display: none; } }
@media (max-width: 900px), (max-aspect-ratio: 6 / 5) { .scroll-cue { bottom: 24%; } }
@media (max-width: 560px) { .scroll-cue { bottom: 24%; font-size: .64rem; padding: 9px 16px; } }

@media (max-width: 560px) and (max-aspect-ratio: 3 / 5) {
  .hero-art { aspect-ratio: 900 / 1328; background-image: url('images/gv_slo_quest/gv_slo_mobile.webp'); }
}

.hero {
  position: relative; padding: 70px 0 90px;
  /* translucent so the poster reads faintly through the card, but with a high
     enough floor that text contrast never depends on what is behind it */
  background: linear-gradient(180deg, rgba(28,74,43,.93) 0%, rgba(28,74,43,.98) 55%, var(--base) 100%);
  backdrop-filter: blur(7px);
}
.hero-grid { display: grid; grid-template-columns: 1.15fr .85fr; gap: 50px; align-items: center; }
.eyebrow { font-size: .72rem; letter-spacing: .18em; text-transform: uppercase; color: var(--accent-cool); font-weight: 600; margin-bottom: 10px; }
/* Stacked and big. The old layout's drama came from the display type filling
   the column on two lines, solid word over hollow word, not from one modest
   line. Sunburst rays sit behind it, borrowed from the poster's logo. */
.hero h1 {
  position: relative; font-family: 'Bebas Neue';
  font-size: clamp(3.4rem, 9.5vw, 7.4rem); line-height: .84;
  margin: 0 0 22px; color: var(--cream);
  text-shadow: 0 4px 30px rgba(0,0,0,.4);
}
.hero h1 span { display: block; }
.hero h1::before {
  content: ''; position: absolute; z-index: -1;
  left: -14%; top: 50%; width: 120%; aspect-ratio: 1; transform: translateY(-50%);
  background: repeating-conic-gradient(from 8deg at 50% 50%,
    rgba(239,159,55,.11) 0deg 3.6deg, rgba(239,159,55,0) 3.6deg 9deg);
  -webkit-mask-image: radial-gradient(closest-side, #000 12%, transparent 68%);
  mask-image: radial-gradient(closest-side, #000 12%, transparent 68%);
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .hero h1::before { animation: sunspin 90s linear infinite; }
}
@keyframes sunspin { to { transform: translateY(-50%) rotate(360deg); } }
.hero h1 span { background: linear-gradient(120deg, var(--accent-warm), var(--accent-hot)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero-copy { font-family: 'Fraunces', serif; font-style: italic; font-size: 1.14rem; line-height: 1.6; color: var(--cream-dark); max-width: 52ch; margin: 0 0 30px; }
.hero-actions { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 26px; }
.event-meta { display: flex; flex-wrap: wrap; gap: 18px; font-size: .74rem; letter-spacing: .05em; text-transform: uppercase; color: var(--cream-dark); }
.event-meta span { position: relative; padding-left: 16px; }
.event-meta span::before { content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 7px; height: 7px; border-radius: 50%; background: var(--accent-warm); }

.quest-card { background: rgba(16,46,27,.55); border: 1px solid rgba(138,204,186,.28); border-radius: 28px; padding: 32px; box-shadow: var(--shadow); }
.quest-card h2 { font-family: 'Bebas Neue'; font-size: 2.1rem; margin: 4px 0 12px; color: var(--cream); }
.quest-card p { font-size: .92rem; line-height: 1.55; color: var(--cream-dark); margin: 0 0 22px; }
.countdown { display: grid; grid-template-columns: repeat(4,1fr); gap: 10px; }
.countdown div { background: rgba(138,204,186,.1); border: 1px solid rgba(138,204,186,.25); border-radius: 14px; padding: 12px 4px; text-align: center; }
.countdown strong { display: block; font-family: 'Bebas Neue'; font-size: 1.8rem; color: var(--accent-cool); }
.countdown span { font-size: .58rem; letter-spacing: .1em; text-transform: uppercase; color: var(--cream-dark); }
@media (max-width: 900px) { .hero-grid { grid-template-columns: 1fr; } }

/* ---- sections ---- */
.section { padding: 84px 0; }
.section-light { background: var(--cream); color: var(--base); }
.section-light .eyebrow { color: #8a5316; }
.section-light .section-intro { color: rgba(28,74,43,.72); }
.section-heading-row { display: flex; justify-content: space-between; gap: 30px; align-items: end; margin-bottom: 46px; }
.section-title { font-family: 'Bebas Neue'; font-size: clamp(2.4rem, 5vw, 3.6rem); line-height: .96; margin: 0; max-width: 12ch; }
.section-intro { font-size: 1rem; line-height: 1.6; color: var(--cream-dark); max-width: 42ch; }
@media (max-width: 720px) { .section-heading-row, .cta-inner { align-items: flex-start; flex-direction: column; } .section { padding: 60px 0; } }

/* ---- join panel ---- */
.join-panel { display: grid; grid-template-columns: 1fr; gap: 32px; align-items: center; background: rgba(138,204,186,.06); border: 1px solid rgba(138,204,186,.22); border-radius: 28px; padding: 32px; box-shadow: var(--shadow); }
.section-light .join-panel { background: rgba(28,74,43,.035); border-color: rgba(28,74,43,.12); box-shadow: 0 18px 40px rgba(0,0,0,.10); }
.join-panel-qr { flex: none; width: 148px; height: 148px; border-radius: 18px; overflow: hidden; border: 4px solid var(--cream); box-shadow: 0 10px 26px rgba(0,0,0,.3); }
.join-panel-qr img { display: block; width: 100%; height: 100%; object-fit: cover; background: #fffcf2; }
.join-panel-copy h3 { font-family: 'Bebas Neue'; font-size: 2rem; margin: 4px 0 12px; }
.join-panel-copy p { font-size: .95rem; line-height: 1.55; color: var(--cream-dark); margin: 0 0 14px; }
.section-light .join-panel-copy p { color: rgba(28,74,43,.72); }
.join-panel-proof { font-size: .85rem; color: var(--cream-dark); }
.section-light .join-panel-proof { color: rgba(28,74,43,.6); }
.join-panel-proof a { color: var(--accent-cool); font-weight: 600; }
.section-light .join-panel-proof a { color: #1f6555; }
.join-panel-apps-label { display: block; font-size: .62rem; letter-spacing: .12em; text-transform: uppercase; color: var(--cream-dark); margin: 18px 0 10px; }
.section-light .join-panel-apps-label { color: rgba(28,74,43,.5); }
.join-panel-apps { display: flex; gap: 10px; flex-wrap: wrap; }
@media (max-width: 720px) { .join-panel { grid-template-columns: 1fr; text-align: center; justify-items: center; padding: 26px; } }

/* ---- how it works ---- */
.steps-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 22px; }
.step-card { border-radius: 22px; padding: 30px; background: rgba(138,204,186,.06); border: 1px solid var(--line); }
.section-light .step-card { background: rgba(255,255,255,.5); border-color: var(--line-on-cream); }
.step-num { width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center; background: linear-gradient(135deg, var(--accent-warm), var(--accent-hot)); color: var(--base-deep); font-family: 'Bebas Neue'; font-size: 1.4rem; }
.step-card h3 { font-family: 'Bebas Neue'; font-size: 2rem; margin: 22px 0 10px; }
.step-card p { margin: 0; color: var(--cream-dark); line-height: 1.55; }
.section-light .step-card p { color: rgba(28,74,43,.72); }
@media (max-width: 900px) { .steps-grid { grid-template-columns: 1fr; } }

/* ---- mission teaser cards ---- */
.teaser-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 18px; }
.teaser-card { min-height: 260px; display: flex; flex-direction: column; justify-content: end; position: relative; overflow: hidden; border-radius: 22px; padding: 26px; border: 1px solid var(--line); background: linear-gradient(180deg, rgba(138,204,186,.03), rgba(138,204,186,.1)); }
.section-light .teaser-card { border-color: var(--line-on-cream); background: linear-gradient(180deg, rgba(28,74,43,.02), rgba(28,74,43,.06)); }
.teaser-card::before { content: ''; position: absolute; inset: 0; background: radial-gradient(circle at 80% 10%, rgba(239,159,55,.24), transparent 32%); }
.teaser-card:nth-child(2)::before { background: radial-gradient(circle at 80% 10%, rgba(84,132,48,.26), transparent 32%); }
.teaser-card:nth-child(3)::before { background: radial-gradient(circle at 80% 10%, rgba(138,204,186,.28), transparent 32%); }
.teaser-card:nth-child(4)::before { background: radial-gradient(circle at 80% 10%, rgba(233,130,70,.24), transparent 32%); }
.teaser-card > * { position: relative; z-index: 2; }
.teaser-label { font-size: .68rem; letter-spacing: .14em; text-transform: uppercase; color: var(--accent-warm); font-weight: 600; }
.teaser-card:nth-child(2) .teaser-label { color: #a8cf8f; }
.teaser-card:nth-child(3) .teaser-label { color: var(--accent-cool); }
.teaser-card:nth-child(4) .teaser-label { color: #f0a06e; }
.section-light .teaser-label { color: #8a5316; }
.section-light .teaser-card:nth-child(2) .teaser-label { color: #2f5a17; }
.section-light .teaser-card:nth-child(3) .teaser-label { color: #1f6555; }
.section-light .teaser-card:nth-child(4) .teaser-label { color: #a4441c; }
.teaser-card h3 { font-family: 'Bebas Neue'; font-size: 1.7rem; margin: 10px 0 8px; }
.teaser-card p { margin: 0; font-size: .88rem; line-height: 1.5; color: var(--cream-dark); }
.section-light .teaser-card p { color: rgba(28,74,43,.72); }
@media (max-width: 940px) { .teaser-grid { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 560px) { .teaser-grid { grid-template-columns: 1fr; } }

/* ---- three ways to win ---- */
.win-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 20px; }
.win-card { border-radius: 24px; padding: 28px; border: 1px solid var(--line); background: linear-gradient(180deg, rgba(138,204,186,.03), rgba(138,204,186,.09)); position: relative; overflow: hidden; }
.win-card::before { content: ''; position: absolute; inset: 0; border-radius: inherit; background: radial-gradient(circle at 85% 0%, rgba(239,159,55,.2), transparent 40%); }
.win-card:nth-child(2)::before { background: radial-gradient(circle at 85% 0%, rgba(84,132,48,.22), transparent 40%); }
.win-card:nth-child(3)::before { background: radial-gradient(circle at 85% 0%, rgba(233,130,70,.2), transparent 40%); }
.win-card > * { position: relative; z-index: 2; }
.win-label { text-transform: uppercase; letter-spacing: .15em; font-size: .72rem; color: var(--accent-warm); }
.win-card:nth-child(2) .win-label { color: #a8cf8f; }
.win-card:nth-child(3) .win-label { color: #f0a06e; }
.win-card h3 { font-family: 'Bebas Neue'; font-size: 2rem; margin: 14px 0 10px; }
.win-card p { margin: 0; color: var(--cream-dark); line-height: 1.55; }
.section-light .win-card { border-color: var(--line-on-cream); background: linear-gradient(180deg, rgba(28,74,43,.02), rgba(28,74,43,.06)); }
.section-light .win-card::before { background: radial-gradient(circle at 85% 0%, rgba(239,159,55,.22), transparent 40%); }
.section-light .win-card:nth-child(2)::before { background: radial-gradient(circle at 85% 0%, rgba(84,132,48,.2), transparent 40%); }
.section-light .win-card:nth-child(3)::before { background: radial-gradient(circle at 85% 0%, rgba(233,130,70,.18), transparent 40%); }
.section-light .win-label { color: #8a5316; }
.section-light .win-card:nth-child(2) .win-label { color: #2f5a17; }
.section-light .win-card:nth-child(3) .win-label { color: #a4441c; }
.section-light .win-card p { color: rgba(28,74,43,.72); }
@media (max-width: 940px) { .win-grid { grid-template-columns: 1fr; } }

/* ---- rules ---- */
.rules-grid { display: grid; grid-template-columns: repeat(2,1fr); gap: 18px; }
.rule-card { border-radius: 20px; padding: 26px; background: rgba(255,255,255,.5); border: 1px solid var(--line-on-cream); }
.rule-card h3 { font-family: 'Bebas Neue'; font-size: 1.6rem; margin: 0 0 8px; }
.rule-card p { margin: 0; color: rgba(28,74,43,.72); line-height: 1.55; }
@media (max-width: 720px) { .rules-grid { grid-template-columns: 1fr; } }

/* ---- leaderboard ---- */
.leaderboard-shell { border-radius: 28px; overflow: hidden; border: 1px solid var(--line); background: rgba(138,204,186,.05); box-shadow: var(--shadow); }
.leaderboard-header { display: flex; align-items: center; justify-content: space-between; padding: 20px 26px; border-bottom: 1px solid var(--line); flex-wrap: wrap; gap: 10px; }
.live-pill { display: inline-flex; align-items: center; gap: 8px; font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; font-weight: 600; color: var(--base-deep); background: linear-gradient(135deg, var(--accent-warm), var(--accent-hot)); padding: 7px 14px; border-radius: 999px; }
.updated-at { font-size: .74rem; color: var(--cream-dark); }
.pulse-strip { display: grid; grid-template-columns: repeat(3,1fr); gap: 12px; padding: 22px 26px 0; }
.pulse-item { background: rgba(138,204,186,.08); border: 1px solid rgba(138,204,186,.2); border-radius: 18px; padding: 16px 20px; }
.pulse-item strong { display: block; font-family: 'Bebas Neue'; font-size: 2.3rem; color: var(--accent-warm); line-height: 1.05; }
.pulse-item span { color: var(--cream-dark); text-transform: uppercase; font-size: .66rem; letter-spacing: .12em; }
.pulse-latest strong { font-family: 'Oswald'; font-weight: 600; font-size: 1.1rem; color: var(--cream); padding-top: 6px; }
@media (max-width: 720px) { .pulse-strip { grid-template-columns: 1fr; padding: 18px 20px 0; } }
.table-wrap { padding: 22px 26px 26px; overflow-x: auto; }
.leaderboard-table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.leaderboard-table th { text-align: left; font-size: .66rem; letter-spacing: .1em; text-transform: uppercase; color: var(--cream-dark); padding: 10px 12px; border-bottom: 1px solid var(--line); }
.leaderboard-table td { padding: 14px 12px; border-bottom: 1px solid var(--line); }
.rank-badge { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 50%; background: rgba(138,204,186,.14); font-family: 'Bebas Neue'; font-size: 1rem; }
.points { color: var(--accent-warm); font-weight: 600; }
.empty-state { padding: 50px 26px; text-align: center; }
.empty-state h3 { font-family: 'Bebas Neue'; font-size: 1.8rem; margin: 0 0 8px; }
.empty-state p { color: var(--cream-dark); margin: 0; }

/* ---- final CTA ---- */
.cta-band { background: linear-gradient(120deg, var(--accent-warm), var(--accent-hot)); color: var(--base-deep); padding: 60px 0; }
.cta-inner { display: flex; align-items: center; justify-content: space-between; gap: 30px; }
.cta-band h2 { font-family: 'Bebas Neue'; font-size: clamp(2rem, 4.5vw, 2.9rem); margin: 0 0 8px; }
.cta-band p { margin: 0; opacity: .82; max-width: 46ch; }
.cta-band .btn-primary { background: var(--base-deep); color: var(--cream); box-shadow: 0 12px 28px rgba(16,46,27,.35); }

/* ---- footer ---- */
.site-footer { padding: 34px 0 52px; border-top: 1px solid var(--line); }
.footer-inner { display: flex; justify-content: space-between; gap: 34px; flex-wrap: wrap; font-size: .78rem; color: var(--cream-dark); }
/* the two credit lines stack rather than sitting side by side */
.footer-lines { display: flex; flex-direction: column; gap: 7px; max-width: 58ch; }
.nowrap { white-space: nowrap; }
.footer-cols { display: flex; gap: 56px; flex-wrap: wrap; }
.footer-group { display: flex; flex-direction: column; gap: 12px; }
.footer-two { display: flex; gap: 40px; }
.footer-col { display: flex; flex-direction: column; gap: 7px; }
.footer-social { display: flex; flex-direction: column; gap: 7px; }

/* Eye & I's own handle and site sit with the credit lines, on one row. */
/* These two are what someone should leave with, so they outrank the credit
   copy above them: larger, heavier, and in the poster's orange. The exact hue
   is a touch lighter than --accent-hot because pure #e98246 measures 4.27 on this
   forest green, just under the 4.5 needed at this text size. */
.footer-meta { display: flex; align-items: center; gap: 14px; margin-top: 12px; flex-wrap: wrap; }
.footer-meta a { color: #ef9257; font-size: .96rem; font-weight: 600; letter-spacing: .015em; transition: color .2s ease; }
.footer-meta a:hover, .footer-meta a:focus-visible { color: var(--accent-warm); text-decoration: underline; text-underline-offset: 4px; }
.footer-sep { width: 1px; height: 15px; background: rgba(247,240,216,.3); flex: none; }
.footer-social a { color: var(--cream-dark); transition: color .2s ease; width: fit-content; }
.footer-social a:hover, .footer-social a:focus-visible { color: var(--accent-warm); text-decoration: underline; text-underline-offset: 3px; }
.footer-label { font-size: .64rem; letter-spacing: .14em; text-transform: uppercase; color: var(--accent-cool); font-weight: 600; margin-bottom: 2px; }
.footer-col a { color: var(--cream-dark); transition: color .2s ease; width: fit-content; }
.footer-col a:hover, .footer-col a:focus-visible { color: var(--accent-warm); text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 720px) { .footer-cols { gap: 32px; } .footer-inner { gap: 26px; } }

/* The brand is the way home, so it should say so on hover instead of looking
   like a static wordmark. */
.brand-sub { position: relative; display: inline-block; }
.brand-sub-hover { position: absolute; left: 0; top: 0; opacity: 0; color: var(--accent-warm); white-space: nowrap; }
.brand:hover .brand-sub-default, .brand:focus-visible .brand-sub-default { opacity: 0; }
.brand:hover .brand-sub-hover, .brand:focus-visible .brand-sub-hover { opacity: 1; }
.brand-sub-default, .brand-sub-hover { transition: opacity .2s ease; }
.brand:hover .brand-copy strong { color: var(--accent-warm); }
.brand-copy strong { transition: color .2s ease; }

/* ---- Cali Roots proof + champions ---- */
.proof-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 18px; }
.proof-stat { background: rgba(28,74,43,.04); border: 1px solid var(--line-on-cream); border-radius: 20px; padding: 24px; text-align: center; }
.proof-stat strong { display: block; font-family: 'Bebas Neue'; font-size: 3rem; line-height: 1; color: #8a5316; }
.proof-stat span { display: block; margin-top: 6px; font-size: .68rem; letter-spacing: .12em; text-transform: uppercase; color: rgba(28,74,43,.62); }
.champions-row { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-top: 22px; }
.champion-card { background: rgba(255,255,255,.55); border: 1px solid var(--line-on-cream); border-radius: 18px; padding: 20px; }
.champion-award { display: block; font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: #8a5316; margin-bottom: 8px; }
.champion-card strong { font-family: 'Bebas Neue'; font-size: 1.7rem; color: var(--base); }
.proof-link { margin: 26px 0 0; }
.proof-link a { color: #1f6555; font-weight: 600; font-size: .92rem; letter-spacing: .04em; }
.proof-link a:hover { color: var(--base); }
@media (max-width: 900px) { .proof-grid { grid-template-columns: repeat(2,1fr); } .champions-row { grid-template-columns: 1fr; } }

/* ---- The Wall ----
   Tiles carry their own dark surface and overlay caption, so the section
   reads correctly on either a cream or forest-green background. */
.wall-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; }
.wall-tile { position: relative; margin: 0; border-radius: 20px; overflow: hidden; border: 1px solid var(--line); background: var(--base-deep); aspect-ratio: 4 / 5; }
.wall-tile img { width: 100%; height: 100%; object-fit: cover; }
.wall-tile figcaption { position: absolute; left: 0; right: 0; bottom: 0; padding: 26px 16px 12px; font-size: .78rem; letter-spacing: .04em; color: #f7f0d8; background: linear-gradient(180deg, rgba(16,46,27,0), rgba(16,46,27,.85)); }
@media (max-width: 900px) { .wall-grid { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 560px) { .wall-grid { grid-template-columns: 1fr; } }

/* ==========================================================================
   Stacking cards + texture
   Mirrors the house pattern from styles.css: sticky does the covering, and
   stacking only turns on for wide screens with motion allowed. This page is
   standalone, so the .stacks-on class is set by its own inline script.
   ========================================================================== */

/* House grain overlay, copied from styles.css so texture matches the site. */
.grain { position: fixed; inset: 0; pointer-events: none; z-index: 100; opacity: 0.055; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }

/* Panels sit above the pinned poster and carry their own opaque surface. */
.stack-panel { position: relative; z-index: 1; isolation: isolate; overflow-x: clip; overflow-y: visible; }
.hero-art { position: relative; z-index: 0; }

/* Sticky stacking. A panel taller than the viewport gets .no-pin from the
   script, because a pinned panel taller than the screen hides its own bottom. */
body.stacks-on .hero-art { position: sticky; top: 0; }
body.stacks-on .stack-panel { position: sticky; top: 0; }
body.stacks-on .stack-panel.no-pin { position: relative; }
body.stacks-on .cta-band,
body.stacks-on .site-footer { position: relative; z-index: 20; }

/* The lifted-card look: rounded top, shadow above, and the poster's zigzag. */
body.stacks-on .stack-panel { border-radius: 0; box-shadow: 0 -22px 50px rgba(6,23,13,.45); }

/* Zigzag sits OUTSIDE the card, teeth up, resting on its top edge like the
   sawtooth border on the poster. Inset by the corner radius so the teeth run
   along the straight part of the edge and do not float past the curve. */
.card-edge { display: none; }
body.stacks-on .card-edge {
  display: block; position: absolute; top: -7px; left: 0; right: 0; height: 7px;
  background-repeat: repeat-x; background-size: 18px 7px; background-position: left bottom;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='7'%3E%3Cpath d='M0 7 L4.5 0 L9 7 L13.5 0 L18 7 Z' fill='%23f35529'/%3E%3C/svg%3E");
  pointer-events: none; z-index: 8;
}

/* Soft handoff. Two parts:
   1. a whisper of translucency at the very top edge, so the card genuinely
      materialises rather than cutting in. Kept high (.95) because anything
      lower lets the card behind show as legible ghost text.
   2. a shading band that fades down the first ~110px. This is what actually
      reads as the transition, and it can never reveal what is underneath. */
body.stacks-on .stack-panel:not(.hero) { padding-top: 108px; padding-bottom: 72px; }
body.stacks-on .stack-panel:not(.section-light):not(.hero) {
  background-color: transparent;
  background-image: linear-gradient(180deg, rgba(28,74,43,.95) 0, rgba(28,74,43,1) 56px);
}
body.stacks-on .section-light {
  background-color: transparent;
  background-image: linear-gradient(180deg, rgba(247,240,216,.95) 0, rgba(247,240,216,1) 56px);
}
body.stacks-on .stack-panel:not(.hero)::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 110px;
  pointer-events: none; z-index: 2;
  background: linear-gradient(180deg, rgba(6,23,13,.30) 0, rgba(6,23,13,0) 100%);
}
body.stacks-on .section-light::before {
  background: linear-gradient(180deg, rgba(56,74,36,.16) 0, rgba(56,74,36,0) 100%);
}
body.stacks-on .section-light::after { top: 56px; }

/* Copy always sits above the decorative bands, stacking effect or not.
   This used to be scoped to body.stacks-on, which meant that whenever the
   stacking script was inactive -- reduced-motion, narrow viewports, or before
   the script runs -- a positioned decoration (z-index 0) painted on top of
   the static text instead of behind it. The decorative layer is now in flow
   and cannot overlap at all, but this stays as a second line of defence. */
.stack-panel > .container { position: relative; z-index: 3; }

@media (prefers-reduced-motion: reduce) {
  body.stacks-on .hero-art,
  body.stacks-on .stack-panel { position: relative; }
}

/* Shorter windows: tighten the cards so more of them still fit the viewport
   and keep pinning. Without this, stacking only survives on a tall display. */
@media (max-height: 820px) {
  body.stacks-on .stack-panel:not(.hero) { padding-top: 92px; padding-bottom: 52px; }
  body.stacks-on .stack-panel .section-heading-row { margin-bottom: 32px; }
}
@media (max-height: 700px) {
  body.stacks-on .stack-panel:not(.hero) { padding-top: 84px; padding-bottom: 44px; }
  body.stacks-on .stack-panel .section-heading-row { margin-bottom: 24px; }
  body.stacks-on .stack-panel .section-title { font-size: clamp(2rem, 4vw, 2.8rem); }
}

/* ==========================================================================
   Flyer elements. Drawn as SVG in the SLO palette rather than cut from the
   reused as page furniture: clouds in the dark panels, a record behind the
   poster, so they stay crisp at any size and cannot 404. All decorative, all
   aria-hidden, all sitting behind content.
   ========================================================================== */
.deco { position: absolute; pointer-events: none; user-select: none; z-index: 0; }

/* The footer banner. Anchored to the bottom of the page the way the poster's ground
   line anchors the artwork. Cream base so it reads as part of the print. */
.footer-band {
  /* Ratio matches the official gv-slo-banner.png as delivered (2172x724, 3:1).
     The old 4.97:1 box was sized for a banner crop that no longer exists --
     kept at that ratio, this 3:1 art would have its top sliced off by a box
     too short to hold it. */
  position: relative; width: 100%; aspect-ratio: 3 / 1;
  background-color: #8accba;
  background-image: url('images/gv_slo_quest/gv_slo_banner.webp');
  background-size: 100% auto; background-position: center bottom; background-repeat: no-repeat;
}
.footer-band-edge {
  position: absolute; top: 0; left: 0; right: 0; height: 7px; z-index: 9;
  background-repeat: repeat-x; background-size: 18px 7px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='7'%3E%3Cpath d='M0 0 L4.5 7 L9 0 L13.5 7 L18 0 Z' fill='%23f35529'/%3E%3C/svg%3E");
}

@media (max-width: 820px) {
  /* At phone widths the full 3:1 banner would be too short to read, so crop
     to the centre of the artwork instead of shrinking the whole scene. */
  .footer-band { aspect-ratio: auto; height: 190px; background-size: cover; background-position: center; }
}

/* The hand-drawn corner icons, cloud washes and sun discs that used to live
   here are gone -- the page now uses only the supplied poster elements
   (poppies, dunes, seagulls, surfer, badge) via the .decor system further
   down. Their CSS was removed with them rather than left behind as rules
   nothing can match. */

/* ==========================================================================
   Whimsy pass. The reskin had drifted to one display face doing every job,
   which read clean but flat. These bring back the character of the earlier
   layout: outlined display type, a ticket-stub detail, halftone surface, and
   a small flourish on the section kickers.
   ========================================================================== */

/* Outlined display type. The poster shows through the letterforms instead of
   a flat fill, which is where most of the playfulness comes from.
   Wrapped in @supports so browsers without text-stroke keep the gradient. */
@supports ((-webkit-text-stroke: 2px currentColor) or (text-stroke: 2px currentColor)) {
  .hero h1 span {
    background: none; -webkit-background-clip: border-box; background-clip: border-box;
    color: transparent;
    -webkit-text-stroke: 3px var(--accent-warm);
    paint-order: stroke fill;
    text-shadow: 0 3px 22px rgba(6,23,13,.55);
  }
}

/* Ticket stub, tucked in the countdown card the way the old "Quest Pass"
   label was. Dashed edge reads as a perforation. */
.quest-pass {
  position: absolute; top: 16px; right: 16px;
  font-family: 'Oswald', sans-serif; font-size: .58rem; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  color: rgba(247,240,216,.55);
  border: 1px dashed rgba(247,240,216,.32);
  border-radius: 4px; padding: 4px 9px;
  transform: rotate(2.5deg);
}
.quest-card { position: relative; }

/* Halftone dots over the forest panels: printed-poster surface, not flat colour. */
body.stacks-on .stack-panel:not(.section-light):not(.hero)::after,
.hero::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 1; opacity: .5;
  background-image: radial-gradient(rgba(247,240,216,.055) 1px, transparent 1.4px);
  background-size: 7px 7px;
}
body.stacks-on .stack-panel > .container,
.hero > .container { position: relative; z-index: 3; }

/* Kicker flourish: a short gold rule before the label, so the eyebrows read as
   a designed mark rather than plain small caps. */
.eyebrow { display: inline-flex; align-items: center; gap: 9px; }
.eyebrow::before {
  content: ''; width: 16px; height: 2px; flex: none; border-radius: 2px;
  background: currentColor; opacity: .75;
}

/* --------------------------------------------------------------------------
   Line texture on the forest panels. The grain overlay alone reads as noise;
   the old layout had actual visible line work, which is what gave the dark
   areas depth. Two very low-opacity diagonal hatches at different angles and
   spacings, so it never tiles into an obvious repeat.
   -------------------------------------------------------------------------- */
body.stacks-on .stack-panel:not(.section-light):not(.hero)::after,
.hero::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background-image:
    repeating-linear-gradient(58deg,  rgba(247,240,216,.035) 0 1px, transparent 1px 15px),
    repeating-linear-gradient(-49deg, rgba(138,204,186,.030) 0 1px, transparent 1px 23px);
}
.hero > .container { position: relative; z-index: 2; }

/* Cream panels get the same idea in ink, much fainter, so both tones feel
   printed rather than flat. */
body.stacks-on .section-light::before {
  background-image:
    linear-gradient(180deg, rgba(56,74,36,.16) 0, rgba(56,74,36,0) 100%),
    repeating-linear-gradient(58deg, rgba(28,74,43,.030) 0 1px, transparent 1px 17px);
}


/* ==========================================================================
   Mobile decoration
   On desktop the artwork lives in the page margins beside the text column.
   A phone has no margin, so instead of shrinking that idea until it breaks,
   mobile gets its own version: small icons standing along the BOTTOM edge of
   each section, which is space we control, plus the cloud and record washes
   which were always safe to sit behind text. Placed after the desktop hide
   rules on purpose so these win at phone widths.
   ========================================================================== */
/* --------------------------------------------------------------------------
   Weekend band. Rebelution, Collie Buddz, The Expendables and Cydeways play
   SLO on Saturday and San Jose on Sunday, so a real share of this page's
   visitors are going to both. Sending them to the other Quest is the whole
   point; saying the leaderboards are separate up front is what stops someone
   assuming one entry counts twice.
   Sits directly above the CTA band, quieter than it, so the primary action
   on the page is still joining THIS Quest.
   -------------------------------------------------------------------------- */
.weekend-band {
  position: relative; z-index: 5;
  background: var(--base-deep);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 30px 0;
}
.weekend-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 26px; flex-wrap: wrap;
}
.weekend-copy { max-width: 640px; }
.weekend-copy .eyebrow { color: var(--accent-cool); margin-bottom: 8px; }
.weekend-copy p {
  margin: 0; color: var(--cream-dark);
  font-size: .95rem; line-height: 1.6;
}
.weekend-band .btn { flex: none; }
@media (max-width: 720px) {
  .weekend-inner { flex-direction: column; align-items: flex-start; gap: 18px; }
  .weekend-band .btn { width: 100%; }
}

/* ==========================================================================
   Official cutout elements (Sept 2026 delivery). These replace or add to
   the hand-drawn placeholder SVGs with the actual poster artwork, isolated
   as transparent cutouts: the mascot, the three seagulls, a poppy hedge, a
   dune horizon, the badge lockup, and the poster's own squiggle border.
   ========================================================================== */

/* The CTA band carries no mascot. The footer banner sits directly beneath it
   and already has the surfer in the artwork, so a second one here read as a
   duplicate and crowded the headline. */
.cta-band { position: relative; overflow: hidden; }

/* The squiggle-border frame that used to wrap this panel is gone: stretched
   to a wide short panel it lost the proportions that make it read as the
   poster's border, and just looked like a wobbly box. The element file is
   still in the repo if a better use turns up. */
.join-panel { position: relative; }

/* ---- footer badge -------------------------------------------------------
   The Good Vibez SLO lockup, small, sitting above the credit lines it
   belongs to. Distinct from the Eye & I mark in the header -- two different
   brands, never merged into one lockup. */
.footer-badge {
  /* Sits under the goodvibezlive.com line, inside the Good Vibez Presents
     group. margin-top rather than -bottom now that it follows the links
     instead of leading them. */
  height: 44px; width: auto; margin-top: 6px; opacity: .92;
  /* .footer-group is a column flexbox, so its children stretch to the full
     container width by default. On a narrow screen that forced this image
     wider while height stayed fixed -- which is the squashed badge that
     showed up on mobile. align-self stops the stretch; flex:none stops it
     being shrunk instead. Both are load-bearing, keep them. */
  align-self: flex-start; flex: none; max-width: 200px; object-fit: contain;
}
@media (max-width: 720px) { .footer-badge { height: 52px; margin-top: 4px; } }

/* ==========================================================================
   Decorative layer.

   These are FLOW elements at the end of each section, not absolutely
   positioned ones. That is the whole point: an absolutely positioned
   decoration only avoids the copy above it because some hand-tuned
   padding-bottom happens to be taller than the artwork, and that number is
   wrong the moment the art, the font size, or the viewport changes -- which
   is exactly how elements ended up running into the text.

   In flow, the decoration occupies real layout space. The section grows to
   contain it. Overlap is not "avoided", it is impossible, at any width and
   with any size of art.

   The section drops its own padding-bottom and the decoration's margin-top
   provides the gap instead, so the artwork still sits on the section's
   bottom edge the way it did before.
   ========================================================================== */
/* Has to outrank body.stacks-on .stack-panel:not(.hero), which sets its own
   padding-bottom (72px, and 52/44px at the smaller breakpoints) and is more
   specific than a plain .section.has-decor. Without matching that specificity
   the decoration sits on a strip of empty panel instead of the bottom edge --
   that was the gap under the dunes. Scoped the same three ways so it wins at
   every breakpoint, not just the widest. */
.section.has-decor,
body.stacks-on .stack-panel.has-decor:not(.hero) { padding-bottom: 0; }

.decor {
  position: relative; z-index: 0;
  margin-top: 64px;
  pointer-events: none; user-select: none;
  line-height: 0;              /* kills the inline-block descender gap */
  text-align: center;
}
.decor img { display: inline-block; width: auto; height: auto; max-width: 100%; }

/* Poppies: a small centred hedge. */
.decor-poppies img { width: 38%; max-width: 420px; }

/* Dunes: the wide 6:1 landscape, full bleed, capped so it never grows taller
   than the copy above it on a very wide display. */
.decor-dunes { line-height: 0; }
.decor-dunes img { width: 100%; max-width: 1800px; }

/* One gull standing on the near sand of the dune band. Positioned against
   the band itself rather than the section, so it tracks the artwork at every
   width. Overlapping art with art is safe -- there is no copy down here. */
.decor-dunes { display: inline-block; width: 100%; }
.decor-perch {
  position: absolute; left: 50%; transform: translateX(-50%);
  /* Feet on the section's bottom edge, now that the dunes sit flush to it. */
  bottom: 0; width: 4.6%; min-width: 34px; max-width: 78px;
  aspect-ratio: 482 / 441;
  background-image: url('images/gv_slo_quest/art/gull.webp');
  background-size: contain; background-repeat: no-repeat; background-position: bottom center;
}

/* The surfer, walking along the foot of How It Works. */
.decor-surfer img { width: 168px; }

/* The three gulls, centred under the win cards. */
.decor-gulls img { width: 196px; }

/* The leaderboard badge is gone -- it crowded the heading rather than filling
   the space. Its CSS went with it instead of being left as dead rules. */

@media (max-width: 720px) {
  .decor { margin-top: 44px; }
  .decor-poppies img { width: 74%; max-width: 360px; }
  .decor-surfer img { width: 118px; }
  .decor-gulls  img { width: 138px; }
}

/* ---- CTA band mascot ---