/*
 * Theme Name: StartupPH
 * Theme URI: https://startup.ph
 * Description: The Philippine Startup Ecosystem — dark mode premium news and directory theme
 * Version: 1.0.0
 * Author: startup.ph
 * Author URI: https://startup.ph
 * License: Proprietary
 * Text Domain: startupph
 *
 * DESIGN SYSTEM:
 * Primary background dark:  #0A0F1E
 * Card background dark:     #0F172A
 * Background light:         #F8FAFC
 * Accent blue:              #3B82F6
 * Accent green:             #10B981
 * Breaking red:             #EF4444
 * Hot orange:               #F97316
 * Opinion purple:           #8B5CF6
 * Sponsored grey:           #64748B
 * Text primary dark:        #FFFFFF
 * Text secondary dark:      #94A3B8
 * Text muted dark:          #64748B
 * Text primary light:       #0F172A
 * Headings: Satoshi Bold (Fontshare)
 * Body: Inter Regular (Google Fonts)
 */

/* ============================================================
   CSS CUSTOM PROPERTIES — DESIGN TOKENS
   ============================================================ */
:root {
    /* Dark mode colors (default) */
    --bg-primary:       #0A0F1E;
    --bg-card:          #0F172A;
    --bg-surface:       #1A2235;
    --border-color:     #1E293B;
    --border-hover:     #3B82F6;

    /* Text */
    --text-primary:     #FFFFFF;
    --text-secondary:   #94A3B8;
    --text-muted:       #64748B;

    /* Accent colors */
    --accent-blue:      #3B82F6;
    --accent-green:     #10B981;
    --accent-red:       #EF4444;
    --accent-orange:    #F97316;
    --accent-purple:    #8B5CF6;
    --accent-grey:      #64748B;

    /* Typography */
    --font-heading:     'Satoshi', 'Inter', sans-serif;
    --font-body:        'Inter', sans-serif;

    /* Spacing */
    --radius-card:      8px;
    --radius-btn:       4px;
    --transition-fast:  200ms ease;

    /* Layout */
    --container-max:    1440px;
    --container-pad:    1.5rem;
}

/* Light mode overrides — applied whenever <html> lacks the .dark class */
html:not(.dark) {
    --bg-primary:       #F8FAFC;
    --bg-card:          #FFFFFF;
    --bg-surface:       #F1F5F9;
    --border-color:     #E2E8F0;
    --border-hover:     #3B82F6;
    --text-primary:     #0F172A;
    --text-secondary:   #475569;
    --text-muted:       #94A3B8;
}

/* ============================================================
   RESET AND BASE
   ============================================================ */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;
    font-size: 16px;
}

body {
    background-color: var(--bg-primary);
    color: var(--text-primary);
    font-family: var(--font-body);
    font-size: 1rem;
    line-height: 1.6;
    min-height: 100vh;
    overflow-x: hidden; /* guards against unintentional horizontal scroll site-wide;
                            does not affect the intentional overflow-x:auto rows below */
    transition: background-color var(--transition-fast), color var(--transition-fast);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* ============================================================
   TYPOGRAPHY
   ============================================================ */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-heading);
    font-weight: 700;
    line-height: 1.2;
    color: var(--text-primary);
}

h1 { font-size: clamp(2rem, 5vw, 3.5rem); }
h2 { font-size: clamp(1.5rem, 3vw, 2.25rem); }
h3 { font-size: clamp(1.25rem, 2.5vw, 1.75rem); }
h4 { font-size: 1.25rem; }
h5 { font-size: 1.125rem; }
h6 { font-size: 1rem; }

p {
    color: var(--text-secondary);
    line-height: 1.7;
    margin-bottom: 1rem;
}

a {
    color: var(--accent-blue);
    text-decoration: none;
    transition: color var(--transition-fast), opacity var(--transition-fast);
}

a:hover {
    opacity: 0.85;
}

img {
    max-width: 100%;
    height: auto;
    display: block;
}

ul, ol {
    list-style: none;
}

button {
    cursor: pointer;
    border: none;
    background: none;
    font-family: var(--font-body);
}

input, textarea, select {
    font-family: var(--font-body);
    font-size: 1rem;
}

/* ============================================================
   LAYOUT UTILITIES
   ============================================================ */
.container {
    max-width: var(--container-max);
    margin: 0 auto;
    padding: 0 var(--container-pad);
    width: 100%;
}

.section-padding {
    padding: 4rem 0;
}

.section-padding-sm {
    padding: 2rem 0;
}

/* Grid system */
.grid-3 {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
}

.grid-4 {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1.5rem;
}

.grid-2 {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1.5rem;
}

/* ============================================================
   CARD COMPONENT — revised token (supersedes the old heavier-border /
   accent-hover-glow spec). Hairline border, tighter padding, and a
   subtle background-lift on hover only — no border-color change, no
   box-shadow, no transform. Typography scale and whitespace carry
   visual hierarchy instead of card chrome. Applies to every card type:
   news cards, startup/investor/job/event cards, tools, and (loosely —
   it was never chrome-heavy to begin with) the lead story card.
   ============================================================ */
.card {
    background: var(--bg-card);
    border: 0.5px solid var(--border-color);
    border-radius: var(--radius-card);
    padding: 1.25rem;
    transition: background-color var(--transition-fast);
}

.card:hover {
    background: var(--bg-surface);
}

/* ============================================================
   GLOSSARY CARD — whole-card click target via the "stretched link"
   technique, not a giant wrapping <a>. Fixed 2026-07-18 — live QA found
   only the title text itself was clickable, not the category badge or
   description below it, a much smaller target than the visible card.
   Wrapping the ENTIRE card markup in one <a> was considered and rejected:
   the card also renders a category <span> — nesting another interactive
   element inside an outer <a> produces invalid, browser-mangled HTML
   (nested <a>/<button>-like elements are not allowed). No prior "solved"
   pattern for this actually existed elsewhere in the theme (checked
   content-news.php and the whole codebase first) — news cards only link
   their thumbnail and title separately, the same limited-target pattern,
   just less noticeable with longer headline text. This is the standard,
   accessible-safe fix instead: keep exactly one real <a> (the title,
   .glossary-card-link below), and stretch it to cover the whole card via
   an absolutely-positioned ::after pseudo-element. Only one interactive
   element ever exists in the DOM — no nested-anchor problem — while the
   full card area is clickable. If a second real link is ever added inside
   this card, it needs position:relative + z-index:2 to stay clickable
   above the stretched overlay (z-index:1).
   ============================================================ */
.glossary-card {
    position: relative;
}

.glossary-card-link::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
}

/* ============================================================
   BADGE / TAG COMPONENTS
   ============================================================ */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.6rem;
    border-radius: 4px;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    line-height: 1.4;
}

.badge-breaking {
    background: var(--accent-red);
    color: #FFFFFF;
}

.badge-hot {
    background: var(--accent-orange);
    color: #FFFFFF;
}

.badge-opinion {
    background: var(--accent-purple);
    color: #FFFFFF;
}

.badge-sponsored {
    background: var(--accent-grey);
    color: #FFFFFF;
}

