/* ==========================================================================
   oc.css — the OpenCT web design system.
   ONE source of truth for every public page. Built 2026-07-17.

   WHY THIS FILE EXISTS
   The site was two generations glued together: index/about/events each carried
   their own inline <style> (~470 lines, copy-pasted and drifting), while
   detail/contact/service ran on an old Bootstrap template via includes/header.php.
   There was no single place to apply a design system because there wasn't one.
   Every page that links this file is on-brand by default instead of by copy-paste.

   SOURCE OF TRUTH FOR TOKENS
   Values mirror the APP's design system — lib/core/design/oc_design.dart
   (OcColors / OcSpace / OcRadius / OcShadow). The web and the app must read as
   ONE product. If a token changes there, change it here. Do not invent values.

   DIRECTION
   Type-led branding (large display, tight tracking) on product-grade UI
   (dark ground, hairline borders, one blue glow). Committed dark: a deliberate
   single visual world, not a missing light theme.

   CONTENTS
     1. Tokens          6. Sections
     2. Reset + base    7. Event cards
     3. Typography      8. Download CTA
     4. Header + nav    9. Footer
     5. Buttons        10. Responsive

   CONVENTIONS — read before editing
     · Declare a selector ONCE. This file's predecessors declared .section and
       .stat-card twice and papered the collisions over with !important.
     · Never re-declare section padding: it comes from --s-section, set once.
     · Never hardcode a colour that a token already names.
     · Never wrap an indexed column in CSS... that one's for the SQL, but the
       principle holds: don't fight the system you're standing on.
   ========================================================================== */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* ==========================================================================
   OpenCT web design system — 2026-07-17
   Direction: TYPE-LED BRANDING on PRODUCT-GRADE UI.
     · Typography, hero, headline scale, spacing, boldness  → Direction C
     · Palette, dark ground, blue glow, grid texture, cards,
       buttons, stats, premium-SaaS restraint               → Direction B
   Brand hue stays OcColors.brand #2563EB from the app (lib/core/design/
   oc_design.dart) so web and app read as ONE product. On this dark ground the
   brand LIFTS to #4C8DFF for text/edges — exactly what the app does in dark
   mode (OcColors.brandDark) — because #2563EB on near-black fails contrast.
   COMMITTED DARK: this is a deliberate single-world design (Linear/Arc), not a
   missing light theme. The ground is blue-biased near-black, never pure grey.
   ========================================================================== */
