html, body { margin: 0; }
body {
    min-height: 100vh;
    background: var(--page);
    color: var(--dark);
    font-family: system-ui, sans-serif;
}
h1 { margin-top: 0; margin-bottom: 0; text-align: center; }
/* The art box: fixed, 5% from every viewport edge, whatever the aspect ratio. The picture is composed of three SVGs:
   the sun (fixed size S, top left) and the two long rays, which are stretched along their length only (thickness stays).
   --u is one SVG unit: the sun's 140x140 box is S wide. The border is the old page border, as a CSS gradient on ::after. */
.art {
    position: fixed; inset: 5vw; container-type: size; overflow: clip; /* the rays' ends reach past the box */
    --S: min(clamp(6rem, 23cqw, 40cqw), 40cqh); --u: calc(var(--S) / 140);
    background: var(--page);
}
/* The page border (.5% of the art width, as in the former SVG: 3 of 600 units), drawn over the rays. */
.art::after { content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none; border: max(2px, .5cqw) solid; border-image: linear-gradient(135deg, var(--dark), var(--light)) 1; }
.art-part { position: absolute; display: block; overflow: visible; } /* rays reach slightly past the 140 box */
.sun { left: 0; top: 0; width: var(--S); height: var(--S); }
.ray-v { left: 0; top: calc(97 * var(--u)); width: var(--S); height: calc(100% - 96.5 * var(--u)); } /* starts with a short vertical stub inside the sun ring, so the stretch (which tilts the start of the curve) never opens a gap */
.ray-h { top: 0; left: calc(98 * var(--u)); height: var(--S); width: calc(100% - 97.5 * var(--u)); }
/* Nav: the strip left of the vertical ray (at least 50 units wide at any height), bottom left; font scales with the sun. */
nav { position: absolute; z-index: 1; left: calc(10 * var(--u)); bottom: calc(10 * var(--u)); display: flex; flex-direction: column; gap: calc(var(--S) / 25); font-size: clamp(.75rem, calc(var(--S) / 14.6), 16pt); }
nav a { color: var(--dark); text-decoration: none; }
nav a[aria-current] { font-weight: bold; text-decoration: underline; }
/* Content area: right of the vertical ray (which reaches 102 units) and below the horizontal one (down to 80 units); scrolls if longer. */
main { position: absolute; left: calc(120 * var(--u)); right: 5%; top: calc(120 * var(--u)); bottom: 2.5%; overflow-y: auto; font-size: clamp(1rem, 1.6cqw, 16pt); display: flex; flex-direction: column; }
main > * { flex-shrink: 0; }
/* Small art box (phones, small windows): the strip is too narrow for readable text, so the nav lies in the band right of the sun, above the horizontal ray (free above 55 units). */
@container (max-width: 48rem) or (max-height: 28rem) {
    nav { left: calc(148 * var(--u)); right: 3%; top: 0; bottom: auto; height: calc(52 * var(--u)); flex-direction: row; align-items: center; gap: .2rem .6rem; font-size: min(.8rem, 3.8cqw); flex-wrap: nowrap; overflow: hidden; }
}
/* 404 page: a large emoji centered in <main>, nudged up by 7.5% of its height. */
.not-found { min-height: 85%; display: flex; align-items: center; justify-content: center; font-size: 4rem; }
/* Legal pages: compact German text inside <main>. */
.legal { font-size: .9rem; hyphens: auto; }
.legal h2 { margin-top: 0; font-size: 1.3rem; }
.legal h3 { font-size: 1.05rem; }
.legal h4 { font-size: .95rem; }
.legal a { color: var(--dark); overflow-wrap: anywhere; }
.legal blockquote { margin: 1.5rem 0; padding-left: 1rem; border-left: 3px solid var(--light); }
/* Site footer: last item in <main>, pinned to its bottom (margin-top: auto) until longer content pushes it down. */
footer { margin-top: auto; padding-top: 1rem; text-align: center; font-size: .75rem; }
footer p { margin: .25rem 0; }
footer .license { display: inline-block; } /* wraps as a unit when the line is too narrow */
footer a { color: var(--dark); text-decoration: none; }
footer a:hover, footer a:focus-visible { text-decoration: underline; }
.projects { list-style: none; margin: 0; padding: 0; }
.projects li { display: flex; align-items: center; gap: 1.5rem; margin-bottom: 1.5rem; }
.projects li:nth-child(even) { flex-direction: row-reverse; text-align: right; } /* alternating alignment */
.projects li > div { min-width: 0; overflow-wrap: anywhere; hyphens: auto; }
.projects li img { flex-shrink: 0; }
.projects h2 { margin: 0 0 .25rem; font-size: 1.1rem; }
.projects p { margin: 0 0 .25rem; }
.projects a { color: var(--dark); }
.gallery { display: flex; flex-wrap: wrap; gap: 1.5rem; margin: 0; padding: 0; list-style: none; container-type: inline-size; }
@container (max-width: 31rem) { .gallery { justify-content: center; } } /* too narrow for two frames: single column, centered */
/* Sheet frame: 225px (A4 ratio), 3.5px gradient border like the page border but rotated by 180 degrees. */
.frame {
    position: relative; width: 225px; max-width: calc(100% - 7px); box-sizing: content-box; margin: 0;
    border: 3.5px solid transparent;
    background: linear-gradient(var(--page), var(--page)) padding-box, linear-gradient(135deg, var(--light), var(--dark)) border-box;
}
.frame img { display: block; width: 225px; max-width: 100%; aspect-ratio: 1 / 1.414; object-fit: cover; background: #fff; }
.frame.fixed-height img { width: 100%; height: 300px; aspect-ratio: auto; object-fit: contain; object-position: center; background: none; } /* same height as an A4 sheet at 225px; wider images shrink to the frame width and sit vertically centered */
.frame .bar { display: flex; flex-direction: column; align-items: stretch; gap: .4rem; padding: .4rem; }
.frame .title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .85rem; font-weight: bold; color: var(--dark); } /* on its own line, ellipsized so all frames have equal height */
.frame .downloads { display: flex; gap: .5rem; } /* buttons at least 24px tall, with room between them */
.frame .downloads :is(a, span) { padding: .5rem .7rem; font-size: .75rem; font-weight: bold; color: var(--page); }
.frame .downloads a { position: relative; overflow: hidden; text-decoration: none;
    background: linear-gradient(115deg, var(--dark) 0 50%, var(--light) 50% 100%) 0 0 / 220% 100%; /* hard-edged diagonal sweep: hover slides the light half in */
    transition: background-position .45s cubic-bezier(.2,.8,.2,1), color .45s; }