.badge-verified {
    background: var(--accent-blue);
    color: #FFFFFF;
}

/* Used by startupph_editorial_badge() (functions.php) for editorially-curated
   Startup profiles — was defined but unused by any template before 2026-07-16. */
.badge-featured {
    background: linear-gradient(135deg, #F59E0B, #D97706);
    color: #FFFFFF;
}

.tag {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem 0.75rem;
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 500;
    background: rgba(59, 130, 246, 0.1);
    color: var(--accent-blue);
    border: 1px solid rgba(59, 130, 246, 0.2);
}

/* ============================================================
   BUTTON COMPONENTS
   ============================================================ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.75rem 1.5rem;
    border-radius: var(--radius-btn);
    font-size: 0.875rem;
    font-weight: 600;
    transition: all var(--transition-fast);
    min-height: 44px; /* Accessibility: minimum touch target */
    gap: 0.5rem;
}

.btn-primary {
    background: var(--accent-blue);
    color: #FFFFFF;
    border: 2px solid var(--accent-blue);
}

.btn-primary:hover {
    background: #2563EB;
    border-color: #2563EB;
    color: #FFFFFF;
    opacity: 1;
}

.btn-outline {
    background: transparent;
    color: var(--accent-blue);
    border: 2px solid var(--accent-blue);
}

.btn-outline:hover {
    background: var(--accent-blue);
    color: #FFFFFF;
    opacity: 1;
}

.btn-sm {
    padding: 0.5rem 1rem;
    font-size: 0.8rem;
    min-height: 36px;
}

.btn-lg {
    padding: 1rem 2rem;
    font-size: 1rem;
}

/* ============================================================
   SECTION HEADERS
   ============================================================ */
.section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 2rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--border-color);
}

.section-title {
    font-family: var(--font-heading);
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--text-primary);
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.section-title::before {
    content: '';
    display: block;
    width: 4px;
    height: 1.5rem;
    background: var(--accent-blue);
    border-radius: 2px;
}

.section-link {
    color: var(--accent-blue);
    font-size: 0.875rem;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

/* ============================================================
   SKELETON LOADING SCREENS
   ============================================================ */
.skeleton {
    background: linear-gradient(90deg, var(--bg-surface) 25%, var(--border-color) 50%, var(--bg-surface) 75%);
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.5s infinite;
    border-radius: var(--radius-card);
}

@keyframes skeleton-shimmer {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

.skeleton-text {
    height: 1rem;
    border-radius: 4px;
    margin-bottom: 0.5rem;
}

.skeleton-title {
    height: 1.5rem;
    border-radius: 4px;
    margin-bottom: 0.75rem;
    width: 80%;
}

.skeleton-card {
    height: 280px;
}

/* ============================================================
   SCROLL ANIMATIONS — FADE IN FROM BOTTOM
   ============================================================ */
.animate-on-scroll {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.5s ease, transform 0.5s ease;
}

.animate-on-scroll.visible {
    opacity: 1;
    transform: translateY(0);
}

/* ============================================================
   FORMS BASE STYLES
   ============================================================ */
.form-group {
    margin-bottom: 1.5rem;
}

.form-label {
    display: block;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 0.5rem;
}

.form-label span.required {
    color: var(--accent-red);
    margin-left: 0.25rem;
}

/* Optional-field marker — gold asterisk PLUS explicit "(optional)" text.
   Gold alone isn't a convention most users recognize on sight (unlike the
   near-universal red-required-asterisk), so the marker is always paired
   with the literal word. Added 2026-07-13. */
.form-label span.optional-marker {
    color: #D4AF37;
    margin-left: 0.25rem;
}

.form-label span.optional-text {
    color: var(--text-muted);
    font-weight: 400;
    font-size: 0.8rem;
    margin-left: 0.15rem;
}

/* Shared handle-only social link field — fixed, non-editable base-URL
   prefix beside a plain-handle input. See startupph_social_handle_field()
   in functions.php. Added 2026-07-13. */
.social-handle-field {
    display: flex;
    align-items: stretch;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-card);
    overflow: hidden;
    background: var(--bg-surface);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.social-handle-field:focus-within {
    border-color: var(--accent-blue);
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}

.social-handle-prefix {
    display: flex;
    align-items: center;
    padding: 0 0.75rem 0 1rem;
    font-size: 0.875rem;
    color: var(--text-muted);
    background: var(--bg-primary);
    white-space: nowrap;
    user-select: none;
    border-right: 1px solid var(--border-color);
}

.social-handle-input.form-input {
    border: none;
    border-radius: 0;
    background: transparent;
    min-width: 0;
}

.social-handle-input.form-input:focus {
    box-shadow: none;
    border-color: transparent;
}

/* Per-field validation error state — added 2026-07-14. Every submission
   form now collects ALL field-level problems on a failed submit (instead
   of stopping at the first one) and marks each offending field with this
   class, paired with an inline message from startupph_field_error(). The
   red border must win over both the plain focus outline and the locked
   social-handle field's own border, hence !important + the extra
   selectors for that wrapper. */
.form-input.has-error,
.form-select.has-error,
.form-textarea.has-error,
.social-handle-field.has-error {
    border-color: var(--accent-red) !important;
}

.form-input.has-error:focus,
.form-select.has-error:focus,
.form-textarea.has-error:focus,
.social-handle-field.has-error:focus-within {
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15) !important;
}

.field-error-msg {
    color: var(--accent-red);
    font-size: 0.8rem;
    margin-top: 0.4rem;
    line-height: 1.4;
}

.form-input,
.form-select,
.form-textarea {
    width: 100%;
    padding: 0.75rem 1rem;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-card);
    color: var(--text-primary);
    font-size: 0.875rem;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
    min-height: 44px;
}

.form-input:focus,
.form-select:focus,
.form-textarea:focus {
    outline: none;
    border-color: var(--accent-blue);
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}

.form-textarea {
    min-height: 120px;
    resize: vertical;
}

.form-hint {
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-top: 0.35rem;
}

.form-error {
    font-size: 0.75rem;
    color: var(--accent-red);
    margin-top: 0.35rem;
}

.char-counter {
    font-size: 0.75rem;
    color: var(--text-muted);
    text-align: right;
    margin-top: 0.25rem;
}

.char-counter.near-limit {
    color: var(--accent-orange);
}

.char-counter.at-limit {
    color: var(--accent-red);
}

/* ============================================================
   NOTICE / ALERT BOX
   ============================================================ */
.notice {
    padding: 1rem 1.25rem;
    border-radius: var(--radius-card);
    border-left: 4px solid var(--accent-blue);
    background: rgba(59, 130, 246, 0.08);
    color: var(--text-secondary);
    font-size: 0.875rem;
    margin-bottom: 1.5rem;
}

.notice-warning {
    border-left-color: var(--accent-orange);
    background: rgba(249, 115, 22, 0.08);
}

/* ============================================================
   CRYPTO PRICE WIDGET — Crypto category archive page ONLY
   Small, self-contained (not global chrome). Reuses the price/change
   element classes crypto-ticker.js already targets by ID.
   ============================================================ */
