/* ============================================================================
   Martijn van der Linden — "The Working Record"
   Site shell: tokens, base typography, header, footer.
   Plain CSS, single stylesheet, no build step (ADR 0002).
   Design system of record: DESIGN.md at the repo root.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1. Fonts — self-hosted, variable woff2 (no third-party request). See
   theme/static/fonts/README.md. One file per family covers the weight axis.
   --------------------------------------------------------------------------- */

@font-face {
    font-family: "Libre Franklin";
    src: url("../fonts/libre-franklin-latin-wght-normal.bc34823f42d5.woff2") format("woff2");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Source Serif 4";
    src: url("../fonts/source-serif-4-latin-wght-normal.197cc326af6d.woff2") format("woff2");
    font-weight: 200 900;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Source Serif 4";
    src: url("../fonts/source-serif-4-latin-wght-italic.d40e4e4ee088.woff2") format("woff2");
    font-weight: 200 900;
    font-style: italic;
    font-display: swap;
}

/* ---------------------------------------------------------------------------
   2. Tokens
   --------------------------------------------------------------------------- */

:root {
    /* Color — ink on paper, one olive voice (DESIGN.md §2). Canonical OKLCH. */
    --color-bg:            oklch(1 0 0);
    --color-ink:           oklch(0.21 0.012 120);
    --color-slate:         oklch(0.44 0.012 120);
    --color-hairline:      oklch(0.90 0.008 120);
    --color-mist:          oklch(0.975 0.006 120);
    --color-primary:       oklch(0.46 0.105 118);
    --color-primary-deep:  oklch(0.36 0.09 120);
    --color-primary-tint:  oklch(0.95 0.03 115);

    /* Type families — sans builds the room, serif holds the words (DESIGN.md §3). */
    --font-sans:  "Libre Franklin", "Helvetica Neue", Arial, sans-serif;
    --font-serif: "Source Serif 4", Georgia, "Times New Roman", serif;

    /* Type scale — fluid display/headline, fixed reading sizes (rem, never px). */
    --text-display:  clamp(2rem, 1.1rem + 3.8vw, 4rem);
    --text-headline: clamp(1.6rem, 1.3rem + 1.3vw, 2.1rem);
    --text-title:    1.375rem;
    --text-body:     1.125rem;
    --text-ui:       0.9375rem;
    --text-label:    0.75rem;

    --leading-tight: 1.05;
    --leading-snug:  1.15;
    --leading-title: 1.3;
    --leading-body:  1.65;
    --leading-ui:    1.5;

    --tracking-display:  -0.02em;
    --tracking-headline: -0.01em;
    --tracking-label:     0.08em;

    /* Spacing — 4pt scale. */
    --space-2xs: 0.25rem;
    --space-xs:  0.5rem;
    --space-sm:  0.75rem;
    --space-md:  1rem;
    --space-lg:  1.5rem;
    --space-xl:  2rem;
    --space-2xl: 3rem;
    --space-3xl: 4rem;
    --space-4xl: 6rem;

    /* Radii. */
    --radius-sm:   4px;
    --radius-pill: 999px;

    /* Layout. */
    --container: 64rem;
    --gutter:    clamp(1.25rem, 5vw, 2.5rem);
    --measure:   68ch;

    /* Motion — "responsive": state changes + one quiet settle. */
    --ease-out:    cubic-bezier(0.22, 1, 0.36, 1);
    --dur-state:   160ms;
    --dur-settle:  400ms;

    /* Semantic z-index scale. */
    --z-header: 100;
    --z-skiplink: 200;

    color-scheme: light;
}

/* ---------------------------------------------------------------------------
   3. Reset — minimal; keeps list/heading semantics intact for rich text.
   --------------------------------------------------------------------------- */

*,
*::before,
*::after {
    box-sizing: border-box;
}

* {
    margin: 0;
}

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

img,
picture,
svg {
    display: block;
    max-width: 100%;
}

button,
input,
textarea,
select {
    font: inherit;
    color: inherit;
}

::selection {
    background: var(--color-primary-tint);
    color: var(--color-ink);
}

