body { font-family: 'Inter', sans-serif; transition: background-color 0.3s ease, color 0.3s ease; }
body { background-color: #f7f7f7; color: #1e293b; }
.dark body { background-color: #0f172a; color: #f1f5f9; }

/* Academic serif display for the nameplate + section headings.
   Reinforces the "research portfolio" identity (Crimson Pro, per UI/UX design system).
   Scoped via selector — remove these two rules + the Crimson Pro font link to revert. */
header h1,
main section > h2 {
    font-family: 'Crimson Pro', Georgia, 'Times New Roman', serif;
    letter-spacing: 0;
}

.chart-container { position: relative; width: 100%; max-width: 600px; margin: auto; height: 350px; max-height: 400px; }
@media (min-width: 768px) { .chart-container { height: 400px; } }

::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #e0e0e0; }
.dark ::-webkit-scrollbar-track { background: #1e293b; }
::-webkit-scrollbar-thumb { background: #1E3A5F; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #305C8C; }

.carousel-item { display: none; animation: fade 0.5s ease-in-out; }
.carousel-item.active { display: block; }
@keyframes fade { from { opacity: 0.4; } to { opacity: 1; } }

/* COVA "Pipeline in Action" mini-carousel — scoped class so it stays fully
   independent of the File System project's global .carousel-item state. */
.cova-carousel-item { display: none; animation: fade 0.5s ease-in-out; }
.cova-carousel-item.active { display: block; }

.nav-link { transition: color 0.2s ease, background-color 0.2s ease; }

/* The nav pill comes from Tailwind's `hover:bg-*` utility, which is not
   gated on pointer type. On touch that leaves a tapped nav item visibly
   highlighted after the drawer closes. Suppress the pill where hover is
   unavailable and give the press itself an :active pill instead.
   `html`-scoped to outrank the utility regardless of injection order. */
@media (hover: none) {
    html .nav-link:hover { background-color: transparent; }
}
html .nav-link:active      { background-color: #B0C6DD; } /* navy-200  */
html.dark .nav-link:active { background-color: #1E293B; } /* slate-800 */

/* ------------------------------------------------------------------
   Shared interactive components.

   These exist as named classes rather than repeated utility strings
   because every contrast bug found so far came from the same cause:
   the same button styled in five places, so a fix applied to one copy
   silently missed the others. One rule per component = one place to fix.
   ------------------------------------------------------------------ */

/* Social links bar under the header. */
.social-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;          /* touch-target floor set in v2.9.0 */
    padding: 0 0.5rem;         /* constant, so the hover pill causes no reflow */
    border-radius: 0.375rem;
    font-weight: 600;
    color: #1E3A5F;            /* navy   — 10.7:1 on the light page */
    transition: color 0.2s ease, background-color 0.2s ease;
}
/* The pill was slate-100, which lands at 1.13:1 against the white header —
   mathematically present, visually absent. Light mode has almost no surface
   hierarchy (header white, page #f7f7f7, cards white all sit within 1.05:1
   of each other), so a pale tint has nothing to register against. navy-200
   reads at 1.74:1, comfortably past the 1.32:1 that already works in dark
   mode, where small steps are far more perceptible. */
html.dark .social-link {
    color: #B0C6DD;            /* navy-200 — 10.3:1 on the dark page */
}
@media (hover: hover) {
    .social-link:hover {
        background-color: #B0C6DD; /* navy-200 */
        color: #0F1E33;            /* navy-900 — 9.6:1 on that pill */
    }
    html.dark .social-link:hover {
        background-color: #1E293B; /* slate-800 */
        color: #D6E1EE;            /* navy-100 — 10.1:1 on that pill */
    }
}
.social-link:active {
    background-color: #B0C6DD;
    color: #0F1E33;
}
html.dark .social-link:active {
    background-color: #1E293B;
    color: #D6E1EE;
}

/* Solid navy (light) / gold (dark) buttons.

   The two themes need opposite mechanics, because their fills sit at
   opposite ends of the scale:

   LIGHT — the fill deepens to navy-900. Modelled on the ScamLingua
   Source Code button, which works because its card is tinted: the block
   darkens by a clear step and the white label reads brighter against it
   (11.5:1 -> 16.8:1). A brightness filter was tried first and was too
   subtle here, since a lighter navy just drifts toward the white page.

   DARK — the gold fill brightens instead. Deepening gold would collapse
   it toward the slate card; brightening lifts it off the page and takes
   navy-on-gold from 5.8:1 to 7.3:1.

   Both selectors are html-scoped so they outrank Tailwind's utilities
   regardless of stylesheet injection order. */
.btn-solid {
    min-height: 44px;          /* padding alone left the text-xs buttons at 38px */
    transition: filter 0.2s ease, box-shadow 0.2s ease,
                background-color 0.2s ease, border-color 0.2s ease;
}

/* Hover effects are gated behind `hover: hover` so touch devices never
   enter them. Without the guard, iOS applies :hover on tap and leaves it
   applied until you tap elsewhere — a link stays visibly "held down"
   after you have already navigated away and come back. `:active` gives
   touch users the same feedback for the duration of the press instead. */
@media (hover: hover) {
    .btn-solid:hover {
        box-shadow: 0 4px 10px -2px rgba(15, 30, 51, 0.35);
    }
    html:not(.dark) .btn-solid:hover {
        background-color: #0F1E33; /* navy-900 — white label at 16.8:1 */
        border-color: #0F1E33;
    }
    html.dark .btn-solid:hover {
        filter: brightness(1.12);  /* gold-300 lifts; navy label at 7.3:1 */
    }
}
html:not(.dark) .btn-solid:active {
    background-color: #0F1E33;
    border-color: #0F1E33;
}
html.dark .btn-solid:active {
    filter: brightness(1.12);
}

/* Back-to-top fade. `invisible` (visibility:hidden) is used rather than
   opacity alone so the button leaves the tab order while hidden. */
#backToTop { transition: opacity 0.25s ease, visibility 0.25s ease, transform 0.25s ease, background-color 0.2s ease; transform: translateY(0.5rem); }
#backToTop.is-visible { transform: translateY(0); }
/* Keep clear of the iOS Safari toolbar and the home indicator. env() falls
   back to 0px everywhere else, so desktop spacing is unchanged. */
#backToTop {
    bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
    right: calc(1.5rem + env(safe-area-inset-right, 0px));
}

/* Keyboard focus. :focus-visible fires for keyboard/AT navigation but not
   mouse clicks, so this is invisible to pointer users and essential for
   everyone else. Gold in dark mode for the same reason the badge uses it —
   navy disappears against slate. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
    outline: 3px solid #4F57D6;
    outline-offset: 2px;
    border-radius: 0.375rem;
}
html.dark a:focus-visible,
html.dark button:focus-visible,
html.dark summary:focus-visible,
html.dark [tabindex]:focus-visible {
    outline-color: #DDB44A;
}
/* Solid navy/gold buttons (Download Résumé, Verify on Credly). In dark mode
   their fill IS gold-300, so the rule above would draw a gold ring around a
   gold button. White keeps the ring unambiguous. Must follow that rule —
   same specificity, so source order decides. */
html.dark .btn-solid:focus-visible {
    outline-color: #F8FAFC;
}

/* Skip link: off-screen until focused, so keyboard users can jump past the
   sticky header and its nav instead of tabbing through it on every load. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
}
.skip-link:focus {
    left: 0.5rem;
    top: 0.5rem;
    padding: 0.75rem 1rem;
    background: #1E3A5F;
    color: #ffffff;
    border-radius: 0.5rem;
    font-weight: 600;
    text-decoration: none;
}

/* Browsers drop background fills when printing; force the "actively seeking" badge
   to keep its tint and border so it still reads as a badge in a PDF export. */
.seeking-badge { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
section { transition: background-color 0.3s ease, border-color 0.3s ease; }

/* Scroll Reveal Animation */
.reveal {
    opacity: 0;
    transform: translateY(30px);
    transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.reveal.visible {
    opacity: 1;
    transform: translateY(0);
}
/* When GSAP/ScrollTrigger is active it owns the reveal animation via inline
   styles, so we disable the CSS transition to avoid double-animating.
   The CSS above remains the fallback for no-JS / reduced-motion / print. */
.js-gsap .reveal { transition: none; }

/* Animated Counter */
.counter-value {
    font-variant-numeric: tabular-nums;
    transition: color 0.3s ease;
}

/* Tech Tags */
.tech-tag {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem 0.625rem;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.025em;
    border-radius: 9999px;
    transition: all 0.2s ease;
}
.tech-tag:hover {
    transform: scale(1.05);
}

/* Research Card Gradient Border */
.research-card {
    position: relative;
    background: linear-gradient(135deg, rgba(129, 140, 248, 0.12), rgba(30, 58, 95, 0.06));
    border: 1px solid rgba(129, 140, 248, 0.35);
}
.dark .research-card {
    background: linear-gradient(135deg, rgba(129, 140, 248, 0.16), rgba(30, 58, 95, 0.10));
    border-color: rgba(129, 140, 248, 0.40);
}

/* Research "Technical Details" Accordion (native <details>) */
details.research-details > summary {
    cursor: pointer;
    list-style: none;
}
details.research-details > summary::-webkit-details-marker { display: none; }
details.research-details > summary .chevron {
    display: inline-block;
    transition: transform 0.2s ease;
}
details.research-details[open] > summary .chevron { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) {
    details.research-details > summary .chevron { transition: none; }
}

/* Metric Card Pulse */
.metric-card {
    position: relative;
    overflow: hidden;
}
.metric-card::before {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
    animation: shimmer 3s infinite;
}
@keyframes shimmer {
    0% { left: -100%; }
    100% { left: 100%; }
}

/* Print Stylesheet */
@media print {
    header { position: static; box-shadow: none; border-bottom: 1px solid #ddd; }
    .no-print { display: none !important; }
    body { background: white !important; color: black !important; }
    section { break-inside: avoid; box-shadow: none; border: 1px solid #eee !important; margin-bottom: 1rem; padding: 1rem !important; }
    a { text-decoration: underline; color: #1E3A5F !important; }
    a[href]:after { content: " (" attr(href) ")"; font-size: 0.75rem; color: #666; }
    .chart-container { max-height: 300px; }
    main { padding-bottom: 0 !important; }
    .reveal { opacity: 1 !important; transform: none !important; }
    .metric-card::before { display: none; }
    /* Force research accordions fully open for clean PDF export */
    details.research-details { display: block !important; }
    details.research-details > summary { pointer-events: none; cursor: default; }
    details.research-details > summary .chevron { display: none; }
    details.research-details > summary::after { content: " (expanded)"; font-size: 0.7rem; color: #888; font-weight: normal; }
    /* Reveal every COVA "Pipeline in Action" screenshot (not just the active slide) for a complete PDF */
    .cova-carousel-item { display: block !important; break-inside: avoid; margin-bottom: 0.5rem; }
    /* Contact links already show the address as their label — don't repeat it as a href suffix */
    .contact-link[href]:after { content: "" !important; }
}

/* Reduced Motion */
@media (prefers-reduced-motion: reduce) {
    .reveal { transition: none; opacity: 1; transform: none; }
    .metric-card::before { animation: none; }
    .carousel-item { animation: none; }
    .cova-carousel-item { animation: none; }
}
