/* Shared styles for every page of ranjan-sarkar.github.io */

/* Adding custom font. font-display: swap shows the text straight away in a fallback font and
   switches when the font file arrives; without it, slow connections see no text for up to 3s. */
@font-face {
    font-family: 'GoogleSans-Regular';
    src: url('../font/GoogleSans-Regular.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

@font-face {
    font-family: 'GoogleSans-Regular';
    src: url('../font/GoogleSans-Medium.ttf') format('truetype');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

@font-face {
    font-family: 'GoogleSans-Regular';
    src: url('../font/GoogleSans-Bold.ttf') format('truetype');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

.font-regular {
    font-family: 'GoogleSans-Regular', sans-serif;
    font-weight: 400;
    letter-spacing: 0em;
}

.font-medium {
    font-family: 'GoogleSans-Regular', sans-serif;
    font-weight: 450;
    letter-spacing: -0.02em;
}

.font-semibold {
    font-family: 'GoogleSans-Regular', sans-serif;
    font-weight: 500;
    letter-spacing: -0.03em;
}

.font-bold {
    font-family: 'GoogleSans-Regular', sans-serif;
    font-weight: 700;
    letter-spacing: -0.03em;
}

.google-font {
    font-family: 'GoogleSans-Regular', sans-serif;
    font-weight: 400;
    letter-spacing: 0.022em;
    color: #202124;
}

/* The © in the footer. Google Sans draws © small and raised, like a superscript, so it uses Inter's
   full-size ©, which matches the height of the text beside it. Only that one character is embedded
   here (1 KB, Inter by Rasmus Andersson, SIL Open Font License), so nothing extra is downloaded. */
@font-face {
    font-family: 'Inter Copyright';
    src: url(data:font/woff2;base64,d09GMgABAAAAAARMAA8AAAAAB9wAAAPxAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG4EiHG4GYD9TVEFUXgA8EQgKgyyDGgsKAAE2AiQDEAQgBYRcByAMBxu4BsgEHug9vz8zSXu7aZu7RBbyLIzKWQqfeACqR3x8wfn/95sVEqlwkUio1AT3XfR+TB9iMvG2VLdQRbSJxp2+vFC31DZGVB89qtTHy89CAAYDWIg+fOT46bRatiYbp34oG4hRP77ETlIfHdH+h9CKZL5AxgLZJPU9NslYi44EGdQlJm5+2IOgLlUA1YQEkJKaWOlm1C8qYNocomlx8VYoEMDmgjQm1c+R6eSKBDVtz2NiUL+2pD7uDE4r3c7/Neu0om2qpwh0VztxGBe4uusfAGcDI/kB++UliUvDO0b/DulU54qD+eCp0yZM42Us7lS8/H918Hwtj4O4RM7rC12fMhZgH37QQ3+u6c3sgEylc2tFHzhnVShsC4GI2EviQjHHt6I2bjQkUgw5GGbJWFAdjsjnBSlF3w3jonwgIxEP19UG2H0xbrk0t7NcEZSdH505VyUOq+g4VCase2qPy8+rD3s+aEFExZjgG3RD131uQ3f7rJo115jrKlfjF8Ys4cM7V/q16YYI5hp4hRdDzKwJMjOD1QE0URSJaNM+MLalXPbuKxZnGKWSsbdQYHOx6N1XnuUtFArG3lIpsveH0hdiy5flH6krvy9+zubPCt/N3Fcul8WWUmvB/Vxx8JbLj/F897jJbpRS5dLGIUp9Vt6EYvBLL29+2XkJxcublVKbX3558CalvlOXnU3q5cv5GYsttVhtGvwd1DZffmmTgl95ePFhpVR6Mc6vq3OvvZ5bFX44O3vKv/ixOWb7oumjV40ctWrM6FErV40cPd1eaI597FscO3NzdnjV63butVu7LtVamc+vrHXp0spa+XytlZcu1VqZz68U/9HFB+tK865dm1dat25e6dq10rx1lP1y8OgPHbqv9+kPFlUO+M2jecrKP6zcdTiYf+HvOv/Fa971fH019o9J4TEuqGX/XeePcM27PVaat+kuJUbLVlAxmdtSTTK9HR9pKh4H0ZRN+rn6uAJ6FSF9WLRLQGAICySmqA4aXfgBdOrzNrjoxVFwU5/VWpNeDM0LgU8ggD7UpTH0xUNtUPgweMjDfO5UUiRYQpIx2CwhToRldKUHaWwmeVhA4uMsphFhLQFmESBCiDC2M9smTY5+dOb3T5EmQJIgKZI0oWHh8BwBOpEiS4gx5P4AWToyle4PsYI4S4BnEmSOSN/zLHrQiS50oSv9CUHTpiO9LNMe9GYZXVi6AsuctYrZ2qlMYwLT6D/1GpkVbXjYprJryG5aWnTD/GsvLKYTJoC1hMMmkyVFlADLWsohrDgq3PzGOSzaHBttbX6YFSylE8uuhTu3rCfI0RnsyNz2x2WVNvv/OvsxB58IZ8dj/QAAAAA=) format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    unicode-range: U+00A9;
}

.inter-font {
    font-family: 'Inter Copyright', 'GoogleSans-Regular', sans-serif;
}

/* Font system. Google Sans is the site's voice: headings, text, menus, buttons, course codes, tags.
   Paper titles (Home, Publications) and the seminar title in News use Tailwind's font-serif italic
   (the device's serif: Georgia / Times New Roman on Windows). The BibTeX box uses JetBrains Mono
   (from Google Fonts, link in each page's <head>). */
:root {
    --font-sans: 'GoogleSans-Regular', sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Consolas, monospace;
}

.citation-pre {
    font-family: var(--font-mono);
}

/* Type scale: one size per role, used on every page. Headings grow smoothly with the screen
   width (clamp: phone size -> desktop size) instead of jumping at a breakpoint.
   Use the .type-* classes in the markup, or the variables in component rules (.cp-title etc.). */
:root {
    --fs-display: clamp(2.5rem, 1.75rem + 3.2vw, 3.75rem);       /* 40 -> 60px  home page name */
    --fs-page-title: clamp(1.875rem, 1.35rem + 2.2vw, 2.75rem);  /* 30 -> 44px  page title (h1) */
    --fs-section: clamp(1.625rem, 1.3rem + 1.1vw, 2rem);         /* 26 -> 32px  section heading (h2) */
    --fs-title: clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);          /* 20 -> 24px  talk / publication title */
    --fs-card-title: clamp(1.125rem, 1.05rem + 0.3vw, 1.25rem);  /* 18 -> 20px  card title, sub-heading (h3) */
}

/* Screen-size scaling: every size on the site is in rem, so the root font size below scales the whole
   page at once (text, spacing, content width, photo). Sizes are CSS pixels, i.e. after Windows / macOS
   display scaling. Percentages (of the browser's 16px default) keep a reader's own font-size setting
   working. Phones, tablets and anything not matched below stay at the default 100% (16px).
   Laptops are told apart from desktops by height: they are short, so the hero fits on the first screen.
   The desktop steps below go mainly by height. Their width minimums are kept well under the screen
   width so a browser narrowed by vertical tabs or a side panel (about 250-600px) keeps the same size;
   each minimum still leaves room for the 80rem (max-w-7xl) content column at that root size. */

/* 15.6" laptops: 1920x1080 at 125% scaling, a window about 1536 x 730 */
@media (min-width: 1024px) and (max-height: 760px) {
    html { font-size: 93.75%; }   /* 15px */
}

/* 14" laptops: 1366x768, or 1920x1080 at 150% scaling, a window about 1280-1366 x 600-650 */
@media (min-width: 1024px) and (max-height: 660px) {
    html { font-size: 90.625%; }  /* 14.5px */
}

/* Full-HD desktop monitors (1920x1080 at 100%) and 1080p laptops without scaling */
@media (min-width: 1600px) and (min-height: 880px) {
    html { font-size: 106.25%; }  /* 17px */
}

/* QHD and ultrawide monitors (2560x1440, 3440x1440 at 100%) */
@media (min-width: 1920px) and (min-height: 1200px) {
    html { font-size: 118.75%; }  /* 19px */
}

/* 4K monitors at 100% scaling */
@media (min-width: 2560px) and (min-height: 1600px) {
    html { font-size: 150%; }     /* 24px */
}

.type-display {
    font-size: var(--fs-display);
    line-height: 1.1;
    letter-spacing: -0.02em;
}

.type-page-title {
    font-size: var(--fs-page-title);
    line-height: 1.15;
    letter-spacing: -0.015em;
}

.type-section {
    font-size: var(--fs-section);
    line-height: 1.25;
    letter-spacing: -0.01em;
}

.type-title {
    font-size: var(--fs-title);
    line-height: 1.35;
}

.type-card-title {
    font-size: var(--fs-card-title);
    line-height: 1.4;
}

/* Define color variables for light and dark themes */
:root {
    --bg-light-primary: #f8fafc;
    /* slate-50 */
    --bg-light-secondary: #ffffff;
    --text-light-primary: #1e293b;
    /* slate-800 */
    --text-light-secondary: #475569;
    /* slate-600 */
    --border-light: #e2e8f0;
    /* slate-200 */
    --accent-light: #4F46E5;
    /* indigo-600 */
    --accent-hover-light: #4338CA;
    /* indigo-700 */

    --bg-dark-primary: #0f172a;
    /* slate-900 */
    --bg-dark-secondary: #1e293b;
    /* slate-800 */
    --text-dark-primary: #f1f5f9;
    /* slate-100 */
    --text-dark-secondary: #94a3b8;
    /* slate-400 */
    --border-dark: #334155;
    /* slate-700 */
    --accent-dark: #818CF8;
    /* indigo-400 */
    --accent-hover-dark: #6366F1;
    /* indigo-500 */
}

/* Apply theme colors using CSS variables */
body {
    font-family: 'GoogleSans-Regular', sans-serif;
    /* White base; alternate sections add a faint tint on top (see "Section alternate background") */
    background-color: var(--bg-light-secondary);
    color: var(--text-light-primary);
    transition: background-color 0.3s ease, color 0.3s ease;
}

.dark body {
    background-color: var(--bg-dark-primary);
    color: var(--text-dark-primary);
}

/* Decorative background: faint glows + contour lines, fixed behind all content */
.bg-decor {
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    overflow: hidden;
    /* its own graphics layer, so scrolling the page slides content over it without redrawing it,
       and redrawing the lines (the scroll ripple in main.js) never touches the rest of the page */
    contain: strict;
    will-change: transform;
    color: var(--accent-light);
    background:
        radial-gradient(60rem 40rem at 100% 0%, rgba(79, 70, 229, 0.08), transparent 70%),
        radial-gradient(50rem 36rem at 0% 100%, rgba(14, 165, 233, 0.07), transparent 70%);
}

.dark .bg-decor {
    color: var(--accent-dark);
    background:
        radial-gradient(60rem 40rem at 100% 0%, rgba(129, 140, 248, 0.09), transparent 75%),
        radial-gradient(50rem 36rem at 0% 100%, rgba(56, 189, 248, 0.08), transparent 75%);
}

.bg-decor svg {
    width: 100%;
    height: 100%;
    opacity: 0.12;
    animation: bg-curves-in 1.6s ease-out both;   /* lines fade in as a page opens */
}

@keyframes bg-curves-in {
    from {
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .bg-decor svg {
        animation: none;
    }
}

.dark .bg-decor svg {
    opacity: 0.14;
}

/* Portrait screens (phones): the wide 1440x900 artwork above gets cropped to a thin middle strip,
   which hides both curve fans. There, show each fan pinned to its own corner instead
   (these two corner SVGs are built from the same paths in js/main.js). */
.bg-decor svg.bg-decor-corner {
    display: none;
}

@media (max-aspect-ratio: 1/1) {
    .bg-decor svg:not(.bg-decor-corner) {
        display: none;
    }

    .bg-decor svg.bg-decor-corner {
        display: block;
        position: absolute;
        /* each fan's artwork is 710 x 440 units */
        width: 80vw;
        height: calc(80vw * 440 / 710);
    }

    .bg-decor-top-right {
        top: 0;
        right: 0;
    }

    /* nudged partly off-screen so this fan sits tighter into its corner */
    .bg-decor-bottom-left {
        bottom: -6vw;
        left: -10vw;
    }

    /* keep lines as thick as on desktop even though the fans are scaled down */
    .bg-decor-corner path {
        vector-effect: non-scaling-stroke;
    }
}

@media print {
    .bg-decor {
        display: none;
    }
}

/* Component-specific theming */
.themed-bg-primary {
    background-color: var(--bg-light-primary);
}

.dark .themed-bg-primary {
    background-color: var(--bg-dark-primary);
}

.themed-bg-secondary {
    background-color: var(--bg-light-secondary);
}

.dark .themed-bg-secondary {
    background-color: var(--bg-dark-secondary);
}

.themed-text-primary {
    color: var(--text-light-primary);
}

.dark .themed-text-primary {
    color: var(--text-dark-primary);
}

.themed-text-secondary {
    color: var(--text-light-secondary);
}

.dark .themed-text-secondary {
    color: var(--text-dark-secondary);
}

.themed-border {
    border-color: var(--border-light);
}

.dark .themed-border {
    border-color: var(--border-dark);
}

.themed-accent {
    color: var(--accent-light);
}

.dark .themed-accent {
    color: var(--accent-dark);
}

.themed-accent-bg {
    background-color: var(--accent-light);
}

.dark .themed-accent-bg {
    background-color: var(--accent-dark);
}

.themed-accent-bg-hover:hover {
    background-color: var(--accent-hover-light);
}

/* "See more" links under a section (View all publications / courses, View on Google Scholar):
   a soft accent pill with the label and an arrow in a filled circle. On hover the pill deepens,
   gains a soft shadow and the arrow slides forward. An optional leading icon uses .more-link-lead. */
.more-link {
    display: inline-flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.4rem 0.4rem 0.4rem 1.25rem;
    border: 1px solid rgba(79, 70, 229, 0.2);
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.06);
    color: var(--accent-light);
    font-weight: 600;
    font-size: 0.95rem;
    transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}

.more-link-lead {
    margin-right: -0.25rem;
    font-size: 1.05em;
}

.more-link-arrow {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border-radius: 999px;
    background-color: var(--accent-light);
    color: #ffffff;
    font-size: 0.8rem;
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

.more-link:hover,
.more-link:focus-visible {
    border-color: rgba(79, 70, 229, 0.45);
    background-color: rgba(79, 70, 229, 0.11);
    box-shadow: 0 8px 18px -10px rgba(79, 70, 229, 0.55);
    outline: none;
}

.more-link:focus-visible {
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.3);
}

.more-link:hover .more-link-arrow,
.more-link:focus-visible .more-link-arrow {
    transform: translateX(3px);
}

/* links to another site: the arrow points up-right, and slides that way on hover */
.more-link-arrow.is-external i {
    transform: rotate(-45deg);
}

.more-link:hover .more-link-arrow.is-external,
.more-link:focus-visible .more-link-arrow.is-external {
    transform: translate(2px, -2px);
}

.more-link:active {
    transform: scale(0.98);
}

.dark .more-link {
    border-color: rgba(129, 140, 248, 0.25);
    background-color: rgba(129, 140, 248, 0.1);
    color: var(--accent-dark);
}

.dark .more-link-arrow {
    background-color: var(--accent-dark);
    color: #0f172a;
}

.dark .more-link:hover,
.dark .more-link:focus-visible {
    border-color: rgba(129, 140, 248, 0.5);
    background-color: rgba(129, 140, 248, 0.16);
    box-shadow: 0 8px 18px -10px rgba(0, 0, 0, 0.6);
}

.dark .more-link:focus-visible {
    box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.35);
}

@media (prefers-reduced-motion: reduce) {
    .more-link:hover .more-link-arrow,
    .more-link:focus-visible .more-link-arrow,
    .more-link:active {
        transform: none;
    }
}

/* News: "View Poster" / "View Slides" after a news line. A small accent pill with the label and
   an arrow, like a mini "View all" link; on hover the pill deepens, gains a soft shadow and the
   arrow slides forward. */
.news-action {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-left: 0.35rem;
    padding: 0.1rem 0.7rem;
    border: 1px solid rgba(79, 70, 229, 0.2);
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.06);
    color: var(--accent-light);
    font-size: 0.875rem;
    font-weight: 600;
    white-space: nowrap;
    transition: background-color 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease, transform 0.2s ease;
}

.news-action i {
    font-size: 0.75em;
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

.news-action:hover,
.news-action:focus-visible {
    border-color: rgba(79, 70, 229, 0.45);
    background-color: rgba(79, 70, 229, 0.12);
    box-shadow: 0 6px 14px -8px rgba(79, 70, 229, 0.55);
    outline: none;
}

.news-action:focus-visible {
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.3);
}

.news-action:hover i,
.news-action:focus-visible i {
    transform: translateX(3px);
}

.news-action:active {
    transform: scale(0.96);
}

.dark .news-action {
    border-color: rgba(129, 140, 248, 0.25);
    background-color: rgba(129, 140, 248, 0.1);
    color: var(--accent-dark);
}

.dark .news-action:hover,
.dark .news-action:focus-visible {
    border-color: rgba(129, 140, 248, 0.5);
    background-color: rgba(129, 140, 248, 0.17);
    box-shadow: 0 6px 14px -8px rgba(0, 0, 0, 0.6);
}

.dark .news-action:focus-visible {
    box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.35);
}

