/* gx-worker-critical.css — first-viewport critical CSS for the three worker SSR templates
 * (_city.html, _bands-for-hire.html, _how-to-get-gigs.html).
 *
 * Loaded BLOCKING after gx-base.css and BEFORE gx-shared-ui.css is preloaded.
 * Contains ONLY rules needed to paint the first viewport (hero + topbar) without
 * waiting for gx-shared-ui.css. Everything below the fold (FAQ, comparison table,
 * scene cards, related grid, radar, footer) stays in gx-shared-ui.css and arrives
 * after the preload swap.
 *
 * THIS FILE IS AN ADDITIVE DUPLICATE — gx-shared-ui.css continues to ship the same
 * rules unchanged. Do NOT remove rules from gx-shared-ui.css when editing this file.
 * If a rule changes in sb-shared.css, mirror the change here in the same edit.
 *
 * Source: rules copied verbatim from gx-shared-ui.css. Line numbers as of 2026-04-29.
 */

/* ── Hero shell ─────────────────────────────────────────────────────────
   Production has TWO .hero blocks in gx-shared-ui.css; the second (line
   8520) overrides the first because of source order. We mirror the
   FINAL cascade state here. min-height is 90vh, not 100vh; .hero h1
   does NOT have white-space: nowrap; .hero-alpha-badge has a bordered
   pill treatment. */
.hero {
    min-height: 90vh;
    display: flex; align-items: center; justify-content: center; text-align: center;
    padding: calc(var(--sb-space-3xl) + 13px) var(--sb-space-lg) calc(var(--sb-space-3xl) - 35px);
    position: relative; overflow: hidden;
}
.hero h1 {
    font-size: clamp(2.5rem, 6vw, 4.5rem);
    font-weight: 900;
    line-height: 1.1;
    margin-bottom: calc(var(--sb-space-lg) - 12px);
}
@media (max-width: 768px) {
    .hero h1 { font-size: 2rem; white-space: normal; }
}
/* .hero-sub h1 size override — these worker-SSR templates use the
   smaller sub-page sizing. Mirrored from gx-shared-ui.css so h1 has
   its final size from first paint instead of shrinking when async
   CSS arrives (caused CLS 0.197 on /gigs/how-to-get-gigs-birmingham,
   2026-05-02). Mobile diff was 2.5-4.5rem → 1.9rem = big shift. */
.hero-sub h1 { font-size: clamp(2.2rem, 5vw, 3.8rem); }
@media (max-width: 768px) {
    .hero-sub h1 { font-size: 1.9rem; }
}
.hero-alpha-badge {
    display: inline-block;
    font-size: var(--sb-text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--sb-ice);
    border: 1px solid var(--sb-ice);
    padding: 6px 16px;
    border-radius: var(--sb-radius-sm);
    margin-bottom: calc(var(--sb-space-xl) - 12px);
}
.hero .sb-btn-primary:hover { color: #fff !important; -webkit-text-fill-color: #fff !important; }

/* ── Hero photo + overlays (lines 7288-7334) ────────────────────────── */
.hero-photo {
    position: absolute;
    inset: 0;
    background: url('assets/hero-concert.webp') center/cover no-repeat;
    background: image-set(url('assets/hero-concert.webp') type('image/webp'), url('assets/hero-concert.jpg') type('image/jpeg')) center/cover no-repeat;
    z-index: 0;
}
.hero-photo::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(8,12,20,0.70) 0%, rgba(8,12,20,0.85) 100%);
}
.hero-content {
    position: relative;
    z-index: 2;
    max-width: 800px;
}
.hero-tag {
    display: inline-block;
    padding: 6px 16px;
    font-size: var(--sb-text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--sb-ice);
    border: 1px solid var(--sb-ice);
    border-radius: var(--sb-radius-sm);
    margin-bottom: calc(var(--sb-space-lg) - 12px);
    margin-top: 25px;
}
.hero-subtitle {
    font-size: var(--sb-text-xl);
    color: var(--sb-text-secondary);
    max-width: 600px;
    margin: 0 auto calc(var(--sb-space-xl) - 12px);
    line-height: 1.6;
}
.hero-divider {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: linear-gradient(90deg, var(--sb-ice), var(--sb-sky), var(--sb-violet));
    opacity: 1;
}
.hero-subtitle-3line { min-height: 4.8em; }
.gig-hero-ctas { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-bottom: var(--sb-space-2xl); }