.crypto-widget {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    padding: 1rem 1.25rem;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-card);
    margin-bottom: 2rem;
}

.crypto-item {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    font-weight: 600;
}

.crypto-symbol {
    color: var(--text-muted);
}

.crypto-price {
    color: var(--text-primary);
}

.crypto-change.positive {
    color: var(--accent-green);
}

.crypto-change.negative {
    color: var(--accent-red);
}

/* ============================================================
   HEADER
   Base height is 64px (desktop); shrinks to ~52px on mobile (see
   mobile media query below). The header is the entire fixed stack,
   one layer — no ticker bar and no category-tabs row (both removed;
   see CLAUDE.md session log, 2026-07-10).
   ============================================================ */
.site-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    background: rgba(10, 15, 30, 0.95);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border-color);
    transition: background var(--transition-fast);
}

html:not(.dark) .site-header {
    background: rgba(248, 250, 252, 0.95);
}

.header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 64px;
    gap: 1.5rem;
}

.site-logo {
    flex-shrink: 0;
    display: flex;
    align-items: center;
}

.site-logo img,
.site-logo svg {
    height: 36px;
    width: auto;
}

/* Collapsed icon by default, expands into an inline input on click —
   was previously an always-open 400px box. Fixed 2026-07-10. flex:1
   removed (was forcing the collapsed state to still claim maximum
   available header-inner space, which isn't needed once the box no
   longer sits open). */
.header-search {
    display: flex;
    align-items: center;
    position: relative;
}

.header-search-toggle {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    transition: all var(--transition-fast);
    flex-shrink: 0;
}

.header-search-toggle:hover,
.header-search-toggle[aria-expanded="true"] {
    border-color: var(--accent-blue);
    color: var(--accent-blue);
}

.header-search-icon {
    width: 16px;
    height: 16px;
    pointer-events: none;
}

/* Collapsed: zero width, clipped, invisible — expanded via JS toggling
   .expanded on the parent .header-search container. */
.header-search-wrapper {
    width: 0;
    overflow: hidden;
    opacity: 0;
    margin-left: 0;
    transition: width 200ms ease, opacity 200ms ease, margin-left 200ms ease;
}

.header-search.expanded .header-search-wrapper {
    width: 260px;
    opacity: 1;
    margin-left: 0.5rem;
}

.header-search-input {
    width: 100%;
    padding: 0.5rem 1rem;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: 99px;
    color: var(--text-primary);
    font-size: 0.875rem;
    transition: border-color var(--transition-fast);
}

.header-search-input:focus {
    outline: none;
    border-color: var(--accent-blue);
}

.header-right {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-shrink: 0;
}

.desktop-nav {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.desktop-nav a {
    color: var(--text-secondary);
    font-size: 0.85rem;
    font-weight: 500;
    padding: 0.4rem 0.6rem;
    border-radius: var(--radius-btn);
    transition: color var(--transition-fast), background var(--transition-fast);
    white-space: nowrap;
}

.desktop-nav a:hover {
    color: var(--text-primary);
    background: var(--bg-surface);
    opacity: 1;
}

.desktop-nav a.nav-submit {
    color: var(--accent-blue);
    border: 1px solid rgba(59, 130, 246, 0.3);
}

.desktop-nav a.nav-submit:hover {
    background: rgba(59, 130, 246, 0.1);
}

/* Theme toggle button */
.theme-toggle {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    transition: all var(--transition-fast);
    flex-shrink: 0;
}

.theme-toggle:hover {
    border-color: var(--accent-blue);
    color: var(--accent-blue);
}

/* Mobile hamburger — visible by default (mobile-first). Hidden only at
   >=1025px via the single min-width query near the desktop hamburger rule
   below, which is the one and only place either hamburger's visibility
   is decided, so the two can never disagree and show at the same time. */
.hamburger-btn {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-btn);
    color: var(--text-secondary);
    transition: color var(--transition-fast);
    flex-shrink: 0;
}

.hamburger-btn:hover {
    color: var(--text-primary);
}

/* Icon-only, squarish (var(--radius-btn) — not fully rounded), 1px accent-blue
   border, transparent background, icon centered. Stays display:none at every
   width until animations.js removes [hidden] (only after beforeinstallprompt
   fires) AND the mobile media query below also matches — see that query for
   the actual reveal rule. */
.pwa-header-install-btn {
    width: 44px;
    height: 44px;
    display: none;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-btn);
    background: transparent;
    border: 1px solid var(--accent-blue);
    color: var(--text-secondary);
    transition: color var(--transition-fast);
    flex-shrink: 0;
}

.pwa-header-install-btn:hover {
    color: var(--accent-blue);
}

/* ============================================================
   MOBILE HAMBURGER PANEL — slides from right
   ============================================================ */
.mobile-nav-overlay {
    position: fixed;
    inset: 0;
    z-index: 1100;
    pointer-events: none;
}

.mobile-nav-overlay.open {
    pointer-events: all;
}

.mobile-nav-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    opacity: 0;
    transition: opacity 0.3s ease;
}

.mobile-nav-overlay.open .mobile-nav-backdrop {
    opacity: 1;
}

.mobile-nav-panel {
    position: absolute;
    top: 0;
    right: 0;
    width: 85%;
    max-width: 420px;
    height: 90%;
    background: var(--bg-card);
    border-left: 1px solid var(--border-color);
    border-bottom: 1px solid var(--border-color);
    transform: translateX(100%);
    transition: transform 0.3s ease;
    display: flex;
    flex-direction: column;
    /* No overflow-y here — the search bar and social row below must stay
       fixed in place; only .mobile-nav-scroll-area (the nav links) scrolls. */
}

.mobile-nav-overlay.open .mobile-nav-panel {
    transform: translateX(0);
}

.mobile-nav-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem;
    border-bottom: 1px solid var(--border-color);
}

.mobile-nav-close {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    border-radius: var(--radius-btn);
    transition: color var(--transition-fast);
}

.mobile-nav-close:hover {
    color: var(--text-primary);
}

.mobile-nav-search {
    padding: 1rem;
    border-bottom: 1px solid var(--border-color);
}

.mobile-nav-search input {
    width: 100%;
    padding: 0.65rem 1rem;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-card);
    color: var(--text-primary);
    font-size: 0.9rem;
}

/* Scrollable body — only the nav links scroll; search bar above and
   social row below stay fixed in place. flex:1 + min-height:0 is what
   makes overflow-y:auto actually constrain to the available space
   instead of growing the whole panel. position:relative anchors the
   fade below to this container's VISIBLE viewport, not its scrollable
   content — so the fade doesn't add to scrollHeight and doesn't cause
   navigation.js's overflow check to false-positive. */
