/*
 * rozly.nl — the site's whole appearance.
 *
 * A file rather than a <style> in the layout: the browser fetches it once and reuses it
 * on every page.
 *
 * Utility classes, so the markup names what a thing looks like rather than what it is —
 * a template composes `.jb .g2 .dim2` instead of naming a component, which produces a
 * genuinely different document even where the page underneath is an ordinary listing.
 *
 * The names are short to the point of being terse, and that is the scheme rather than an
 * accident of it.
 *
 * This file is served to anyone who asks for it, so it says nothing about any site but
 * this one. That is not squeamishness: a comment naming another domain is a link between
 * them that needs no crawler to find — open the stylesheet and read it. The reasoning
 * that used to sit here, comparing this scheme against the ones next door, belongs in
 * the commit that made the choice, where git keeps it and no visitor is served it.
 */

:root {
    --bg: #0f1720;
    --surface: #17212b;
    --surface-2: #1e2b38;
    --line: #26374a;
    --text: #e6edf3;
    --muted: #8fa3b8;
    --accent: #4db6ac;
    --accent-ink: #06302c;
    --radius: 999px;
    --radius-box: 18px;
}

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

body {
    background: var(--bg);
    color: var(--text);
    /* A grotesque, no webfont: the German pair runs Segoe/system-ui and
       Georgia, so the face differs from both without fetching anything from a third
       party — which every page of every site would otherwise do against one host. */
    font-family: "Helvetica Neue", Arial, "Liberation Sans", sans-serif;
    font-size: 16px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }

/* ---- layout utilities ------------------------------------------------- */

.wr { width: 100%; max-width: 1240px; margin: 0 auto; padding: 0 20px; }
.st > * + * { margin-top: 14px; }
.gy > * + * { margin-top: 34px; }
.row { display: flex; align-items: center; }
.jb { display: flex; align-items: center; justify-content: space-between; }
.wp { flex-wrap: wrap; }
.g1 { gap: 6px; }
.g2 { gap: 12px; }
.g3 { gap: 20px; }
/* Flex children refuse to shrink below their content, so a long city name would push
   its neighbour off the tile. Names are data — they cannot be designed around. */
.m0 { min-width: 0; }
.gr { flex: 1 1 auto; }
.cut { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctr { text-align: center; }

/* ---- type ------------------------------------------------------------- */

.h1 { font-size: 28px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.25; }
/* Vak en naam op één regel: de naam krimpt en wordt zo nodig afgekapt, het vak nooit —
   anders eet een lange naam juist het woord op dat de kop leesbaar maakt. `min-width: 0`
   is wat een flex-kind uberhaupt laat krimpen; zonder dat duwt de naam het vak eruit.

   Geen `gap`: het liggend streepje staat in de opmaak (zie profile.html.twig) en is hier
   zelf een flex-item, dus een gap zou er aan weerszijden ruimte omheen zetten. De scheiding
   hoort in de tekst thuis, want die wordt geïndexeerd; de opmaak laat hem alleen niet
   afbreken. */
.h1:has(.vk) { display: flex; align-items: baseline; }
.h1 > .cut { min-width: 0; }
.h1 .vk { flex: none; font-size: .5em; font-weight: 500; letter-spacing: 0; opacity: .65; }
.h2 { font-size: 20px; font-weight: 650; letter-spacing: -0.005em; }
.h3 { font-size: 16px; font-weight: 600; }
.ld { font-size: 17px; color: var(--muted); max-width: 62ch; }
.dim2 { color: var(--muted); }
.sm { font-size: 14px; }
.xs { font-size: 13px; }
.num { font-variant-numeric: tabular-nums; }

/* ---- masthead: fixed to the top, content passes beneath it ------------ */

.top {
    position: sticky;
    top: 0;
    z-index: 20;
    background: rgba(15, 23, 32, 0.86);
    backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--line);
}
.bar { height: 62px; }

/* The wordmark: capitals, letterspaced, one weight, no pictorial mark beside it. The
   letterspacing is what makes it this site's — one neighbour splits its name into two
   colours, another sets it plain, neither of them spaced.
   One letter carries colour: the last, so the accent closes the word instead of breaking
   it. That is a different thing from a split wordmark, where a whole syllable changes
   colour mid-name; here the spacing has already separated every letter, so a single one
   reads as a mark rather than as a seam. */