/* ---------------------------------------------------------------------------
   4. Base typography
   --------------------------------------------------------------------------- */

body {
    /* Sticky footer: header / main / footer in a full-height column so the
       footer always rests at the bottom, even on short pages. */
    display: flex;
    flex-direction: column;
    min-height: 100svh;
    background: var(--color-bg);
    color: var(--color-ink);
    font-family: var(--font-serif);
    font-size: var(--text-body);
    line-height: var(--leading-body);
    font-optical-sizing: auto;
    font-kerning: normal;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

/* Architecture is sans; reading is serif. */
h1, h2, h3, h4 {
    font-family: var(--font-sans);
    color: var(--color-ink);
    text-wrap: balance;
    /* Long publication titles must wrap, never overflow on narrow screens. */
    overflow-wrap: break-word;
    hyphens: auto;
}

h1 {
    font-size: var(--text-display);
    font-weight: 800;
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-display);
}

h2 {
    font-size: var(--text-headline);
    font-weight: 700;
    line-height: var(--leading-snug);
    letter-spacing: var(--tracking-headline);
}

h3 {
    font-size: var(--text-title);
    font-weight: 600;
    line-height: var(--leading-title);
}

h4 {
    font-size: var(--text-ui);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
}

p {
    text-wrap: pretty;
}

strong, b {
    font-weight: 600;
}

em, i {
    font-style: italic;
}

small {
    font-size: var(--text-ui);
}

/* In-text links carry the olive voice (DESIGN.md "The One Voice Rule"). */
a {
    color: var(--color-primary);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.15em;
    transition: color var(--dur-state) var(--ease-out);
}

a:hover {
    color: var(--color-primary-deep);
}

/* Focus — a visible 2px olive ring on every interactive element. */
:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
    border-radius: 2px;
}

:focus:not(:focus-visible) {
    outline: none;
}

/* Primary action — the solid Deep Olive button (DESIGN.md §5). Shared by the
   Download PDF call to action and the search submit; works as <button> or <a>. */
.button-primary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    font-weight: 500;
    line-height: 1;
    color: var(--color-bg);
    background: var(--color-primary-deep);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: 0.7rem 1.4rem;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--dur-state) var(--ease-out);
}

.button-primary:hover {
    background: var(--color-ink);
    color: var(--color-bg);
}

/* ---------------------------------------------------------------------------
   5. Layout — one centered column shared by header, page, footer.
   --------------------------------------------------------------------------- */

.site-header__inner,
.page,
.site-footer__inner {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

.page {
    /* Grow to fill the column so the footer is pushed to the bottom. */
    flex: 1 0 auto;
    padding-block: clamp(var(--space-2xl), 6vw, var(--space-4xl));
    /* A quiet page-load settle (see reduced-motion override below). */
    animation: settle var(--dur-settle) var(--ease-out) both;
}

/* Rhythm between top-level blocks on a page. */
.page > * + * {
    margin-top: var(--space-lg);
}

@keyframes settle {
    from {
        opacity: 0;
        transform: translateY(8px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* Skip link — first focusable element; reveals on focus. */
.skip-link {
    position: absolute;
    left: var(--gutter);
    top: 0;
    z-index: var(--z-skiplink);
    transform: translateY(-150%);
    background: var(--color-ink);
    color: var(--color-bg);
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    font-weight: 500;
    text-decoration: none;
    padding: var(--space-xs) var(--space-md);
    border-radius: var(--radius-sm);
    transition: transform var(--dur-state) var(--ease-out);
}

.skip-link:focus {
    transform: translateY(var(--space-sm));
    color: var(--color-bg);
}

/* ---------------------------------------------------------------------------
   6. Rich-text page body (Standard Pages: Home, About, Contact)
   --------------------------------------------------------------------------- */

.page-body {
    max-width: var(--measure);
}

.page-body > * + * {
    margin-top: var(--space-md);
}

.page-body h2 {
    margin-top: var(--space-2xl);
}

.page-body h3 {
    margin-top: var(--space-xl);
}

.page-body ul,
.page-body ol {
    padding-left: 1.3em;
}

.page-body ul {
    list-style: disc;
}

.page-body ol {
    list-style: decimal;
}

.page-body li + li {
    margin-top: var(--space-2xs);
}

.page-body li::marker {
    color: var(--color-slate);
}

/* Quiet block quote — indentation + voice, never a colored side-stripe. */
.page-body blockquote {
    margin-inline: 0;
    padding-left: var(--space-lg);
    color: var(--color-slate);
    font-style: italic;
}

/* ---------------------------------------------------------------------------
   7. Header — brand left, page-tree nav right, one hairline below.
   --------------------------------------------------------------------------- */

.site-header {
    border-bottom: 1px solid var(--color-hairline);
}

.site-header__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-xs) var(--space-xl);
    padding-block: var(--space-lg);
}

.site-brand {
    font-family: var(--font-sans);
    font-size: 1.125rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--color-ink);
    text-decoration: none;
}

.site-brand:hover {
    color: var(--color-primary);
}

.site-nav__list {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-lg);
    list-style: none;
    padding: 0;
}