.mobile-nav-scroll-area {
    position: relative;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

.mobile-nav-links {
    padding: 0.5rem 0;
}

/* Bottom fade — absolutely positioned against .mobile-nav-scroll-area's
   visible viewport (not its scrollable content), so it always sits at
   the true bottom edge regardless of scroll position. navigation.js
   toggles the .visible class based on real scroll position — hidden by
   default, shown only while there's more to scroll, hidden again at
   the end. pointer-events:none so it never blocks taps on links. */
.mobile-nav-scroll-fade {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 40px;
    background: linear-gradient(to bottom, transparent, var(--bg-card));
    pointer-events: none;
    opacity: 0;
    transition: opacity 200ms ease;
}

.mobile-nav-scroll-fade.visible {
    opacity: 1;
}

/* Shared chevron styling — used inside both .mobile-nav-scroll-fade and
   .desktop-panel-scroll-fade. Centered at the bottom edge, gentle bounce
   animation only plays while the fade itself is visible (parent's
   .visible class), so it never distracts when there's nothing to scroll. */
.scroll-fade-chevron {
    position: absolute;
    bottom: 4px;
    left: 50%;
    transform: translateX(-50%);
    color: var(--text-muted);
}

.mobile-nav-scroll-fade.visible .scroll-fade-chevron,
.desktop-panel-scroll-fade.visible .scroll-fade-chevron {
    animation: scroll-chevron-bounce 1.4s ease-in-out infinite;
}

@keyframes scroll-chevron-bounce {
    0%, 100% { transform: translateX(-50%) translateY(0); }
    50%      { transform: translateX(-50%) translateY(4px); }
}

@media (prefers-reduced-motion: reduce) {
    .mobile-nav-scroll-fade.visible .scroll-fade-chevron,
    .desktop-panel-scroll-fade.visible .scroll-fade-chevron {
        animation: none;
    }
}

.mobile-nav-links a {
    display: flex;
    align-items: center;
    padding: 0.9rem 1.25rem;
    color: var(--text-secondary);
    font-size: 1rem;
    font-weight: 500;
    transition: color var(--transition-fast), background var(--transition-fast);
    border-radius: 0;
}

.mobile-nav-links a:hover {
    color: var(--text-primary);
    background: var(--bg-surface);
    opacity: 1;
}

.mobile-nav-socials {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem 1.25rem 1.5rem;
    border-top: 1px solid var(--border-color);
    margin-top: auto;
}

.mobile-nav-socials a {
    color: var(--text-muted);
    transition: color var(--transition-fast);
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.mobile-nav-socials a:hover {
    color: var(--accent-blue);
    opacity: 1;
}

/* ============================================================
   DESKTOP HAMBURGER + SIDE PANEL
   Separate from the mobile hamburger/panel above. Hidden by default
   (mobile-first) — the single @media (min-width:1025px) query further
   down is the only place that flips it to visible and hides the mobile
   hamburger, so exactly one of the two hamburgers is ever visible at
   any screen width, with no gap or overlap between the two states.
   ============================================================ */
.desktop-hamburger-btn {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    display: none;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    transition: all var(--transition-fast);
    flex-shrink: 0;
}

.desktop-hamburger-btn:hover {
    border-color: var(--accent-blue);
    color: var(--accent-blue);
}

.desktop-side-panel-overlay {
    position: fixed;
    inset: 0;
    z-index: 1150;
    pointer-events: none;
}

.desktop-side-panel-overlay.open {
    pointer-events: all;
}

.desktop-side-panel-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    opacity: 0;
    transition: opacity 0.3s ease;
}

.desktop-side-panel-overlay.open .desktop-side-panel-backdrop {
    opacity: 1;
}

.desktop-side-panel {
    position: absolute;
    top: 0;
    right: 0;
    width: 400px;
    max-width: 100%;
    height: 100%;
    background: var(--bg-card);
    border-left: 1px solid var(--border-color);
    transform: translateX(100%);
    transition: transform 0.3s ease;
    display: flex;
    flex-direction: column;
    padding: 1.5rem;
    /* No overflow-y here — only .desktop-panel-scroll-area (links + latest
       headlines) scrolls; the close button row stays fixed at the top. */
}

/* position:relative anchors the fade below to this container's VISIBLE
   viewport, not its scrollable content — see the matching comment on
   .mobile-nav-scroll-area for why. */
.desktop-panel-scroll-area {
    position: relative;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

.desktop-panel-scroll-fade {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 40px;
    background: linear-gradient(to bottom, transparent, var(--bg-card));
    pointer-events: none;
    opacity: 0;
    transition: opacity 200ms ease;
}

.desktop-panel-scroll-fade.visible {
    opacity: 1;
}

.desktop-side-panel-overlay.open .desktop-side-panel {
    transform: translateX(0);
}

.desktop-panel-header {
    display: flex;
    align-items: center;
    justify-content: flex-end; /* only the close button lives here now — no visible "Menu" title */
    margin-bottom: 1.5rem;
    flex-shrink: 0;
}

.desktop-panel-close {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    border-radius: var(--radius-btn);
    transition: color var(--transition-fast);
}

.desktop-panel-close:hover {
    color: var(--text-primary);
}

.desktop-panel-links {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.desktop-panel-links a {
    padding: 0.65rem 0.5rem;
    color: var(--text-secondary);
    font-size: 0.95rem;
    font-weight: 500;
    border-radius: var(--radius-btn);
    transition: color var(--transition-fast), background var(--transition-fast);
}

.desktop-panel-links a:hover {
    color: var(--text-primary);
    background: var(--bg-surface);
    opacity: 1;
}

.desktop-panel-divider {
    height: 1px;
    background: var(--border-color);
    margin: 1.5rem 0;
    flex-shrink: 0;
}

.desktop-panel-latest-label {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    margin-bottom: 1rem;
}

.desktop-panel-latest-item {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--border-color);
}

.desktop-panel-latest-item:last-child {
    border-bottom: none;
}

.desktop-panel-latest-title {
    color: var(--text-primary);
    font-size: 0.9rem;
    font-weight: 500;
    line-height: 1.4;
}

.desktop-panel-latest-time {
    color: var(--text-muted);
    font-size: 0.75rem;
}

/* ============================================================
   MAIN CONTENT — CSS SAFETY-NET OFFSET
   layout-offsets.js overwrites #mainContent's padding-top via inline
   style once it measures the real header height. Inline style always
   wins over this rule when JS runs. This CSS rule is the fallback for
   the case JS never runs at all (blocked, errors, disabled) — a plain
   CSS rule here is more durable than only relying on the matching
   inline style already present on the <main> tag in header.php.
   64px = single fixed header layer (no category-tabs row anymore, as
   of the 2026-07-10 fix pass — see CLAUDE.md session log); the mobile
   media query below tightens this further to match the ~52px mobile
   header.
   ============================================================ */
#mainContent {
    padding-top: 64px;
}

/* ============================================================
   LEAD STORY CARD — first article in the Latest News section
   Full-width card: image + content side-by-side on desktop, image
   stacked on top of content on mobile. Larger headline and a
   3-line excerpt distinguish it from the smaller grid cards below.
   ============================================================ */
.lead-story-card {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    margin-bottom: 2rem;
    padding-bottom: 2rem;
    border-bottom: 1px solid var(--border-color);
}

.lead-story-image {
    aspect-ratio: 16 / 9;
    border-radius: var(--radius-card);
    overflow: hidden;
    background: var(--bg-surface);
}

.lead-story-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.lead-story-image.skeleton {
    aspect-ratio: 16 / 9;
}

.lead-story-body {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.lead-story-title {
    font-family: var(--font-heading);
    font-size: clamp(1.5rem, 3.5vw, 2.25rem);
    font-weight: 700;
    line-height: 1.15;
    margin: 0.75rem 0 1rem;
}

.lead-story-excerpt {
    font-size: 1rem;
    color: var(--text-secondary);
    line-height: 1.6;
    margin-bottom: 1rem;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

@media (min-width: 769px) {
    .lead-story-card {
        flex-direction: row;
        align-items: stretch;
        gap: 2rem;
    }

    .lead-story-image {
        flex: 0 0 45%;
    }

    .lead-story-body {
        flex: 1;
        min-width: 0;
    }
}

/* ============================================================
   NEWS GRID SECTION
   ============================================================ */
.news-card {
    display: flex;
    flex-direction: column;
    /* Overrides .card's own padding to 0 — the image must bleed edge to
       edge, and .news-card-body below carries its own (tighter) padding
       for the text zone. Without this override the two paddings stacked,
       insetting the image when it should sit flush against the card edge. */
    padding: 0;
}

.news-card-image {
    width: 100%;
    aspect-ratio: 16/9;
    background: var(--bg-surface);
    border-radius: var(--radius-card) var(--radius-card) 0 0;
    overflow: hidden;
    position: relative;
}

.news-card-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.4s ease;
}

.news-card:hover .news-card-image img {
    transform: scale(1.03);
}

/* CSS placeholder when no image */
/* Was flex + center-aligned, sized for a small fixed-pixel icon floating
   on this element's own static gradient background. The SVG itself
   (startupph_placeholder_svg()) now renders at width/height:100% with
   preserveAspectRatio="slice", so it fills this entire box edge-to-edge
   like a real photo with object-fit:cover — no more separate visible
   background behind it, and no flex-centering needed since there's no
   longer a small child to center. The gradient below is now purely a
   defensive fallback for the rare case the SVG fails to render at all.
   Fixed 2026-07-13. */
.news-card-image-placeholder {
    display: block;
    width: 100%;
    height: 100%;
    background:
        linear-gradient(135deg, var(--bg-surface) 0%, var(--border-color) 100%);
}

.news-card-image-placeholder svg {
    display: block;
    width: 100%;
    height: 100%;
}

.news-card-body {
    padding: 1rem;
    flex: 1;
    display: flex;
    flex-direction: column;
}

.news-card-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    flex-wrap: wrap;
}

.news-card-title {
    font-family: var(--font-heading);
    font-size: 1rem;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.3;
    margin-bottom: 0.6rem;
    flex: 1;
}

.news-card-excerpt {
    font-size: 0.85rem;
    color: var(--text-secondary);
    line-height: 1.5;
    margin-bottom: 1rem;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.news-card-footer {
    /* Read time only, no byline (removed 2026-07-11) — flex-start instead of
       the old space-between, which only made sense with two items (author +
       read time) pinned to opposite edges. */
    display: flex;
    align-items: center;
    justify-content: flex-start;
    margin-top: auto;
    font-size: 0.75rem;
    color: var(--text-muted);
}

/* ============================================================
   STARTUP CARD
   ============================================================ */
.startup-card {
    display: flex;
    flex-direction: column;
}

.startup-card-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.startup-logo {
    width: 48px;
    height: 48px;
    border-radius: 8px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    overflow: hidden;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.startup-logo img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.startup-name {
    font-family: var(--font-heading);
    font-size: 1rem;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 0.15rem;
}

.startup-tagline {
    font-size: 0.85rem;
    color: var(--text-secondary);
    line-height: 1.4;
    margin-bottom: 0.75rem;
}

.startup-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: auto;
}

/* Tier 1 — Featured gold border */
.startup-card.tier-1 {
    border-color: rgba(245, 158, 11, 0.4);
}

.startup-card.tier-1:hover {
    border-color: rgba(245, 158, 11, 0.8);
    box-shadow: 0 4px 20px rgba(245, 158, 11, 0.1);
}

/* ============================================================
   EVENT CARD
   ============================================================ */
.event-card-date {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    background: rgba(59, 130, 246, 0.1);
    border: 1px solid rgba(59, 130, 246, 0.2);
    border-radius: var(--radius-card);
    flex-shrink: 0;
}

.event-date-month {
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--accent-blue);
    line-height: 1;
}

.event-date-day {
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1;
}

/* ============================================================
   JOB CARD
   ============================================================ */
.job-card-company-logo {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    overflow: hidden;
    flex-shrink: 0;
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer {
    background: var(--bg-card);
    border-top: 1px solid var(--border-color);
    margin-top: 4rem;
}

.footer-main {
    padding: 3rem 0;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 3rem;
}

.footer-col-title {
    font-family: var(--font-heading);
    font-size: 0.85rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    margin-bottom: 1.25rem;
}

.footer-links {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.footer-links a {
    color: var(--text-secondary);
    font-size: 0.875rem;
    transition: color var(--transition-fast);
}

.footer-links a:hover {
    color: var(--text-primary);
    opacity: 1;
}

/* ============================================================
   FOOTER ACCORDION — mobile only (see mobile media query below)
   On desktop these render exactly like the old static heading:
   full-width row, no pointer cursor, chevron hidden, content always
   expanded (no max-height applied outside the mobile query).
   ============================================================ */
.footer-accordion-toggle {
    display: flex;
    flex-wrap: nowrap; /* heading text and chevron always stay on one line */
    align-items: center;
    justify-content: space-between;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    padding: 0;
    margin-bottom: 1.25rem; /* matches old .footer-col-title margin-bottom */
    cursor: default;
}

.footer-accordion-toggle .footer-col-title {
    margin-bottom: 0; /* spacing now comes from the toggle button itself */
    flex: 1; /* heading takes the remaining space; chevron stays pinned right */
    min-width: 0;
}

.footer-accordion-chevron {
    display: none; /* shown only on mobile */
    flex-shrink: 0;
    color: var(--text-muted);
    transition: transform 200ms ease;
}

.footer-accordion-content {
    overflow: hidden;
}

.footer-social {
    display: flex;
    gap: 0.75rem;
    margin-top: 1.25rem;
}

.footer-social a {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    transition: all var(--transition-fast);
}

.footer-social a:hover {
    border-color: var(--accent-blue);
    color: var(--accent-blue);
    opacity: 1;
}

.footer-bottom {
    border-top: 1px solid var(--border-color);
    padding: 1.25rem 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 0.8rem;
    color: var(--text-muted);
    flex-wrap: wrap;
    gap: 0.75rem;
}

.footer-bottom a {
    color: var(--text-muted);
    transition: color var(--transition-fast);
}

.footer-bottom a:hover {
    color: var(--text-primary);
    opacity: 1;
}

.ecosystem-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.75rem;
    color: var(--accent-blue);
    background: rgba(59, 130, 246, 0.08);
    border: 1px solid rgba(59, 130, 246, 0.2);
    padding: 0.3rem 0.75rem;
    border-radius: 99px;
    margin-top: 1rem;
}

/* ============================================================
   NEWSLETTER SECTION
   ============================================================ */
.newsletter-section {
    background: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(139, 92, 246, 0.05) 100%);
    border-top: 1px solid var(--border-color);
    border-bottom: 1px solid var(--border-color);
}

.newsletter-form {
    display: flex;
    gap: 0.75rem;
    max-width: 480px;
    margin: 1.5rem 0 0.75rem;
}

.newsletter-input {
    flex: 1;
    padding: 0.75rem 1rem;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-btn);
    color: var(--text-primary);
    font-size: 0.875rem;
    min-height: 44px;
}