/* City-stats bar — SSR'd into the page but its full flex/padding/translate
   layout only kicks in once the async-loaded shared-ui CSS arrives. Without
   these declarations the bar grows ~37px tall when CSS lands, pushing
   everything below it up and producing CLS ≥ 0.09 (measured 2026-04-29 on
   /gigs/bands-for-hire-bristol). Just the layout properties — font sizes
   and per-cell text styling stay in shared CSS to keep critical-CSS small. */
.city-stats-bar {
    display: flex; justify-content: center; align-items: center;
    gap: var(--sb-space-xl);
    padding: var(--sb-space-lg);
    max-width: 900px; margin: 0 auto;
    position: relative; z-index: 2;
    transform: translateY(-50%);
}
/* Centre stat numbers + labels horizontally inside each column.
   shared-ui now uses `.city-stat { flex: 1 1 0; padding: 0 var(--sb-space-md) }`
   for equal-width columns + symmetric padding, so no per-page override
   is needed. Keeping a flex-1 fallback here in case worker-critical is
   served before shared-ui (FOUC window). */
body.gx-city-page .city-stat { flex: 1 1 0; min-width: 0; text-align: center; padding: 0 var(--sb-space-md); }
/* Mobile: smaller stat-label text. body.gx-city-page bumps specificity
   above the (0,1,1) shared-ui rule so we win regardless of source order. */
@media (max-width: 768px) {
  body.gx-city-page .city-stat-num {
    font-size: 1.4rem; line-height: 1.1; margin-bottom: 2px;
  }
  body.gx-city-page .city-stat-label {
    font-size: 0.7rem; line-height: 1.25;
    letter-spacing: 0.01em; margin-top: 4px;
  }
}
@media (max-width: 768px) {
    /* On mobile, .city-stats-bar uses translateY(-50%) to overlap the hero
       (line 7405 in gx-shared-ui.css). That eats the visible gap below the
       CTAs — match the gap above .hero-alpha-badge by adding extra space.
       body.gx-city-page raises specificity above the late-arriving shared-ui
       rule (.gig-hero-ctas, same specificity but later source order). */
    body.gx-city-page .gig-hero-ctas { margin-bottom: calc(var(--sb-space-2xl) + var(--sb-space-xl)); }
    /* Mobile: reduce horizontal padding/gap so 4 columns aren't crushed
       (default xl padding on each side eats most of phone width). */
    body.gx-city-page .city-stats-bar { gap: 4px; padding: var(--sb-space-md) 4px; }
    body.gx-city-page .city-stat,
    body.gx-city-page .city-stats-bar .city-stat:first-child {
      padding-left: 6px; padding-right: 6px;
    }
    /* Rate-benchmark CTA pair stacks on mobile. Source order is
       Breakdown then Submit so we don't need flex-direction reverse
       (that caused CLS when async shared-ui CSS landed). Cap button
       width at 280px to match the rate cards above (grid minmax 280). */
    body.gx-city-page #city-rate-benchmark .city-live-gigs-more .sb-btn {
        width: 100%; max-width: 280px;
    }
    /* Reserve the height that gx-genre-score.js will fill at runtime so
       the hero doesn't shift downward by ~207px when JS renders the
       widget. THIS WAS THE MAIN CLS CULPRIT (shift score 0.27). */
    body.gx-city-page #gx-genre-score { min-height: 285px; display: block; }
}

/* Related-card heading sizing — must be in critical CSS because adjacency
   uses <h3> for heading-order a11y and the default browser h3 size
   (1.17em + 1em margins) is much larger than the eventual 0.9rem. Without
   this rule, the 6 adjacency cards render tall on first paint, then shrink
   when async shared-ui CSS lands, causing a large CLS shift. Mirrors the
   .related-card h3, .related-card h4 rule in sb-shared.css / gx-shared-ui.css. */
.related-card h3, .related-card h4 {
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--sb-text);
    margin: 0 0 4px 0;
    height: 2.5em;
    line-height: 1.25;
    overflow: hidden;
}

/* ── Gradient text used in hero h1 (line 1576) ──────────────────────── */
.sb-gradient-text {
    background: var(--sb-gradient-text);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    display: inline;
}

