/* ============================================================
   TIXA — public site design system
   One file for every page a buyer sees. Styles used to be pasted
   inline into each page, so index and buy had drifted apart.

   The canvas is dark on purpose: the product is event posters,
   and a light SaaS background fights the artwork instead of
   framing it. Every colour is a token, so the whole site can be
   flipped to light by redefining :root.
   ============================================================ */

:root {
    /* Canvas */
    --ink:        #0B0D12;   /* page */
    --surface:    #141821;   /* cards */
    --surface-2:  #1C212C;   /* raised / hover */
    --hairline:   #262C3A;

    /* Text */
    --text:       #F4F6FA;
    --text-dim:   #99A1B3;
    --text-faint: #858DA0;

    /* Accent — actions and energy. Green is NOT used for the brand:
       it is reserved for M-Pesa and success, so it keeps its meaning. */
    --accent:     #FF5A3C;
    --accent-ink: #14060A;   /* dark ON the accent: white measured 3.1:1, this is ~6:1 */
    --accent-dim: rgba(255, 90, 60, 0.14);

    --mpesa:      #3FCF6B;   /* payment + success only */
    --warn:       #FFB020;

    --font-display: 'Plus Jakarta Sans', system-ui, sans-serif;
    --font-body:    'Inter', system-ui, -apple-system, sans-serif;

    --r-sm: 10px;
    --r:    14px;
    --r-lg: 20px;

    --shadow:    0 1px 2px rgba(0,0,0,.4);
    --shadow-lg: 0 18px 40px -12px rgba(0,0,0,.65);

    --gutter: 16px;
    --max:    1180px;
}

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

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
    font-family: var(--font-body);
    background: var(--ink);
    color: var(--text);
    line-height: 1.55;
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--accent); color: var(--accent-ink); }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

.wrap { width: 100%; max-width: var(--max); margin: 0 auto; padding: 0 var(--gutter); }

/* ---------- Announcement ---------- */
.ann {
    background: var(--accent);
    color: var(--accent-ink);
    text-align: center;
    padding: 9px var(--gutter);
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: .04em;
}

/* ---------- Nav ---------- */
.nav {
    position: sticky; top: 0; z-index: 50;
    background: rgba(11, 13, 18, .82);
    backdrop-filter: saturate(140%) blur(12px);
    border-bottom: 1px solid var(--hairline);
}
.nav-in {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; height: 62px;
}
.brand {
    font-family: var(--font-display);
    font-weight: 800; font-size: 22px; letter-spacing: -.02em;
}
.brand span { color: var(--accent); }
.nav-links { display: flex; align-items: center; gap: 22px; }
.nav-links a:not(.btn) {
    font-size: 14px; font-weight: 500; color: var(--text-dim);
    transition: color .15s;
}
.nav-links a:not(.btn):hover { color: var(--text); }
@media (max-width: 620px) { .nav-links a.hide-sm { display: none; } }

