/* ==============================================================
   Andrés Fábrega — site styles
   Single stylesheet shared by all pages.
   ============================================================== */

/* ==============================
   Design tokens
   ============================== */
:root {
    color-scheme: light;

    /* Brand green. One value for both the header rule and every link:
       4.91:1 on the cream ground, so it clears WCAG AA for normal text,
       while its high saturation (0.85 vs 0.47 for the old #3a7a2c) is
       what makes it read vividly despite being dark. Contrast and
       saturation are independent — this is bright without being light. */
    --color-accent: #1f7d0a;

    /* Surfaces */
    --color-bg: #fbf7ee;

    /* Text. Deliberately a single strength: every line of content reads
       at full weight, with emphasis carried by weight and italics rather
       than by greying text back. A warm near-black rather than #000 —
       15.4:1 on the cream, and it shares the ground's hue, where a
       neutral black would read as cold against it. */
    --color-text: #211f1b;

    /* Links are the brand green itself — aliased rather than copied so the
       two can never drift apart. Hover darkens to 7.3:1. */
    --color-link: var(--color-accent);
    --color-link-hover: #175f07;

    /* Nav sits on the page rather than in a bar: same ground as the body,
       separated only by a hairline. Links therefore use the body link
       green (4.9:1 on cream) — the lighter #6fbb57 that worked on the old
       dark bar manages only 2.2:1 here and would be unreadable. */
    --color-nav-bg: var(--color-bg);
    --color-nav-text: var(--color-text);
    --color-nav-link: var(--color-link);
    --color-nav-rule: #cfc6b2;

    --color-focus: #1f6feb;

    /* System font stack — zero network requests, native rendering
       on every platform. */
    --font-body: system-ui, -apple-system, "Segoe UI", Roboto,
                 "Helvetica Neue", Arial, sans-serif;
    --font-heading: var(--font-body);

    /* Fluid type: scales between the 360px and 880px viewport widths */
    --size-base: clamp(1rem, 0.96rem + 0.18vw, 1.0625rem);
    --size-h1:   clamp(1.5rem, 1.34rem + 0.7vw, 1.85rem);
    --size-h2:   clamp(1.25rem, 1.16rem + 0.4vw, 1.45rem);
    --size-h3:   clamp(1.08rem, 1.04rem + 0.2vw, 1.15rem);
    --size-small: 0.94rem;

    --content-max-width: 880px;
    --content-padding: clamp(1.25rem, 0.7rem + 2.4vw, 2rem);

    /* Comfortable reading measure for running text. The column stays
       880px everywhere so page-to-page alignment holds; only prose is
       capped. Applied via .prose, so the publication list keeps the
       full width it actually needs.
       NB: 1ch is the width of "0", which is wider than the average
       lowercase letter, so this is not a character count — 65ch
       measures out to ~76 real characters on Research, the same as 60ch
       did, but 54px wider, which brings the page's right edge closer to
       the other pages'. 70ch is the next step that changes anything and
       it lands at ~91 chars, too long to track comfortably. */
    --measure: 65ch;

    /* Hanging indent for the papers list — sized to hold a two-digit
       number plus a gap. */
    --entry-indent: 2.3em;

    /* Talks indent separately: the marker is a single dot, so the papers
       indent left it stranded 37px from its own title. Sized for a bullet
       instead. The two pages therefore do not share a text edge — that is
       the deliberate trade for a bullet that sits close to what it marks. */
    --talk-indent: 1.25em;

    --nav-height: 61px;
    --radius: 5px;
    --transition: 0.2s ease;
}

/* ==============================
   Reset & base
   ============================== */
*, *::before, *::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    /* Scrollbar handling is deliberately left to the browser default.
       Long pages (Papers) therefore get a scrollbar and short ones don't,
       which shifts centred content by half a scrollbar (~7.5px) between
       pages. That is accepted on purpose. The alternatives are worse:
       overflow-y:scroll parks an empty scrollbar on every short page, and
       scrollbar-gutter reserves space outside <body>, which the
       full-bleed nav cannot paint into — leaving a gap beside it.
       None of this is visible with overlay scrollbars (macOS default,
       and all touch devices). */
    /* Keep #main clear of the sticky header when the skip link jumps to it */
    scroll-padding-top: var(--nav-height);
}