.newsletter-input:focus {
    outline: none;
    border-color: var(--accent-blue);
}

/* ============================================================
   FILTERS SIDEBAR / BAR
   ============================================================ */
.filter-sidebar {
    width: 260px;
    flex-shrink: 0;
}

.filter-group {
    margin-bottom: 1.5rem;
}

.filter-group-title {
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin-bottom: 0.75rem;
}

.filter-checkbox {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    cursor: pointer;
    font-size: 0.875rem;
    color: var(--text-secondary);
    transition: color var(--transition-fast);
}

.filter-checkbox:hover {
    color: var(--text-primary);
}

.filter-checkbox input[type="checkbox"] {
    width: 16px;
    height: 16px;
    accent-color: var(--accent-blue);
}

/* ============================================================
   DIRECTORY LAYOUT
   ============================================================ */
.directory-layout {
    display: flex;
    gap: 2rem;
    align-items: flex-start;
}

.directory-main {
    flex: 1;
    min-width: 0;
}

/* Tier section dividers */
.tier-section {
    margin-bottom: 3rem;
}

.tier-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.5rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--border-color);
}

.tier-badge {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0.25rem 0.6rem;
    border-radius: 4px;
}

.tier-badge-1 {
    background: linear-gradient(135deg, rgba(245, 158, 11, 0.15), rgba(217, 119, 6, 0.15));
    color: #F59E0B;
    border: 1px solid rgba(245, 158, 11, 0.3);
}