/* ---------- Buttons ---------- */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    font-family: var(--font-body);
    font-size: 15px; font-weight: 600;
    padding: 13px 20px;
    border-radius: 999px;
    border: 1px solid transparent;
    cursor: pointer;
    transition: transform .12s ease, background .15s, border-color .15s;
    min-height: 46px;            /* comfortable thumb target */
    white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { background: #ff6f55; }
.btn-ghost {
    background: transparent; color: var(--text);
    border-color: var(--hairline);
}
.btn-ghost:hover { background: var(--surface-2); }
.btn-wa { background: var(--mpesa); color: #04210F; }
.btn-wa:hover { background: #55dc7f; }
.btn-sm { padding: 9px 15px; font-size: 13.5px; min-height: 38px; }
.btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn-block { display: flex; width: 100%; }

/* ---------- Hero ---------- */
.hero { padding: 56px 0 34px; }
.hero h1 {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: clamp(34px, 8.5vw, 64px);
    line-height: 1.02;
    letter-spacing: -.035em;
    max-width: 14ch;
}
.hero h1 em { font-style: normal; color: var(--accent); }
.hero p {
    color: var(--text-dim);
    font-size: clamp(15px, 3.6vw, 18px);
    margin-top: 16px;
    max-width: 46ch;
}
.hero-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 26px; }

/* ---------- Trust strip ----------
   This replaces the old "2 LIVE EVENTS / 2 CURATORS" counters.
   Counters shrink with inventory and told every visitor the
   marketplace was empty; these three facts are always true. */
.trust {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 10px;
    margin: 30px 0 10px;
}
.trust-item {
    display: flex; align-items: flex-start; gap: 11px;
    background: var(--surface);
    border: 1px solid var(--hairline);
    border-radius: var(--r);
    padding: 15px;
}
.trust-ico {
    flex: none; width: 34px; height: 34px; border-radius: 10px;
    display: grid; place-items: center;
    background: var(--accent-dim); font-size: 17px;
}
.trust-item b { display: block; font-size: 14.5px; font-weight: 600; }
.trust-item span { font-size: 13px; color: var(--text-dim); }

/* ---------- Sections ---------- */
.section { padding: 40px 0; }
.section-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 16px; margin-bottom: 20px;
}
.eyebrow {
    font-size: 11.5px; font-weight: 700; letter-spacing: .14em;
    text-transform: uppercase; color: var(--accent);
}
.section-head h2 {
    font-family: var(--font-display);
    font-weight: 800; letter-spacing: -.025em;
    font-size: clamp(24px, 5.5vw, 34px);
    margin-top: 4px;
}

/* ---------- Filters ---------- */
.filters {
    display: flex; gap: 8px; overflow-x: auto; padding-bottom: 6px;
    margin-bottom: 20px;
    scrollbar-width: none;
}
.filters::-webkit-scrollbar { display: none; }
.pill {
    flex: none;
    background: var(--surface); color: var(--text-dim);
    border: 1px solid var(--hairline);
    border-radius: 999px;
    padding: 8px 15px;
    font-size: 13.5px; font-weight: 500;
    font-family: var(--font-body);
    cursor: pointer;
    transition: all .15s;
}
.pill:hover { color: var(--text); border-color: var(--text-faint); }
.pill.on { background: var(--text); color: var(--ink); border-color: var(--text); font-weight: 600; }

/* ---------- Event cards ---------- */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
    gap: 18px;
}
.card {
    display: flex; flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--hairline);
    border-radius: var(--r-lg);
    overflow: hidden;
    transition: transform .18s ease, border-color .18s, box-shadow .18s;
}
.card:hover {
    transform: translateY(-3px);
    border-color: var(--text-faint);
    box-shadow: var(--shadow-lg);
}
.poster {
    position: relative;
    aspect-ratio: 4 / 5;          /* fixed, so nothing jumps while posters load */
    background: var(--surface-2);
    overflow: hidden;
}
.poster img { width: 100%; height: 100%; object-fit: cover; object-position: center top; }

/* Designed fallback for an event with no poster — a typographic
   plate rather than a grey rectangle, so a card without artwork
   still looks deliberate. */
.poster-fb {
    position: absolute; inset: 0;
    display: grid; place-items: center;
    padding: 20px; text-align: center;
}
.poster-fb b {
    font-family: var(--font-display);
    font-weight: 800; letter-spacing: -.03em;
    font-size: clamp(22px, 5vw, 30px);
    line-height: 1.08;
    color: #fff;
    text-shadow: 0 2px 20px rgba(0,0,0,.35);
}
.poster-tags {
    position: absolute; inset: 10px 10px auto 10px;
    display: flex; justify-content: space-between; gap: 8px;
}
.tag {
    font-size: 11px; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase;
    padding: 5px 9px; border-radius: 999px;
    background: rgba(11,13,18,.72);
    backdrop-filter: blur(6px);
    color: #fff;
}
.tag-hot { background: var(--accent); color: var(--accent-ink); }
.card-date {
    position: absolute; left: 10px; bottom: 10px;
    background: rgba(11,13,18,.8);
    backdrop-filter: blur(6px);
    border-radius: var(--r-sm);
    padding: 6px 10px; text-align: center; line-height: 1;
}
.card-date .m { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .1em; color: var(--accent); }
.card-date .d { display: block; font-family: var(--font-display); font-weight: 800; font-size: 19px; margin-top: 2px; }