:root {
    /* Brand */
    --primary: #2563EB;          /* OcColors.brand — fills, glows, gradients */
    --primary-lift: #4C8DFF;     /* OcColors.brandDark — brand ON dark, for text/edges */
    --primary-dark: #1D4ED8;
    --primary-light: #60A5FA;
    --primary-soft: #15223D;     /* OcColors.brandSoftDark — tinted chip */

    /* Ink on dark */
    --accent: #F4F6FA;
    --text: #F4F6FA;
    --text-body: #C7CBD4;
    --text-muted: #828B9C;
    --text-light: #828B9C;
    --text-faint: #6E7788;

    /* Ground — B's blue-biased near-black. Not #000, not neutral grey. */
    --bg: #0B0D12;
    --bg-soft: #0D1016;          /* cells / cards */
    --bg-alt: #12161F;           /* elevated */

    /* Hairlines — B's precision comes from these being barely there */
    --border: rgba(255,255,255,0.07);
    --border-strong: rgba(255,255,255,0.12);

    /* Status */
    --success: #22C55E;
    --mint: #34D399;             /* OcColors.liveDot */
    --warning: #FB923C;
    --danger: #EF4444;

    --grad: linear-gradient(135deg, #2563EB 0%, #60A5FA 100%);
    --grad-dark: linear-gradient(135deg, #1D4ED8 0%, #2563EB 100%);
    --glow: rgba(37,99,235,0.30);           /* B's blue glow */
    --grid-line: rgba(255,255,255,0.028);   /* B's grid texture */

    /* Elevation — on dark, depth comes from hairlines + glow, not drop shadow */
    --shadow-sm: 0 2px 12px rgba(0,0,0,0.4);
    --shadow-md: 0 6px 20px rgba(0,0,0,0.5);
    --shadow-lg: 0 16px 44px rgba(0,0,0,0.6);

    /* Radius — B is tighter than the app's mobile scale; small radii read engineered */
    --r-xs: 4px;
    --r-sm: 6px;
    --r-md: 8px;
    --r-lg: 12px;
    --r-xl: 16px;
    --r-xxl: 24px;
    --r-pill: 999px;

    /* ── Compatibility aliases ──────────────────────────────────────────────
       The pre-2026-07-17 system named some tokens differently. Renaming them in
       oc.css without checking consumers left pages referencing var(--warn) and
       var(--hint), which resolve to NOTHING — CSS does not warn, it just renders
       the element with no colour. (Found on ticket_details "Only N left" and on
       events_details.) Aliases keep old call-sites working; new code should use
       the canonical names above.
       Before renaming a token, grep the site for var(--old-name) first. */
    --warn: var(--warning);
    --hint: var(--text-faint);
    --muted: var(--text-muted);
    --surface: var(--bg-soft);
    --brand: var(--primary);

    /* Spacing — OcSpace, extended for C's spacious rhythm */)
    --s-xs: 4px;
    --s-sm: 8px;
    --s-md: 12px;
    --s-lg: 16px;
    --s-xl: 24px;
    --s-xxl: 32px;
    --s-huge: 48px;
    --s-massive: 64px;
    --s-section: 120px;          /* C: the page must breathe */
}
@media (max-width: 768px) { :root { --s-section: 76px; } }

html { scroll-behavior: smooth; }
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; overflow-x: hidden; }

a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
.material-icons-round { vertical-align: middle; }

/* ── TYPOGRAPHY · Direction C ──────────────────────────────────────────────
   Inter, matching the app (oc_design.dart uses GoogleFonts.inter everywhere).
   The old scale was a template scale — h2 topped out at 2.75rem with 1.6 body
   leading, which is why the page read "regular" no matter what colour it wore.
   C commits: heavier weights, MUCH tighter tracking, sub-1 line-height on the
   display. At 900/-0.055em, Inter stops being a UI font and becomes branding. */
h1, h2, h3 { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: var(--text); text-wrap: balance; }
/* [leading, fixed twice — 2026-07-17]
   Desktop wants 0.9: at 6.5rem the lines lock together and that IS the look.
   Mobile cannot have it. A line-height below 1 makes each line box SHORTER than the
   glyphs it holds, so "people." descenders drop into the line beneath. First attempt
   (0.98) was still under 1 and still collided — only visible by screenshotting a real
   phone at 6x; the source looks fine either way. 1.04 clears it. */
h1 { font-size: clamp(2.35rem, 8.4vw, 6.5rem); font-weight: 900; letter-spacing: -0.05em; line-height: 1.04; }
@media (min-width: 700px) { h1 { line-height: 0.9; letter-spacing: -0.055em; } }
h2 { font-size: clamp(2rem, 4.6vw, 3.4rem); font-weight: 850; letter-spacing: -0.042em; line-height: 1.0; }
h3 { font-size: clamp(1.05rem, 2vw, 1.25rem); font-weight: 700; letter-spacing: -0.02em; line-height: 1.25; }

/* Section rhythm + eyebrow live in the SECTIONS block further down — declared
   once, no !important. Don't re-declare them here. */

.container { max-width: 1200px; margin: 0 auto; padding: 0 24px; }


/* ========== HEADER ========== */
.site-header { position: fixed; top: 0; left: 0; right: 0; z-index: 100; padding: 18px 0; transition: all 0.3s ease; }
/* [dark 2026-07-17] Was a white bar sliding over a dark page. Now the ground,
   blurred, held by a single hairline — B's restraint. */