/* ── CTA buttons used in the hero + topbar (lines 1651-1707, 2198-2203) ─ */
.sb-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sb-space-sm);
    padding: 10px 20px;
    font-size: var(--sb-text-sm);
    font-weight: 600;
    border: none;
    border-radius: var(--sb-radius);
    cursor: pointer;
    transition: all var(--sb-transition);
    white-space: nowrap;
    text-decoration: none;
    line-height: 1.4;
}
.sb-btn-primary {
    background: var(--sb-gradient);
    color: #fff;
    border: 1px solid transparent;
    box-shadow: var(--sb-shadow-glow);
}
.sb-btn-outline {
    background: transparent;
    color: var(--sb-ice);
    border: 1px solid var(--sb-ice);
}
.sb-btn-lg {
    padding: 14px 28px;
    font-size: var(--sb-text-base);
}

/* ── Skip link — must be hidden offscreen by default (lines 1530-1547) ──── */
.sb-skip-link {
    position: absolute;
    top: -100%;
    left: 16px;
    z-index: 10000;
    padding: 10px 20px;
    background: var(--sb-bg-body);
    color: var(--sb-ice);
    border: 1px solid var(--sb-ice);
    border-radius: var(--sb-radius);
    font-size: var(--sb-text-sm);
    font-weight: 600;
    text-decoration: none;
    transition: top 0.15s ease;
}
.sb-skip-link:focus {
    top: 12px;
}

/* Pre-position #sb-topbar placeholder so it's out of flow from first
   paint, matching .sb-topbar (position:fixed). Without this the
   placeholder is in flow as 0px empty div, then sb-sales.js adds the
   .sb-topbar class — that transition shifts the hero ~60px under PSI's
   slow-net simulation. UI-neutral mirror of the gx-shared-ui.css rule. */
#sb-topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--sb-topbar-height);
    z-index: 1000;
}
/* ── Topbar shell — rendered into #sb-topbar by sb-sales.js (lines 4132+) ─ */
.sb-topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--sb-topbar-height);
    background: rgba(12,17,32,0.85) no-repeat bottom / 100% 2px;
    background-image: linear-gradient(90deg, rgba(56,189,248,1), rgba(14,165,233,1), rgba(99,102,241,1));
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    border-bottom: none;
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    padding: 0 var(--sb-space-lg);
    z-index: 1000;
    font-family: var(--sb-font);
    color: var(--sb-text);
}
.sb-topbar-left {
    display: flex;
    align-items: center;
    gap: 0;
    flex-shrink: 0;
}
.sb-topbar-logo {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    cursor: pointer;
    text-decoration: none;
    flex-shrink: 0;
    width: 200px;
}
.sb-topbar-right {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--sb-space-md);
}
.sb-topbar-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sb-space-xs);
}
.sb-topbar-link {
    padding: 8px 14px;
    color: var(--sb-text-secondary);
    font-size: var(--sb-text-sm);
    font-weight: 500;
    text-decoration: none;
    border-bottom: 2px solid transparent;
}

/* ── Topbar logo internals — sb-sales.js renders .sb-topbar-mark, .sb-eq,
       and the GigXchange wordmark inside .sb-topbar-logo. Without these the
       logo collapses to bare unstyled text. (lines 4205-4234, 7121-7137) ─── */
.sb-topbar-mark {
    width: 26px;
    height: 30px;
    flex-shrink: 0;
}
.sb-topbar-logo > .sb-eq {
    height: 22px !important;
    gap: 3px !important;
    align-self: flex-end;
}
.sb-topbar-logo > .sb-eq .sb-eq-bar {
    width: 4.5px !important;
    border-radius: 2px !important;
}
.sb-topbar-logo > span:not(.sb-eq) {
    font-size: 1.55rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    display: block;
    color: var(--sb-text);
    line-height: 1;
    transform: none;
    transition: filter 0.3s ease;
}
.sb-topbar-logo > span:not(.sb-eq) {
    font-family: 'Rajdhani', var(--sb-font);
}
.sb-eq {
    display: inline-flex;
    align-items: flex-end;
    gap: 2.5px;
    height: 1.3em;
    vertical-align: middle;
}
.sb-eq-bar {
    width: 3.5px;
    border-radius: 1px;
    background: var(--sb-gradient);
}
.sb-eq .sb-eq-bar:nth-child(1) { height: 40%; }
.sb-eq .sb-eq-bar:nth-child(2) { height: 70%; }
.sb-eq .sb-eq-bar:nth-child(3) { height: 100%; }
.sb-eq .sb-eq-bar:nth-child(4) { height: 55%; }

/* The X letter in the GIGXCHANGE wordmark — rendered in ice blue by the
   .sb-x span. Without this rule the X falls back to white and the
   wordmark loses its branded accent at first paint. (lines 4248-4254) */