@media (prefers-reduced-motion: reduce) {
    .news-action:hover i,
    .news-action:focus-visible i,
    .news-action:active {
        transform: none;
    }
}

/* Home: on desktop monitors (Full HD, QHD, ultrawide, 4K) the hero fills the first screen under the
   4rem header with its content centred (equal room above and below), so the photo and bio sit in the
   middle and the News section starts below the fold.
   Matched by the browser window, not the screen: landscape, at least 1024px wide and 800px tall.
   Vertical tabs or a side panel take width and horizontal tabs take height, but a desktop window
   stays well inside these limits either way. Laptops (windows under about 760px tall, see the
   screen-size scaling above) and tablets in portrait keep the plain padding from the Tailwind classes.
   If the content is taller than the window, the padding alone sets the gap.
   "#home" outweighs Tailwind's md:pt-14 / md:pb-28. */
@media (min-width: 1024px) and (min-height: 800px) and (min-aspect-ratio: 1/1) {
    #home {
        display: flex;
        flex-direction: column;
        justify-content: center;
        min-height: calc(100vh - 4rem);
        min-height: calc(100svh - 4rem);
        padding-top: 4.5rem;
        padding-bottom: 4.5rem;
    }
}

/* Home: role tag above the name, a soft accent pill like the research tags below */
.home-role {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.85rem;
    border: 1px solid rgba(79, 70, 229, 0.2);
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.06);
    color: var(--accent-light);
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.dark .home-role {
    border-color: rgba(129, 140, 248, 0.25);
    background-color: rgba(129, 140, 248, 0.1);
    color: var(--accent-dark);
}

/* Home: institute line under the name */
.home-institute {
    font-size: 1.125rem;
    font-weight: 500;
}

@media (min-width: 768px) {
    .home-institute {
        font-size: 1.25rem;
    }
}

/* Home: the short intro paragraph. Justified on wide screens (1024px and up); below that the text
   column is too narrow and justifying leaves wide gaps between words, so it keeps the hero's
   alignment (centred on phones, left on tablets). */
.home-intro {
    line-height: 1.6;
}

@media (min-width: 1024px) {
    .home-intro {
        text-align: justify;
        hyphens: auto;
        -webkit-hyphens: auto;
    }
}

/* Home: the bio, one fact per line. A two-column grid: a small uppercase label with an icon on the
   left, the value on the right, with a thin accent rule down the side. The block is only as wide
   as its content, so on phones it sits centred under the name while its lines stay left-aligned.
   Research areas are soft accent tags. Very narrow phones put each label above its value. */
.home-bio {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: baseline;
    gap: 0.5rem 1.5rem;
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
    padding: 0.25rem 0 0.25rem 1.25rem;
    border-left: 2px solid rgba(79, 70, 229, 0.3);
    text-align: left;
}

@media (min-width: 768px) {
    .home-bio {
        margin-inline: 0;
    }
}

.home-bio dt {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-light-secondary);
    white-space: nowrap;
}

.home-bio dt i {
    width: 1rem;
    text-align: center;
    color: var(--accent-light);
}

.home-bio dd {
    margin: 0;
    font-weight: 500;
}

.home-bio-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.home-bio-tags li {
    padding: 0.2rem 0.7rem;
    border: 1px solid rgba(79, 70, 229, 0.2);
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.06);
    color: var(--accent-light);
    font-size: 0.85rem;
    font-weight: 500;
}

.dark .home-bio {
    border-left-color: rgba(129, 140, 248, 0.35);
}

.dark .home-bio dt {
    color: var(--text-dark-secondary);
}

.dark .home-bio dt i {
    color: var(--accent-dark);
}

.dark .home-bio-tags li {
    border-color: rgba(129, 140, 248, 0.25);
    background-color: rgba(129, 140, 248, 0.1);
    color: var(--accent-dark);
}

@media (max-width: 420px) {
    .home-bio {
        grid-template-columns: 1fr;
        gap: 0.3rem;
    }

    .home-bio dd + dt {
        margin-top: 0.4rem;
    }
}

/* Home: Resume button. On hover the label slides left to make room for an arrow that fades in,
   and the button lifts a little with a soft accent shadow; it presses in on click.
   The button keeps its width throughout, so the icons beside it never move. */
.resume-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding: 0.75rem 1.5rem;
    border-radius: 0.75rem;
    background-color: var(--accent-light);
    color: #ffffff;
    font-weight: 600;
    box-shadow: 0 2px 6px -2px rgba(79, 70, 229, 0.35);
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s ease, background-color 0.4s ease;
}

.resume-btn-label,
.resume-btn-icon {
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease;
}

.resume-btn-icon {
    position: absolute;
    right: 0.95rem;
    font-size: 0.8em;
    opacity: 0;
    transform: translateX(-6px);
}

.resume-btn:hover,
.resume-btn:focus-visible {
    background-color: var(--accent-hover-light);
    transform: translateY(-1px);
    box-shadow: 0 8px 16px -8px rgba(79, 70, 229, 0.5);
    outline: none;
}

.resume-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.3), 0 8px 16px -8px rgba(79, 70, 229, 0.5);
}

.resume-btn:hover .resume-btn-label,
.resume-btn:focus-visible .resume-btn-label {
    transform: translateX(-0.55rem);
}

.resume-btn:hover .resume-btn-icon,
.resume-btn:focus-visible .resume-btn-icon {
    opacity: 1;
    transform: none;
}

.resume-btn:active {
    transform: scale(0.98);
    transition-duration: 0.1s;
}

.dark .resume-btn {
    background-color: var(--accent-dark);
    box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.5);
}

.dark .resume-btn:hover,
.dark .resume-btn:focus-visible {
    background-color: var(--accent-hover-dark);
    box-shadow: 0 8px 16px -8px rgba(0, 0, 0, 0.6);
}

.dark .resume-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.35), 0 8px 16px -8px rgba(0, 0, 0, 0.6);
}

@media (prefers-reduced-motion: reduce) {
    .resume-btn:hover,
    .resume-btn:focus-visible,
    .resume-btn:active,
    .resume-btn:hover .resume-btn-label,
    .resume-btn:focus-visible .resume-btn-label {
        transform: none;
    }
}

.dark .themed-accent-bg-hover:hover {
    background-color: var(--accent-hover-dark);
}

.seminar-embed {
    width: min(100%, calc((100vh - 14rem) * 16 / 9.5));
    aspect-ratio: 16 / 9.5;
    margin: 0 auto;
    border-radius: 0.75rem;
    overflow: hidden;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

.seminar-embed iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: 0.75rem;
}

/* Loading placeholders (main.js adds the classes; only on slow connections, cached pictures skip it).
   A picture still downloading shows a soft shimmer sweeping across its space, then fades in once
   it arrives. Embedded slides (.seminar-embed) shimmer in their frame and fade in when ready.
   Reduced motion: a plain placeholder colour, no sweep or fade. */
:root {
    --skeleton-base: #e2e8f0;
    --skeleton-shine: #f8fafc;
}

.dark {
    --skeleton-base: #1e293b;
    --skeleton-shine: #334155;
}

img.is-loading,
.seminar-embed.is-loading {
    color: transparent;   /* hides alt text some browsers draw while waiting */
    background-color: var(--skeleton-base);
    background-image: linear-gradient(100deg, transparent 30%, var(--skeleton-shine) 50%, transparent 70%);
    background-size: 200% 100%;
    background-repeat: no-repeat;
    animation: skeleton-shimmer 1.4s ease-in-out infinite;
}

.seminar-embed iframe {
    transition: opacity 0.5s ease;
}

.seminar-embed.is-loading iframe {
    opacity: 0;
}

img.is-loaded {
    animation: image-fade-in 0.5s ease-out;
}

@keyframes skeleton-shimmer {
    from {
        background-position: 150% 0;
    }

    to {
        background-position: -50% 0;
    }
}

@keyframes image-fade-in {
    from {
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    img.is-loading,
    .seminar-embed.is-loading,
    img.is-loaded {
        animation: none;
    }

    .seminar-embed iframe {
        transition: none;
    }
}

/* Talks page: the type tag on each talk card (poster, seminar, talk series): a soft accent capsule
   with the label. */
.talk-tag {
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 0.95rem;
    border: 1px solid rgba(79, 70, 229, 0.18);
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.07);
    color: var(--accent-light);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1;
}

.dark .talk-tag {
    border-color: rgba(129, 140, 248, 0.25);
    background-color: rgba(129, 140, 248, 0.12);
    color: var(--accent-dark);
}

/* Talks page cards: one even padding (smaller on phones), a header with the type tag, the title and
   the details, then a thin rule before the poster / slides.
   Details (date, event, venue): each icon sits in a small accent square, lined up with the first
   line of its text so it stays in place when the text wraps. Side by side on wide screens, one per
   line on phones, a little smaller than body text so the title leads.
   Hover: the card's border turns accent with a soft shadow and the icon squares deepen. */
.talk-card {
    padding: 1.25rem;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 10px 28px -16px rgba(15, 23, 42, 0.14);
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

@media (min-width: 640px) {
    .talk-card {
        padding: 1.75rem;
    }
}

@media (min-width: 1024px) {
    .talk-card {
        padding: 2.25rem 2.5rem;
    }
}

.talk-card:hover {
    border-color: rgba(79, 70, 229, 0.35);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 18px 36px -18px rgba(79, 70, 229, 0.3);
}

.dark .talk-card:hover {
    border-color: rgba(129, 140, 248, 0.4);
    box-shadow: 0 18px 36px -18px rgba(0, 0, 0, 0.7);
}

.talk-head {
    margin-bottom: 1.5rem;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid var(--border-light);
}

.dark .talk-head {
    border-bottom-color: var(--border-dark);
}

.talk-head .talk-tag {
    margin-bottom: 0.9rem;
}

.talk-head h2 {
    margin-bottom: 1rem;
}

.talk-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1.75rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.9375rem;
    line-height: 1.45;
    color: var(--text-light-secondary);
}

.dark .talk-meta {
    color: var(--text-dark-secondary);
}

.talk-meta li {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    min-width: 0;
}

.talk-meta-icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 1.75rem;
    height: 1.75rem;
    margin-top: -0.2rem;   /* centres the square on the first line of text */
    border-radius: 0.5rem;
    background-color: rgba(79, 70, 229, 0.08);
    color: var(--accent-light);
    font-size: 0.8rem;
    transition: background-color 0.3s ease;
}

.talk-card:hover .talk-meta-icon {
    background-color: rgba(79, 70, 229, 0.14);
}

.dark .talk-meta-icon {
    background-color: rgba(129, 140, 248, 0.12);
    color: var(--accent-dark);
}

.dark .talk-card:hover .talk-meta-icon {
    background-color: rgba(129, 140, 248, 0.2);
}

@media (max-width: 639px) {
    .talk-head {
        margin-bottom: 1.25rem;
        padding-bottom: 1.25rem;
    }

    .talk-meta {
        flex-direction: column;
        gap: 0.65rem;
        font-size: 0.875rem;
    }

    .talk-meta-icon {
        width: 1.6rem;
        height: 1.6rem;
        margin-top: -0.18rem;
        font-size: 0.75rem;
    }
}

/* Blurry Header */
header {
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    background-color: rgba(255, 255, 255, 0.8);
    border-bottom-width: 1px;
    border-color: var(--border-light);
}

.dark header {
    background-color: rgba(15, 23, 42, 0.8);
    border-color: var(--border-dark);
}

/* Active nav link style */
header nav a.active {
    color: var(--accent-light);
    font-weight: 600;
}

.dark header nav a.active {
    color: var(--accent-dark);
}

/* Active nav link style - mobile*/
header nav a.active,
.mobile-nav a.active {
    color: var(--accent-light);
    font-weight: 600;
}

.dark header nav a.active,
.dark .mobile-nav a.active {
    color: var(--accent-dark);
}

/* Tablets (768-1023px): the header links sit 20px apart instead of 32px, so with Contact added the
   bar still leaves comfortable room after the name. ("header nav" outweighs Tailwind's space-x-8.) */
@media (min-width: 768px) and (max-width: 1023px) {
    header nav.space-x-8 > :not([hidden]) ~ :not([hidden]) {
        margin-left: 1.25rem;
        margin-right: 0;
    }
}

/* Mobile menu (phones and small tablets): a panel that drops down over the page from under the header.
   It is light on older phones: it never changes the header's height or pushes the page (the header
   has a background blur, which is costly to redraw every frame), and only opacity and transform are
   animated, which the phone's GPU handles. Opening: the panel fades in sliding down 8px and its links
   follow one after another; closing is a quicker fade back up. visibility switches off only once the
   closing fade has finished, so the hidden panel cannot be tapped. */
.mobile-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    box-shadow: 0 18px 32px -18px rgba(15, 23, 42, 0.3);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-8px);
    will-change: opacity, transform;
    transition: opacity 0.16s ease-in, transform 0.16s ease-in, visibility 0s linear 0.16s;
}

.mobile-nav.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: none;
    transition: opacity 0.22s ease-out, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
}

.dark .mobile-nav {
    box-shadow: 0 18px 32px -18px rgba(0, 0, 0, 0.7);
}

/* the links slide in one after another (the TA course list keeps its own open/close animation) */
.mobile-nav > div > :not(.mobile-ta-submenu) {
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, transform 0.15s ease;
}

.mobile-nav.is-open > div > :not(.mobile-ta-submenu) {
    opacity: 1;
    transform: none;
    transition: opacity 0.25s ease-out, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.mobile-nav.is-open > div > :nth-child(2) { transition-delay: 0.03s; }
.mobile-nav.is-open > div > :nth-child(3) { transition-delay: 0.06s; }
.mobile-nav.is-open > div > :nth-child(4) { transition-delay: 0.09s; }
.mobile-nav.is-open > div > :nth-child(n + 5) { transition-delay: 0.12s; }

/* menu button: the bars and the cross sit on top of each other and cross-fade with a quarter turn */
.mobile-menu-button {
    display: grid;
    place-items: center;
}

.mobile-menu-button > i {
    grid-area: 1 / 1;
    transition: opacity 0.2s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

#mobile-menu-close-icon,
.mobile-menu-button.is-open #mobile-menu-open-icon {
    opacity: 0;
    transform: rotate(-90deg) scale(0.8);
}

.mobile-menu-button.is-open #mobile-menu-open-icon {
    transform: rotate(90deg) scale(0.8);
}

.mobile-menu-button.is-open #mobile-menu-close-icon {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .mobile-nav,
    .mobile-nav > div > :not(.mobile-ta-submenu),
    .mobile-menu-button > i {
        transform: none !important;
    }
}