.tier-badge-2 {
    background: rgba(59, 130, 246, 0.1);
    color: var(--accent-blue);
    border: 1px solid rgba(59, 130, 246, 0.2);
}

.tier-badge-3 {
    background: rgba(100, 116, 139, 0.1);
    color: var(--text-muted);
    border: 1px solid rgba(100, 116, 139, 0.2);
}

/* ============================================================
   GLOSSARY
   ============================================================ */
/* display/flex-wrap/overflow/mask handled by the shared .hscroll-tabs class
   (added alongside this one in the markup) — was flex-wrap:wrap, causing
   the same multi-row-wrapping bug as the other filter rows. Fixed
   2026-07-13. */
.glossary-alpha-index {
    gap: 0.4rem;
    margin-bottom: 2rem;
}

.alpha-btn {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-secondary);
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    transition: all var(--transition-fast);
}

.alpha-btn:hover,
.alpha-btn.active {
    background: var(--accent-blue);
    color: #FFFFFF;
    border-color: var(--accent-blue);
    opacity: 1;
}

.glossary-term {
    padding: 2rem 0;
    border-bottom: 1px solid var(--border-color);
}

.glossary-term h2 {
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
}

.glossary-beginner {
    font-size: 1rem;
    color: var(--accent-blue);
    font-weight: 500;
    margin-bottom: 1rem;
}

/* ============================================================
   PAGINATION
   ============================================================ */
.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin-top: 3rem;
}

.page-btn {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-btn);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-secondary);
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    transition: all var(--transition-fast);
}

.page-btn:hover,
.page-btn.active {
    background: var(--accent-blue);
    color: #FFFFFF;
    border-color: var(--accent-blue);
    opacity: 1;
}

/* Native WordPress pagination (paginate_links() / the_posts_pagination()) —
   a genuinely different mechanism from .pagination/.page-btn above.
   .pagination/.page-btn is server-rendered directly by
   template-startups.php (real <a href> links, so it works with JS off)
   AND rebuilt client-side by filters.js after every AJAX filter/sort/page
   change (2026-07-20 unification — previously .page-btn only ever existed
   as JS-built <button> elements with no server-rendered equivalent at
   all). ul.page-numbers below covers a genuinely separate case: real
   WP_Query pagination (e.g. template-glossary.php, archive.php) and was
   previously entirely unstyled: .pagination/.page-btn never actually
   matched WordPress's own output classes (.nav-links/.page-numbers), so
   real pagination links rendered with no spacing between them and
   visually merged. Fixed 2026-07-17. Targets the tag-qualified selectors
   deliberately, since WordPress reuses the class "page-numbers" on both the
   outer <ul> and each individual link/span inside it. */
ul.page-numbers {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    list-style: none;
    padding: 0;
    margin: 2rem 0 0;
}

a.page-numbers,
span.page-numbers {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    height: 40px;
    padding: 0 0.75rem;
    border-radius: var(--radius-btn);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-secondary);
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    transition: all var(--transition-fast);
}

a.page-numbers:hover,
span.page-numbers.current {
    background: var(--accent-blue);
    color: #FFFFFF;
    border-color: var(--accent-blue);
}

span.page-numbers.dots {
    background: transparent;
    border: none;
}

/* ============================================================
   DEVELOPING STORIES
   ============================================================ */
.story-timeline {
    position: relative;
    padding-left: 2rem;
}

.story-timeline::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--border-color);
}

.story-timeline-item {
    position: relative;
    padding-bottom: 2rem;
}

.story-timeline-item::before {
    content: '';
    position: absolute;
    left: -2.35rem;
    top: 0.4rem;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--accent-blue);
    border: 2px solid var(--bg-card);
}

/* ============================================================
   BREADCRUMB
   ============================================================ */
.breadcrumb {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    color: var(--text-muted);
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
}

.breadcrumb a {
    color: var(--text-muted);
    transition: color var(--transition-fast);
}

.breadcrumb a:hover {
    color: var(--accent-blue);
    opacity: 1;
}

.breadcrumb-sep {
    color: var(--border-color);
}

/* ============================================================
   SINGLE ARTICLE
   ============================================================ */
.article-header {
    max-width: 780px;
    margin: 0 auto 2rem;
}

.article-body {
    max-width: 780px;
    margin: 0 auto;
}

.article-body p {
    margin-bottom: 1.5rem;
    font-size: 1.05rem;
    line-height: 1.8;
}

.article-body h2 {
    margin-top: 2.5rem;
    margin-bottom: 1rem;
}

.article-body h3 {
    margin-top: 2rem;
    margin-bottom: 0.75rem;
}

.article-what-this-means {
    background: rgba(59, 130, 246, 0.06);
    border-left: 4px solid var(--accent-blue);
    padding: 1.5rem;
    border-radius: 0 var(--radius-card) var(--radius-card) 0;
    margin-top: 2rem;
}

.article-what-this-means h3 {
    color: var(--accent-blue);
    margin-top: 0;
    margin-bottom: 0.75rem;
    font-size: 1.1rem;
}

/* ============================================================
   SECTION DIVIDERS
   ============================================================ */
.section-divider {
    height: 1px;
    background: var(--border-color);
    margin: 3rem 0;
}