.sb-x {
    font-size: 1em;
    color: var(--sb-ice);
    font-weight: 800;
    display: inline;
    line-height: 1;
}

/* Topbar logo hover glow — drop-shadow + brightness on the wordmark, plus
   speeds up the equalizer pulse animation. Strictly :hover so doesn't
   affect first paint, but if the user hovers during the ~200 ms window
   before gx-shared-ui.css lands they'd otherwise see a flat hover state.
   Cheap to include. (lines 4236-4241) */
.sb-topbar-logo:hover > span:not(.sb-eq) {
    filter: brightness(1.3) drop-shadow(0 0 6px rgba(56, 189, 248, 0.5));
}
.sb-topbar-logo:hover > .sb-eq .sb-eq-bar {
    animation-duration: 0.3s !important;
}

/* The X letter in the GIGXCHANGE wordmark is rendered in ice blue. Without
   this rule the X falls back to white and the wordmark loses its branded
   accent at first paint. (lines 4248-4254) */
.sb-x {
    font-size: 1em;
    color: var(--sb-ice);
    font-weight: 800;
    display: inline;
    line-height: 1;
}

/* Topbar logo hover glow — drop-shadow + brightness lift on the wordmark,
   plus speeds up the equalizer pulse animation. Strictly :hover so doesn't
   affect first paint, but if the user hovers during the ~200ms window
   before gx-shared-ui.css lands they'd otherwise see a flat hover state.
   Cheap to include. (lines 4236-4241) */
.sb-topbar-logo:hover > span:not(.sb-eq) {
    filter: brightness(1.3) drop-shadow(0 0 6px rgba(56, 189, 248, 0.5));
}
.sb-topbar-logo:hover > .sb-eq .sb-eq-bar {
    animation-duration: 0.3s !important;
}

/* Topbar dropdown menus must stay hidden by default. The hover-to-open
   behaviour is decorative and will arrive with gx-shared-ui.css; without
   this rule the dropdown items flow inline as visible text inside the
   first viewport. (lines 4296-4297) */
.sb-topbar-dropdown-menu { display: none; }

/* ── Mobile topbar restructure (lines 6983-7018, 7034-7037) ──────────────
   On mobile, gx-shared-ui.css overrides the desktop grid layout: it hides
   the nav links, switches to flex space-between, absolutely-centers the
   logo, and uses :has() to reorder the right-side CTAs so the primary
   button sits left of the logo and the outline button sits right. Without
   these rules, mobile shows desktop nav links and the production mobile
   CTA arrangement is missing. Decorative rules and unrelated app/sidebar
   rules from the same media query are NOT included. */
@media (max-width: 768px) {
    .sb-topbar-nav { display: none; }
    .sb-topbar-search { display: none; }
    .sb-topbar {
        display: flex;
        justify-content: space-between;
    }
    .sb-topbar-left { flex: 0 0 auto; }
    .sb-topbar-logo {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, calc(-50% - 2px));
        width: auto;
        align-items: center;
        line-height: 1;
    }
    .sb-topbar-logo > span:not(.sb-eq) {
        font-size: 1.55rem;
        line-height: 1;
    }
    .sb-topbar-logo > .sb-eq {
        height: 18px !important;
        align-self: center;
    }
    .sb-topbar-right { flex: 0 0 auto; }
    .sb-topbar:has(.sb-topbar-nav) { padding: 0 var(--sb-space-sm); align-items: center; position: sticky; top: 0; }
    .sb-topbar:has(.sb-topbar-nav) .sb-topbar-right { display: contents; }
    .sb-topbar:has(.sb-topbar-nav) .sb-topbar-right .sb-btn-primary { order: -1; margin-right: auto; }
    .sb-topbar:has(.sb-topbar-nav) .sb-topbar-right .sb-btn-secondary,
    .sb-topbar:has(.sb-topbar-nav) .sb-topbar-right .sb-btn-outline { order: 1; margin-left: auto; background: transparent; color: var(--sb-ice); border: 1px solid var(--sb-ice); }
    .sb-topbar:has(.sb-topbar-nav) .sb-topbar-right .sb-btn { font-size: var(--sb-text-xs); padding: 6px 10px; min-width: 90px; text-align: center; }
}
@media (max-width: 480px) {
    .sb-topbar { padding: 0 var(--sb-space-md); }
}
@media (max-width: 400px) {
    .sb-topbar:has(.sb-topbar-nav) .sb-topbar-logo > span:not(.sb-eq) { font-size: 0.85rem; }
    .sb-topbar:has(.sb-topbar-nav) .sb-topbar-logo .sb-eq { gap: 2px; }
    .sb-topbar:has(.sb-topbar-nav) .sb-topbar-right .sb-btn { padding: 6px 10px; font-size: 11px; }
}