/* Keep the header steady while a page loads: the theme toggle shows the moon (light mode) or the
   sun (dark mode) straight from the 'dark' class set in <head>, so it has its full size at first paint */
#theme-icon-dark {
    display: inline-block;
}

#theme-icon-light,
.dark #theme-icon-dark {
    display: none;
}

.dark #theme-icon-light {
    display: inline-block;
}

/* ...and each desktop nav link reserves the width of its bold (active) label, carried in data-text,
   so marking the current page or section as active never nudges the links beside it */
header nav a[data-text] {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
}

header nav a[data-text]::before {
    content: attr(data-text);
    order: 1; /* after the label, so the link's text baseline stays on the label (keeps TA in line) */
    font-weight: 600;
    height: 0;
    overflow: hidden;
    visibility: hidden;
    pointer-events: none;
    user-select: none;
}

/* Header nav underline: grows from the centre on hover (faint) and stays under the current section (solid).
   Only the top-level desktop links carry data-nav, so dropdown and mobile links are unaffected. */
header nav a[data-nav] {
    position: relative;
}

header nav a[data-nav]::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -6px;
    height: 2px;
    border-radius: 9999px;
    background-color: var(--accent-light);
    opacity: 0;
    transform: scaleX(0);
    transform-origin: center;
    transition: transform 0.3s ease, opacity 0.3s ease;
}

.dark header nav a[data-nav]::after {
    background-color: var(--accent-dark);
}

header nav a[data-nav]:hover::after,
header nav a[data-nav]:focus-visible::after {
    opacity: 0.45;
    transform: scaleX(1);
}

header nav a[data-nav].active::after {
    opacity: 1;
    transform: scaleX(1);
}

.ta-dropdown-menu {
    opacity: 0;
    transform: translateY(0.5rem) scale(0.98);
    pointer-events: none;
    visibility: hidden;
    transition: opacity 180ms ease, transform 180ms ease;
}

.ta-dropdown::after {
    content: '';
    position: absolute;
    left: 0;
    top: 100%;
    width: 100%;
    height: 0.75rem;
}

.ta-dropdown.is-open .ta-dropdown-menu {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
    visibility: visible;
}

.ta-dropdown.is-open .ta-dropdown-trigger svg {
    transform: rotate(180deg);
}

.ta-dropdown-trigger svg {
    transition: transform 180ms ease;
}

.mobile-ta-submenu {
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transform: translateY(-0.25rem);
    transition: max-height 240ms ease, opacity 180ms ease, transform 180ms ease;
}

.mobile-ta-submenu.is-open {
    max-height: 22rem;
    opacity: 1;
    transform: translateY(0);
}

.mobile-ta-toggle svg {
    transition: transform 180ms ease;
}

.mobile-ta-toggle[aria-expanded="true"] svg {
    transform: rotate(180deg);
}

.ta-dropdown-menu a.is-active,
.ta-dropdown-menu a:hover,
.ta-dropdown-menu a:focus-visible {
    background-color: var(--bg-light-primary);
    color: var(--text-light-primary);
}

.dark .ta-dropdown-menu a.is-active,
.dark .ta-dropdown-menu a:hover,
.dark .ta-dropdown-menu a:focus-visible {
    background-color: var(--bg-dark-primary);
    color: var(--text-dark-primary);
}

.ta-dropdown-menu a.is-active span:first-child,
.ta-dropdown-menu a:hover span:first-child,
.ta-dropdown-menu a:focus-visible span:first-child,
.dark .ta-dropdown-menu a.is-active span:first-child,
.dark .ta-dropdown-menu a:hover span:first-child,
.dark .ta-dropdown-menu a:focus-visible span:first-child {
    opacity: 0.9;
}

/* Timeline styles (Background page): date | dot | card */
.timeline-item {
    position: relative;
    display: flex;
    align-items: flex-start;
    padding-bottom: 1.5rem;
}

.timeline-item:last-child {
    padding-bottom: 0;
}

/* line from this entry's dot down to the next one (the last entry has none) */
.timeline-item::before {
    content: '';
    position: absolute;
    left: 7rem;
    top: 1.875rem;
    width: 2px;
    height: 100%;
    border-radius: 9999px;
    background-color: rgba(79, 70, 229, 0.18);
    transform: translateX(-50%);
}

.timeline-item:last-child::before {
    display: none;
}

.dark .timeline-item::before {
    background-color: rgba(129, 140, 248, 0.22);
}

.timeline-date {
    width: 7rem;
    flex-shrink: 0;
    text-align: right;
    padding-right: 1.25rem;
    margin-top: 1.25rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-light-secondary);
    white-space: nowrap;
}

.dark .timeline-date {
    color: var(--text-dark-secondary);
}

/* past entries: hollow dot; current entries (.is-current, set by js/main.js): filled dot */
.timeline-dot {
    position: absolute;
    left: 7rem;
    top: 1.375rem;
    height: 1rem;
    width: 1rem;
    border-radius: 9999px;
    border: 2px solid var(--accent-light);
    background-color: var(--bg-light-secondary);
    transform: translateX(-50%);
    z-index: 10;
}

.dark .timeline-dot {
    border-color: var(--accent-dark);
    background-color: var(--bg-dark-secondary);
}

.timeline-item.is-current .timeline-dot {
    border-color: var(--bg-light-secondary);
    background-color: var(--accent-light);
}

.dark .timeline-item.is-current .timeline-dot {
    border-color: var(--bg-dark-secondary);
    background-color: var(--accent-dark);
}

.timeline-item.is-current .timeline-date {
    color: var(--accent-light);
    font-weight: 600;
}

.dark .timeline-item.is-current .timeline-date {
    color: var(--accent-dark);
}

/* each entry sits in a clean bordered card; the border picks up the accent on hover */
.timeline-content {
    flex-grow: 1;
    min-width: 0;
    margin-left: 1.5rem;
    padding: 1rem 1.15rem;
    border: 1px solid var(--border-light);
    border-radius: 1rem;
    background-color: var(--bg-light-secondary);
    transition: border-color 0.3s ease, transform 0.3s ease;
}

.timeline-item:hover .timeline-content {
    border-color: rgba(79, 70, 229, 0.4);
    transform: translateX(4px);
}

.dark .timeline-content {
    border-color: var(--border-dark);
    background-color: rgba(15, 23, 42, 0.55);
}

.dark .timeline-item:hover .timeline-content {
    border-color: rgba(129, 140, 248, 0.45);
}

/* "Current" tag after the title of entries that run to "Present" (added by js/main.js) */
.timeline-current {
    display: inline-flex;
    align-items: center;
    margin-left: 0.5rem;
    padding: 0.1rem 0.5rem;
    border-radius: 9999px;
    font-size: 0.65rem;
    font-weight: 700;
    line-height: 1rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    vertical-align: 0.15em;
    color: var(--accent-light);
    background-color: rgba(79, 70, 229, 0.1);
}

.dark .timeline-current {
    color: var(--accent-dark);
    background-color: rgba(129, 140, 248, 0.16);
}

/* Education / Experience headings with an icon badge */
.bg-heading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
}

.bg-heading-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 0.8rem;
    font-size: 1.05rem;
    color: var(--accent-light);
    background-color: rgba(79, 70, 229, 0.1);
}

.dark .bg-heading-icon {
    color: var(--accent-dark);
    background-color: rgba(129, 140, 248, 0.16);
}

/* outline button (Back to Portfolio, next to the solid Download CV button) */
.bg-secondary-btn {
    border: 1px solid var(--border-light);
    color: var(--text-light-primary);
    background-color: var(--bg-light-secondary);
}

.bg-secondary-btn:hover,
.bg-secondary-btn:focus-visible {
    border-color: var(--accent-light);
    color: var(--accent-light);
}

.dark .bg-secondary-btn {
    border-color: var(--border-dark);
    color: var(--text-dark-primary);
    background-color: transparent;
}

.dark .bg-secondary-btn:hover,
.dark .bg-secondary-btn:focus-visible {
    border-color: var(--accent-dark);
    color: var(--accent-dark);
}

/* Action buttons (Download CV, Back to Portfolio, the poster card's View Poster / Read the Paper):
   on hover the button lifts a little with a soft shadow and its icon nudges in the direction of the
   action (down for a download, left for back, up for open); it presses in on click. */
.action-btn {
    display: inline-block;
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.35s ease,
        background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
}

.action-btn-icon {
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

.action-btn:hover,
.action-btn:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 10px 18px -10px rgba(79, 70, 229, 0.55);
}

.action-btn:hover .action-btn-icon.is-down,
.action-btn:focus-visible .action-btn-icon.is-down {
    transform: translateY(2px);
}

.action-btn:hover .action-btn-icon.is-left,
.action-btn:focus-visible .action-btn-icon.is-left {
    transform: translateX(-3px);
}

.action-btn:hover .action-btn-icon.is-up,
.action-btn:focus-visible .action-btn-icon.is-up {
    transform: translateY(-2px);
}

.action-btn:active {
    transform: scale(0.98);
    transition-duration: 0.1s;
}

.action-btn-outline:hover,
.action-btn-outline:focus-visible {
    border-color: var(--accent-light);
    color: var(--accent-light);
}

.dark .action-btn:hover,
.dark .action-btn:focus-visible {
    box-shadow: 0 10px 18px -10px rgba(0, 0, 0, 0.7);
}

.dark .action-btn-outline:hover,
.dark .action-btn-outline:focus-visible {
    border-color: var(--accent-dark);
    color: var(--accent-dark);
}

@media (prefers-reduced-motion: reduce) {
    .action-btn:hover,
    .action-btn:focus-visible,
    .action-btn:active,
    .action-btn:hover .action-btn-icon,
    .action-btn:focus-visible .action-btn-icon {
        transform: none;
    }
}

/* Background page: Education and Experience side by side from laptop width (1024px) up. On tablets
   two columns, each with its own date column, left cards too narrow for their titles, so they
   stack there as on phones. ".grid" makes this win over Tailwind's grid-cols-1, which loads later. */
@media (min-width: 1024px) {
    .grid.bg-columns {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* phones: date moves above its card and the timeline runs down the left edge */
@media (max-width: 640px) {
    .timeline-item {
        flex-direction: column;
        padding-left: 1.75rem;
    }

    .timeline-item::before {
        left: 0.5rem;
        top: 0.65rem;
    }

    .timeline-dot {
        left: 0.5rem;
        top: 0.15rem;
    }

    .timeline-date {
        width: auto;
        margin: 0 0 0.5rem;
        padding-right: 0;
        text-align: left;
    }

    .timeline-content {
        width: 100%;
        margin-left: 0;
    }
}

/* Institution logos on the Background timeline: a small white tile beside each entry's text */
.timeline-content.has-logo {
    display: grid;
    grid-template-columns: 2.75rem minmax(0, 1fr);
    column-gap: 0.9rem;
    align-items: start;
}

.timeline-content.has-logo > .inst-logo {
    grid-row: span 6;   /* tile sits in its own column beside the title and every line under it */
}

.inst-logo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0.3rem;
    border: 1px solid var(--border-light);
    border-radius: 0.75rem;
    background-color: #ffffff;   /* logos are drawn for a white background, in both themes */
}

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

.dark .inst-logo {
    border-color: var(--border-dark);
}

@media (max-width: 480px) {
    .timeline-content.has-logo {
        grid-template-columns: 2.25rem minmax(0, 1fr);
        column-gap: 0.7rem;
    }

    .inst-logo {
        width: 2.25rem;
        height: 2.25rem;
        padding: 0.2rem;
        border-radius: 0.6rem;
    }
}

/* Home: affiliations row under the bio (logo tile + two-line label per affiliation) */
.affiliations {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 1rem 1.25rem;
}

@media (min-width: 768px) {
    .affiliations {
        justify-content: flex-start;
    }
}

.affiliation {
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    text-align: left;
    border-radius: 0.75rem;
}

.affiliation .inst-logo {
    flex-shrink: 0;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.affiliation:hover .inst-logo,
.affiliation:focus-visible .inst-logo {
    transform: translateY(-2px);
    box-shadow: 0 6px 14px -4px rgba(15, 23, 42, 0.25);
}

.affiliation-text {
    display: flex;
    flex-direction: column;
    font-size: 0.875rem;
    line-height: 1.3;
}

.affiliation-name {
    font-weight: 600;
    transition: color 0.3s ease;
}

.affiliation:hover .affiliation-name,
.affiliation:focus-visible .affiliation-name {
    color: var(--accent-light);
}

.dark .affiliation:hover .affiliation-name,
.dark .affiliation:focus-visible .affiliation-name {
    color: var(--accent-dark);
}

.affiliation-divider {
    width: 1px;
    height: 2rem;
    background-color: var(--border-light);
}

.dark .affiliation-divider {
    background-color: var(--border-dark);
}

/* phones: one affiliation per line, stacked as a centred block with the logos lined up on the left */
@media (max-width: 639px) {
    .affiliations {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.75rem;
        width: fit-content;
        margin-left: auto;
        margin-right: auto;
    }

    .affiliation-divider {
        display: none;
    }
}

/* Animation for sections on scroll */
.scroll-animate {
    opacity: 0;
    transform: translateY(30px);
    transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}

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

/* the page behind an open popup stays put (main.js adds the class); the gutter keeps the
   scrollbar's width reserved so the page does not shift sideways when it is hidden */
html.modal-open {
    overflow: hidden;
    scrollbar-gutter: stable;
}

/* Modal styles (course details, BibTeX citation).
   Hidden with visibility + opacity rather than display: none, because a transition cannot run
   from display: none; visibility flips only once the closing fade has finished.
   Opening (about 0.4s): the dimmed, blurred backdrop fades in, the box grows out of the spot that
   was clicked (--modal-origin, set by setModalOrigin in main.js) and its rows follow just behind.
   Only opacity and transform are animated, and the backdrop blur stays a fixed size (it fades with
   the backdrop), so the browser can run it all on the GPU without dropping frames.
   Closing is a quick fade and shrink. */
.modal {
    position: fixed;
    z-index: 50;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    overscroll-behavior: contain;   /* scrolling a tall popup never carries on to the page */
    background-color: rgba(15, 23, 42, 0.55);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.15s ease-in, visibility 0s linear 0.15s;
}

.modal.show {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 0.25s ease-out, visibility 0s;
}

.modal-content {
    position: relative;
    margin: 5% auto;
    width: 90%;
    max-width: 43.75rem;   /* 700px at the default size; scales with the screen tiers */
    opacity: 0;
    transform: translateY(10px) scale(0.94);
    transform-origin: var(--modal-origin, 50% 50%);
    will-change: transform, opacity;
    transition: opacity 0.15s ease-in, transform 0.15s ease-in;
}

/* popups (course, BibTeX): same 1rem corners as the course cards instead of Tailwind's rounded-lg;
   the buttons inside use 0.75rem, like the BibTeX text box, so they keep the same rounded feel */
.modal .modal-content {
    border-radius: 1rem;
}

.modal .modal-content .themed-accent-bg {
    border-radius: 0.75rem;
}

/* popup close button (top right): on hover or keyboard focus a soft accent circle appears behind
   the cross, which turns accent-coloured; it presses in when clicked */
.modal-close {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.25rem;
    height: 2.25rem;
    margin: -0.25rem -0.5rem -0.25rem 0;   /* the circle grows into the padding, so the title row keeps its height */
    border-radius: 999px;
    transition: background-color 0.2s ease, color 0.2s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.modal-close svg {
    width: 1.25rem;
    height: 1.25rem;
}

.modal-close:hover,
.modal-close:focus-visible {
    background-color: rgba(79, 70, 229, 0.1);
    color: var(--accent-light);
    outline: none;
}

.modal-close:focus-visible {
    box-shadow: 0 0 0 2px var(--accent-light);
}

.modal-close:active {
    transform: scale(0.88);
}

.dark .modal-close:hover,
.dark .modal-close:focus-visible {
    background-color: rgba(129, 140, 248, 0.15);
    color: var(--accent-dark);
}

.dark .modal-close:focus-visible {
    box-shadow: 0 0 0 2px var(--accent-dark);
}

@media (prefers-reduced-motion: reduce) {
    .modal-close:active {
        transform: none;
    }
}

.modal.show .modal-content {
    opacity: 1;
    transform: none;
    transition: opacity 0.2s ease-out, transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

/* contents: the rows fade up just behind the box, then the topics in quick succession */
.modal.show .modal-content > * {
    animation: modal-item-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.modal.show .modal-content > :nth-child(1) { animation-delay: 0.04s; }
.modal.show .modal-content > :nth-child(2) { animation-delay: 0.07s; }
.modal.show .modal-content > :nth-child(3) { animation-delay: 0.1s; }
.modal.show .modal-content > :nth-child(4) { animation-delay: 0.13s; }
.modal.show .modal-content > :nth-child(n + 5) { animation-delay: 0.16s; }

.modal.show #modal-topics li {
    animation: modal-item-in 0.25s cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: calc(0.1s + var(--i, 0) * 0.02s);
}

@keyframes modal-item-in {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
}

@media (prefers-reduced-motion: reduce) {
    .modal-content,
    .modal.show .modal-content {
        transform: none;
    }

    .modal.show .modal-content > *,
    .modal.show #modal-topics li {
        animation: none;
    }
}

/* popup buttons sit side by side; on a phone they wrap one under the other, 0.875rem apart */
.button-container {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    margin-top: 1.5rem;
    /* mt-6 */
    margin-bottom: 1rem;
    /* mb-4 */
    gap: 0.875rem 1rem;
}

/* Course popup buttons (View Coding Tutorials, View Full Course Page): a leading icon, the label
   and an arrow in a small circle. On hover the button lifts with a soft accent shadow, the icon
   tilts and the arrow slides forward (up-right for another site,
   .is-external set in main.js); it presses in when clicked. The tutorials button is a soft accent
   tint, the course page button solid accent, so the main action stands out. */
.modal-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.5rem 0.5rem 1.1rem;
    border: 1px solid transparent;
    border-radius: 0.75rem;
    font-weight: 600;
    font-size: 0.95rem;
    line-height: 1.25;
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s ease,
        background-color 0.3s ease, border-color 0.3s ease;
}

.modal-btn-icon {
    font-size: 0.95em;
    transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.modal-btn-arrow {
    display: grid;
    place-items: center;
    width: 1.85rem;
    height: 1.85rem;
    border-radius: 0.55rem;
    font-size: 0.75rem;
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), background-color 0.3s ease;
}

.modal-btn.is-external .modal-btn-arrow i {
    transform: rotate(-45deg);
}

.modal-btn:hover,
.modal-btn:focus-visible {
    transform: translateY(-2px);
    outline: none;
}

.modal-btn:hover .modal-btn-icon,
.modal-btn:focus-visible .modal-btn-icon {
    transform: rotate(-8deg) scale(1.15);
}

.modal-btn:hover .modal-btn-arrow,
.modal-btn:focus-visible .modal-btn-arrow {
    transform: translateX(3px);
}

.modal-btn.is-external:hover .modal-btn-arrow,
.modal-btn.is-external:focus-visible .modal-btn-arrow {
    transform: translate(2px, -2px);
}

.modal-btn:active {
    transform: translateY(0) scale(0.97);
    transition-duration: 0.1s;
}

/* soft: accent tint, like the "View all" links */
.modal-btn-soft {
    border-color: rgba(79, 70, 229, 0.22);
    background-color: rgba(79, 70, 229, 0.07);
    color: var(--accent-light);
}

.modal-btn-soft .modal-btn-arrow {
    background-color: var(--accent-light);
    color: #ffffff;
}

.modal-btn-soft:hover,
.modal-btn-soft:focus-visible {
    border-color: rgba(79, 70, 229, 0.45);
    background-color: rgba(79, 70, 229, 0.12);
    box-shadow: 0 10px 20px -12px rgba(79, 70, 229, 0.6);
}

/* solid: filled accent */
.modal-btn-solid {
    background-color: var(--accent-light);
    color: #ffffff;
    box-shadow: 0 2px 6px -2px rgba(79, 70, 229, 0.4);
}

.modal-btn-solid .modal-btn-arrow {
    background-color: rgba(255, 255, 255, 0.18);
}

.modal-btn-solid:hover,
.modal-btn-solid:focus-visible {
    background-color: var(--accent-hover-light);
    box-shadow: 0 12px 22px -10px rgba(79, 70, 229, 0.65);
}

.modal-btn-solid:hover .modal-btn-arrow,
.modal-btn-solid:focus-visible .modal-btn-arrow {
    background-color: rgba(255, 255, 255, 0.28);
}

.modal-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.35), 0 10px 20px -12px rgba(79, 70, 229, 0.6);
}

