* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

:root {
    /* Brand fonts, declared once so a different typeface can be dropped in here
       (plus the @font-face in fonts.css) without touching every rule. Three
       roles: the display font is the site name in the header (Grenze Gotisch,
       blackletter); the heading font is every section/article heading and the
       big nav category labels (Grenze); the body font carries all running
       copy + UI (IBM Plex Sans). Keep the generic fallback in each value. */
    --font-body: 'IBM Plex Sans', sans-serif;
    --font-heading: 'Grenze', serif;
    --font-display: 'Grenze Gotisch', serif;

    /* User-selectable text scale (Options → font size). Multiplies font sizes
       only — never the rem-based spacing/layout — so the page just reads larger
       or smaller. Medium is exactly 1 (identical rendering). The category nav
       label is intentionally excluded: its size feeds layout geometry, so it
       stays fixed (see --category-label-size). */
    --font-scale: 1;

    --text-color: #000;
    --bg-color: #fff;
    /* Theme flip: animate background, text AND border together so the whole page
       crosses over as one (borders previously snapped instantly, which read as a
       stagger against the fading backgrounds). */
    --page-color-transition: background-color .2s ease, color .2s ease, border-color .2s ease;
    --color-work: #ffd3b6;
    --color-play: #a8e6cf;
    --color-thoughts: #dcedc1;
    --color-contact: #ffaaa5;
    --label-bg-color: #fff;
    /* Horizontal breathing room on wide monitors: grows once the viewport
       exceeds the content band, otherwise a flat 2rem gutter. */
    --content-inset: max(2rem, calc((100vw - 1400px) / 2));
    /* Size of the active category label ("Work"/"Contact"/…). Deterministic,
       stepped at the same breakpoints the rest of the layout uses, so the label
       no longer drifts continuously with viewport width. Shared so layouts can
       clear the label that the content scrolls under. */
    --category-label-size: 2.5rem;
    /* Extra inset (beyond the base 2rem corner) that the active label gains to
       ride out to the content gutter. Zero until the viewport passes ~1528px,
       then it tracks the growing gutter so the label lands on the same
       content-inset line as the page content. */
    --label-gutter-shift: max(0px, calc(var(--content-inset) - 4rem));
    /* Deterministic Work project image-column width. Shared with the search bar
       so the bar can align its left edge to the text column (cqw resolves
       against .work-layout at each use site). */
    --work-image-col: clamp(300px, 46cqw, 600px);
    /* Width reserved for the Work year timeline on the right of the content. */
    --work-timeline-w: 60px;
    /* Shared max width for the search pills (Work + Thoughts). */
    --search-cap: 500px;
    /* Blueprint grid lines drawn on the Play canvas: slightly darker than the
       Play background in light mode. */
    --play-grid-major: rgba(0, 0, 0, 0.11);
    --play-grid-minor: rgba(0, 0, 0, 0.05);
}

html[data-font-size="small"] { --font-scale: 0.875; }
html[data-font-size="large"] { --font-scale: 1.15; }

/* Dyslexia-friendly font option: swaps both the body font and the heading font
   to OpenDyslexic (so headings don't stay in the harder-to-read display serif),
   defeating the accessibility intent. The site-name display font is left as-is. */
html[data-font-face="dyslexic"] {
    --font-body: 'OpenDyslexic', sans-serif;
    --font-heading: 'OpenDyslexic', sans-serif;
}

[data-theme="dark"] {
    --text-color: #fff;
    --bg-color: #000;
    --color-work: #592408;
    --color-play: #0b452f;
    --color-thoughts: #274503;
    --color-contact: #540e0b;
    --label-bg-color: #000;
    /* Brighter than the Play background in dark mode. */
    --play-grid-major: rgba(255, 255, 255, 0.12);
    --play-grid-minor: rgba(255, 255, 255, 0.05);
}

@media (min-width: 768px) {
    :root { --category-label-size: 3.5rem; }
}

@media (min-width: 1025px) {
    :root { --category-label-size: 4.5rem; }
}

body {
    background-color: var(--bg-color);
    color: var(--text-color);
    font-family: var(--font-body);
    /* Base size for all inheriting text; the multiplier is the only thing the
       font-size option changes (rem keeps spacing fixed). */
    font-size: calc(1rem * var(--font-scale));
    transition: var(--page-color-transition);
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior-y: auto;
    height: 100dvh;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* Manual "Reduce animations" option (Options menu). Mirrors the media query
   above so the same universal duration-zero applies when the user opts in,
   regardless of their OS setting. (The option never re-enables motion for a
   system-reduce user — it only ever adds reduction.) */
html[data-reduce-motion] *,
html[data-reduce-motion] *::before,
html[data-reduce-motion] *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
}