/* Mobile hero-tag — at <=480px the tag pill shrinks. Without this,
   the tag is 8 px taller in critical-only than baseline at 390px,
   contributing to the cumulative hero stack offset. (line 8658) */
@media (max-width: 480px) {
    .hero-tag { font-size: 0.6rem; padding: 4px 10px; }
}

/* ── Genre score widget (city template only) — appears between hero subtitle
       and CTAs, above the fold on tall mobile. Structural rules only. (lines
       15414+ in gx-shared-ui.css). Decorative ::before gradient border, the
       gradient-text fill on .gx-genre-score-num, and the right-side radar
       sub-block stay deferred. ───────────────────────────────────────────── */
.gx-genre-score-card {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sb-space-lg);
    max-width: 640px;
    margin: var(--sb-space-lg) auto var(--sb-space-2xl);
    padding: var(--sb-space-lg);
    position: relative;
    background: rgba(15, 21, 37, 0.70);
    border-radius: 16px;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.gx-genre-score-left {
    flex: 1 1 auto;
    text-align: center;
}
.gx-genre-score-left .gx-genre-score-number { justify-content: center; }
.gx-genre-score-left .gx-genre-score-summary { margin-left: auto; margin-right: auto; }
.gx-genre-score-label {
    font-size: 0.75em;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--sb-text-muted);
    font-weight: 600;
    margin-bottom: 6px;
}
.gx-genre-score-number {
    display: flex;
    align-items: baseline;
    gap: 2px;
    line-height: 1;
    margin-bottom: 8px;
}
.gx-genre-score-summary {
    font-size: 0.88em;
    color: var(--sb-text);
    line-height: 1.45;
    max-width: 230px;
}
/* Variant used when genre score has insufficient data (the "Emerging scene"
   state shown in the validation screenshot). Switches the card to a stacked
   column layout so the message reads top-to-bottom. */
.gx-genre-score-card--emerging {
    flex-direction: column;
    text-align: center;
}
/* Gradient border around the genre-score card. Originally flagged as
   decorative, but production has it above-the-fold on city pages and the
   user spotted its absence as a regression. Adding to match production
   first paint. (lines 15431-15442) */
.gx-genre-score-card::before {
    content: '';
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    padding: 1px;
    background: linear-gradient(135deg, var(--sb-ice), var(--sb-sky), var(--sb-violet));
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    pointer-events: none;
}
/* Emerging-state inner labels — required for visual parity with production.
   (lines 15499-15521) The gradient title is decorative in nature but its
   absence is visually obvious so it's included. */
.gx-genre-score-emerging-label {
    font-size: 0.75em;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--sb-text-muted);
    font-weight: 600;
    margin-bottom: 4px;
}
.gx-genre-score-emerging-title {
    font-family: var(--sb-font-display, 'Rajdhani', sans-serif);
    font-size: 1.8em;
    font-weight: 700;
    background: linear-gradient(135deg, var(--sb-ice), var(--sb-violet));
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    margin-bottom: 6px;
}
.gx-genre-score-emerging-sub {
    font-size: 0.88em;
    color: var(--sb-text-muted);
}
/* When the cookie banner is active (sb-shared.js adds the body class on
   page load if no consent stored), reserve bottom space so the fixed
   banner doesn't visually overlap hero CTAs. Without this rule the
   content shows behind the banner and the OPEN ALPHA badge / CTA row
   gets cut off in the first viewport. (line 6324) */
body.sb-cookie-active { padding-bottom: 60px; }
@media (max-width: 600px) {
    body.sb-cookie-active { padding-bottom: 120px; }
}

/* Mobile genre-score card — stack into a column at <=640px. (lines 15523-15536) */
@media (max-width: 640px) {
    .gx-genre-score-card {
        flex-direction: column;
        text-align: center;
        gap: var(--sb-space-md);
        max-width: calc(100% - 24px);
        padding: var(--sb-space-md);
        box-sizing: border-box;
    }
    .gx-genre-score-left { text-align: center; }
    .gx-genre-score-number { justify-content: center; }
    .gx-genre-score-summary { max-width: none; }
}