.dark .modal-btn-soft {
    border-color: rgba(129, 140, 248, 0.28);
    background-color: rgba(129, 140, 248, 0.1);
    color: var(--accent-dark);
}

.dark .modal-btn-soft .modal-btn-arrow {
    background-color: var(--accent-dark);
    color: #0f172a;
}

.dark .modal-btn-soft:hover,
.dark .modal-btn-soft:focus-visible {
    border-color: rgba(129, 140, 248, 0.5);
    background-color: rgba(129, 140, 248, 0.17);
    box-shadow: 0 10px 20px -12px rgba(0, 0, 0, 0.7);
}

.dark .modal-btn-solid {
    background-color: var(--accent-dark);
    color: #0f172a;
    box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.5);
}

.dark .modal-btn-solid .modal-btn-arrow {
    background-color: rgba(15, 23, 42, 0.12);
}

.dark .modal-btn-solid:hover,
.dark .modal-btn-solid:focus-visible {
    background-color: var(--accent-hover-dark);
    box-shadow: 0 12px 22px -10px rgba(0, 0, 0, 0.7);
}

.dark .modal-btn-solid:hover .modal-btn-arrow,
.dark .modal-btn-solid:focus-visible .modal-btn-arrow {
    background-color: rgba(15, 23, 42, 0.2);
}

.dark .modal-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.4), 0 10px 20px -12px rgba(0, 0, 0, 0.7);
}

@media (prefers-reduced-motion: reduce) {
    .modal-btn:hover,
    .modal-btn:focus-visible,
    .modal-btn:active,
    .modal-btn:hover .modal-btn-icon,
    .modal-btn:focus-visible .modal-btn-icon,
    .modal-btn:hover .modal-btn-arrow,
    .modal-btn:focus-visible .modal-btn-arrow,
    .modal-btn.is-external:hover .modal-btn-arrow,
    .modal-btn.is-external:focus-visible .modal-btn-arrow {
        transform: none;
    }
}

.hidden {
    display: none;
}

.row-col-list {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 0.5rem;
    list-style-position: outside;
    padding-left: 10px;
    max-width: 56.25rem;   /* 900px */
}

.row-col-list li {
    margin: 0.06rem 0;
}

.inline-bullets {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem 1rem;
    font-size: 1rem;
}

.inline-bullets li {
    position: relative;
    padding-left: 1.2em;
}

.inline-bullets li::before {
    content: "•";
    position: absolute;
    left: 0;
    font-weight: bold;
    font-size: 1.2em;
}

/* Section alternate background.
   Only a very faint tint is layered over the base colour, so the background
   contours show through almost equally in both kinds of section. */
#main-content > section:nth-child(even) {
    background-color: rgba(30, 90, 160, 0.03);
    /* ≈ slate-50 over white */
}

.dark #main-content > section:nth-child(even) {
    background-color: transparent;
}

.dark #main-content > section:nth-child(odd) {
    background-color: rgba(148, 163, 184, 0.1);
    /* ≈ slate-800 over slate-900 */
}

/* Full pages (Talks, Background, courses) */
.course-page.themed-bg-secondary,
.fullscreen-page.themed-bg-secondary {
    background-color: transparent;
}

.dark .course-page.themed-bg-secondary,
.dark .fullscreen-page.themed-bg-secondary {
    background-color: rgba(148, 163, 184, 0.1);
}

/* Scroll to Top Button: slides up into view once the page is scrolled (main.js adds .show).
   On hover it lifts a little, deepens and gains a soft accent glow, and the chevron nudges up;
   it presses in when clicked. */
#scroll-to-top {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.3s, transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), background-color 0.3s ease,
        box-shadow 0.3s ease;
    pointer-events: none;
}

#scroll-to-top svg {
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

#scroll-to-top.show {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

#scroll-to-top.show:hover,
#scroll-to-top.show:focus-visible {
    opacity: 1;   /* overrides the hover:opacity-90 in the markup */
    transform: translateY(-3px);
    background-color: var(--accent-hover-light);
    box-shadow: 0 12px 24px -10px rgba(79, 70, 229, 0.7);
    outline: none;
}

#scroll-to-top.show:focus-visible {
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.35), 0 12px 24px -10px rgba(79, 70, 229, 0.7);
}

#scroll-to-top:hover svg,
#scroll-to-top:focus-visible svg {
    transform: translateY(-2px);
}

#scroll-to-top.show:active {
    transform: translateY(0) scale(0.92);
    transition-duration: 0.1s;
}

.dark #scroll-to-top.show:hover,
.dark #scroll-to-top.show:focus-visible {
    background-color: var(--accent-hover-dark);
    box-shadow: 0 12px 24px -10px rgba(0, 0, 0, 0.7);
}

.dark #scroll-to-top.show:focus-visible {
    box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.4), 0 12px 24px -10px rgba(0, 0, 0, 0.7);
}

@media (prefers-reduced-motion: reduce) {
    #scroll-to-top.show:hover,
    #scroll-to-top.show:focus-visible,
    #scroll-to-top.show:active,
    #scroll-to-top:hover svg,
    #scroll-to-top:focus-visible svg {
        transform: none;
    }
}

/* Hidden News Items Logic */
.hidden-news-item {
    display: none !important;
}

/* "Older news" toggle under the news list: a thin line that fades in from both edges, with a pill
   button in the middle (label, count of older items, chevron that flips when the list is open) */
.news-more {
    display: flex;
    align-items: center;
    gap: 1rem;
    max-width: 64rem;
    margin: 2.25rem auto 0;
}

.news-more[hidden] {
    display: none;
}

.news-more::before,
.news-more::after {
    content: '';
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--border-light));
}

.news-more::after {
    background: linear-gradient(90deg, var(--border-light), transparent);
}

.news-more-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.45rem 0.6rem 0.45rem 1rem;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background-color: var(--bg-light-secondary);
    color: var(--text-light-secondary);
    font-size: 0.875rem;
    font-weight: 600;
    transition: color 0.3s ease, border-color 0.3s ease, background-color 0.3s ease, box-shadow 0.3s ease;
}

.news-more-count {
    min-width: 1.4rem;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background-color: rgba(100, 116, 139, 0.12);
    font-size: 0.72rem;
    text-align: center;
    transition: background-color 0.3s ease, color 0.3s ease;
}

.news-more-count[hidden] {
    display: none;
}

.news-more-chevron {
    width: 1rem;
    height: 1rem;
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.news-more-btn[aria-expanded="true"] .news-more-chevron {
    transform: rotate(180deg);
}

.news-more-btn:hover,
.news-more-btn:focus-visible {
    border-color: rgba(79, 70, 229, 0.45);
    color: var(--accent-light);
    box-shadow: 0 8px 18px -12px rgba(79, 70, 229, 0.5);
    outline: none;
}

.news-more-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.25);
}

.news-more-btn:hover .news-more-count,
.news-more-btn:focus-visible .news-more-count {
    background-color: rgba(79, 70, 229, 0.12);
}

.dark .news-more::before {
    background: linear-gradient(90deg, transparent, var(--border-dark));
}

.dark .news-more::after {
    background: linear-gradient(90deg, var(--border-dark), transparent);
}

.dark .news-more-btn {
    border-color: var(--border-dark);
    background-color: var(--bg-dark-secondary);
    color: var(--text-dark-secondary);
}

.dark .news-more-count {
    background-color: rgba(148, 163, 184, 0.15);
}

.dark .news-more-btn:hover,
.dark .news-more-btn:focus-visible {
    border-color: rgba(129, 140, 248, 0.5);
    color: var(--accent-dark);
    box-shadow: 0 8px 18px -12px rgba(0, 0, 0, 0.6);
}

.dark .news-more-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.3);
}

.dark .news-more-btn:hover .news-more-count,
.dark .news-more-btn:focus-visible .news-more-count {
    background-color: rgba(129, 140, 248, 0.16);
}

/* ---------- Publication cards (publications.html) ---------- */
.pub-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding: 1.5rem 1.5rem 1.5rem 1.75rem;
    border: 1px solid var(--border-light);
    border-radius: 1rem;
    background-color: var(--bg-light-secondary);
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.05);
    overflow: hidden;
    transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}

/* accent strip down the left edge, in the same indigo -> sky colours as the background glow */
.pub-card::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 4px;
    background: linear-gradient(180deg, var(--accent-light), #38bdf8);
}

.pub-card:hover {
    transform: translateY(-4px);
    border-color: rgba(79, 70, 229, 0.35);
    box-shadow: 0 20px 40px -16px rgba(79, 70, 229, 0.28);
}

.dark .pub-card {
    border-color: var(--border-dark);
    background-color: var(--bg-dark-secondary);
    box-shadow: none;
}

.dark .pub-card::before {
    background: linear-gradient(180deg, var(--accent-dark), #38bdf8);
}

.dark .pub-card:hover {
    border-color: rgba(129, 140, 248, 0.45);
    box-shadow: 0 20px 40px -16px rgba(2, 6, 23, 0.7);
}

@media (min-width: 768px) {
    .pub-card {
        flex-direction: row;
        align-items: center;   /* figure sits in the vertical middle of the card */
        gap: 2rem;
        padding: 1.75rem 2rem 1.75rem 2.25rem;
    }
}

.pub-figure {
    display: block;
    flex-shrink: 0;
    width: 100%;
    max-width: 28rem;
    margin: 0 auto;
    padding: 0.75rem;
    border: 1px solid var(--border-light);
    border-radius: 0.75rem;
    background-color: #ffffff;   /* paper figures are drawn on white, in both themes */
    overflow: hidden;
}

.dark .pub-figure {
    border-color: var(--border-dark);
}

@media (min-width: 768px) {
    .pub-figure {
        width: 20rem;
        margin: 0;
    }
}

.pub-figure img {
    display: block;
    width: 100%;
    height: auto;
    transition: transform 0.4s ease;
}

.pub-card:hover .pub-figure img {
    transform: scale(1.04);
}

.pub-venue {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.8rem;
    border-radius: 9999px;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent-light);
    background-color: rgba(79, 70, 229, 0.08);
}

.pub-venue::before {
    content: '';
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 9999px;
    background-color: currentColor;
}

.dark .pub-venue {
    color: var(--accent-dark);
    background-color: rgba(129, 140, 248, 0.14);
}

/* a long venue name can carry a short form (pub-venue-full / pub-venue-short) for phones */
.pub-venue-short {
    display: none;
}

/* phones: keep the venue on one line */
@media (max-width: 480px) {
    .pub-venue {
        font-size: 0.68rem;
        letter-spacing: 0.03em;
        padding: 0.35rem 0.7rem;
    }

    .pub-venue-full {
        display: none;
    }

    .pub-venue-short {
        display: inline;
    }
}

.pub-date {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-light-secondary);
    white-space: nowrap;
}

.dark .pub-date {
    color: var(--text-dark-secondary);
}

/* Paper / Poster / Code / Cite buttons */
.pub-link {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.4rem 0.95rem;
    border: 1px solid var(--border-light);
    border-radius: 9999px;
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.25rem;
    color: var(--accent-light);
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
}

.pub-link:hover,
.pub-link:focus-visible {
    border-color: var(--accent-light);
    background-color: var(--accent-light);
    color: #ffffff;
    transform: translateY(-1px);
}