.site-header.scrolled { background: rgba(11,13,18,0.82); backdrop-filter: blur(16px) saturate(140%); box-shadow: 0 1px 0 var(--border); padding: 12px 0; }
.header-inner { display: flex; align-items: center; justify-content: space-between; }
.logo { display: flex; align-items: center; }
.logo-img { height: 46px; width: auto; object-fit: contain; filter: brightness(0) invert(1); transition: filter 0.3s; }
/* logo stays inverted — the page is dark at every scroll position now */
.logo-dot { width: 10px; height: 10px; background: var(--primary); border-radius: 50%; box-shadow: 0 0 0 4px rgba(37,99,235,0.2); }
.nav { display: flex; align-items: center; gap: 8px; }
.nav a { padding: 8px 14px; font-size: 13.5px; font-weight: 500; color: var(--text-muted); transition: color .2s; border-radius: var(--r-sm); }

.nav a:hover { color: var(--text); }

.nav-cta { padding: 10px 20px !important; background: var(--text); color: var(--bg) !important; border-radius: var(--r-md); font-weight: 600 !important; transition: transform 0.2s, background 0.2s !important; }
.nav-cta:hover { background: #fff; transform: translateY(-1px); }
.menu-toggle { display: none; background: none; border: none; color: var(--text); cursor: pointer; padding: 8px; }

.menu-toggle .material-icons-round { font-size: 28px; }


/* ========== SECTIONS ==========
   [2026-07-17] Was a flat `padding: 100px 0` with centred heads. Two changes:
   (1) C's rhythm via --s-section, set ONCE here — an earlier pass declared
       .section twice and papered the collision over with !important. Fixed at
       source instead: one .section rule, no !important, no cascade fight.
   (2) Heads go LEFT. Centred everything is the template tell; a left rag with a
       real measure reads composed, and gives the big h2 somewhere to run. */
.section { padding: var(--s-section) 0; }
.section-head { text-align: left; max-width: 60ch; margin: 0 0 52px; }
.section-eyebrow {
    display: inline-flex; align-items: center; padding: 0; background: none;
    color: var(--primary-lift); border-radius: 0; font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.11em; margin-bottom: 14px;
}
.section-head h2 { color: var(--text); margin-bottom: 16px; }
.section-head p { font-size: 1.02rem; color: var(--text-muted); }

/* ========== EVENT GRID — Direction B's cards ==========
   On a dark ground a #fff card is a hole punched in the page. B's card is the
   ground, one shade lifted, held by a hairline — depth comes from the border and
   a blue edge on hover, never from a drop shadow. */
.event-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.event-card {
    background: var(--bg-soft); border-radius: var(--r-lg); overflow: hidden;
    border: 1px solid var(--border); position: relative;
    transition: transform .3s cubic-bezier(0.4,0,0.2,1), border-color .3s ease, background .3s ease;
}
.event-card:hover {
    transform: translateY(-4px); box-shadow: var(--shadow-lg);
    border-color: rgba(76,141,255,0.34); background: var(--bg-alt);
}
.event-img { position: relative; aspect-ratio: 16/10; overflow: hidden; background: var(--bg-alt); }
.event-img img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s; }
.event-card:hover .event-img img { transform: scale(1.06); }
.event-cat { position: absolute; top: 12px; left: 12px; background: rgba(11,13,18,0.82); backdrop-filter: blur(8px); border: 1px solid var(--border-strong); padding: 5px 10px; border-radius: var(--r-pill); font-size: 10.5px; font-weight: 700; color: var(--primary-lift); text-transform: uppercase; letter-spacing: 0.06em; }
.event-app-tag { position: absolute; top: 14px; right: 14px; padding: 6px 10px; background: rgba(15,23,42,0.85); backdrop-filter: blur(8px); color: #fff; border-radius: 100px; font-size: 11px; font-weight: 700; display: inline-flex; align-items: center; gap: 4px; }
.event-live { position: absolute; top: 14px; right: 14px; padding: 6px 10px; background: #EF4444; color: #fff; border-radius: 100px; font-size: 11px; font-weight: 700; display: inline-flex; align-items: center; gap: 4px; box-shadow: 0 4px 12px rgba(239,68,68,0.5); letter-spacing: 0.4px; }
.event-live-dot { width: 6px; height: 6px; background: #fff; border-radius: 50%; animation: livePulse 1.5s ease-in-out infinite; }
.event-app-tag .material-icons-round { font-size: 14px; }
.event-body { padding: 20px; }
.event-title { font-size: 1.1rem; font-weight: 700; color: var(--accent); margin-bottom: 12px; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.event-meta { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--text-muted); }
.event-meta-row { display: flex; align-items: center; gap: 6px; }
.event-meta-row .material-icons-round { font-size: 16px; color: var(--primary); }
.event-meta-row span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.event-attendees { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; font-size: 13px; }
.event-attendees .count { display: flex; align-items: center; gap: 6px; font-weight: 600; color: var(--text); }
.event-attendees .count .material-icons-round { font-size: 16px; color: var(--primary); }
.event-link { color: var(--primary); font-weight: 700; display: flex; align-items: center; gap: 4px; transition: gap 0.2s; }
.event-link:hover { gap: 8px; }


/* ========== DOWNLOAD CTA ========== */
.download-cta { background: var(--grad-dark); color: #fff; border-radius: 28px; padding: 60px; position: relative; overflow: hidden; max-width: 1160px; margin: 60px auto 120px; }
.download-cta::before { content: ''; position: absolute; top: -50%; right: -20%; width: 70%; height: 200%; background: radial-gradient(circle, rgba(96,165,250,0.25) 0%, transparent 60%); animation: pulse 8s ease-in-out infinite; }
.download-cta-inner { display: grid; grid-template-columns: 1.2fr 1fr; gap: 48px; align-items: center; position: relative; z-index: 1; }
.download-cta h2 { color: #fff; margin-bottom: 12px; }
.download-cta p { opacity: 0.9; margin-bottom: 28px; font-size: 1.05rem; }
.download-cta-img { text-align: center; }
.download-cta-img img { max-width: 280px; filter: drop-shadow(0 30px 60px rgba(0,0,0,0.3)); animation: float 6s ease-in-out infinite; }


/* ========== FOOTER — Direction B's structure, Direction C's sign-off ==========
   BUG FIXED 2026-07-17: this read `background: var(--accent)`. In the old light
   design --accent was a dark navy (#0C1E4A) and the footer used it as a dark
   panel. The dark rebuild redefined --accent to the near-white INK (#F4F6FA), so
   the footer became a white block carrying rgba(255,255,255,0.7) text — an
   invisible footer. Lesson: a token renamed by meaning ("accent") but consumed by
   appearance ("dark thing") breaks silently. It now names what it wants: the
   ground, one shade up, held by a hairline. */
.site-footer { background: var(--bg-soft); color: var(--text-muted); padding: 72px 0 0; border-top: 1px solid var(--border); position: relative; overflow: hidden; }
.footer-top { display: grid; grid-template-columns: 2fr 1fr 1fr 1.1fr; gap: 40px; padding-bottom: 44px; border-bottom: 1px solid var(--border); }
@media (max-width: 900px) { .footer-top { grid-template-columns: 1fr 1fr; gap: 32px; } }
@media (max-width: 560px) { .footer-top { grid-template-columns: 1fr; } }
.footer-brand h4 { color: var(--text); font-size: 17px; font-weight: 800; font-family: 'Inter'; letter-spacing: -0.03em; margin-bottom: 10px; }
.footer-brand p { font-size: 13.5px; line-height: 1.65; max-width: 34ch; margin-bottom: 20px; color: var(--text-muted); }
.footer-social { display: flex; gap: 8px; }
.footer-social a { width: 34px; height: 34px; border-radius: var(--r-sm); border: 1px solid var(--border); background: transparent; display: flex; align-items: center; justify-content: center; color: var(--text-muted); transition: color .18s ease, border-color .18s ease, background .18s ease; }
.footer-social a:hover { color: var(--text); border-color: rgba(76,141,255,0.4); background: var(--primary-soft); }
.footer-social a:focus-visible { outline: 2px solid var(--primary-lift); outline-offset: 2px; }
.footer-social svg { width: 17px; height: 17px; }
.footer-col h5 { color: var(--text); font-size: 11px; font-weight: 700; margin-bottom: 14px; text-transform: uppercase; letter-spacing: 0.11em; }
.footer-col ul { list-style: none; display: flex; flex-direction: column; gap: 9px; }
.footer-col a { font-size: 13.5px; color: var(--text-muted); transition: color .18s ease; }
.footer-col a:hover { color: var(--text); }
.footer-col a:focus-visible { outline: 2px solid var(--primary-lift); outline-offset: 2px; border-radius: 2px; }
/* store links — the website's job is to send people to the app, so the footer
   carries the download rather than making people scroll back up for it */
.footer-store { display: flex; flex-direction: column; gap: 8px; }
.footer-store a { display: inline-flex; align-items: center; gap: 8px; padding: 9px 12px; border: 1px solid var(--border); border-radius: var(--r-sm); font-size: 12.5px; font-weight: 600; color: var(--text-body); transition: border-color .18s ease, background .18s ease; }
.footer-store a:hover { border-color: rgba(76,141,255,0.4); background: var(--primary-soft); }
.footer-store a:focus-visible { outline: 2px solid var(--primary-lift); outline-offset: 2px; }
.footer-store .material-icons-round { font-size: 17px; color: var(--primary-lift); }
.footer-bottom { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; font-size: 12.5px; color: var(--text-faint); padding: 22px 0 26px; }
.footer-bottom .live-in { display: inline-flex; align-items: center; gap: 7px; }
.footer-bottom .live-in i { width: 5px; height: 5px; border-radius: 50%; background: var(--mint); box-shadow: 0 0 7px var(--mint); }
/* C's sign-off: the wordmark, oversized and stroked, bleeding off the bottom.
   Decorative only — aria-hidden, and it must never intercept a click. */
.footer-wordmark {
    display: block; margin: 0 auto -0.24em; max-width: 1200px; padding: 0 24px;
    font-size: clamp(4rem, 17vw, 13rem); font-weight: 900; letter-spacing: -0.06em;
    line-height: 0.78; text-align: center; color: transparent; user-select: none;
    pointer-events: none; -webkit-text-stroke: 1px rgba(244,246,250,0.09);
}
@supports not ((-webkit-text-stroke: 1px #000) or (text-stroke: 1px #000)) {
    .footer-wordmark { color: rgba(244,246,250,0.05); }
}


/* ========== 10. RESPONSIVE — shared only ==========
   Page-specific breakpoints (hero, stats, cities, how, bento, marquee) live with
   their page, NOT here. A shared file that knows about one page's hero is not a
   design system, it's a second copy of that page. */

@media (min-width: 1600px) {
    .container { max-width: 1400px; }
    .download-cta { max-width: 1360px; }
}

@media (max-width: 1024px) {
    .container { padding: 0 20px; }
    .event-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Tablet portrait — nav collapses to the sheet */
@media (max-width: 960px) {
    .download-cta-inner { grid-template-columns: 1fr; text-align: center; }
    .download-cta { padding: 48px 32px; }
    .download-cta-img img { max-width: 240px; margin: 32px auto 0; }
    .footer-top { grid-template-columns: 1fr 1fr; gap: 32px; }
    .nav { display: none; }
    .menu-toggle { display: block; }
    /* dark sheet, not the white one the old template dropped over the page */
    .nav.open {
        display: flex; position: absolute; top: 100%; left: 0; right: 0;
        flex-direction: column; background: rgba(11,13,18,0.97); backdrop-filter: blur(16px);
        border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
        padding: 12px; gap: 2px;
    }
    .nav.open a { color: var(--text-body) !important; width: 100%; text-align: center; padding: 12px; }
    .nav.open .nav-cta { background: var(--text); color: var(--bg) !important; margin-top: 8px; }
}

@media (max-width: 720px) {
    /* buttons shrink to content so primary/secondary stay a PAIR, not two banners */
    .btn-p, .btn-s { width: auto; flex: 0 0 auto; padding: 11px 16px; font-size: 13.5px; }
    .download-cta { padding: 40px 24px; }
}

@media (max-width: 560px) {
    .container { padding: 0 16px; }
    .event-grid { grid-template-columns: 1fr; gap: 16px; }
    .footer-top { grid-template-columns: 1fr; }
}

@media (max-width: 400px) {
    .download-cta { margin: 40px auto 60px; padding: 36px 20px; border-radius: 20px; }
    .download-cta h2 { font-size: 1.75rem; }
    .logo { font-size: 19px; }
}

/* Motion is decoration; never let it fight a vestibular condition. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* ========== 11. UTILITIES ========== */

/* Scroll-reveal. The class the JS adds is `.visible` — NOT `.in`. If you rename
   it, rename it in includes/oc_footer.php too: a mismatch here doesn't error, it
   just leaves every revealed element permanently invisible, which is a horrible
   thing to debug. Elements are visible by default until JS marks them, so a JS
   failure degrades to "everything shown" rather than a blank page. */
.reveal { opacity: 0; transform: translateY(30px); transition: opacity .7s ease, transform .7s ease; }
.reveal.visible { opacity: 1; transform: translateY(0); }

/* No JS? Show everything. */
.no-js .reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    .reveal { opacity: 1; transform: none; transition: none; }
}

/* ========== 12. TOUCH TARGETS ==========
   Audited 2026-07-17 across every page: nothing set a minimum tap height, and the
   computed heights came out at roughly
       .chip ~30px · .ticket-buy-btn ~37px · .btn-apply ~38px · .btn-p ~40px
   against the 44px both Apple and Google publish. The Book button on the ticket
   page was among the worst — the single control the whole site exists to get
   pressed, and it was ~7px short on a phone.

   Applied only on coarse pointers: a mouse hits a 30px chip fine, and inflating
   desktop controls to thumb size would look clumsy. `pointer: coarse` targets
   touch devices regardless of viewport width, which is more honest than guessing
   from a breakpoint — a small laptop window is not a thumb.

   min-height (not padding) so it cannot fight each component's own padding. */
@media (pointer: coarse) {
    /* Widened 2026-07-17 after screenshotting real pages at 390px: the first pass
       only covered classes I happened to name, and missed standalone links that a
       thumb still has to hit — .back-link was 22px, post.php's brand link 15px.
       NOT applied to links inside prose (.ev-about a, .co-* a): a link in a running
       paragraph should not be 44px tall, it should sit on the text baseline. */
    .btn-p, .btn-s, .nav-cta, .btn-apply, .chip,
    .ticket-buy-btn, .co-pay-btn, .event-link, .menu-toggle,
    .back-link, .brand a, .footer-store a, .empty .btn, .hv-cta a {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    /* Footer links were ~18px rows — effectively un-tappable next to each other.
       Give them room without changing how they look. */
    .footer-col ul { gap: 4px; }
    .footer-col a { display: inline-flex; align-items: center; min-height: 40px; }
    .footer-social a { min-width: 44px; min-height: 44px; }
    /* Chips sit in a scrolling row; keep them pill-shaped rather than tall boxes. */
    .chip { padding-top: 0; padding-bottom: 0; }
}