body::after {
    content: '';
    position: fixed;
    inset: 0;
    border: 1px solid var(--text-color);
    pointer-events: none;
    z-index: 9999;
    transition: var(--page-color-transition);
}

header h1, .category-trigger {
    user-select: none;
    -webkit-user-select: none;
    -webkit-user-drag: none;
}

.category-content {
    position: absolute;
    inset: 0;
    z-index: 5;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.25s ease-out, visibility 0.25s ease-out;
}


.content-viewport {
    width: 100%;
    height: 100%;
    padding: 2rem;
    overflow-y: auto;
    overflow-x: hidden;
    color: var(--text-color);
    display: flex;
    flex-direction: column;
    gap: 2rem;
}

#work-content .content-viewport {
    display: block;
    padding-left: var(--content-inset);
    /* Reserve room on the right for the year timeline (+ its 2rem gutter). */
    padding-right: calc(var(--content-inset) + var(--work-timeline-w) + 2rem);
}

@media (max-width: 1024px) {
    #work-content .content-viewport,
    .content-viewport {
        padding-right: 28px;
    }
}


.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
}

/* Material Symbols, inlined as SVG via the #i-* sprite at the top of <body>.
   Sized in em so font-size controls them; themed through currentColor. */
.ms-icon {
    width: 1em;
    height: 1em;
    flex-shrink: 0;
    fill: currentColor;
    vertical-align: middle;
}

/* Accessibility Focus States */
:focus-visible {
    outline: 2px dashed var(--text-color);
    outline-offset: 2px;
}

[tabindex="-1"]:focus {
    outline: none !important;
}

/* --- Skip to content (keyboard) ----------------------------------------- */
.skip-link {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 10000;
    padding: 0.6rem 1rem;
    background: var(--text-color);
    color: var(--bg-color);
    font-family: var(--font-body);
    font-size: 1.1rem;
    text-decoration: none;
    transform: translateY(-120%);
    transition: transform 0.15s ease;
}

.skip-link:focus {
    transform: translateY(0);
    outline: 2px dashed var(--bg-color);
    outline-offset: -6px;
}

/* --- Keyboard shortcuts modal ------------------------------------------- */
.kbd-modal {
    position: fixed;
    inset: 0;
    z-index: 4000;
    display: flex;
    align-items: center;
    justify-content: center;
}

.kbd-modal[hidden] {
    display: none;
}

.kbd-modal-backdrop {
    position: absolute;
    inset: 0;
    background: color-mix(in srgb, var(--bg-color) 70%, transparent);
    backdrop-filter: blur(4px);
    opacity: 0;
    transition: opacity 0.3s ease;
}

.kbd-modal.active .kbd-modal-backdrop {
    opacity: 1;
}

.kbd-modal-panel {
    position: relative;
    width: min(440px, calc(100vw - 2rem));
    background: var(--bg-color);
    border: 1px solid var(--text-color);
    border-radius: 20px;
    padding: 1.75rem;
    transform: translateY(24px);
    opacity: 0;
    transition: transform 0.3s cubic-bezier(0.2, 0, 0.2, 1), opacity 0.3s ease;
}

.kbd-modal.active .kbd-modal-panel {
    transform: translateY(0);
    opacity: 1;
}

.kbd-modal-panel h2 {
    margin: 0 0 1.1rem;
    font-family: var(--font-heading);
    font-size: 1.7rem;
    font-weight: 400;
}

.kbd-list {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
}

.kbd-row {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.kbd-row dt {
    flex-shrink: 0;
    display: flex;
    gap: 0.3rem;
}

.kbd-row dd {
    margin: 0;
    font-size: 1.05rem;
    line-height: 1.3;
}

.kbd-row kbd {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.7rem;
    height: 1.7rem;
    padding: 0 0.4rem;
    border: 1px solid var(--text-color);
    border-radius: 6px;
    font-family: var(--font-body);
    font-size: 1rem;
    line-height: 1;
}

.kbd-modal-close {
    position: absolute;
    top: 0.85rem;
    right: 0.85rem;
    width: 36px;
    height: 36px;
    border: none;
    background: transparent;
    cursor: pointer;
    color: var(--text-color);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
}

@media (prefers-reduced-motion: reduce) {
    .skip-link,
    .kbd-modal-panel,
    .kbd-modal-backdrop {
        transition: none;
    }
}