.dark .pub-link {
    border-color: var(--border-dark);
    color: var(--accent-dark);
}

.dark .pub-link:hover,
.dark .pub-link:focus-visible {
    border-color: var(--accent-dark);
    background-color: var(--accent-dark);
    color: var(--bg-dark-primary);
}

.pub-link.is-disabled,
.pub-link.is-disabled:hover {
    border-style: dashed;
    border-color: var(--border-light);
    background-color: transparent;
    color: var(--text-light-secondary);
    cursor: default;
    transform: none;
}

.dark .pub-link.is-disabled,
.dark .pub-link.is-disabled:hover {
    border-color: var(--border-dark);
    color: var(--text-dark-secondary);
}

/* ---------- "New" tag on recent news (added by js/main.js to items from the last 30 days) ---------- */
.news-new-tag {
    display: inline-flex;
    align-items: center;
    margin-left: 0.6rem;
    vertical-align: 0.1em;
    padding: 0.15rem 0.55rem;
    border-radius: 9999px;
    font-size: 0.625rem;
    font-weight: 700;
    line-height: 1rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: #ffffff;
    background-color: var(--accent-light);
    box-shadow: 0 4px 12px -4px rgba(79, 70, 229, 0.6);
    /* the shimmer is a double-width gradient behind the text that slides back and forth: moving it
       with transform is done by the graphics chip, without repainting the tag on every frame */
    position: relative;
    overflow: hidden;
    isolation: isolate;
}

.news-new-tag::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 200%;
    z-index: -1;
    background-image: linear-gradient(120deg, var(--accent-light), #0ea5e9, var(--accent-light));
    animation: news-new-shimmer 5s ease-in-out infinite;
}

.dark .news-new-tag {
    color: #0f172a;
    background-color: var(--accent-dark);
    box-shadow: 0 4px 14px -4px rgba(129, 140, 248, 0.55);
}

.dark .news-new-tag::before {
    background-image: linear-gradient(120deg, var(--accent-dark), #38bdf8, var(--accent-dark));
}

/* the timeline line of a new item picks up the accent colour */
.news-item.is-new > div:last-child {
    border-color: var(--accent-light);
}

.dark .news-item.is-new > div:last-child {
    border-color: var(--accent-dark);
}

@keyframes news-new-shimmer {
    0%, 100% { transform: translateX(0); }
    50% { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
    .news-new-tag::before {
        animation: none;
    }
}

.publication-meta-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.75rem;
}

.citation-pre {
    white-space: pre-wrap;
    word-break: break-word;
    font-family: "Courier New", monospace;
    font-size: 0.9rem;
    line-height: 1.5;
    border: 1px solid var(--border-light);
    border-radius: 0.75rem;
    padding: 1rem;
    background-color: #f8fafc;
}

.dark .citation-pre {
    border-color: var(--border-dark);
    background-color: #0f172a;
}

/* ---------- Citation In-Modal Toast Status (bottom-left corner of popup) ---------- */
#cite-copy-status {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.25rem;
    min-height: 2.25rem;
    padding: 0.35rem 0.85rem;
    border-radius: 9999px;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1),
                transform 0.25s cubic-bezier(0.16, 1, 0.3, 1),
                background-color 0.25s ease,
                border-color 0.25s ease,
                color 0.25s ease;
    pointer-events: none;
    background: transparent;
    border: 1px solid transparent;
}

#cite-copy-status.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

#cite-copy-status.is-success {
    color: #065f46; /* emerald-800 */
    background-color: #ecfdf5; /* emerald-50 */
    border-color: #a7f3d0; /* emerald-200 */
    box-shadow: 0 2px 8px -2px rgba(16, 185, 129, 0.25);
}

.dark #cite-copy-status.is-success {
    color: #6ee7b7; /* emerald-300 */
    background-color: rgba(6, 78, 59, 0.4); /* emerald-900/40 */
    border-color: rgba(52, 211, 153, 0.35);
    box-shadow: 0 2px 10px -2px rgba(0, 0, 0, 0.4), 0 0 12px rgba(16, 185, 129, 0.15);
}

#cite-copy-status.is-error {
    color: #991b1b; /* red-800 */
    background-color: #fef2f2; /* red-50 */
    border-color: #fecaca; /* red-200 */
}

.dark #cite-copy-status.is-error {
    color: #fca5a5; /* red-300 */
    background-color: rgba(127, 29, 29, 0.4);
    border-color: rgba(248, 113, 113, 0.35);
}

#cite-copy-status.is-pending {
    color: var(--text-light-secondary);
    background-color: rgba(0, 0, 0, 0.04);
    border-color: var(--border-light);
}

.dark #cite-copy-status.is-pending {
    color: var(--text-dark-secondary);
    background-color: rgba(255, 255, 255, 0.05);
    border-color: var(--border-dark);
}

/* Toast pop animation on trigger */
#cite-copy-status.toast-pop {
    animation: cite-status-pop 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes cite-status-pop {
    0% {
        opacity: 0;
        transform: translateY(6px) scale(0.96);
    }
    100% {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

@media (prefers-reduced-motion: reduce) {
    #cite-copy-status {
        transition: opacity 0.15s ease-out;
        transform: none !important;
    }
    #cite-copy-status.toast-pop {
        animation: none;
    }
}

/* Social icons (home bio, footer "Connect"). Hover or keyboard focus: the icon turns accent (white
   in the always-dark footer), lifts 2px and grows a little with a gentle spring, while a soft accent
   circle (::after) fades and grows in behind it; a click presses it in. The link itself never scales,
   so the tooltip (::before) keeps its size. Only colour, opacity and transform animate, so it stays
   smooth on phones too. */
.social-icon-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: color 0.25s ease;
}

.social-icon-link > i,
.social-icon-link > svg {
    transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Font Awesome's fa-lg sets line-height 0.05em; inside these flex links that made the icon (and the
   link) about 1px tall. A normal line-height gives each link the real size of its icon. */
.social-icon-link > i {
    line-height: 1;
}

.social-icon-link {
    --halo: 2.5rem;   /* size of the hover circle; the tooltip keeps 8px clear of it */
}

.social-icon-link::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    z-index: -1;
    width: var(--halo);
    height: var(--halo);
    margin: calc(var(--halo) / -2) 0 0 calc(var(--halo) / -2);
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.1);
    opacity: 0;
    transform: scale(0.6);
    pointer-events: none;
    transition: opacity 0.3s ease, transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.social-icon-link:hover,
.social-icon-link:focus-visible {
    color: var(--accent-light);
    outline: none;
}

.social-icon-link:hover::after,
.social-icon-link:focus-visible::after {
    opacity: 1;
    transform: scale(1);
}

.social-icon-link:focus-visible::after {
    box-shadow: 0 0 0 2px var(--accent-light);
}

.social-icon-link:hover > i,
.social-icon-link:hover > svg,
.social-icon-link:focus-visible > i,
.social-icon-link:focus-visible > svg {
    transform: translateY(-2px) scale(1.1);
}

.social-icon-link:active > i,
.social-icon-link:active > svg {
    transform: scale(0.92);
    transition-duration: 0.1s;
}

.dark .social-icon-link:hover,
.dark .social-icon-link:focus-visible {
    color: var(--accent-dark);
}

.dark .social-icon-link::after {
    background-color: rgba(129, 140, 248, 0.15);
}

.dark .social-icon-link:focus-visible::after {
    box-shadow: 0 0 0 2px var(--accent-dark);
}

/* footer: always dark, so the icon turns white over a soft indigo circle in both themes */
footer .social-icon-link:hover,
footer .social-icon-link:focus-visible,
.dark footer .social-icon-link:hover,
.dark footer .social-icon-link:focus-visible {
    color: #ffffff;
}

footer .social-icon-link::after,
.dark footer .social-icon-link::after {
    background-color: rgba(129, 140, 248, 0.18);
}

footer .social-icon-link:focus-visible::after,
.dark footer .social-icon-link:focus-visible::after {
    box-shadow: 0 0 0 2px var(--accent-dark);
}

@media (prefers-reduced-motion: reduce) {
    .social-icon-link > i,
    .social-icon-link > svg,
    .social-icon-link::after {
        transform: none !important;
    }
}

.social-icon-link::before {
    position: absolute;
    left: 50%;
    opacity: 0;
    pointer-events: none;
    transform: translateX(-50%) translateY(4px);
    transition: opacity 0.18s ease, transform 0.18s ease;
    z-index: 20;
}

.social-icon-link::before {
    content: attr(data-tooltip);
    bottom: calc(50% + var(--halo) / 2 + 8px);   /* 8px above the hover circle */
    white-space: nowrap;
    padding: 0.45rem 0.7rem;
    border-radius: 0.55rem;
    background: rgba(15, 23, 42, 0.96);
    color: #f8fafc;
    font-size: 0.72rem;
    line-height: 1;
    font-weight: 600;
    letter-spacing: 0.01em;
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.22);
}

.social-icon-link:hover::before,
.social-icon-link:focus-visible::before {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

.dark .social-icon-link::before {
    background: rgba(226, 232, 240, 0.98);
    color: #0f172a;
}

/* Footer (always dark): the tooltip is light so it stands out, and it opens below the icon so it
   does not cover the "Connect" heading just above the row */
footer .social-icon-link::before {
    top: calc(50% + var(--halo) / 2 + 8px);      /* 8px below the hover circle */
    bottom: auto;
    transform: translateX(-50%) translateY(-4px);
    background: rgba(226, 232, 240, 0.98);
    color: #0f172a;
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);
}

footer .social-icon-link:hover::before,
footer .social-icon-link:focus-visible::before {
    transform: translateX(-50%) translateY(0);
}

/* Header buttons (search, theme switch): a small tooltip below the button, in the same style as the
   social-icon tooltips. Its text comes from data-tip (set in js/main.js). It shows after a short
   pause on mouse hover, and on keyboard focus; not on touch screens, where a tap would leave it on. */
#search-button,
#theme-toggle {
    position: relative;
}

#search-button::after,
#theme-toggle::after {
    content: attr(data-tip);
    position: absolute;
    top: calc(100% + 10px);
    left: 50%;
    z-index: 70;
    padding: 0.45rem 0.7rem;
    border-radius: 0.55rem;
    background: rgba(15, 23, 42, 0.96);
    color: #f8fafc;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1;
    white-space: nowrap;
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.22);
    opacity: 0;
    pointer-events: none;
    transform: translate(-50%, -4px);
    transition: opacity 0.18s ease, transform 0.18s ease;
}

/* the theme switch sits at the right edge of the header, so its tooltip lines up with its right side */
#theme-toggle::after {
    left: auto;
    right: 0;
    transform: translateY(-4px);
}

@media (hover: hover) {
    #search-button:hover::after,
    #theme-toggle:hover::after {
        opacity: 1;
        transform: translate(-50%, 0);
        transition-delay: 0.25s;
    }

    /* the button itself grows 10% on hover (hover:scale-110); shrink the tooltip back to normal size */
    #theme-toggle:hover::after {
        transform: scale(0.909);
        transform-origin: top right;
    }
}

#search-button:focus-visible:not(.focus-quiet)::after,
#theme-toggle:focus-visible::after {
    opacity: 1;
    transform: translate(-50%, 0);
}

#theme-toggle:focus-visible::after {
    transform: none;
}

.dark #search-button::after,
.dark #theme-toggle::after {
    background: rgba(226, 232, 240, 0.98);
    color: #0f172a;
}

/* =====================================================================
   Small UI details (each block is independent and can be removed on its own)
   ===================================================================== */

/* 1. Staggered reveal: cards and list items fade up one after another when their section appears.
      js/main.js adds .reveal-child and a --reveal-delay to each item. The animation only fills
      "backwards" (before it starts), so the cards' own hover lift keeps working afterwards. */
.scroll-animate.visible .reveal-child {
    animation: reveal-up 0.6s cubic-bezier(0.22, 0.61, 0.36, 1) var(--reveal-delay, 0ms) backwards;
}

@keyframes reveal-up {
    from {
        opacity: 0;
        transform: translateY(18px);
    }
}

/* 2. Header depth: a soft shadow appears under the sticky header once the page is scrolled */
header.is-scrolled {
    box-shadow: 0 8px 24px -14px rgba(15, 23, 42, 0.25);
}

.dark header.is-scrolled {
    box-shadow: 0 8px 24px -12px rgba(0, 0, 0, 0.7);
}

/* 3. Inline text links (and text-style buttons such as Cite): an accent underline slides in from the left on hover */
a.text-accent,
button.text-accent {
    text-decoration: none;
    background-image: linear-gradient(var(--accent-light), var(--accent-light));
    background-position: 0 100%;
    background-repeat: no-repeat;
    background-size: 0% 1.5px;
    transition: background-size 0.3s ease, color 0.2s ease;
}

.dark a.text-accent,
.dark button.text-accent {
    background-image: linear-gradient(var(--accent-dark), var(--accent-dark));
}

a.text-accent:hover,
a.text-accent:focus-visible,
button.text-accent:hover,
button.text-accent:focus-visible {
    text-decoration: none;
    background-size: 100% 1.5px;
}

/* 4. Research and TA cards: accent border on hover, alongside their existing lift */
#research .grid > div,
#TA .course-card {
    transition-property: transform, box-shadow, border-color;
}

/* Cards across the site share the course cards' 1rem corners (instead of Tailwind's rounded-lg):
   TA course cards (ta.html and the homepage), Research Interests (homepage) and talks (talks.html) */
#TA .course-card,
#research .grid > div,
#main-content .talk-card {
    border-radius: 1rem;   /* the id outweighs Tailwind's rounded-lg, which loads after this file */
}

#research .grid > div:hover,
#TA .course-card:hover {
    border-color: rgba(79, 70, 229, 0.45);
}

.dark #research .grid > div:hover,
.dark #TA .course-card:hover {
    border-color: rgba(129, 140, 248, 0.5);
}

/* Home: compact TA cards (.ta-feature) — code, title and instructor on the left; the term badge
   (top) and a round arrow (bottom) on the right. ta.html keeps the full-size cards; these open the
   same course modal (they are .course-card too). */
.ta-feature {
    display: flex;
    align-items: stretch;
    gap: 1rem;
    padding: 1.25rem 1.25rem 1.25rem 1.5rem;
    border: 1px solid var(--border-light);
    border-radius: 1rem;
    background-color: var(--bg-light-secondary);
    box-shadow: 0 4px 14px -6px rgba(15, 23, 42, 0.12);
    cursor: pointer;
    transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}

.ta-feature:hover {
    transform: translateY(-4px);
    box-shadow: 0 18px 30px -12px rgba(15, 23, 42, 0.25);
}

.dark .ta-feature {
    border-color: var(--border-dark);
    background-color: var(--bg-dark-secondary);
    box-shadow: 0 4px 14px -6px rgba(0, 0, 0, 0.4);
}

.dark .ta-feature:hover {
    box-shadow: 0 18px 30px -12px rgba(0, 0, 0, 0.6);
}

.ta-feature-body {
    flex: 1;
    min-width: 0;
}

.ta-feature-code {
    margin-bottom: 0.2rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: var(--text-light-secondary);
}

.dark .ta-feature-code {
    color: var(--text-dark-secondary);
}

.ta-feature-title {
    margin-bottom: 0.35rem;
    font-size: var(--fs-card-title);
    font-weight: 600;
    line-height: 1.35;
}

.ta-feature-aside {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: space-between;
    gap: 0.75rem;
    flex-shrink: 0;
}

.ta-feature-term {
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.09);
    color: var(--accent-light);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    white-space: nowrap;
}

.dark .ta-feature-term {
    background-color: rgba(129, 140, 248, 0.14);
    color: var(--accent-dark);
}

.ta-feature-go {
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    color: var(--accent-light);
    font-size: 0.85rem;
    transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease, transform 0.3s ease;
}

.dark .ta-feature-go {
    border-color: var(--border-dark);
    color: var(--accent-dark);
}

/* hover: the arrow fills with the accent and nudges forward */
.ta-feature:hover .ta-feature-go {
    background-color: var(--accent-light);
    border-color: var(--accent-light);
    color: #ffffff;
    transform: translateX(3px);
}