.mk {
    font-size: 19px;
    font-weight: 600;
    letter-spacing: 0.34em;
    text-indent: 0.34em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* Rose, not the site's teal: the name is the one place the palette is allowed a second
   colour, and a "y" set apart by the letterspacing carries it like a stem. */
.mk i { font-style: normal; color: #e8749b; }

.nav { display: flex; align-items: center; gap: 22px; }
.nav a { color: var(--muted); font-size: 15px; }
.nav a:hover { color: var(--text); }
.nav a.on { color: var(--accent); }

/* ---- cards: columns of photographs ------------------------------------ */

/* Rows were tried first and were wrong here. A row this site can fill holds a photograph
   and two words — a name and an age — so on a 1240px page the text ended a fifth of the
   way across and the remaining four fifths were empty card. The picture is what a visitor
   came for, so the column is sized by the picture. */
/* Zelfde reden als bij de galerij hieronder: een plafond, want een tegel die met het
   scherm meegroeit rekt het kleinste bewaarde voorbeeld het hardst uit.

   Het plafond staat op de KAART, niet op het spoor. `auto-fill` telt kolommen naar het
   MAXIMUM van een spoor, niet naar het minimum: met een hard plafond paste de rij alleen
   een veelvoud van 179px en bleef de rest als gat rechts liggen — tot 158px op een gewoon
   laptopvenster, breder dan de kolom die er niet meer bij kon. Met `1fr` telt de browser
   naar de ondergrens en vult hij de rij; de kaart erbinnen blijft alsnog bij haar
   plafond. */
.ls {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
    gap: 14px;
    /* Gecentreerd binnen het spoor, niet links: het spoor is soms iets breder dan het
       plafond van de kaart, en dan hoort die speling aan beide kanten te staan. Links
       uitlijnen zou de rij scheef laten aflopen. */
    justify-items: center;
}

.cd {
    display: flex;
    flex-direction: column;
    /* Waar het plafond echt zit. Het spoor mag meegroeien zodat de rij vol raakt; de kaart
       groeit niet mee, want daarboven wordt het bewaarde voorbeeld zichtbaar uitgerekt. */
    width: 100%;
    max-width: 165px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-box);
    overflow: hidden;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.cd:hover { border-color: var(--accent); background: var(--surface-2); }

.pic {
    width: 100%;
    aspect-ratio: 3 / 4;
    background: var(--surface-2);
}
/* Cropped from the top rather than the middle: these are portraits taken by their
   subjects, so the face sits in the upper third and a centred crop keeps the floor. */
.pic img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 22%; }
.bd { padding: 12px 14px 14px; display: flex; flex-direction: column; }

/* Two columns on a phone rather than one: the floor is set so that a phone-width screen
   fits two of them and not three, because one column makes every card a full screen tall
   and three make the face too small to read. */
@media (max-width: 560px) {
    /* Ruimer plafond dan op de desktop, maar wél een plafond. Het weghalen leek eerst
       redelijk — bij 390px is het spoor inderdaad smaller dan 165 — maar dat geldt maar
       voor een deel van de telefoons: op een toestel van 430 wordt de tegel 191 en bij één
       kolom zelfs 309. Precies de uitrekking die dit bestand op de desktop bestrijdt, en
       juist op de schermen met factor 2 of 3, waar ze het hardst te zien is. */
    .ls { grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr)); gap: 10px; }
    .cd { max-width: 200px; }
    .pic { width: 100%; aspect-ratio: 3 / 4; }
    .bd { padding: 14px 16px 16px; }
}

/* ---- the opening screen ----------------------------------------------- */

/* Text on one side, photographs on the other. Text first in the source so a narrow
   screen reads the sentence before the pictures, and so a crawler meets the heading
   before four images. */
.hd {
    display: grid;
    grid-template-columns: minmax(0, 4fr) minmax(0, 5fr);
    gap: 34px;
    /* Aligned to the top rather than centred: the two columns are of unlike height, and
       centring the short one hangs the heading in the middle of empty space. */
    align-items: start;
    padding: 36px 0 28px;
}

.hd .h1 { font-size: 42px; line-height: 1.12; }
.hd .ld { margin-top: 14px; max-width: 40ch; }

/* Four panels in one row, each keeping the photograph's own 3/4. Two rows of squares was
   tried and was worse on both counts: a square takes a slice out of a portrait, and these
   are donor photographs framed every which way — some have no head in them at all — so
   the tighter the crop the more of them lose their subject. One row is also shorter than
   the text beside it, which is what stops the empty column under the button. */
.hd-p {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
}

/* Squarer than the photographs want to be. At 3/4 two rows of panels stand far below the
   end of the text beside them: the text is four short lines and cannot be stretched to
   meet a block that tall, so the button sits above a column of empty page. */