.site-nav__link {
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    font-weight: 500;
    color: var(--color-slate);
    text-decoration: none;
    padding-block: var(--space-2xs);
    border-bottom: 2px solid transparent;
    transition: color var(--dur-state) var(--ease-out),
                border-color var(--dur-state) var(--ease-out);
}

.site-nav__link:hover {
    color: var(--color-ink);
}

/* Active section: ink text + the single olive underline indicator. */
.site-nav__link.is-active {
    color: var(--color-ink);
    border-bottom-color: var(--color-primary);
}

/* Narrowest viewports: brand stacks above the nav row (DESIGN.md §5), so the
   menu never overflows the viewport. Roomier tap targets on touch screens. */
@media (max-width: 28rem) {
    .site-header__inner {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-sm);
    }

    .site-nav__link {
        padding-block: var(--space-xs);
    }
}

/* ---------------------------------------------------------------------------
   8. Footer — one hairline above; quiet sans, plenty of air.
   Hand-edited shell (ADR 0003): no settings model behind it.
   --------------------------------------------------------------------------- */

.site-footer {
    margin-top: var(--space-4xl);
    border-top: 1px solid var(--color-hairline);
}

.site-footer__inner {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
    padding-block: var(--space-2xl);
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    line-height: var(--leading-ui);
    color: var(--color-slate);
}

.site-footer__name {
    font-weight: 600;
    color: var(--color-ink);
}

.site-footer__legal {
    margin-top: var(--space-md);
    font-size: var(--text-label);
}

/* ---------------------------------------------------------------------------
   9. Publication Index — search, filter chips, the ledger of rows.
   The signature surface (DESIGN.md §5): a bibliographic ledger, not cards.
   --------------------------------------------------------------------------- */

/* Accessible-only label (search field). */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* --- Search ------------------------------------------------------------- */

.publication-search {
    margin-top: var(--space-2xl);
    max-width: 34rem;
}

.publication-search__field {
    display: flex;
    gap: var(--space-xs);
}

.publication-search input[type="search"] {
    flex: 1 1 auto;
    min-width: 0;
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    color: var(--color-ink);
    background: var(--color-bg);
    border: 1px solid var(--color-hairline);
    border-radius: var(--radius-sm);
    padding: 0.7rem 0.9rem;
    transition: border-color var(--dur-state) var(--ease-out);
}

.publication-search input[type="search"]::placeholder {
    color: var(--color-slate);
}

.publication-search input[type="search"]:focus-visible {
    border-color: var(--color-primary);
}

.publication-search button {
    flex: 0 0 auto; /* sits beside the input; doesn't shrink */
}

/* --- Filter chips ------------------------------------------------------- */

.publication-filter {
    margin-top: var(--space-lg);
}

.publication-filter ul {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
    list-style: none;
    padding: 0;
}