.dark .ta-feature:hover .ta-feature-go {
    background-color: var(--accent-dark);
    border-color: var(--accent-dark);
    color: #0f172a;
}

/* phones: the term badge moves under the instructor so the text gets the full card width */
@media (max-width: 480px) {
    .ta-feature {
        flex-direction: column;
        gap: 0.75rem;
        padding: 1rem 1rem 1rem 1.15rem;
    }

    .ta-feature-aside {
        align-items: flex-start;
    }

    /* the whole card is the button, so the arrow can go */
    .ta-feature-go {
        display: none;
    }
}

/* =====================================================================
   Course pages (rl.html, robot.html, aicps.html, amfai.html) — .cp-*
   Header (code badge, title, semester), a main column of numbered tutorial /
   assignment cards, and a sidebar with the course details, syllabus and official page link.
   ===================================================================== */
.cp-page {
    min-height: 100vh;
    padding: 3rem 0 5rem;
}

@media (min-width: 768px) {
    .cp-page {
        padding-top: 4.5rem;
    }
}

.cp-hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    margin-bottom: 2.75rem;
}

/* Separator under the course title (all course pages): a hairline that fades out at both ends with
   a short accent bar in the middle. As the page opens the line draws out from the centre and the
   bar grows in. */
.cp-hero::after {
    content: "";
    display: block;
    width: 100%;
    max-width: 56rem;
    height: 3px;
    margin-top: 2rem;
    background-image:
        linear-gradient(var(--accent-light), var(--accent-light)),
        linear-gradient(90deg, transparent, var(--border-light) 18%, var(--border-light) 82%, transparent);
    background-position: center, center;
    background-repeat: no-repeat;
    background-size: 3.5rem 3px, 100% 1px;
    animation: cp-separator-in 0.6s cubic-bezier(0.22, 0.61, 0.36, 1) 0.1s both;
}

.dark .cp-hero::after {
    background-image:
        linear-gradient(var(--accent-dark), var(--accent-dark)),
        linear-gradient(90deg, transparent, var(--border-dark) 18%, var(--border-dark) 82%, transparent);
}

@keyframes cp-separator-in {
    from {
        background-size: 0 3px, 0% 1px;
    }
}

@media (max-width: 639px) {
    .cp-hero {
        margin-bottom: 2rem;
    }

    .cp-hero::after {
        margin-top: 1.5rem;
        background-size: 2.75rem 3px, 100% 1px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .cp-hero::after {
        animation: none;
    }
}





.cp-code {
    padding: 0.3rem 0.9rem;
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.09);
    color: var(--accent-light);
    font-size: 0.875rem;
    font-weight: 700;
    letter-spacing: 0.08em;
}

.cp-title {
    max-width: 48rem;
    margin: 0.9rem 0 0.5rem;
    font-size: var(--fs-page-title);
    font-weight: 700;
    line-height: 1.2;
}

.cp-subtitle {
    font-size: 1.125rem;
}

/* two columns from 900px: content + sidebar */
.cp-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 2.5rem;
}

@media (min-width: 900px) {
    .cp-layout {
        grid-template-columns: minmax(0, 1fr) 21rem;
        align-items: start;
    }
}

.cp-section + .cp-section {
    margin-top: 2.75rem;
}

.cp-heading {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
    font-size: var(--fs-section);
    font-weight: 700;
}

.cp-heading-icon {
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 0.65rem;
    background-color: rgba(79, 70, 229, 0.09);
    color: var(--accent-light);
    font-size: 1rem;
}

.cp-count {
    padding: 0.1rem 0.6rem;
    border-radius: 999px;
    background-color: rgba(100, 116, 139, 0.12);
    color: var(--text-light-secondary);
    font-size: 0.8rem;
    font-weight: 600;
}

.cp-text {
    line-height: 1.75;
}

/* tutorial / assignment cards; the whole card opens the link (the button's ::after covers it) */
.cp-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.cp-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 1.1rem;
    padding: 1.15rem 1.25rem;
    border: 1px solid var(--border-light);
    border-radius: 1rem;
    background-color: var(--bg-light-secondary);
    box-shadow: 0 4px 14px -8px rgba(15, 23, 42, 0.12);
    transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}

.cp-item:hover,
.cp-item:focus-within {
    transform: translateY(-3px);
    border-color: rgba(79, 70, 229, 0.45);
    box-shadow: 0 16px 30px -14px rgba(15, 23, 42, 0.25);
}

.cp-num {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 3rem;
    height: 3rem;
    border-radius: 0.85rem;
    background-color: rgba(79, 70, 229, 0.09);
    color: var(--accent-light);
    font-size: 1.15rem;
    font-weight: 700;
    transition: background-color 0.3s ease, color 0.3s ease;
}

.cp-item:hover .cp-num,
.cp-item:focus-within .cp-num {
    background-color: var(--accent-light);
    color: #ffffff;
}

.cp-item-body {
    flex: 1;
    min-width: 0;
}

.cp-item-title {
    font-size: var(--fs-card-title);
    font-weight: 600;
    line-height: 1.4;
}

.cp-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1.1rem;
    margin-top: 0.3rem;
    font-size: 0.875rem;
}

.cp-meta span {
    display: inline-flex;
    align-items: baseline;
    gap: 0.45rem;
}

.cp-meta i {
    color: var(--accent-light);
    font-size: 0.85em;
}

.cp-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.7rem;
}

.cp-tags li {
    padding: 0.1rem 0.6rem;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    color: var(--text-light-secondary);
    font-size: 0.75rem;
    font-weight: 500;
}

.cp-open {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    flex-shrink: 0;
    padding: 0.45rem 0.95rem;
    border: 1px solid rgba(79, 70, 229, 0.35);
    border-radius: 999px;
    color: var(--accent-light);
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
    transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}

.cp-open::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;   /* the whole card is the link */
}

.cp-item:hover .cp-open,
.cp-open:focus-visible {
    border-color: var(--accent-light);
    background-color: var(--accent-light);
    color: #ffffff;
    outline: none;
}

/* sidebar panels (Course Details, Syllabus). In Course Details each label has a small icon; the
   value below it is indented by the icon's width plus the gap (--cp-fact-indent), so it lines up
   with the label's text rather than with the icon. */
.cp-panel {
    --cp-fact-icon: 1rem;
    --cp-fact-gap: 0.5rem;
    --cp-fact-indent: calc(var(--cp-fact-icon) + var(--cp-fact-gap));
    padding: 1.5rem;
    border: 1px solid var(--border-light);
    border-radius: 1rem;
    background-color: var(--bg-light-secondary);
    box-shadow: 0 4px 14px -8px rgba(15, 23, 42, 0.12);
}

.cp-panel + .cp-panel {
    margin-top: 1.25rem;
}

.cp-panel-title {
    margin-bottom: 1.15rem;
    padding-bottom: 0.8rem;
    border-bottom: 1px solid var(--border-light);
    font-size: var(--fs-card-title);
    font-weight: 700;
}

.cp-facts > div + div {
    margin-top: 1.3rem;
}

.cp-facts dt {
    display: flex;
    align-items: center;
    gap: var(--cp-fact-gap);
    margin-bottom: 0.4rem;
    color: var(--text-light-secondary);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.cp-facts dt i {
    flex-shrink: 0;
    width: var(--cp-fact-icon);
    color: var(--accent-light);
    text-align: center;
}

.cp-facts dd {
    padding-left: var(--cp-fact-indent);
    color: var(--text-light-primary);
    font-size: 0.95rem;
    line-height: 1.5;
}

.cp-people {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-top: 0.25rem;
}

.cp-people li {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.9rem;
}

.cp-avatar {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 1.85rem;
    height: 1.85rem;
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.09);
    color: var(--accent-light);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.02em;
}

.cp-syllabus {
    counter-reset: cp-syllabus;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}

.cp-syllabus li {
    counter-increment: cp-syllabus;
    display: grid;
    grid-template-columns: 1.6rem minmax(0, 1fr);
    gap: 0.6rem;
    align-items: baseline;
    color: var(--text-light-secondary);
    font-size: 0.9rem;
    line-height: 1.5;
}

.cp-syllabus li::before {
    content: counter(cp-syllabus);
    display: grid;
    place-items: center;
    width: 1.6rem;
    height: 1.6rem;
    border-radius: 0.5rem;
    background-color: rgba(79, 70, 229, 0.09);
    color: var(--accent-light);
    font-size: 0.75rem;
    font-weight: 700;
    transform: translateY(-0.1rem);
}

.cp-syllabus strong {
    color: var(--text-light-primary);
    font-weight: 600;
}

.cp-empty {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--text-light-secondary);
    font-size: 0.9rem;
}

.cp-empty i {
    color: var(--accent-light);
}

.cp-official {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    margin-top: 1.25rem;
    padding: 0.85rem 1rem;
    border-radius: 0.75rem;
    background-color: var(--accent-light);
    color: #ffffff;
    font-weight: 600;
    box-shadow: 0 10px 20px -10px rgba(79, 70, 229, 0.6);
    transition: background-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}

.cp-official:hover,
.cp-official:focus-visible {
    background-color: var(--accent-hover-light);
    transform: translateY(-2px);
    box-shadow: 0 14px 24px -10px rgba(79, 70, 229, 0.7);
}

.cp-official i {
    font-size: 0.85em;
}

/* dark theme */
.dark .cp-meta i,
.dark .cp-facts dt i,
.dark .cp-empty i {
    color: var(--accent-dark);
}

.dark .cp-code,
.dark .cp-heading-icon,
.dark .cp-num,
.dark .cp-avatar,
.dark .cp-syllabus li::before {
    background-color: rgba(129, 140, 248, 0.14);
    color: var(--accent-dark);
}

.dark .cp-count {
    background-color: rgba(148, 163, 184, 0.15);
    color: var(--text-dark-secondary);
}

.dark .cp-item,
.dark .cp-panel {
    border-color: var(--border-dark);
    background-color: var(--bg-dark-secondary);
    box-shadow: 0 4px 14px -8px rgba(0, 0, 0, 0.5);
}

.dark .cp-item:hover,
.dark .cp-item:focus-within {
    border-color: rgba(129, 140, 248, 0.5);
    box-shadow: 0 16px 30px -14px rgba(0, 0, 0, 0.6);
}

.dark .cp-item:hover .cp-num,
.dark .cp-item:focus-within .cp-num {
    background-color: var(--accent-dark);
    color: #0f172a;
}

.dark .cp-tags li {
    border-color: var(--border-dark);
    color: var(--text-dark-secondary);
}

.dark .cp-open {
    border-color: rgba(129, 140, 248, 0.4);
    color: var(--accent-dark);
}

.dark .cp-item:hover .cp-open,
.dark .cp-open:focus-visible {
    border-color: var(--accent-dark);
    background-color: var(--accent-dark);
    color: #0f172a;
}

.dark .cp-panel-title {
    border-bottom-color: var(--border-dark);
}

.dark .cp-facts dt,
.dark .cp-syllabus li,
.dark .cp-empty {
    color: var(--text-dark-secondary);
}

.dark .cp-facts dd,
.dark .cp-syllabus strong {
    color: var(--text-dark-primary);
}

.dark .cp-official {
    background-color: var(--accent-dark);
    color: #0f172a;
    box-shadow: 0 10px 20px -10px rgba(0, 0, 0, 0.6);
}

.dark .cp-official:hover,
.dark .cp-official:focus-visible {
    background-color: #a5b4fc;
}

/* phones: the button drops under the card text */
@media (max-width: 560px) {
    .cp-item {
        flex-wrap: wrap;
        align-items: flex-start;
        padding: 1rem;
        gap: 0.9rem;
    }

    .cp-num {
        width: 2.5rem;
        height: 2.5rem;
        font-size: 1rem;
    }

    .cp-item-body {
        flex-basis: calc(100% - 3.4rem);
    }

    .cp-open {
        margin-left: 3.4rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .cp-item:hover,
    .cp-item:focus-within,
    .cp-official:hover,
    .cp-official:focus-visible {
        transform: none;
    }
}

/* Text that only screen readers announce (e.g. "Instructor:" next to the icon on course cards) */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Course cards (homepage and TA page): instructor icon in place of the word "Instructor" */
.card-instructor-icon {
    margin-right: 0.5rem;
    color: var(--accent-light);
    font-size: 0.9em;
}

.dark .card-instructor-icon {
    color: var(--accent-dark);
}

/* TA page: course cards (.ta-card) — title with the course code badge in the top-right corner,
   instructor below, and a footer strip with the semester and "View Details". The footer sits at
   the bottom, so cards of different heights line up in the grid. */
.ta-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}

.ta-card[hidden] {
    display: none;   /* the course search hides cards; display: flex above would override [hidden] */
}

.ta-card:hover {
    transform: translateY(-6px);
    box-shadow: 0 25px 50px -12px rgba(15, 23, 42, 0.25);
}

.ta-card-body {
    flex: 1;
    padding: 1.5rem 1.5rem 1.25rem;
}

.ta-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.5rem;
}

.ta-card-title {
    font-size: var(--fs-card-title);
    font-weight: 600;
    line-height: 1.4;
}

.ta-card-code {
    flex-shrink: 0;
    margin-top: 0.2rem;
    padding: 0.2rem 0.65rem;
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.09);
    color: var(--accent-light);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    white-space: nowrap;
}

.ta-card .course-match {
    margin: 0.65rem 0 0;
}

.ta-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.85rem 1.5rem;
    border-top: 1px solid var(--border-light);
    background-color: var(--bg-light-primary);
}

.ta-card-term {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-light-secondary);
}

/* "View Details" is a button linking to the course page; the rest of the card opens the popup */
.ta-card-more {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.9rem;
    border: 1px solid rgba(79, 70, 229, 0.35);
    border-radius: 999px;
    color: var(--accent-light);
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
    text-decoration: none;
    transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}

.ta-card-more:hover,
.ta-card-more:focus-visible {
    border-color: var(--accent-light);
    background-color: var(--accent-light);
    color: #ffffff;
    outline: none;
}

.ta-card-more i {
    font-size: 0.8em;
    transition: transform 0.3s ease;
}

.ta-card:hover .ta-card-more i {
    transform: translateX(4px);
}

.dark .ta-card:hover {
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
}

.dark .ta-card-code {
    background-color: rgba(129, 140, 248, 0.14);
    color: var(--accent-dark);
}

.dark .ta-card-foot {
    border-top-color: var(--border-dark);
    background-color: rgba(15, 23, 42, 0.35);
}

.dark .ta-card-term {
    color: var(--text-dark-secondary);
}

.dark .ta-card-more {
    border-color: rgba(129, 140, 248, 0.4);
    color: var(--accent-dark);
}

.dark .ta-card-more:hover,
.dark .ta-card-more:focus-visible {
    border-color: var(--accent-dark);
    background-color: var(--accent-dark);
    color: #0f172a;
}

@media (max-width: 480px) {
    .ta-card-body {
        padding: 1.25rem 1.25rem 1rem;
    }

    .ta-card-foot {
        padding: 0.75rem 1.25rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .ta-card:hover {
        transform: none;
    }
}

/* TA page: course search (search field, semester chips, count) — behaviour in js/main.js */
.course-filter {
    max-width: 44rem;
    margin: 0 auto 2.5rem;
}

.course-filter-field {
    position: relative;
    display: flex;
    align-items: center;
    height: 3.25rem;
    padding: 0 0.5rem 0 1.1rem;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background-color: var(--bg-light-secondary);
    box-shadow: 0 6px 20px -10px rgba(15, 23, 42, 0.18);
    transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

.course-filter-field:hover {
    border-color: rgba(79, 70, 229, 0.35);
}

.course-filter-field:focus-within {
    border-color: var(--accent-light);
    box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.12), 0 10px 26px -12px rgba(79, 70, 229, 0.35);
}

.course-filter-icon {
    width: 1.25rem;
    height: 1.25rem;
    flex-shrink: 0;
    color: var(--text-light-secondary);
    transition: color 0.25s ease, transform 0.3s ease;
}

.course-filter-field:focus-within .course-filter-icon {
    color: var(--accent-light);
    transform: scale(1.08);
}

#course-filter-input {
    flex: 1;
    min-width: 0;
    height: 100%;
    padding: 0 0.75rem;
    border: 0;
    outline: none;
    background: transparent;
    color: var(--text-light-primary);
    font-size: 1rem;
}