body {
    font-family: var(--font-body);
    font-size: var(--size-base);
    line-height: 1.6;
    color: var(--color-text);
    background-color: var(--color-bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* NB: no overflow-x here. Any overflow on <body> makes it a scroll
       container, which stops the sticky header from sticking to the
       viewport. Sideways overflow is prevented at the source instead,
       by overflow-wrap on links and paper titles. */
}

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

/* Visible only to screen readers */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Keyboard-only "jump past the nav" link */
.skip-link {
    position: absolute;
    left: 0.5rem;
    top: -4rem;
    z-index: 100;
    padding: 0.6rem 1rem;
    background-color: var(--color-bg);
    color: var(--color-link);
    border: 2px solid var(--color-accent);
    border-radius: var(--radius);
    font-size: var(--size-small);
    transition: top var(--transition);
}

.skip-link:focus {
    top: 0.5rem;
}

/* Consistent, visible keyboard focus everywhere */
:focus-visible {
    outline: 3px solid var(--color-focus);
    outline-offset: 2px;
    border-radius: 2px;
}

/* ==============================
   Navigation
   ============================== */
.site-nav {
    background-color: var(--color-nav-bg);
    /* Papers runs ~2000px; keep navigation reachable the whole way down */
    position: sticky;
    top: 0;
    z-index: 30;
}

/* The rule sits on .nav-inner rather than .site-nav so it spans the
   content column instead of the whole window — a full-width line is the
   only element on the page not aligned to the 880px measure, which is
   what made it read as a stray band. Drawn as a pseudo-element so it
   lands inside the padding, flush with the text below it. */
.nav-inner::after {
    content: "";
    position: absolute;
    left: var(--content-padding);
    right: var(--content-padding);
    bottom: 0;
    height: 2px;
    background: var(--color-accent);
}

.nav-inner {
    max-width: var(--content-max-width);
    margin: 0 auto;
    padding: 0 var(--content-padding);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: var(--nav-height);
    position: relative;
}

.site-name {
    font-size: clamp(1.2rem, 1.05rem + 0.6vw, 1.45rem);
    font-weight: 500;
    letter-spacing: -0.01em;
}

.site-name a {
    color: var(--color-nav-text);
    text-decoration: none;
    transition: opacity var(--transition);
}

.site-name a:hover {
    opacity: 0.8;
}

.nav-links {
    list-style: none;
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

/* Flex row so each link sits beside its separator rather than above it */
.nav-links li {
    display: flex;
    align-items: center;
}

.nav-links a {
    display: block;
    white-space: nowrap;
    color: var(--color-nav-link);
    text-decoration: none;
    font-size: 1rem;
    padding: 0.45rem 0.7rem;
    border-radius: 4px;
    text-decoration-thickness: 1.5px;
    text-underline-offset: 3px;
    transition: color var(--transition), background-color var(--transition);
}

.nav-links a:hover {
    text-decoration: underline;
}

/* Current page marker: an underline rule rather than a colour change,
   so it reads as "you are here" without competing with hover. */
.nav-links a[aria-current="page"] {
    color: var(--color-nav-text);
    text-decoration: underline;
    text-decoration-color: var(--color-accent);
    text-decoration-thickness: 2px;
}

.nav-links li:not(:last-child)::after {
    content: "";
    display: inline-block;
    width: 1.5px;
    height: 18px;
    /* Neutral, not green — on the light header a green separator would
       add colour without carrying meaning. */
    background: var(--color-nav-rule);
    vertical-align: middle;
    margin-left: 0.15rem;
}

/* ==============================
   Mobile menu (CSS-only, no JS)
   ============================== */
.nav-toggle-input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}

.nav-toggle-label {
    display: none;
    cursor: pointer;
    border: 1.5px solid var(--color-nav-link);
    border-radius: 4px;
    /* 44x44 minimum touch target */
    width: 44px;
    height: 44px;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 3.5px;
}

.nav-toggle-label span {
    display: block;
    width: 18px;
    height: 2px;
    background-color: var(--color-nav-link);
    border-radius: 1px;
    transition: transform 0.3s ease, opacity 0.3s ease;
}

/* Mirror :focus-visible onto the label, since the real input is hidden */
.nav-toggle-input:focus-visible + .nav-toggle-label {
    outline: 3px solid var(--color-focus);
    outline-offset: 2px;
}

/* ==============================
   Layout
   ============================== */
.page-content {
    max-width: var(--content-max-width);
    margin: 0 auto;
    padding: 2rem var(--content-padding) 4rem;
}

/* ==============================
   Profile / about
   ============================== */
.profile {
    display: flex;
    /* 2rem, not 2.5: the bio's right rag leaves ~17px of its own air, so a
       40px gutter read as ~57px of white beside the photo. Dropping to 32px
       lands the closest line at 36px. Going further is wasted — at 1.75rem
       the text re-wraps identically and the gap stays 36px. */
    gap: 2rem;
    align-items: flex-start;
}

.profile-bio {
    flex: 1;
    min-width: 0;
}

.profile-photo {
    flex-shrink: 0;
    width: 200px;
}

.profile-photo img {
    width: 100%;
    display: block;
    /* Let the photograph carry itself: no border, no shadow */
    border-radius: 3px;
}

/* ==============================
   Typography
   ============================== */
h1, h2, h3 {
    font-family: var(--font-heading);
    color: var(--color-text);
    line-height: 1.25;
    text-wrap: balance;
    letter-spacing: -0.015em;
}

h1 {
    font-size: var(--size-h1);
    /* No underline: the nav rule already divides the top of the page, and
       a second rule 90px below it read as striping. Size and weight are
       enough to mark a heading. */
    margin-bottom: 1.4rem;
}

h2 {
    font-size: var(--size-h2);
    margin-top: 2.25rem;
    margin-bottom: 1rem;
}

h3 {
    font-size: var(--size-h3);
    margin-top: 1.75rem;
    margin-bottom: 0.4rem;
}

p {
    margin-bottom: 0.9rem;
    text-wrap: pretty;
}

a {
    color: var(--color-link);
    text-decoration: none;
    text-decoration-thickness: 1.5px;
    text-underline-offset: 2px;
    /* Long URLs and titles must never force a horizontal scrollbar */
    overflow-wrap: break-word;
    transition: color var(--transition);
}

a:hover {
    color: var(--color-link-hover);
    text-decoration: underline;
}

/* Cap running text at a comfortable measure. The heading rule and the
   column itself still span the full width, so page-to-page alignment
   is unaffected — only the line length changes. */
.prose p,
.prose li {
    max-width: var(--measure);
}

.content-list {
    margin: 0 0 0.9rem 1.4rem;
}

.content-list li {
    margin-bottom: 0.55rem;
    text-wrap: pretty;
}

/* ==============================
   Papers and talks
   ============================== */

/* Both pages list the same kind of thing — a record with a title and
   stacked detail lines — so they share one type treatment and one
   hanging indent. Changing either here changes both. */
.paper,
.talk-list,
.impact-list {
    font-size: 1rem;
    line-height: 1.42;
}

/* The note annotates the list, not the heading, so it sits close to the
   list and keeps the h1's full gap above it — otherwise it lands
   equidistant between the two and reads as a fourth item in an evenly
   spaced stack. No margin-top here: h1's 1.4rem margin-bottom collapses
   against it and wins, so anything smaller would be inert. */
.paper-note {
    margin-bottom: 0.4rem;
    font-size: var(--size-small);
}

/* Adjacent sibling margins collapse, so .paper's 1.33rem would win and
   reopen the gap. Overriding it here is what keeps the note attached. */
.paper-note + .paper {
    margin-top: 0.4rem;
}

.paper {
    /* 21.3px — tuned so venue-to-next-title measures 44px, matching the
       original spacing. Not 1.4rem: the tighter line-height above removes
       ~1.1px of leading from the venue's line box, so the margin has to
       give that back. */
    margin-top: 1.33rem;
    /* A publication entry is a record, not running prose: its stacked
       title / authors / venue lines want tighter leading than the 1.6
       used for paragraphs, and a slightly smaller size. */
    font-size: 1rem;
    line-height: 1.42;
    /* No max-width on purpose. The longest title needs 784px and the
       column gives 816px, so every title fits on a single line — any
       narrower and they start wrapping. (The 16px size above is what
       buys that headroom; at 17px the longest title needs 833px.) */
    /* Titles are long; never let them overflow the column */
    overflow-wrap: break-word;
    /* Hanging indent: the number sits in the margin and every other line
       of the entry aligns to a single left edge. */
    position: relative;
    padding-left: var(--entry-indent);
}

.paper-num {
    position: absolute;
    left: 0;
    /* Right-aligned so "9." and "14." end on the same edge. The box is
       sized to the widest number ("14." inks 23.5px) so that number sits
       flush with the column edge rather than pushed in from it. */
    width: 1.6em;
    text-align: right;
}

/* Co-authors read at full body-text strength, like every other line on
   the site. Andrés's own name stands out by weight alone. */
.paper-authors b {
    font-weight: 800;
}

.paper-venue {
    font-style: italic;
}


/* ==============================
   Talks and impact
   ============================== */
/* Impact shares Talks' treatment rather than duplicating it: both are
   bulleted records sitting on the column edge, and they should stay
   identical if either is retuned. Changing these rules changes both.
   The bullet is drawn as a pinned pseudo-element rather than a list
   marker. A real marker is laid out relative to the list box, which put
   it inboard of the heading; this puts it on the column edge, exactly
   where a paper's number sits. */
.talk-list,
.impact-list {
    list-style: none;
    margin-left: 0;
}

.talk,
.impact-item {
    position: relative;
    padding-left: var(--talk-indent);
    /* Matches .paper, so the gap between entries is identical */
    margin-top: 1.33rem;
}

.talk:first-child,
.impact-item:first-child {
    margin-top: 0;
}

.talk::before,
.impact-item::before {
    content: "•";
    position: absolute;
    left: 0;
}

/* Impact entries are running sentences rather than short titles, so they
   get balanced wrapping and their own measure — wider than --measure
   (65ch), which wrapped every ~78 characters and read as cramped.
   78ch = 787px is the narrowest cap at which all four items still fit on
   two lines; the threshold is 784px, and below it the first and last go
   to three. That makes this knife-edge: editing an entry, or any font
   fallback that measures wider, will reflow it to three lines. Nothing
   breaks if that happens — but if two lines matter, re-check after
   editing the text. */
.impact-item {
    max-width: 78ch;
    text-wrap: pretty;
}

.talk-title {
    /* 700, as paper titles are */
    font-weight: 700;
    color: var(--color-text);
    text-wrap: pretty;
}

.talk-venues {
    list-style: none;
    /* No extra gap: a paper's venue line sits straight under its authors,
       and a talk's venues should do the same. */
}

/* ==============================
   Small screens
   ============================== */
@media (max-width: 680px) {
    .nav-toggle-label {
        display: flex;
    }

    .nav-links {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        z-index: 20;
        background-color: var(--color-nav-bg);
        flex-direction: column;
        align-items: stretch;
        padding: 0.5rem 0 0.75rem;
        /* The panel is the same cream as the page behind it, so it needs
           its own edge and a stronger shadow to read as a layer. Matches
           the header rule, so an open menu stays bounded in green. */
        border-bottom: 2px solid var(--color-accent);
        box-shadow: 0 10px 20px rgba(33, 31, 27, 0.16);
        gap: 0;
    }

    .nav-toggle-input:checked ~ .nav-links {
        display: flex;
    }

    /* Hamburger morphs into an X */
    .nav-toggle-input:checked + .nav-toggle-label span:nth-child(1) {
        transform: rotate(45deg) translate(4px, 4.5px);
    }

    .nav-toggle-input:checked + .nav-toggle-label span:nth-child(2) {
        opacity: 0;
    }

    .nav-toggle-input:checked + .nav-toggle-label span:nth-child(3) {
        transform: rotate(-45deg) translate(4px, -4.5px);
    }

    .nav-links li {
        display: block;
    }

    .nav-links li::after {
        display: none !important;
    }

    .nav-links a {
        /* Comfortable full-width tap targets */
        padding: 0.75rem var(--content-padding);
        font-size: 1.05rem;
        border-radius: 0;
    }

    .profile {
        flex-direction: column;
        gap: 1.75rem;
    }

    .profile-photo {
        order: -1;
        width: 150px;
    }
}

/* ==============================
   Motion preferences
   ============================== */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* ==============================
   Print
   ============================== */
@media print {
    .site-nav,
    .skip-link,
    .profile-photo {
        display: none;
    }

    body {
        background: #fff;
        color: #000;
        font-size: 10.5pt;
    }

    .page-content {
        max-width: none;
        padding: 0;
    }

    a {
        color: #000;
        text-decoration: underline;
    }

    /* Spell out link targets, which are invisible on paper */
    .paper a[href^="http"]::after,
    .content-list a[href^="http"]::after {
        content: " (" attr(href) ")";
        font-size: 8.5pt;
        word-break: break-all;
    }

    .paper,
    .talk {
        break-inside: avoid;
    }
}