.frame .downloads a::after { content: ""; position: absolute; inset: 0 auto 0 -60%; width: 40%; transform: skewX(-20deg); background: linear-gradient(90deg, transparent, var(--page), transparent); opacity: 0; pointer-events: none; } /* shine streak */
.frame .downloads span { background: var(--dark); opacity: .35; cursor: not-allowed; }
.frame .downloads a:hover, .frame .downloads a:focus-visible { background-position: 100% 0; color: var(--dark); }
.frame .downloads a:hover::after, .frame .downloads a:focus-visible::after { animation: shine .7s ease-out; }
.frame .downloads a:active { transform: translateY(1px); }
@keyframes shine { 0% { left: -60%; opacity: .7; } 100% { left: 130%; opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .frame .downloads a { transition: none; } .frame .downloads a:hover::after, .frame .downloads a:focus-visible::after { animation: none; } }
/* Background art: the sun rays (not the page border) swap their dark and light gradient ends back and forth; the rays, the inner ring and the face (eyes, mouth) all share one cycle and phase. Only without a reduced-motion preference. */
@keyframes glow { to { stop-color: var(--light); } }
@keyframes glow-rev { to { stop-color: var(--dark); } }
@keyframes glow-stroke { to { stroke: var(--light); } }
@media (prefers-reduced-motion: no-preference) {
    .art-part linearGradient > stop:first-child { animation: glow 3s ease-in-out infinite alternate; }
    .art-part linearGradient > stop:last-child { animation: glow-rev 3s ease-in-out infinite alternate; }
    .art-part :is([data-curve-id="c5"], [data-curve-id="c6"], [data-curve-id="c7"], [data-curve-id="c8"], [data-curve-id="c20"], [data-curve-id="c21"], [data-curve-id="c22"]) { animation: glow-stroke 3s ease-in-out infinite alternate; }
}
/* Keyboard focus: a predictable outline that only appears for keyboard users. */
/* (the same links as in the forced-colors block below) */
:is(nav, .frame .downloads, .projects, .legal, footer) a:focus-visible { outline: 2px solid var(--dark); outline-offset: 2px; }
/* Users who ask for more contrast: black on white, regardless of the random palette. */
@media (prefers-contrast: more) {
    :root { --dark: #000; --page: #fff; }
    nav a, footer a { text-decoration: underline; }
}
/* Forced colors (e.g. Windows High Contrast): system colors for borders, buttons and focus. */
@media (forced-colors: active) {
    .frame { border-color: CanvasText; }
    .frame .downloads a, .frame .downloads span { border: 1px solid ButtonText; }
    nav a, footer a { text-decoration: underline; }
    :is(nav, .frame .downloads, .projects, .legal, footer) a:focus-visible { outline-color: Highlight; }
}