#course-filter-input::placeholder {
    color: var(--text-light-secondary);
    opacity: 0.8;
}

#course-filter-input::-webkit-search-cancel-button {
    display: none;   /* the round clear button below replaces it */
}

.course-filter-clear {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 999px;
    color: var(--text-light-secondary);
    animation: course-pop-in 0.25s cubic-bezier(0.22, 1, 0.36, 1);
    transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
}

.course-filter-clear[hidden] {
    display: none;
}

.course-filter-clear svg {
    width: 1rem;
    height: 1rem;
}

.course-filter-clear:hover,
.course-filter-clear:focus-visible {
    background-color: rgba(79, 70, 229, 0.1);
    color: var(--accent-light);
    transform: rotate(90deg);
    outline: none;
}

@keyframes course-pop-in {
    from {
        opacity: 0;
        transform: scale(0.6);
    }
}

.course-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem 1rem;
    margin-top: 1rem;
    padding: 0 0.25rem;
}

.course-filter-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.course-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.35rem 0.5rem 0.35rem 0.85rem;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background-color: var(--bg-light-secondary);
    color: var(--text-light-secondary);
    font-size: 0.8125rem;
    font-weight: 500;
    transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease, opacity 0.25s ease,
        transform 0.2s ease;
}

.course-chip:hover {
    border-color: rgba(79, 70, 229, 0.45);
    color: var(--accent-light);
}

.course-chip:active {
    transform: scale(0.96);
}

.course-chip:focus-visible {
    outline: 2px solid var(--accent-light);
    outline-offset: 2px;
}

.course-chip-count {
    min-width: 1.35rem;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background-color: rgba(100, 116, 139, 0.12);
    font-size: 0.7rem;
    font-weight: 600;
    text-align: center;
    transition: background-color 0.25s ease, color 0.25s ease;
}

.course-chip[aria-pressed="true"] {
    border-color: var(--accent-light);
    background-color: var(--accent-light);
    color: #ffffff;
}

.course-chip[aria-pressed="true"] .course-chip-count {
    background-color: rgba(255, 255, 255, 0.22);
}

/* a semester with nothing for the words typed stays clickable but steps back */
.course-chip.is-empty:not([aria-pressed="true"]) {
    opacity: 0.5;
}

.course-filter-status {
    font-size: 0.8125rem;
    color: var(--text-light-secondary);
}

/* matching words on the cards, and the topic line shown when a topic matched */
mark.course-hit {
    padding: 0 0.1em;
    border-radius: 0.2em;
    background-color: rgba(79, 70, 229, 0.16);
    color: inherit;
}

.course-match {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin: -0.5rem 0 1rem;
    font-size: 0.875rem;
    color: var(--text-light-secondary);
    animation: course-fade-up 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

.course-match i {
    color: var(--accent-light);
    font-size: 0.75rem;
}

.course-match[hidden] {
    display: none;
}

@keyframes course-fade-up {
    from {
        opacity: 0;
        transform: translateY(4px);
    }
}

/* nothing matched */
.course-filter-empty {
    max-width: 28rem;
    margin: 0 auto;
    padding: 2.5rem 1.5rem;
    text-align: center;
}

.course-filter-empty[hidden] {
    display: none;
}

.course-filter-empty-icon {
    display: inline-grid;
    place-items: center;
    width: 3.25rem;
    height: 3.25rem;
    margin-bottom: 1rem;
    border-radius: 999px;
    background-color: rgba(79, 70, 229, 0.09);
    color: var(--accent-light);
    font-size: 1.2rem;
}

.course-filter-empty-title {
    margin-bottom: 0.35rem;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.course-filter-reset {
    margin-top: 1.25rem;
    padding: 0.5rem 1.1rem;
    border: 1px solid var(--accent-light);
    border-radius: 999px;
    color: var(--accent-light);
    font-size: 0.875rem;
    font-weight: 600;
    transition: background-color 0.25s ease, color 0.25s ease;
}

.course-filter-reset:hover,
.course-filter-reset:focus-visible {
    background-color: var(--accent-light);
    color: #ffffff;
}

/* dark theme */
.dark .course-filter-field,
.dark .course-chip {
    border-color: var(--border-dark);
    background-color: var(--bg-dark-secondary);
}

.dark .course-filter-field {
    box-shadow: 0 6px 20px -10px rgba(0, 0, 0, 0.5);
}

.dark .course-filter-field:hover {
    border-color: rgba(129, 140, 248, 0.4);
}

.dark .course-filter-field:focus-within {
    border-color: var(--accent-dark);
    box-shadow: 0 0 0 4px rgba(129, 140, 248, 0.15), 0 10px 26px -12px rgba(0, 0, 0, 0.6);
}

.dark .course-filter-icon,
.dark .course-filter-clear,
.dark #course-filter-input::placeholder,
.dark .course-chip,
.dark .course-filter-status,
.dark .course-match {
    color: var(--text-dark-secondary);
}

.dark #course-filter-input {
    color: var(--text-dark-primary);
}

.dark .course-filter-field:focus-within .course-filter-icon,
.dark .course-chip:hover,
.dark .course-match i,
.dark .course-filter-clear:hover,
.dark .course-filter-clear:focus-visible {
    color: var(--accent-dark);
}

.dark .course-filter-clear:hover,
.dark .course-filter-clear:focus-visible {
    background-color: rgba(129, 140, 248, 0.14);
}

.dark .course-chip:hover {
    border-color: rgba(129, 140, 248, 0.5);
}

.dark .course-chip-count {
    background-color: rgba(148, 163, 184, 0.15);
}

.dark .course-chip[aria-pressed="true"] {
    border-color: var(--accent-dark);
    background-color: var(--accent-dark);
    color: #0f172a;
}

.dark .course-chip[aria-pressed="true"] .course-chip-count {
    background-color: rgba(15, 23, 42, 0.18);
}

.dark .course-chip:focus-visible {
    outline-color: var(--accent-dark);
}

.dark mark.course-hit {
    background-color: rgba(129, 140, 248, 0.28);
}

.dark .course-filter-empty-icon {
    background-color: rgba(129, 140, 248, 0.14);
    color: var(--accent-dark);
}

.dark .course-filter-reset {
    border-color: var(--accent-dark);
    color: var(--accent-dark);
}

.dark .course-filter-reset:hover,
.dark .course-filter-reset:focus-visible {
    background-color: var(--accent-dark);
    color: #0f172a;
}

/* phones: chips and the count each get their own line, centred */
@media (max-width: 480px) {
    .course-filter-field {
        height: 3rem;
        padding-left: 0.95rem;
    }

    #course-filter-input {
        padding: 0 0.6rem;
        font-size: 1rem;   /* stays 16px so iOS does not zoom in */
    }

    .course-filter-bar {
        flex-direction: column;
    }

    .course-filter-chips {
        justify-content: center;
    }
}

@media (prefers-reduced-motion: reduce) {
    .course-filter-clear,
    .course-match {
        animation: none;
    }

    .course-filter-clear:hover,
    .course-filter-clear:focus-visible {
        transform: none;
    }
}

/* 5. Theme switch: colours fade over (js/main.js adds .theme-switching briefly) and the icon spins in */
.theme-switching,
.theme-switching *,
.theme-switching *::before,
.theme-switching *::after {
    transition: background-color 0.4s ease, color 0.4s ease, border-color 0.4s ease, fill 0.4s ease,
        stroke 0.4s ease !important;
}

#theme-toggle.is-spinning svg {
    animation: theme-icon-in 0.5s cubic-bezier(0.3, 0.7, 0.4, 1);
}

@keyframes theme-icon-in {
    from {
        opacity: 0;
        transform: rotate(-120deg) scale(0.6);
    }
}

/* 6. Finishing touches: accent text selection and a slim, theme-matched scrollbar */
::selection {
    background-color: rgba(79, 70, 229, 0.18);
}

.dark ::selection {
    background-color: rgba(129, 140, 248, 0.32);
}

html {
    scrollbar-width: thin;
    scrollbar-color: rgba(79, 70, 229, 0.35) transparent;
}

html.dark {
    scrollbar-color: rgba(129, 140, 248, 0.35) transparent;
}

/* 7. Profile photo: the picture zooms gently inside its round frame on hover; the frame itself stays put.
   For a smooth zoom in every browser:
   - the frame is its own layer (translateZ) and an isolated group, so Safari keeps the round clip
     while the picture inside is scaling, instead of flashing square corners;
   - the picture stays on its own GPU layer all the time (will-change). Without it Chrome, Edge and
     Firefox move it onto a layer only while it animates: a hitch as the zoom starts, then a visible
     sharpness "snap" when it ends and the picture is redrawn;
   - the zoom only runs on devices with a real hover (mouse, trackpad). On phones a tap would leave
     the picture stuck zoomed in. */
.profile-photo {
    isolation: isolate;
    transform: translateZ(0);
}

.profile-photo picture {
    display: block;
    width: 100%;
    height: 100%;
}

.profile-photo img {
    transform: scale(1);
    transform-origin: 50% 50%;
    will-change: transform;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    transition: transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1);
}

@media (hover: hover) and (pointer: fine) {
    .profile-photo:hover img {
        transform: scale(1.08);
    }
}

/* Side-by-side layout (768px and up): the photo shrinks with its column, up to 28rem (the column caps it on tablets and small laptops),
   so it never runs into the text on the right. "#home" makes this win over Tailwind's md:w-96 / md:h-96. */
@media (min-width: 768px) {
    #home .profile-photo {
        width: min(28rem, 100%);
        height: auto;
        aspect-ratio: 1 / 1;
    }

    /* The photo's top lines up with the name (3rem = the role tag above it) instead of the photo
       being centred on the whole text column. On narrower windows the text wraps into more lines
       and the column grows, and a centred photo would slide down and off the bottom of the screen;
       on wide screens the two come out practically the same. */
    #home .home-photo-col {
        align-self: flex-start;
        margin-top: 3rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .scroll-animate.visible .reveal-child,
    #theme-toggle.is-spinning svg {
        animation: none;
    }

    .profile-photo:hover img {
        transform: none;
    }
}

/* =====================================================================
   Footer background design: soft corner glows and a thin gradient line along the top. The footer stays dark in both themes.
   ===================================================================== */
footer {
    position: relative;
    overflow: hidden;
    isolation: isolate;   /* keeps the decoration behind the footer's text and links */
    scroll-margin-top: 5rem;   /* "Contact" in the header jumps here; the sticky header never covers its top */
}

footer::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image:
        radial-gradient(38rem 18rem at 100% 0%, rgba(99, 102, 241, 0.16), transparent 70%),
        radial-gradient(32rem 16rem at 0% 100%, rgba(56, 189, 248, 0.10), transparent 70%);
    background-repeat: no-repeat;
}

footer::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    pointer-events: none;
    background: linear-gradient(90deg, transparent, rgba(129, 140, 248, 0.6) 30%, rgba(56, 189, 248, 0.5) 70%, transparent);
}

/* Footer columns: Contact gets more room for the long e-mail addresses, which also moves
   Quick Links a little to the right towards Connect. Addresses wrap instead of overflowing. */
@media (min-width: 768px) {
    footer .footer-grid {   /* "footer" makes this win over Tailwind's md:grid-cols-3, which loads later */
        grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr);
    }
}

.footer-grid a[href^="mailto:"] {
    overflow-wrap: anywhere;
}

/* Footer contact (e-mail, location): hovering a row turns its icon accent and lifts it a little;
   the links turn white as an accent underline slides in from the left.
   The footer is always dark, so these use the dark-theme accent in both themes. */
.footer-contact > i {
    transition: color 0.25s ease, transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.footer-contact:hover > i,
.footer-contact:focus-within > i {
    color: var(--accent-dark);
    transform: translateY(-2px) scale(1.12);
}

.footer-link {
    text-decoration: none;
    background-image: linear-gradient(var(--accent-dark), var(--accent-dark));
    background-position: 0 100%;
    background-repeat: no-repeat;
    background-size: 0% 1.5px;
    transition: background-size 0.3s ease, color 0.2s ease;
}

.footer-link:hover,
.footer-link:focus-visible {
    color: #ffffff;
    background-size: 100% 1.5px;
    outline: none;
}

/* Footer quick links: on hover the link turns white and a short accent dash grows in on its left.
   The dash sits outside the text, so nothing moves (the column is centred on phones). */
.footer-quick-link {
    position: relative;
    transition: color 0.2s ease;
}

.footer-quick-link::before {
    content: "";
    position: absolute;
    top: 50%;
    right: calc(100% + 0.45rem);
    width: 0.6rem;
    height: 1.5px;
    border-radius: 1px;
    background-color: var(--accent-dark);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.footer-quick-link:hover,
.footer-quick-link:focus-visible {
    color: #ffffff;
    outline: none;
}

.footer-quick-link:hover::before,
.footer-quick-link:focus-visible::before {
    transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
    .footer-contact:hover > i,
    .footer-contact:focus-within > i {
        transform: none;
    }
}

@media print {
    footer::before,
    footer::after {
        display: none;
    }
}

/* ---------- Site search (dialog built by main.js) ---------- */
html.search-open {
    overflow: hidden;
}

/* Header button: soft accent glow, and the magnifier tilts a little on hover.
   The glow shows on mouse hover (not on touch screens, where a tap would leave it stuck on) and on
   keyboard focus, but not when focus is handed back after the search box closes (.focus-quiet). */
#search-button {
    transition: color 0.25s ease, background-color 0.25s ease, box-shadow 0.25s ease, transform 0.2s ease;
}

#search-button svg {
    transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}

#search-button:focus-visible:not(.focus-quiet) {
    color: var(--accent-light);
    background-color: rgba(79, 70, 229, 0.1);
    box-shadow: 0 0 0 1px rgba(79, 70, 229, 0.22), 0 6px 16px -6px rgba(79, 70, 229, 0.55);
}

#search-button:focus-visible:not(.focus-quiet) svg {
    transform: rotate(-12deg) scale(1.1);
}

.dark #search-button:focus-visible:not(.focus-quiet) {
    color: var(--accent-dark);
    background-color: rgba(129, 140, 248, 0.14);
    box-shadow: 0 0 0 1px rgba(129, 140, 248, 0.3), 0 6px 18px -6px rgba(129, 140, 248, 0.6);
}

@media (hover: hover) {
    #search-button:hover {
        color: var(--accent-light);
        background-color: rgba(79, 70, 229, 0.1);
        box-shadow: 0 0 0 1px rgba(79, 70, 229, 0.22), 0 6px 16px -6px rgba(79, 70, 229, 0.55);
    }

    #search-button:hover svg {
        transform: rotate(-12deg) scale(1.1);
    }

    .dark #search-button:hover {
        color: var(--accent-dark);
        background-color: rgba(129, 140, 248, 0.14);
        box-shadow: 0 0 0 1px rgba(129, 140, 248, 0.3), 0 6px 18px -6px rgba(129, 140, 248, 0.6);
    }
}

#search-button:active {
    transform: scale(0.92);
}

.search-overlay {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: 12vh 16px 16px;
    background: rgba(15, 23, 42, 0.45);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    opacity: 0;
    transition: opacity 0.2s ease-in;
}

.search-overlay.is-open {
    opacity: 1;
    transition: opacity 0.25s ease-out;
}

.search-overlay[hidden] {
    display: none;
}