.publication-filter-option a {
    display: inline-block;
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    line-height: 1;
    color: var(--color-slate);
    background: var(--color-bg);
    border: 1px solid var(--color-hairline);
    border-radius: var(--radius-pill);
    padding: 0.45rem 0.9rem;
    text-decoration: none;
    transition: background var(--dur-state) var(--ease-out),
                color var(--dur-state) var(--ease-out),
                border-color var(--dur-state) var(--ease-out);
}

.publication-filter-option a:hover {
    background: var(--color-mist);
    color: var(--color-ink);
}

/* Selected type: the one solid olive chip (the active state). */
.publication-filter-option.is-active a {
    background: var(--color-primary-deep);
    border-color: var(--color-primary-deep);
    color: var(--color-bg);
}

/* --- Result count ------------------------------------------------------- */

.publication-count {
    margin-top: var(--space-2xl);
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    color: var(--color-slate);
}

/* --- The ledger --------------------------------------------------------- */

.publication-list {
    margin-top: var(--space-sm);
    list-style: none;
    padding: 0;
    border-top: 1px solid var(--color-hairline);
}

.publication-list-item {
    /* Full-bleed hover within the column: negative inline margin + padding. */
    margin-inline: calc(var(--space-md) * -1);
    padding: var(--space-xl) var(--space-md);
    border-bottom: 1px solid var(--color-hairline);
    transition: background var(--dur-state) var(--ease-out);
}

.publication-list-item:hover {
    background: var(--color-mist);
}

/* Title: the bibliographic serif voice (overrides the sans h2 base). Sized
   up from the base title token to give each ledger entry more presence. */
.publication-title {
    margin: 0;
    font-family: var(--font-serif);
    font-size: clamp(1.375rem, 1.15rem + 0.9vw, 1.625rem);
    font-weight: 600;
    line-height: var(--leading-title);
    letter-spacing: normal;
    text-wrap: balance;
}

.publication-title a {
    color: var(--color-primary);
    text-decoration: none;
}

.publication-title a:hover {
    color: var(--color-primary-deep);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.publication-meta {
    margin-top: var(--space-2xs);
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    color: var(--color-slate);
}

.publication-year {
    font-variant-numeric: tabular-nums;
}

.publication-authors::before {
    content: "·";
    margin-inline: 0.5ch;
}

/* Type tags: quiet, non-interactive — never confused with filter chips. */
.publication-types {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2xs);
    list-style: none;
    padding: 0;
    margin-top: var(--space-sm);
}

.publication-type {
    font-family: var(--font-sans);
    font-size: var(--text-label);
    font-weight: 600;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--color-slate);
    background: var(--color-mist);
    border-radius: var(--radius-sm);
    padding: 0.2rem 0.5rem;
}

/* --- Empty state -------------------------------------------------------- */

.publication-empty {
    margin-top: var(--space-2xl);
    padding-block: var(--space-2xl);
    border-top: 1px solid var(--color-hairline);
    text-align: center;
    font-family: var(--font-sans);
    color: var(--color-slate);
}

.publication-empty__clear {
    display: inline-block;
    margin-top: var(--space-sm);
    font-size: var(--text-ui);
}

/* ---------------------------------------------------------------------------
   10. Publication detail — the destination page. Title, meta, abstract set at
   reading measure, and the Download PDF primary action (DESIGN.md §5).
   --------------------------------------------------------------------------- */

/* Generous rhythm between the header, the abstract, and the download. */
.publication > * + * {
    margin-top: var(--space-2xl);
}

/* Header group: meta and type tags hug the title. */
.publication__header .publication-meta {
    margin-top: var(--space-md);
}

.publication__header .publication-types {
    margin-top: var(--space-md);
}

/* Abstract: a section heading, then the summary held to a reading measure
   (.page-body carries the 68ch cap and paragraph/list rhythm). */
.publication-abstract > * + * {
    margin-top: var(--space-md);
}

/* Download — the single most important action on the site. Solid primary
   button, not a text link. Sits left, sized to its label. */
.publication-download {
    margin-top: var(--space-2xl);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm);
}

/* ---------------------------------------------------------------------------
   11. Search results — same search field and ledger voice as the index.
   --------------------------------------------------------------------------- */