/* ============================================================
   LOADING STATES
   ============================================================ */
.loading-indicator {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3rem;
    color: var(--text-muted);
    font-size: 0.875rem;
}

/* The automatic bottom-sheet PWA install prompt (.pwa-install-prompt) and
   its markup were removed 2026-07-14 — install is now manual-only via the
   header icon (.pwa-header-install-btn, below). */

/* ============================================================
   RESPONSIVE — TABLET (768px)
   ============================================================ */
@media (max-width: 1024px) {
    .desktop-nav,
    .header-search {
        display: none;
    }

    .footer-main {
        grid-template-columns: repeat(2, 1fr);
        gap: 2rem;
    }

    .grid-3,
    .grid-4 {
        grid-template-columns: repeat(2, 1fr);
    }

    .directory-layout {
        flex-direction: column;
    }

    .filter-sidebar {
        width: 100%;
    }

    /* Startups directory mobile filters — REDESIGNED 2026-07-19 (second
       pass). The first fix used an inline accordion (tap a chip, that
       category's checkboxes expand in place) — reconsidered per COE:
       with 4 real facets (Tier/Stage/Industry/Location), an expanded
       category still pushed the startup grid down the page every time,
       which wasn't materially better than the original fully-stacked
       sidebar. Replaced with a modal/bottom-sheet pattern instead — the
       standard approach for multi-facet mobile filtering. Default view
       below this breakpoint is now just the Sort control (already a
       compact single <select>, left completely alone) + a "Filters"
       button, then straight into visible startup cards; the sheet only
       ever appears as a temporary, dismissible overlay on top of the
       page, and the grid underneath never moves while it's open. */
    .mobile-filters-btn {
        display: block;
        width: 100%;
        margin-bottom: 1rem;
        padding: 0.75rem 1rem;
        border-radius: var(--radius-btn);
        border: 1px solid var(--border-color);
        background: var(--bg-card);
        color: var(--text-primary);
        font-size: 0.9rem;
        font-weight: 600;
        font-family: inherit;
        text-align: center;
    }

    .mobile-filter-backdrop {
        display: none;
    }

    .mobile-filter-backdrop.is-open {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.5);
        z-index: 999;
    }

    .mobile-filter-modal {
        display: none;
    }

    .mobile-filter-modal.is-open {
        display: block;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        max-height: 80vh;
        overflow-y: auto;
        background: var(--bg-card);
        border-radius: 16px 16px 0 0;
        padding: 1.5rem;
        z-index: 1000;
        box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.25);
    }

    .mobile-filter-modal-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 1rem;
        padding-bottom: 1rem;
        border-bottom: 1px solid var(--border-color);
    }

    .mobile-filter-modal-title {
        font-weight: 700;
        font-size: 1rem;
    }

    .mobile-filter-modal-close {
        background: none;
        border: none;
        font-size: 1.1rem;
        color: var(--text-secondary);
        line-height: 1;
        padding: 0.25rem;
    }

    .mobile-filters-apply-btn {
        display: block;
        width: 100%;
        margin-top: 1.5rem;
        padding: 0.85rem;
        border-radius: var(--radius-btn);
        border: none;
        background: var(--accent-blue);
        color: #FFFFFF;
        font-weight: 700;
        font-size: 0.95rem;
        font-family: inherit;
    }
}

/* Mobile-only controls hidden by default (desktop) — the max-width:1024px
   query above is the only place any of these are ever shown or take on
   fixed/overlay positioning. Declared outside any media query so they're
   unconditionally inert until that breakpoint overrides them — on desktop
   the wrapped filter-groups inside #mobileFilterModal render as completely
   normal, non-fixed stacked content in the sidebar (no visual difference
   from before this redesign). */
.mobile-filters-btn,
.mobile-filter-backdrop,
.mobile-filter-modal-header,
.mobile-filters-apply-btn {
    display: none;
}

/* ============================================================
   DESKTOP BREAKPOINT — single source of truth for which hamburger
   shows. Below 1025px: mobile hamburger (default, set above).
   At 1025px and up: desktop hamburger. Nothing else in the
   stylesheet touches either button's display property, so there is
   exactly one place that can ever put both (or neither) on screen
   at once — this query.
   ============================================================ */
@media (min-width: 1025px) {
    .hamburger-btn {
        display: none;
    }

    .desktop-hamburger-btn {
        display: flex;
    }

    /* Homepage category strip exists to fill the visibility gap left
       by .desktop-nav being hidden below 1025px (see the max-width:
       1024px query above) — at this same breakpoint .desktop-nav is
       fully visible again, so the strip would just be a second,
       differently-ordered duplicate of it directly underneath. Hides
       here rather than a new breakpoint, per COE (2026-07-15): reuse
       the exact width where the main nav already reappears. */
    .home-category-strip {
        display: none;
    }
}

/* ============================================================
   RESPONSIVE — MOBILE (375px)
   ============================================================ */