.search-panel {
    width: 100%;
    max-width: 40rem;
    max-height: 76vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* clip, unlike hidden, is not a scroll container: scrollIntoView() on a result can then never
       scroll the panel itself (which would jolt the input up while the height is still growing) */
    overflow: clip;
    border: 1px solid var(--border-light);
    border-radius: 1rem;
    background: var(--bg-light-secondary);
    color: var(--text-light-primary);
    box-shadow: 0 25px 50px -12px rgba(15, 23, 42, 0.35);
    opacity: 0;
    transform: translateY(-14px) scale(0.96);
    transition: opacity 0.18s ease-in, transform 0.2s ease-in;
}

.search-overlay.is-open .search-panel {
    opacity: 1;
    transform: none;
    transition: opacity 0.25s ease-out, transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.search-field {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.875rem 1rem;
}

/* status line + results; hidden while the box is empty, so it is only the input */
.search-body {
    display: flex;
    flex-direction: column;
    min-height: 0;
    border-top: 1px solid var(--border-light);
}

.search-panel.is-empty .search-body {
    display: none;
}

.search-field-icon {
    width: 1.25rem;
    height: 1.25rem;
    flex-shrink: 0;
    color: var(--text-light-secondary);
}

#search-input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: none;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 1.0625rem;
}

#search-input::-webkit-search-cancel-button {
    display: none;
}

.search-esc {
    flex-shrink: 0;
    padding: 0.125rem 0.4rem;
    border: 1px solid var(--border-light);
    border-radius: 0.375rem;
    font: inherit;
    font-size: 0.75rem;
    color: var(--text-light-secondary);
}

/* no Esc key on phones and tablets */
@media (hover: none) {
    .search-esc {
        display: none;
    }
}

/* narrow phones: tighter padding so the whole placeholder fits (text stays 16px so iOS does not zoom) */
@media (max-width: 480px) {
    .search-field {
        gap: 0.625rem;
        padding: 0.875rem;
    }

    #search-input {
        font-size: 1rem;
    }
}

/* below the narrowest iPhone (375px) the zoom issue does not apply, so the text can shrink a touch */
@media (max-width: 374px) {
    #search-input {
        font-size: 0.9375rem;
    }
}

/* Results: grouped by page under a sticky label; each row has an icon tile, a title and a 2-line snippet */
.search-results {
    overflow-y: auto;
    overscroll-behavior: contain;
    min-height: 0;
    padding: 0 0.5rem 0.5rem;
    scroll-padding: 2.25rem 0 0.5rem;
}

.search-group + .search-group {
    margin-top: 0.25rem;
}

.search-group-label {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 0.75rem 0.75rem 0.375rem;
    background: var(--bg-light-secondary);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-light-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.search-result {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 0.625rem 0.75rem;
    border: 1px solid transparent;
    border-radius: 0.75rem;
    color: inherit;
    text-decoration: none;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.search-result-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    border: 1px solid var(--border-light);
    border-radius: 0.625rem;
    background: var(--bg-light-primary);
    color: var(--text-light-secondary);
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.search-result-icon svg {
    width: 1.125rem;
    height: 1.125rem;
}

.search-result-body {
    flex: 1;
    min-width: 0;
}

.search-result-title {
    display: block;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 0.9375rem;
    font-weight: 500;
    line-height: 1.4;
    color: var(--text-light-primary);
}

.search-result-text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    margin-top: 0.125rem;
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--text-light-secondary);
}

.search-result-enter {
    flex: none;
    display: grid;
    color: var(--accent-light);
    opacity: 0;
    transform: translateX(-4px);
    transition: opacity 0.15s ease, transform 0.2s ease;
}

.search-result-enter svg {
    width: 1rem;
    height: 1rem;
}

/* the selected row (arrow keys or mouse) */
.search-result[aria-selected="true"],
.search-result:focus-visible {
    outline: none;
    background: rgba(79, 70, 229, 0.06);
    border-color: rgba(79, 70, 229, 0.18);
}

.search-result[aria-selected="true"] .search-result-icon,
.search-result:focus-visible .search-result-icon {
    background: var(--accent-light);
    border-color: var(--accent-light);
    color: #fff;
}

.search-result[aria-selected="true"] .search-result-enter,
.search-result:focus-visible .search-result-enter {
    opacity: 1;
    transform: none;
}

.search-result mark {
    padding: 0 0.12em;
    border-radius: 0.25em;
    background: rgba(79, 70, 229, 0.12);
    color: var(--accent-light);
    font-weight: 600;
}

.search-results .is-entering {
    animation: search-result-in 0.35s cubic-bezier(0.22, 1, 0.36, 1) both;
    animation-delay: var(--enter-delay, 0ms);
}

@keyframes search-result-in {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: none; }
}

/* "No results" / error message in place of the list */
.search-message {
    padding: 2.25rem 1.5rem 2.5rem;
    text-align: center;
    animation: search-result-in 0.35s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.search-message-icon {
    display: inline-grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    margin-bottom: 0.875rem;
    border-radius: 999px;
    background: rgba(79, 70, 229, 0.08);
    color: var(--accent-light);
}

.search-message-icon svg {
    width: 1.5rem;
    height: 1.5rem;
}

.search-message-title {
    margin: 0;
    font-weight: 600;
    color: var(--text-light-primary);
    overflow-wrap: anywhere;
}

.search-message-hint {
    margin: 0.25rem 0 0;
    font-size: 0.875rem;
    color: var(--text-light-secondary);
}

/* Footer bar: result count + keyboard hints */
.search-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.5rem 1rem;
    border-top: 1px solid var(--border-light);
    background: var(--bg-light-primary);
    font-size: 0.75rem;
    color: var(--text-light-secondary);
}

.search-panel.is-message .search-footer {
    display: none;
}

.search-keys {
    display: flex;
    gap: 0.875rem;
    white-space: nowrap;
}

.search-keys kbd {
    display: inline-grid;
    place-items: center;
    min-width: 1.25rem;
    height: 1.25rem;
    margin-right: 0.2rem;
    padding: 0 0.25rem;
    border: 1px solid var(--border-light);
    border-bottom-width: 2px;
    border-radius: 0.3rem;
    background: var(--bg-light-secondary);
    font: inherit;
    font-size: 0.6875rem;
}

@media (hover: none) {
    .search-keys {
        display: none;
    }
}

/* "Notebook" tag after a notebook's group label */
.search-group-badge {
    margin-left: 0.5rem;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background: rgba(79, 70, 229, 0.1);
    color: var(--accent-light);
    font-size: 0.625rem;
    letter-spacing: 0.04em;
}

/* Recent searches (empty box): label row with a "Clear" button, no footer bar */
.search-recent-label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.search-recent-clear {
    padding: 0.125rem 0.375rem;
    border-radius: 0.375rem;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: var(--accent-light);
    transition: background-color 0.15s ease;
}

.search-recent-clear:hover,
.search-recent-clear:focus-visible {
    outline: none;
    background: rgba(79, 70, 229, 0.1);
}

.search-panel.is-recent .search-footer {
    display: none;
}

.search-result.is-recent .search-result-title {
    font-weight: 400;
}

/* highlight left on the page after opening a result */
mark.search-hit {
    padding: 0 0.1em;
    border-radius: 0.2em;
    background: rgba(79, 70, 229, 0.18);
    color: inherit;
}

.search-target {
    scroll-margin-top: 5rem;
    animation: search-target-flash 2.4s ease-out;
    border-radius: 0.375rem;
}

@keyframes search-target-flash {
    0%, 40% { box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.35); }
    100% { box-shadow: 0 0 0 4px rgba(79, 70, 229, 0); }
}

.dark .search-panel {
    border-color: var(--border-dark);
    background: var(--bg-dark-secondary);
    color: var(--text-dark-primary);
}

.dark .search-body,
.dark .search-esc,
.dark .search-footer,
.dark .search-keys kbd {
    border-color: var(--border-dark);
}

.dark .search-field-icon,
.dark .search-esc,
.dark .search-footer,
.dark .search-group-label,
.dark .search-result-text,
.dark .search-message-hint {
    color: var(--text-dark-secondary);
}

.dark .search-group-label {
    background: var(--bg-dark-secondary);
}

.dark .search-result-title,
.dark .search-message-title {
    color: var(--text-dark-primary);
}

.dark .search-result-icon {
    border-color: var(--border-dark);
    background: rgba(15, 23, 42, 0.55);
    color: var(--text-dark-secondary);
}

.dark .search-result-enter {
    color: var(--accent-dark);
}

.dark .search-result[aria-selected="true"],
.dark .search-result:focus-visible {
    background: rgba(129, 140, 248, 0.1);
    border-color: rgba(129, 140, 248, 0.25);
}

.dark .search-result[aria-selected="true"] .search-result-icon,
.dark .search-result:focus-visible .search-result-icon {
    background: var(--accent-dark);
    border-color: var(--accent-dark);
    color: var(--bg-dark-primary);
}

.dark .search-result mark {
    background: rgba(129, 140, 248, 0.16);
    color: var(--accent-dark);
}

.dark .search-message-icon {
    background: rgba(129, 140, 248, 0.14);
    color: var(--accent-dark);
}

.dark .search-footer {
    background: rgba(15, 23, 42, 0.4);
}

.dark .search-keys kbd {
    background: var(--bg-dark-secondary);
}

.dark mark.search-hit {
    background: rgba(129, 140, 248, 0.3);
}

.dark .search-group-badge,
.dark .search-recent-clear:hover,
.dark .search-recent-clear:focus-visible {
    background: rgba(129, 140, 248, 0.16);
}

.dark .search-group-badge,
.dark .search-recent-clear {
    color: var(--accent-dark);
}

/* The footer is always dark, so it uses the dark-theme highlight in both themes */
footer mark.search-hit {
    background: rgba(129, 140, 248, 0.35);
}

footer .search-target {
    animation-name: search-target-flash-footer;
}

@keyframes search-target-flash-footer {
    0%, 40% { box-shadow: 0 0 0 4px rgba(129, 140, 248, 0.6); }
    100% { box-shadow: 0 0 0 4px rgba(129, 140, 248, 0); }
}

@media (prefers-reduced-motion: reduce) {
    .search-target,
    .search-results .is-entering,
    .search-message {
        animation: none;
    }

    .search-overlay,
    .search-overlay.is-open,
    .search-panel,
    .search-overlay.is-open .search-panel,
    #search-button,
    #search-button svg {
        transition: none;
        transform: none;
    }
}

@media print {
    .search-overlay,
    #search-button {
        display: none !important;
    }
}

/* ---------- Skip link: hidden until a keyboard user tabs onto it, jumps past the header ---------- */
.skip-link {
    position: fixed;
    top: 0.75rem;
    left: 1rem;
    z-index: 70;
    padding: 0.625rem 1rem;
    border-radius: 0.75rem;
    background: var(--accent-light);
    color: #fff;
    font-weight: 500;
    text-decoration: none;
    box-shadow: 0 10px 25px -8px rgba(79, 70, 229, 0.6);
    transform: translateY(-160%);
    transition: transform 0.2s ease;
}

.skip-link:focus {
    outline: 2px solid #fff;
    outline-offset: -4px;
    transform: none;
}

.dark .skip-link {
    background: var(--accent-dark);
    color: var(--bg-dark-primary);
}

/* the skip link moves focus to <main>; it does not need a focus ring of its own */
#main-content:focus {
    outline: none;
}

/* ---------- 404 page ---------- */
.notfound {
    padding: 5rem 0 6rem;
    text-align: center;
}

.notfound-code {
    margin: 0;
    font-size: clamp(5.5rem, 18vw, 9rem);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.04em;
    background: linear-gradient(135deg, var(--accent-light), #0ea5e9);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.notfound-title {
    margin: 1rem 0 0.75rem;
    font-size: var(--fs-page-title);
    font-weight: 700;
}

.notfound-text {
    max-width: 34rem;
    margin: 0 auto;
    font-size: 1.0625rem;
    line-height: 1.6;
}

.notfound-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.75rem;
    margin-top: 2rem;
}

.notfound-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1.25rem;
    border: 1px solid var(--border-light);
    border-radius: 0.75rem;
    background: var(--bg-light-secondary);
    color: var(--text-light-primary);
    font: inherit;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}

.notfound-btn svg {
    width: 1.125rem;
    height: 1.125rem;
}

.notfound-btn:hover,
.notfound-btn:focus-visible {
    outline: none;
    transform: translateY(-2px);
    border-color: rgba(79, 70, 229, 0.35);
    box-shadow: 0 10px 24px -12px rgba(79, 70, 229, 0.5);
}

.notfound-btn.is-primary {
    border-color: var(--accent-light);
    background: var(--accent-light);
    color: #fff;
}

.notfound-btn.is-primary:hover,
.notfound-btn.is-primary:focus-visible {
    background: var(--accent-hover-light);
}

.notfound-links {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 1rem;
    max-width: 56rem;
    margin: 3.5rem auto 0;
    text-align: left;
}

.notfound-card {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 1.25rem;
    border: 1px solid var(--border-light);
    border-radius: 1rem;
    background: var(--bg-light-secondary);
    text-decoration: none;
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.notfound-card:hover,
.notfound-card:focus-visible {
    outline: none;
    transform: translateY(-3px);
    border-color: rgba(79, 70, 229, 0.35);
    box-shadow: 0 16px 32px -18px rgba(79, 70, 229, 0.55);
}

.notfound-card-icon {
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    margin-bottom: 0.5rem;
    border-radius: 0.75rem;
    background: rgba(79, 70, 229, 0.1);
    color: var(--accent-light);
}

.notfound-card-icon svg {
    width: 1.25rem;
    height: 1.25rem;
}

.notfound-card-title {
    font-weight: 600;
}

.notfound-card-text {
    font-size: 0.875rem;
}

.dark .notfound-code {
    background-image: linear-gradient(135deg, var(--accent-dark), #38bdf8);
}

.dark .notfound-btn,
.dark .notfound-card {
    border-color: var(--border-dark);
    background: var(--bg-dark-secondary);
    color: var(--text-dark-primary);
}

.dark .notfound-btn:hover,
.dark .notfound-btn:focus-visible,
.dark .notfound-card:hover,
.dark .notfound-card:focus-visible {
    border-color: rgba(129, 140, 248, 0.45);
    box-shadow: 0 16px 32px -18px rgba(129, 140, 248, 0.6);
}

.dark .notfound-btn.is-primary {
    border-color: var(--accent-dark);
    background: var(--accent-dark);
    color: var(--bg-dark-primary);
}

.dark .notfound-btn.is-primary:hover,
.dark .notfound-btn.is-primary:focus-visible {
    background: var(--accent-hover-dark);
}

.dark .notfound-card-icon {
    background: rgba(129, 140, 248, 0.14);
    color: var(--accent-dark);
}

@media (prefers-reduced-motion: reduce) {
    .skip-link,
    .notfound-btn,
    .notfound-card {
        transition: none;
    }

    .notfound-btn:hover,
    .notfound-card:hover {
        transform: none;
    }
}

/* =====================================================================
   Lite mode for low-powered devices. js/main.js adds html.perf-lite on old or low-powered laptops
   (4 GB of memory or less, 4 CPU threads or fewer, Data Saver, or when the background lines'
   opening ripple runs below ~33 fps, e.g. in battery saver). It turns off the effects that make a
   weak graphics chip redraw large areas: the frosted-glass blur behind the header and popups
   (the header's blur is redone on every scroll frame), and the endless shimmers. Everything keeps
   its layout and colours; the theme switch snaps instead of fading (main.js), and the background
   lines stay still. Hover effects, the section fade-ins and the photo zoom stay: they only move
   or fade an element, which even an old graphics chip does smoothly.
   ===================================================================== */
.perf-lite header {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: rgba(255, 255, 255, 0.96);
}

.perf-lite.dark header {
    background-color: rgba(15, 23, 42, 0.96);
}

.perf-lite .modal,
.perf-lite .search-overlay {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.perf-lite .modal {
    background-color: rgba(15, 23, 42, 0.7);
}

.perf-lite .search-overlay {
    background: rgba(15, 23, 42, 0.6);
}

.perf-lite .news-new-tag::before,
.perf-lite img.is-loading,
.perf-lite .seminar-embed.is-loading {
    animation: none;
}