.hp {
    display: block;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    border-radius: var(--radius-box);
    background: var(--surface-2);
}

/* Cropped near the top edge. A square panel takes a slice out of a 3/4 photograph, and
   at 22% that slice started below the chin: the subject stands upright in frame, so the
   head is in the first tenth and everything cropped from above is head. */
.hp img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 8%;
    transition: transform 0.25s ease;
}

.hp:hover img { transform: scale(1.04); }

/* The way in, sized like a door rather than a link in a sentence. */
.go { margin-top: 26px; }

.go a {
    display: inline-flex;
    align-items: baseline;
    gap: 10px;
    padding: 12px 22px;
    border-radius: var(--radius);
    background: var(--accent);
    color: #0d1117;
    font-weight: 650;
}

.go a:hover { filter: brightness(1.08); }
.go .sm { opacity: 0.72; font-weight: 400; }

/* ---- cities: a line to choose from, not an index ----------------------- */

.cs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 18px 0;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
}

.cx {
    display: inline-flex;
    align-items: baseline;
    gap: 7px;
    padding: 7px 13px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
}

.cx:hover { border-color: var(--accent); background: var(--surface-2); }
.cx .nm { font-weight: 600; font-size: 15px; }

/* The opening screen folds before the cards do: two columns of text and photographs
   need more room than two columns of tiles. */
@media (max-width: 900px) {
    .hd { grid-template-columns: 1fr; gap: 24px; padding-top: 26px; }
    .hd .h1 { font-size: 32px; }
    .hd .ld { max-width: 60ch; }
    .hd-p { grid-template-columns: repeat(4, 1fr); }
    .hp { aspect-ratio: 3 / 4; }
}

@media (max-width: 560px) {
    .hd .h1 { font-size: 26px; }
    /* Two across rather than four: at four, each panel is under 80px wide on a phone. */
    .hd-p { grid-template-columns: 1fr 1fr; gap: 8px; }
    .cs { padding: 14px 0; }
}

/* ---- pills ------------------------------------------------------------ */

.pi {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 13px;
    border-radius: var(--radius);
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--muted);
    font-size: 13px;
    line-height: 1.4;
    white-space: nowrap;
}
.hot { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
.dim { background: transparent; }

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 11px 22px;
    border-radius: var(--radius);
    background: var(--accent);
    color: var(--accent-ink);
    font-weight: 600;
    font-size: 15px;
    border: 0;
    cursor: pointer;
}
.gh { background: transparent; border: 1px solid var(--line); color: var(--text); }

/* ---- panels ----------------------------------------------------------- */

.pn {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-box);
    padding: 20px 22px;
}

/* A panel of running text is only as wide as the text wants to be. Sixty-eight characters
   is the measure; the panel used to run the full width with the paragraph capped inside
   it, which left half the box empty and made the border look like a mistake. */
.pn:has(.tx) { max-width: 72ch; }
.dv { border-top: 1px solid var(--line); }

.pl { display: flex; flex-direction: column; }
.ln {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    padding: 12px 2px;
    border-bottom: 1px solid var(--line);
}
.ln:last-child { border-bottom: 0; }
.ln:hover .nm { color: var(--accent); }
.nm { font-weight: 550; }

/* ---- gallery: a grid of panels ----------------------------------------- */

/* A scroll-snapping strip stood here and was wrong on a wide screen: one photograph
   filled 340px of a 1240px page and the rest of the gallery was off to the side, so
   what a visitor saw was a single cropped torso and no indication there were seven
   more. A grid shows the whole set at once, and the full photograph is one click
   away — which is the point at which somebody has chosen one. */
/* De tegel heeft een PLAFOND, niet alleen een ondergrens. Met `1fr` groeide hij mee met
   het scherm — op een breed venster werd hij bijna 300 breed, terwijl het bewaarde
   voorbeeld van sommige donoren 87 pixels is: drie keer uitgerekt, en op een scherm met
   factor 2 zes keer. Dat is de wazigheid die op de foto te zien was.
   Wij schalen niets om; het enige wat we in de hand hebben is hoe groot we het tonen.

   Net als bij de kaarten staat dat plafond op de TEGEL en niet op het spoor: `auto-fill`
   telt kolommen naar het maximum van een spoor, dus een hard plafond laat de rest van de
   rij als gat liggen. */
.sc {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(130px, 100%), 1fr));
    gap: 10px;
    justify-items: center;
}

/* The panel is the figure; the anchor fills it. Sizing the image instead would let a
   tall photograph set the height of its whole row. */
.sc figure {
    width: 100%;
    max-width: 155px;
    border-radius: var(--radius-box);
    overflow: hidden;
    background: var(--surface-2);
}