@media (max-width: 768px) {
    :root {
        --container-pad: 1rem;
    }

    .grid-3,
    .grid-4,
    .grid-2 {
        grid-template-columns: 1fr;
    }

    .footer-main {
        grid-template-columns: 1fr;
        gap: 1.5rem;
    }

    .footer-bottom {
        flex-direction: column;
        align-items: flex-start;
    }

    .section-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.5rem;
    }

    .newsletter-form {
        flex-direction: column;
    }

    /* ── Shrink the fixed header on mobile: ~52px (was 156px total across
       header+ticker+pillstrip before the ticker and, later, the
       category-tabs row were both removed). layout-offsets.js still
       measures the real rendered height — this is just the target/
       fallback value, not a hardcoded replacement for that JS. ── */
    .header-inner {
        height: 52px;
    }

    /* ── Mobile header: PWA install icon, theme toggle, hamburger — in that
       order, inline, no wrap. DOM order is unchanged (theme-toggle then
       hamburger then pwa-btn) so desktop tab order isn't touched by this. ── */
    .header-right {
        flex-wrap: nowrap;
    }

    .pwa-header-install-btn {
        order: 1;
    }

    .theme-toggle {
        order: 2;
    }

    .hamburger-btn {
        order: 3;
    }

    /* Reveal the PWA install icon only below this breakpoint — the JS
       isMobile() check also gates when [hidden] gets removed in the first
       place, this is the visual backstop restricted to the same breakpoint
       named in this session's brief. */
    .pwa-header-install-btn:not([hidden]) {
        display: flex;
    }

    /* ── Horizontal scroll card rows (Latest News grid — the only one left
       on the homepage now). Universal no-scrollbar + right-edge fade +
       deliberately-partial-last-item treatment: padding-right is smaller
       than one card's width (280px) so the last visible card always peeks
       partially past the edge, and mask-image fades that peek to
       transparent — this is the touch-device affordance; desktop/mouse
       gets a separate hover-arrow affordance instead (see
       .hscroll-wrapper / .hscroll-arrow below, injected by
       hscroll-arrows.js). ── */
    .mobile-hscroll {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        gap: 1rem;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        margin-left: calc(var(--container-pad) * -1);
        margin-right: calc(var(--container-pad) * -1);
        padding-left: var(--container-pad);
        padding-right: 2.5rem;
        -webkit-mask-image: linear-gradient(90deg, #000 85%, transparent 100%);
                mask-image: linear-gradient(90deg, #000 85%, transparent 100%);
    }

    .mobile-hscroll::-webkit-scrollbar {
        display: none;
    }

    .mobile-hscroll > * {
        flex: 0 0 280px;
        width: 280px;
        scroll-snap-align: start;
    }

    /* ── Footer accordion — collapsed by default, one section open at a
       time (behavior in footer.php inline script) ── */
    .footer-accordion-toggle {
        cursor: pointer;
        padding: 0.9rem 0;
        margin-bottom: 0;
        border-bottom: 1px solid var(--border-color);
    }

    .footer-accordion-chevron {
        display: block;
    }

    .footer-accordion-toggle[aria-expanded="true"] .footer-accordion-chevron {
        transform: rotate(180deg);
    }

    .footer-accordion-content {
        max-height: 0;
        transition: max-height 0.2s ease;
    }

    .footer-accordion-content .footer-links {
        padding: 1rem 0;
    }

    /* ── Section padding cap + minimum readable text sizes ── */
    .section-padding {
        padding: 2rem 0;
    }

    /* Trims the space between the fixed header and the first real content
       on mobile. Fixed 2026-07-18 — live QA flagged excessive empty
       vertical space before content, most noticeable on Glossary but
       audited sitewide: #mainContent's own padding-top (~64px, synced to
       the real header height by layout-offsets.js) already provides real
       separation from the viewport edge; stacking .section-padding's full
       2rem (32px) TOP padding on top of that before any content — 96px
       total — is redundant on a short mobile viewport where every pixel
       matters. Scoped to :first-child so this only ever touches the very
       first section-padding block on a page (the one sitting directly
       under the header) — any later .section-padding usage further down a
       template (e.g. between homepage sections) is untouched and keeps
       its full, intentional breathing room. Bottom padding is untouched
       here too — only the top value is reduced. Confirmed via a direct
       grep that "container section-padding" is the outermost wrapper,
       and a direct child of #mainContent, across all 16 templates that
       use this pattern. */
    #mainContent > .container.section-padding:first-child {
        padding-top: 1.25rem;
    }

    .news-card-excerpt {
        font-size: 0.875rem;
    }

    /* Defensive collapse — the trigger button is hidden at this width
       (rule above), so this panel can't normally open on mobile, but if
       it's somehow still open from a desktop-to-mobile resize, it should
       match the same 85% width used by the mobile nav panel rather than
       staying at a desktop-only 400px. */
    .desktop-side-panel {
        width: 85%;
    }
}

/* ============================================================
   HORIZONTAL SCROLL TABS — shared pattern for any single-line,
   single-select filter/tab row (category tabs, sub-category tabs,
   investor type/geography filters, event time/format filters, job
   setup filters, glossary category filter, tools type/category
   filters, etc.) that must never wrap to multiple lines. Same
   no-visible-scrollbar + right-edge gradient fade + deliberately-
   partial-last-item treatment used everywhere else on the site for
   horizontally-scrolling content — applies it to text/pill rows too,
   not just card rows. Found wrapping instead of scrolling on the AI
   page's sub-category row (COE live QA, 2026-07-13) and audited for
   every other instance of the same flex-wrap:wrap pattern on a tab-
   style row across the theme; fixed all of them the same way.

   BUG FIX (2026-07-15): the mask-image fade used to be unconditional
   here, painted even when a row's content didn't actually overflow —
   on short rows that genuinely fit (e.g. the homepage category strip
   at some widths), the fade rendered over trailing empty space after
   a fully-visible last item, implying a cutoff that wasn't real.
   mask-image now lives on .hscroll-tabs.is-scrollable only —
   hscroll-cutoff.js toggles that class based on a real overflow
   check, so the fade (and the hard-cutoff enforcement below it) only
   ever appears together with genuine, measured overflow.
   ============================================================ */
.hscroll-tabs {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
    scrollbar-width: none; /* Firefox — no visible scrollbar, ever */
    -ms-overflow-style: none; /* old Edge/IE */
    padding-right: 2rem; /* leaves room for the fade to have something to fade */
}

.hscroll-tabs.is-scrollable {
    -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
            mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
}

.hscroll-tabs::-webkit-scrollbar {
    display: none; /* Chrome/Safari — no visible scrollbar, ever */
}

.hscroll-tabs > a,
.hscroll-tabs > button,
.hscroll-tabs > span {
    flex-shrink: 0;
    white-space: nowrap;
}

/* ============================================================
   HOMEPAGE CATEGORY STRIP — plain-text tab convention (no pill,
   no border box around each item), matching the original pre-2026-07-10
   category-tabs look. Purely navigational (each link leaves the
   homepage for its own section page), so there's no persistent
   "active" state to track — underline only appears on hover/focus.
   Added 2026-07-15.
   ============================================================ */
.home-category-strip {
    border-bottom: 1px solid var(--border-color);
}

.home-category-strip .hscroll-tabs {
    gap: 1.75rem;
    padding: 1rem 0;
}

.home-category-strip .hscroll-tabs a {
    color: var(--text-secondary);
    font-weight: 600;
    font-size: 0.9375rem;
    text-decoration: none;
    padding-bottom: 0.25rem;
    border-bottom: 2px solid transparent;
    transition: color 0.2s ease, border-color 0.2s ease;
}

.home-category-strip .hscroll-tabs a:hover,
.home-category-strip .hscroll-tabs a:focus-visible {
    color: var(--accent-blue);
    border-bottom-color: var(--accent-blue);
}

/* ============================================================
   DESKTOP HOVER-ARROW SCROLL CONTROLS
   Mouse/trackpad-only affordance for horizontally-scrolling card rows
   (.mobile-hscroll) — touch devices instead use the no-scrollbar +
   edge-fade + partial-cutoff treatment defined above, inside the
   max-width:768px block. Not scoped to a media query here because
   hscroll-arrows.js only ever creates .hscroll-wrapper/.hscroll-arrow
   elements on hover-capable, fine-pointer devices, and only for rows
   that actually overflow — the elements simply never exist otherwise,
   so no extra CSS gating is needed.
   ============================================================ */
.hscroll-wrapper {
    position: relative;
}

.hscroll-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    z-index: 2;
}

.hscroll-wrapper:hover .hscroll-arrow {
    opacity: 1;
    pointer-events: auto;
}

.hscroll-arrow:hover {
    border-color: var(--accent-blue);
    color: var(--accent-blue);
}

.hscroll-arrow.at-start,
.hscroll-arrow.at-end {
    opacity: 0 !important;
    pointer-events: none !important;
}

.hscroll-arrow-prev {
    left: 0.5rem;
}

.hscroll-arrow-next {
    right: 0.5rem;
}

/* ============================================================
   PRINT STYLES
   ============================================================ */
@media print {
    .site-header,
    .site-footer,
    .mobile-nav-overlay {
        display: none;
    }

    body {
        background: #FFF;
        color: #000;
    }
}