.card-body { padding: 15px; display: flex; flex-direction: column; gap: 9px; flex: 1; }
.card-title {
    font-family: var(--font-display);
    font-weight: 700; font-size: 17.5px; letter-spacing: -.02em;
    line-height: 1.25;
}
.card-meta { font-size: 13px; color: var(--text-dim); display: flex; flex-direction: column; gap: 3px; }
.card-meta .by { color: var(--text-faint); }
.card-foot {
    margin-top: auto; padding-top: 12px;
    border-top: 1px solid var(--hairline);
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.price { font-family: var(--font-display); font-weight: 800; font-size: 17px; }
.price small { font-weight: 600; font-size: 11.5px; color: var(--text-faint); display: block; letter-spacing: .04em; }
.price.out { color: var(--text-faint); font-size: 14px; }

/* ---------- Empty state ----------
   Inventory is genuinely thin some weeks. An empty shelf should
   still give the visitor somewhere to go. */
.empty {
    border: 1px dashed var(--hairline);
    border-radius: var(--r-lg);
    padding: 52px 24px; text-align: center;
    background: var(--surface);
}
.empty h3 { font-family: var(--font-display); font-weight: 700; font-size: 21px; }
.empty p { color: var(--text-dim); margin: 8px auto 20px; max-width: 42ch; font-size: 14.5px; }

/* ---------- Curators ---------- */
.curators { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
.curator {
    display: flex; align-items: center; gap: 13px;
    background: var(--surface); border: 1px solid var(--hairline);
    border-radius: var(--r); padding: 13px;
}
.avatar {
    flex: none; width: 44px; height: 44px; border-radius: 12px;
    display: grid; place-items: center; overflow: hidden;
    background: var(--accent-dim); color: var(--accent);
    font-family: var(--font-display); font-weight: 800; font-size: 18px;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.curator h4 { font-size: 15px; font-weight: 600; }
.curator p { font-size: 12.5px; color: var(--text-faint); }

/* ---------- Footer ---------- */
.footer {
    border-top: 1px solid var(--hairline);
    padding: 40px 0 34px;
    margin-top: 30px;
}
.footer-top { display: flex; flex-wrap: wrap; gap: 20px; justify-content: space-between; align-items: flex-start; }
.footer .brand { font-size: 20px; }
.footer-tag { color: var(--text-dim); font-size: 13.5px; margin-top: 6px; max-width: 34ch; }
.footer-links { display: flex; flex-wrap: wrap; gap: 18px; }
.footer-links a { font-size: 13.5px; color: var(--text-dim); }
.footer-links a:hover { color: var(--text); }
.footer-note {
    margin-top: 26px; padding-top: 18px;
    border-top: 1px solid var(--hairline);
    font-size: 12.5px; color: var(--text-faint);
    display: flex; flex-wrap: wrap; gap: 6px 18px; justify-content: space-between;
}

/* ---------- Checkout (buy.php) ---------- */
.checkout { max-width: 560px; margin: 0 auto; padding: 24px var(--gutter) 130px; }

.stub {
    background: var(--surface);
    border: 1px solid var(--hairline);
    border-radius: var(--r-lg);
    overflow: hidden;
    margin-bottom: 16px;
}
.stub-poster { position: relative; background: var(--ink); }
.stub-poster img { width: 100%; height: auto; max-height: 70vh; object-fit: contain; }
.stub-poster::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(to top, var(--surface) 2%, rgba(20,24,33,0) 55%);
}
.stub-body { padding: 16px; }
.stub h1 {
    font-family: var(--font-display);
    font-weight: 800; letter-spacing: -.03em;
    font-size: clamp(22px, 6vw, 30px); line-height: 1.12;
}
.stub-meta { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; font-size: 14px; color: var(--text-dim); }

.panel {
    background: var(--surface);
    border: 1px solid var(--hairline);
    border-radius: var(--r-lg);
    padding: 16px;
    margin-bottom: 14px;
}
.panel-label {
    font-size: 11.5px; font-weight: 700; letter-spacing: .13em;
    text-transform: uppercase; color: var(--text-faint);
    margin-bottom: 12px;
}

.ticket-option {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    border: 1.5px solid var(--hairline);
    background: var(--ink);
    border-radius: var(--r);
    padding: 14px;
    margin-bottom: 9px;
    cursor: pointer;
    transition: border-color .15s, background .15s;
}
.ticket-option:hover { border-color: var(--text-faint); }
.ticket-option.selected { border-color: var(--accent); background: var(--accent-dim); }
.ticket-option.gone { opacity: .45; cursor: not-allowed; }
.tt-name { font-weight: 600; font-size: 15.5px; }
.tt-desc { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
.tt-price { font-family: var(--font-display); font-weight: 800; font-size: 16.5px; white-space: nowrap; }

.qty { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.qty label { font-weight: 600; font-size: 15px; }
.qty-ctrl { display: flex; align-items: center; gap: 4px; }
.qty-btn {
    width: 42px; height: 42px; border-radius: 12px;
    background: var(--surface-2); color: var(--text);
    border: 1px solid var(--hairline);
    font-size: 21px; line-height: 1; cursor: pointer;
    display: grid; place-items: center;
    font-family: var(--font-body);
}
.qty-btn:hover { background: var(--hairline); }
.qty-num { min-width: 40px; text-align: center; font-family: var(--font-display); font-weight: 800; font-size: 19px; }

.field { margin-bottom: 13px; }
.field:last-child { margin-bottom: 0; }
.field label { display: block; font-size: 13.5px; font-weight: 600; margin-bottom: 6px; }
.field input {
    width: 100%;
    background: var(--ink);
    border: 1.5px solid var(--hairline);
    border-radius: var(--r);
    padding: 13px 14px;
    color: var(--text);
    font-size: 16px;               /* 16px stops iOS zooming the page on focus */
    font-family: var(--font-body);
    transition: border-color .15s;
}
.field input::placeholder { color: var(--text-faint); }
.field input:focus { outline: none; border-color: var(--accent); }
.field-hint { font-size: 12px; color: var(--text-faint); margin-top: 5px; }

/* The M-Pesa prompt says BYKABIRO LABS, not Tixa. Saying so before
   the prompt appears is what stops people cancelling at the PIN
   screen, so it gets real visual weight rather than fine print. */
.paynote {
    display: flex; gap: 11px; align-items: flex-start;
    background: rgba(63, 207, 107, .08);
    border: 1px solid rgba(63, 207, 107, .3);
    border-radius: var(--r);
    padding: 13px;
    font-size: 13.5px; line-height: 1.5;
    color: var(--text-dim);
    margin-bottom: 14px;
}
.paynote strong { color: var(--mpesa); }

/* Sticky pay bar — on a phone the total and the button should never
   be something you have to scroll to find. */
.paybar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    background: rgba(11,13,18,.94);
    backdrop-filter: blur(14px);
    border-top: 1px solid var(--hairline);
    padding: 12px var(--gutter);
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
}
.paybar-in {
    max-width: 560px; margin: 0 auto;
    display: flex; align-items: center; gap: 13px;
}
.paybar-total { flex: none; }
.paybar-total small { display: block; font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--text-faint); }
.paybar-total b { font-family: var(--font-display); font-weight: 800; font-size: 21px; }

.alert {
    border-radius: var(--r); padding: 13px 15px;
    font-size: 14px; margin-bottom: 14px;
    border: 1px solid;
}
.alert-err { background: rgba(255,90,60,.1); border-color: rgba(255,90,60,.4); color: #ffb4a6; }
.alert-ok  { background: rgba(63,207,107,.1); border-color: rgba(63,207,107,.4); color: #9fe9ba; }

.status-box {
    display: none;
    text-align: center; padding: 26px 18px;
    background: var(--surface); border: 1px solid var(--hairline);
    border-radius: var(--r-lg); margin-bottom: 14px;
}
.status-box.show { display: block; }
.spinner {
    width: 34px; height: 34px; margin: 0 auto 14px;
    border: 3px solid var(--hairline);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- Simple prose pages (terms, privacy, errors) ---------- */
.prose { max-width: 720px; margin: 0 auto; padding: 40px var(--gutter) 60px; }
.prose h1 { font-family: var(--font-display); font-weight: 800; letter-spacing: -.03em; font-size: clamp(28px, 6vw, 40px); }
.prose h2 { font-family: var(--font-display); font-weight: 700; font-size: 20px; margin: 30px 0 10px; }
.prose p, .prose li { color: var(--text-dim); font-size: 15px; margin-bottom: 11px; }
.prose ul { padding-left: 20px; }
.prose strong { color: var(--text); }
.prose .updated { color: var(--text-faint); font-size: 13px; margin-top: 6px; }

.centered { min-height: 70vh; display: grid; place-items: center; text-align: center; padding: 0 var(--gutter); }
.centered h1 { font-family: var(--font-display); font-weight: 800; font-size: clamp(22px, 5vw, 30px); letter-spacing: -.02em; }
.centered p { color: var(--text-dim); margin: 10px 0 22px; }

/* Names below are the ones buy.php's tested payment script already
   toggles. Keeping them means the redesign never touched that script. */
#qty-row { display: none; margin-top: 4px; }
#qty-row.show { display: flex; }

.alert-error {
    background: rgba(255,90,60,.1);
    border: 1px solid rgba(255,90,60,.4);
    color: #ffb4a6;
    border-radius: var(--r);
    padding: 12px 14px;
    font-size: 14px;
    margin-bottom: 14px;
}

.mail-confirm {
    display: none;
    text-align: center;
    background: var(--surface);
    border: 1px solid rgba(63,207,107,.35);
    border-radius: var(--r-lg);
    padding: 26px 18px;
    position: relative;
}
.mail-confirm.show { display: block; }
.mail-stamp {
    position: absolute; top: 12px; right: 12px;
    font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
    color: var(--mpesa); border: 1px solid rgba(63,207,107,.5);
    border-radius: 999px; padding: 4px 10px;
}
.mail-icon { color: var(--mpesa); display: flex; justify-content: center; margin-bottom: 12px; }
.mail-title { font-family: var(--font-display); font-weight: 800; font-size: 19px; }
.mail-sub { font-size: 14px; color: var(--text-dim); margin-top: 6px; }
.mail-sub strong { color: var(--text); }

/* ============================================================
   MOTION
   ============================================================
   Everything here is gated behind html.motion, which an inline
   script in the <head> adds only when JavaScript is running AND
   prefers-reduced-motion is not set. That ordering matters: if
   the reveal states were unconditional, a reader with motion
   turned off — or with JS blocked — would get a permanently
   blank page instead of a calm one.

   Only transform and opacity are animated. Anything that makes
   the browser re-layout would stutter on the cheap Android
   phones most of these tickets are bought on.
   ============================================================ */

/* Cross-page transitions. Chromium honours this; everywhere else
   simply navigates as before. */
@view-transition { navigation: auto; }

::view-transition-old(root), ::view-transition-new(root) { animation-duration: .28s; }

/* ---------- Scroll reveal ---------- */
.motion .reveal {
    opacity: 0;
    transform: translateY(16px);
    will-change: opacity, transform;
}
.motion .reveal.in {
    opacity: 1;
    transform: none;
    transition: opacity .55s cubic-bezier(.22,.61,.36,1),
                transform .55s cubic-bezier(.22,.61,.36,1);
    will-change: auto;
}
/* Stagger, applied by index.php as --i on each card */
.motion .reveal.in { transition-delay: calc(var(--i, 0) * 70ms); }

/* ---------- Hero ---------- */
.motion .hero h1,
.motion .hero p,
.motion .hero-actions { animation: rise .7s cubic-bezier(.22,.61,.36,1) backwards; }
.motion .hero h1        { animation-delay: .05s; }
.motion .hero p         { animation-delay: .16s; }
.motion .hero-actions   { animation-delay: .26s; }

@keyframes rise {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: none; }
}

/* A slow glow behind the headline so the top of the page is never
   completely still. One element, transform only. */
.hero { position: relative; overflow: hidden; }
.hero::before {
    content: '';
    position: absolute;
    top: -220px; left: -160px;
    width: 520px; height: 520px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(255,90,60,.20), transparent 68%);
    pointer-events: none;
}
.motion .hero::before { animation: drift 18s ease-in-out infinite alternate; }
@keyframes drift {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(90px, 40px, 0) scale(1.18); }
}

/* The accent word gets a sweep the first time it is seen. */
.motion .hero h1 em {
    background: linear-gradient(90deg, var(--accent) 0%, #ffb199 45%, var(--accent) 90%);
    background-size: 220% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: sweep 2.6s ease-in-out .5s 1 both;
}
@keyframes sweep {
    from { background-position: 120% 0; }
    to   { background-position: -40% 0; }
}

/* ---------- Announcement ticker ----------
   This bar used to be a static line of text. On an events site a
   ticker earns its keep: it is the one thing that says the page
   is live. The content is duplicated so the loop has no seam. */
.ann { overflow: hidden; padding: 9px 0; }
.ann-track {
    display: flex;
    gap: 0;
    white-space: nowrap;
    width: max-content;
}
.ann-track span { padding: 0 28px; }
.motion .ann-track { animation: ticker 26s linear infinite; }
.ann:hover .ann-track { animation-play-state: paused; }
@keyframes ticker {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
}
/* Without motion it is a centred line again, exactly as before. */
html:not(.motion) .ann-track { width: 100%; justify-content: center; animation: none; }
html:not(.motion) .ann-track span:nth-child(n+2) { display: none; }

/* ---------- Nav condenses on scroll ---------- */
.nav-in { transition: height .25s ease; }
.nav.stuck { box-shadow: 0 6px 24px -10px rgba(0,0,0,.8); }
.nav.stuck .nav-in { height: 52px; }

/* ---------- Event cards ---------- */
.card { will-change: auto; }
.poster img { transition: transform .6s cubic-bezier(.22,.61,.36,1); }
.motion .card:hover .poster img { transform: scale(1.07); }

/* A sheen that crosses the poster on hover. */
.poster::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(105deg, transparent 38%, rgba(255,255,255,.16) 50%, transparent 62%);
    transform: translate3d(-120%, 0, 0);
    pointer-events: none;
}
.motion .card:hover .poster::after { transition: transform .85s ease; transform: translate3d(120%, 0, 0); }

.card-date { transition: transform .3s cubic-bezier(.22,.61,.36,1); }
.motion .card:hover .card-date { transform: translateY(-3px); }

/* Only the genuinely scarce tag pulses, and gently. */
.motion .tag-hot { animation: pulse 2.4s ease-in-out infinite; }
@keyframes pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.06); }
}