.search-results__description {
    margin-top: var(--space-2xs);
    font-family: var(--font-serif);
    color: var(--color-slate);
    max-width: var(--measure);
}

/* Prev / next — quiet sans links spaced apart, hairline above. */
.search-pagination {
    display: flex;
    justify-content: space-between;
    gap: var(--space-md);
    margin-top: var(--space-xl);
    padding-top: var(--space-lg);
    border-top: 1px solid var(--color-hairline);
    font-family: var(--font-sans);
    font-size: var(--text-ui);
}

.search-pagination__next {
    margin-left: auto;
}

/* ---------------------------------------------------------------------------
   12. Reduced motion — every transition/animation has a still alternative.
   --------------------------------------------------------------------------- */

@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;
    }
}

/* --- Contact form ------------------------------------------------------- */

.contact-form {
    --color-error: oklch(0.45 0.15 27); /* form errors only; ~7:1 on white */

    margin-top: var(--space-2xl);
    max-width: 34rem;
    display: grid;
    gap: var(--space-xl);
}

.contact-form__field {
    display: grid;
    gap: var(--space-sm);
    min-width: 0;
    border: 0;
    padding: 0;
}

.contact-form label,
.contact-form legend {
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    font-weight: 500;
    line-height: var(--leading-ui);
    color: var(--color-ink);
}

.contact-form legend {
    float: left; /* lets the fieldset's grid gap apply like it does for labels */
    width: 100%;
    padding: 0;
}

.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form textarea {
    width: 100%;
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    line-height: var(--leading-ui);
    color: var(--color-ink);
    background-color: var(--color-bg);
    border: 1px solid var(--color-hairline);
    border-radius: var(--radius-sm);
    padding: 0.7rem 0.9rem;
    transition: border-color var(--dur-state) var(--ease-out);
}

.contact-form input[type="text"]:hover,
.contact-form input[type="email"]:hover,
.contact-form textarea:hover {
    border-color: var(--color-slate);
}

.contact-form input[type="text"]:focus-visible,
.contact-form input[type="email"]:focus-visible,
.contact-form textarea:focus-visible {
    border-color: var(--color-primary);
}

.contact-form textarea {
    min-height: 10rem;
    resize: vertical;
}

/* Topic — a radio group drawn as the same pill chips as the publication-type
   filter, so the choices are all visible at once instead of hidden in a native
   dropdown. The radio itself stays in the DOM (keyboard, screen readers). */
.contact-form__choices > div {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
}

.contact-form__choices > div > div {
    position: relative;
}

.contact-form__choices label {
    display: inline-block;
    font-size: 0.8125rem;
    line-height: 1;
    color: var(--color-slate);
    background: var(--color-bg);
    border: 1px solid var(--color-hairline);
    border-radius: var(--radius-pill);
    padding: 0.6rem 1rem;
    cursor: pointer;
    transition: background var(--dur-state) var(--ease-out),
                color var(--dur-state) var(--ease-out),
                border-color var(--dur-state) var(--ease-out);
}

.contact-form__choices label:hover {
    background: var(--color-mist);
}

.contact-form__choices input {
    position: absolute;
    opacity: 0;
    inset: 0;
    margin: 0;
    cursor: pointer;
}

.contact-form__choices label:has(input:checked) {
    color: var(--color-bg);
    background: var(--color-primary-deep);
    border-color: var(--color-primary-deep);
}

.contact-form__choices label:has(input:focus-visible) {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* Invalid fields carry the error in border and message, never colour alone. */
.contact-form :is(input, textarea)[aria-invalid="true"],
.contact-form :is(input, textarea)[aria-invalid="true"]:hover,
.contact-form__choices:has([aria-invalid="true"]) label:not(:has(:checked)) {
    border-color: var(--color-error);
}

.contact-form__error {
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    font-weight: 500;
    color: var(--color-error);
}

.contact-form button {
    justify-self: start;
}

.contact-form__sent {
    margin-top: var(--space-2xl);
    font-family: var(--font-sans);
    font-size: var(--text-ui);
    font-weight: 500;
}