.sc a { display: block; aspect-ratio: 3 / 4; }

/* Cropped near the top rather than the middle, for the same reason the cards are:
   these are portraits taken by their subjects and the face sits high in the frame. */
.sc img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 18%; transition: transform 0.25s ease; }
.sc figure:hover img { transform: scale(1.04); }

/* ---- viewer ------------------------------------------------------------ */

/* A dialog, not a div: the top layer, the focus trap and Escape come with it, and none
   of the three is worth writing again. */
.vw {
    width: 100vw;
    max-width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    padding: 0;
    border: 0;
    background: rgba(10, 15, 21, 0.95);
    display: grid;
    place-items: center;
}

.vw:not([open]) { display: none; }
.vw::backdrop { background: rgba(10, 15, 21, 0.95); }

.vw img {
    max-width: 94vw;
    /* Room above for the close button, below for a thumb. */
    max-height: 88dvh;
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: var(--radius-box);
}

.ar {
    position: fixed;
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    border: 0;
    border-radius: var(--radius);
    background: rgba(230, 237, 243, 0.9);
    color: #0d1117;
    font: inherit;
    font-size: 26px;
    line-height: 1;
    cursor: pointer;
}

/* The accent, not a brighter white: the base is already --text at 90%, so raising it to
   full opacity was a change of nothing anybody could see. */
.ar:hover { background: var(--accent); color: var(--accent-ink); }

.pv { left: 14px; top: 50%; transform: translateY(-50%); }
.nx { right: 14px; top: 50%; transform: translateY(-50%); }
.cl { top: 14px; right: 14px; width: 42px; height: 42px; font-size: 22px; }

/* On a phone the arrows sit over the photograph and are easy to hit by accident, and
   swiping is the gesture there anyway — so they move below it.

   Moving them alone was not enough: at 88dvh a portrait fills the screen to within
   50px of the bottom, and an arrow 18px up with 48px of height still covered its last
   32px. So the photograph gives up the room the arrows take rather than sharing it. */
@media (max-width: 560px) {
    .vw img { max-height: 74dvh; }
    .pv, .nx { bottom: 20px; top: auto; transform: none; }
    /* Away from the edges and towards the middle, where a thumb reaches without
       stretching, and clear of the photograph above them. */
    .pv { left: 28%; }
    .nx { right: 28%; }
}

@media (max-width: 560px) {
    /* Zelfde als bij de kaarten: ruimer, maar niet onbegrensd — een telefoon is nu juist
       het scherm waarop een bewaard voorbeeld van 87 pixels het hardst wordt uitgerekt. */
    .sc { grid-template-columns: repeat(auto-fill, minmax(min(140px, 100%), 1fr)); gap: 8px; }
    .sc figure { max-width: 190px; }
}

/* ---- attributes and prices -------------------------------------------- */

.kv { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr)); gap: 2px 26px; }
.kvr { display: flex; justify-content: space-between; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.kvr dt { color: var(--muted); font-size: 14px; }
.kvr dd { font-size: 14px; text-align: right; }

/* ---- footer ----------------------------------------------------------- */

.ft { border-top: 1px solid var(--line); margin-top: 56px; padding: 26px 0 40px; color: var(--muted); font-size: 14px; }
.ft a:hover { color: var(--text); }

/* ---- pagination -------------------------------------------------------- */

.pg { display: flex; gap: 10px; justify-content: center; padding: 26px 0 6px; }

/* ---- age gt ---------------------------------------------------------- */

.gt { min-height: 100dvh; display: grid; place-items: center; padding: 24px; }
.box { max-width: 460px; text-align: center; }

/* The measure, wherever running text appears: the rules and privacy pages set .tx on
   their own article, with no panel around it. */
.tx { max-width: 68ch; }

/* Inside a panel the measure belongs to the panel instead, so the border ends where the
   text ends. Capping the paragraph within a full-width box left half of it empty and made
   the border read as a mistake. */
.pn .tx { max-width: 100%; }
.tx h2 { font-size: 19px; font-weight: 650; margin: 26px 0 8px; }
.tx p { margin: 10px 0; color: var(--muted); }
.tx ul { margin: 10px 0 10px 20px; color: var(--muted); }

/* Wachten op de volgende foto: het bestand komt via PHP uit de opslag en doet er een
   seconde of meer over, terwijl de vorige foto blijft staan. Zonder teken lijkt de
   klik niet aangekomen. */
.vw img[aria-busy] { opacity: 0.4; transition: opacity 0.1s ease; }