/* Cards morph into the checkout poster where the browser supports it.
   The name is set per event in the markup, so each one is unique. */
.poster, .stub-poster { view-transition-name: var(--vt, none); }

/* ---------- Buttons ---------- */
.btn { transition: transform .14s ease, background .15s, border-color .15s, box-shadow .2s; }
.motion .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 24px -10px rgba(255,90,60,.8); }
.motion .btn-wa:hover      { transform: translateY(-2px); box-shadow: 0 10px 24px -10px rgba(63,207,107,.7); }
.motion .btn-ghost:hover   { transform: translateY(-2px); }

/* ---------- Checkout ---------- */
/* A very slow drift on the poster, so the page breathes while
   someone is filling the form. */
.motion .stub-poster img { animation: breathe 26s ease-in-out infinite alternate; }
@keyframes breathe {
    from { transform: scale(1); }
    to   { transform: scale(1.04); }
}

.ticket-option { transition: border-color .18s, background .18s, transform .18s ease; }
.motion .ticket-option:active { transform: scale(.99); }
.motion .ticket-option.selected { animation: pick .32s cubic-bezier(.22,.61,.36,1); }
@keyframes pick {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.015); }
    100% { transform: scale(1); }
}

.paybar { transform: translate3d(0, 0, 0); }
.motion .paybar { animation: slide-up .45s cubic-bezier(.22,.61,.36,1) .25s backwards; }
@keyframes slide-up {
    from { transform: translate3d(0, 100%, 0); }
    to   { transform: translate3d(0, 0, 0); }
}

.motion .status-box.show { animation: rise .4s cubic-bezier(.22,.61,.36,1); }

/* The dispatched-ticket stub lands like a stamp. */
.motion .mail-confirm.show { animation: rise .45s cubic-bezier(.22,.61,.36,1); }
.motion .mail-confirm.show .mail-stamp { animation: stamp .5s cubic-bezier(.34,1.56,.64,1) .2s backwards; }
@keyframes stamp {
    from { opacity: 0; transform: rotate(-16deg) scale(1.7); }
    to   { opacity: 1; transform: rotate(-6deg) scale(1); }
}
.mail-stamp { transform: rotate(-6deg); }

/* Qty buttons give a little feedback on press. */
.qty-btn { transition: background .15s, transform .12s ease; }
.motion .qty-btn:active { transform: scale(.92); }
