source: Klonkt/src/assets/css/style.css@ 746d98a

main
Last change on this file since 746d98a was 19e28ce, checked in by Robin <roboburr@…>, 9 hours ago

Lezen scrolt gewoon, en Lenis is uit Klonkt

Robin, 30-9: "Reader view normaal scrollen, Lenis mag helemaal uit klonkt."

Lenis stond alleen in de leesweergave: op desktop voor zacht scrollen en
snappen naar de bovenkant van een bericht, op mobiel nam hij de vinger over en
bootste mod/read.js het snappen na. Dat is er allemaal uit, en de browser
scrolt nu zoals op elke andere pagina.

ER SNAPT OOK NIETS MEER, en dat is een keuze. Er lag nog een native route
klaar (scroll-snap-type, alleen bij naar beneden scrollen aangezet), maar die
had precies de problemen waarom Lenis er ooit kwam: bij het laden sprong hij
naar het eerste bericht zodat de header wegviel, en bij een harde veeg vloog hij
over een grens. "Normaal scrollen" is zonder snappen; de vault-notitie "Klonkt
Reader Scroll" zegt hoe het native terug kan als dat toch gewild is.

Wat met het snappen meeging:

  • de richtingsdetectie in read.js (wiel, vinger, toetsen, rust-timer): die bestond alleen om het snappen aan en uit te zetten;
  • de volle schermhoogte op het laatste bericht: die maakte zijn bovenkant bereikbaar als snappunt, en is zonder snappen een leeg scherm aan het eind;
  • de afgedwongen schermhoogte per bericht op mobiel: die hoorde bij vegen van bericht naar bericht, en gaf onder een kort bericht dezelfde leegte waar Robin op 20-8 op desktop over viel. De site-instelling "elk bericht een eigen scherm" geldt nu overal voor wie dat wil.

De bewaarde paginamodus (staat uit) leunde op lenis.stop() om de stroom vast te
zetten; dat doet nu een regel CSS op het document, en de balken gaan met native
scrollIntoView. Niet uitgeprobeerd sinds die omzetting, want de modus staat uit;
dat staat er ook bij.

Weg: src/assets/vendor/lenis.mjs en lenis-snap.mjs (57 KB samen, en twee
netwerkrondes voordat er iets werkte), en data-lenis-prevent op het zoekvak.
Scroll anchoring blijft uit: ook zonder snappen hoort wat je leest niet te
verspringen als Load more er berichten bij zet. init() zet een inline
scroll-snap-type van de vorige versie terug, voor een tabblad dat na een
htmx-wissel met de oude module was begonnen.

MOD_V 67 en style.css v111: anders draait een browser die er al was nog een jaar
de oude read.js met Lenis.

Volledige suite 1292 groen; er leunde geen toets op Lenis of het snappen.

Co-Authored-By: Claude Opus 5.5 <noreply@…>

  • Property mode set to 100644
File size: 177.7 KB
Line 
1/* ============================================================
2 LOKALE FONTS — SIL OFL 1.1 licensed, self-hosted
3 Geen third-party requests, geen privacy-leak naar Google.
4 Variable WOFF2 bestanden onder /assets/fonts/
5 Licenses in /assets/fonts/licenses/
6 ============================================================ */
7@font-face {
8 font-family: 'Fraunces';
9 font-style: normal;
10 font-display: swap;
11 font-weight: 100 900;
12 src: url('/assets/fonts/fraunces-latin-full-normal.woff2') format('woff2-variations');
13 unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
14}
15@font-face {
16 font-family: 'Fraunces';
17 font-style: italic;
18 font-display: swap;
19 font-weight: 100 900;
20 src: url('/assets/fonts/fraunces-latin-full-italic.woff2') format('woff2-variations');
21 unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
22}
23@font-face {
24 font-family: 'Literata';
25 font-style: normal;
26 font-display: swap;
27 font-weight: 400 700;
28 src: url('/assets/fonts/literata-latin-opsz-normal.woff2') format('woff2-variations');
29 unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
30}
31@font-face {
32 font-family: 'Literata';
33 font-style: italic;
34 font-display: swap;
35 font-weight: 400 700;
36 src: url('/assets/fonts/literata-latin-opsz-italic.woff2') format('woff2-variations');
37 unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
38}
39@font-face {
40 font-family: 'Plus Jakarta Sans';
41 font-style: normal;
42 font-display: swap;
43 font-weight: 400 700;
44 src: url('/assets/fonts/plus-jakarta-sans-latin-wght-normal.woff2') format('woff2-variations');
45 unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
46}
47@font-face {
48 font-family: 'Plus Jakarta Sans';
49 font-style: italic;
50 font-display: swap;
51 font-weight: 400 700;
52 src: url('/assets/fonts/plus-jakarta-sans-latin-wght-italic.woff2') format('woff2-variations');
53 unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
54}
55
56/* Klonkt publieke stijl — editorial-ish, geen emoji-UI */
57
58:root {
59 --font-display: 'Fraunces', Georgia, serif;
60 --font-body: 'Literata', Georgia, serif;
61 --font-ui: 'Plus Jakarta Sans', system-ui, sans-serif;
62 --font-mono: ui-monospace, Menlo, Consolas, monospace;
63 --radius: 8px; --transition: 180ms cubic-bezier(.4,0,.2,1);
64 --safe-top: env(safe-area-inset-top, 0); --safe-bot: env(safe-area-inset-bottom, 0);
65 /* iOS top inset: starts from env(), but a tiny boot script overrides it with a
66 real/measured value (env() resolves to 0 in some iOS standalone states). */
67 --ios-safe-top: env(safe-area-inset-top, 0px);
68 color-scheme: light dark;
69}
70
71/* Native controls (datum/tijd-picker, scrollbars) het SITE-thema laten volgen i.p.v.
72 de OS-voorkeur, en de datum/tijd-velden de site-accentkleur geven. Zo staat de
73 ingebouwde kalender-popup in dark + accent (de popup zelf is een browser-widget,
74 dus fonts/randen blijven van de browser). */
75[data-theme="dark"] { color-scheme: dark; }
76[data-theme="light"] { color-scheme: light; }
77input[type="date"], input[type="time"], input[type="datetime-local"] { accent-color: var(--accent, #6b8f71); }
78input[type="date"]::-webkit-calendar-picker-indicator,
79input[type="time"]::-webkit-calendar-picker-indicator,
80input[type="datetime-local"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .7; }
81input[type="date"]:hover::-webkit-calendar-picker-indicator,
82input[type="time"]:hover::-webkit-calendar-picker-indicator,
83input[type="datetime-local"]:hover::-webkit-calendar-picker-indicator { opacity: 1; }
84
85/* ============================================================
86 PALETTES — elk palette definieert alleen bg/tekst/rand shades.
87 --accent wordt LOS ingesteld via de accent-kleur config (inline in template).
88 Palette-selector: <html data-palette="forest"> en optioneel data-theme="dark"|"light".
89 Als geen data-palette = Sage default.
90 ============================================================ */
91
92/* ── SAGE (default) ── warme crème ── */
93:root,
94[data-palette="sage"] {
95 --paper: #faf8f3; --paper-2: #f2ede0;
96 --ink: #1a1a1a; --ink-soft: #3d3833; --ink-muted: #6b6560; --ink-faint: #a8a29e;
97 --rule: #e8e2d5; --rule-2: #d4ccb8;
98}
99@media (prefers-color-scheme: dark) {
100 :root:not([data-theme="light"]):not([data-palette]),
101 [data-palette="sage"]:not([data-theme="light"]) {
102 --paper: #1c1a17; --paper-2: #252220;
103 --ink: #f4ede0; --ink-soft: #d4ccb8; --ink-muted: #a8a29e; --ink-faint: #6b6560;
104 --rule: #3d3833; --rule-2: #4d4740;
105 }
106}
107[data-theme="dark"]:not([data-palette]),
108[data-theme="dark"][data-palette="sage"] {
109 --paper: #1c1a17; --paper-2: #252220;
110 --ink: #f4ede0; --ink-soft: #d4ccb8; --ink-muted: #a8a29e; --ink-faint: #6b6560;
111 --rule: #3d3833; --rule-2: #4d4740;
112}
113
114/* ── KLONKT ── merk-standaard: clean wit → bijna-zwart (neutraal, gouden accent) ── */
115[data-palette="klonkt"] {
116 --paper: #ffffff; --paper-2: #f4f4f5;
117 --ink: #09090b; --ink-soft: #27272a; --ink-muted: #52525b; --ink-faint: #a1a1aa;
118 --rule: #e4e4e7; --rule-2: #d4d4d8;
119}
120@media (prefers-color-scheme: dark) {
121 [data-palette="klonkt"]:not([data-theme="light"]) {
122 --paper: #0a0a0a; --paper-2: #18181b;
123 --ink: #fafafa; --ink-soft: #d4d4d8; --ink-muted: #a1a1aa; --ink-faint: #71717a;
124 --rule: #27272a; --rule-2: #3f3f46;
125 }
126}
127[data-theme="dark"][data-palette="klonkt"] {
128 --paper: #0a0a0a; --paper-2: #18181b;
129 --ink: #fafafa; --ink-soft: #d4d4d8; --ink-muted: #a1a1aa; --ink-faint: #71717a;
130 --rule: #27272a; --rule-2: #3f3f46;
131}
132
133/* ── PAPER ── minimalistisch wit → zwart ── */
134[data-palette="paper"] {
135 --paper: #ffffff; --paper-2: #f4f4f5;
136 --ink: #09090b; --ink-soft: #27272a; --ink-muted: #52525b; --ink-faint: #a1a1aa;
137 --rule: #e4e4e7; --rule-2: #d4d4d8;
138}
139@media (prefers-color-scheme: dark) {
140 [data-palette="paper"]:not([data-theme="light"]) {
141 --paper: #0a0a0a; --paper-2: #18181b;
142 --ink: #fafafa; --ink-soft: #d4d4d8; --ink-muted: #a1a1aa; --ink-faint: #71717a;
143 --rule: #27272a; --rule-2: #3f3f46;
144 }
145}
146[data-theme="dark"][data-palette="paper"] {
147 --paper: #0a0a0a; --paper-2: #18181b;
148 --ink: #fafafa; --ink-soft: #d4d4d8; --ink-muted: #a1a1aa; --ink-faint: #71717a;
149 --rule: #27272a; --rule-2: #3f3f46;
150}
151
152
153/* ── FOREST ── olijfgroen → donker bos ── */
154[data-palette="forest"] {
155 --paper: #f2f6ed; --paper-2: #e1ead6;
156 --ink: #1a2e15; --ink-soft: #2d4a26; --ink-muted: #5a6f52; --ink-faint: #8aa080;
157 --rule: #ccddbd; --rule-2: #adc396;
158}
159@media (prefers-color-scheme: dark) {
160 [data-palette="forest"]:not([data-theme="light"]) {
161 --paper: #0d1f12; --paper-2: #1a331e;
162 --ink: #dcf2d0; --ink-soft: #b8d8a8; --ink-muted: #8aad7c; --ink-faint: #5a7a50;
163 --rule: #2d4a30; --rule-2: #3d5e42;
164 }
165}
166[data-theme="dark"][data-palette="forest"] {
167 --paper: #0d1f12; --paper-2: #1a331e;
168 --ink: #dcf2d0; --ink-soft: #b8d8a8; --ink-muted: #8aad7c; --ink-faint: #5a7a50;
169 --rule: #2d4a30; --rule-2: #3d5e42;
170}
171
172/* ── STONE ── beige bruin → koffie ── */
173[data-palette="stone"] {
174 --paper: #f5f0e8; --paper-2: #e7ddce;
175 --ink: #2b2218; --ink-soft: #4a3a2a; --ink-muted: #78614a; --ink-faint: #a8927b;
176 --rule: #d6c6ac; --rule-2: #bda98a;
177}
178@media (prefers-color-scheme: dark) {
179 [data-palette="stone"]:not([data-theme="light"]) {
180 --paper: #1a130b; --paper-2: #2d2416;
181 --ink: #f5e9d5; --ink-soft: #d4c0a3; --ink-muted: #a8927b; --ink-faint: #786452;
182 --rule: #3d2f1e; --rule-2: #534029;
183 }
184}
185[data-theme="dark"][data-palette="stone"] {
186 --paper: #1a130b; --paper-2: #2d2416;
187 --ink: #f5e9d5; --ink-soft: #d4c0a3; --ink-muted: #a8927b; --ink-faint: #786452;
188 --rule: #3d2f1e; --rule-2: #534029;
189}
190
191/* ── MIDNIGHT ── lavendel → donker paars ── */
192[data-palette="midnight"] {
193 --paper: #f3f1f8; --paper-2: #e3ddf0;
194 --ink: #1e1a2e; --ink-soft: #332c4d; --ink-muted: #5e5478; --ink-faint: #9288a8;
195 --rule: #d4cae3; --rule-2: #b8a9cc;
196}
197@media (prefers-color-scheme: dark) {
198 [data-palette="midnight"]:not([data-theme="light"]) {
199 --paper: #0f0a1f; --paper-2: #1c1432;
200 --ink: #e9def7; --ink-soft: #c4b5d9; --ink-muted: #9d88b8; --ink-faint: #6b5a85;
201 --rule: #2d1f4a; --rule-2: #3d2b5e;
202 }
203}
204[data-theme="dark"][data-palette="midnight"] {
205 --paper: #0f0a1f; --paper-2: #1c1432;
206 --ink: #e9def7; --ink-soft: #c4b5d9; --ink-muted: #9d88b8; --ink-faint: #6b5a85;
207 --rule: #2d1f4a; --rule-2: #3d2b5e;
208}
209
210/* ── SUNSET ── roze crème → diep paars-roze ── */
211[data-palette="sunset"] {
212 --paper: #fdf4f3; --paper-2: #f7e1dc;
213 --ink: #2e1618; --ink-soft: #4d282b; --ink-muted: #7a4a4e; --ink-faint: #b08589;
214 --rule: #ebcac3; --rule-2: #d9a8a0;
215}
216@media (prefers-color-scheme: dark) {
217 [data-palette="sunset"]:not([data-theme="light"]) {
218 --paper: #1f0a14; --paper-2: #321525;
219 --ink: #fce7f3; --ink-soft: #f0c5d3; --ink-muted: #c896a8; --ink-faint: #8a6878;
220 --rule: #4a1f35; --rule-2: #5e2d43;
221 }
222}
223[data-theme="dark"][data-palette="sunset"] {
224 --paper: #1f0a14; --paper-2: #321525;
225 --ink: #fce7f3; --ink-soft: #f0c5d3; --ink-muted: #c896a8; --ink-faint: #8a6878;
226 --rule: #4a1f35; --rule-2: #5e2d43;
227}
228
229/* ── SAND ── warm papier (subtiel, niet fel geel) → warm donker ── (key blijft 'cream') */
230[data-palette="cream"] {
231 --paper: #f7f3ec; --paper-2: #ece5d8;
232 --ink: #232019; --ink-soft: #43392c; --ink-muted: #6f6149; --ink-faint: #a3957c;
233 --rule: #ddd3c0; --rule-2: #c9bca3;
234}
235@media (prefers-color-scheme: dark) {
236 [data-palette="cream"]:not([data-theme="light"]) {
237 --paper: #14120d; --paper-2: #221e15;
238 --ink: #efe9dd; --ink-soft: #d0c6b2; --ink-muted: #a3957c; --ink-faint: #6f6149;
239 --rule: #322c20; --rule-2: #463d2c;
240 }
241}
242[data-theme="dark"][data-palette="cream"] {
243 --paper: #14120d; --paper-2: #221e15;
244 --ink: #efe9dd; --ink-soft: #d0c6b2; --ink-muted: #a3957c; --ink-faint: #6f6149;
245 --rule: #322c20; --rule-2: #463d2c;
246}
247
248/* ── OCEAN ── helder blauw → diep oceaanblauw ── */
249[data-palette="ocean"] {
250 --paper: #eef4fb; --paper-2: #d8e6f7;
251 --ink: #0f2942; --ink-soft: #1d4368; --ink-muted: #4a6e90; --ink-faint: #87a3c0;
252 --rule: #c2d8ee; --rule-2: #a3c2e0;
253}
254@media (prefers-color-scheme: dark) {
255 [data-palette="ocean"]:not([data-theme="light"]) {
256 --paper: #081726; --paper-2: #102a42;
257 --ink: #d6e8fb; --ink-soft: #aecbe8; --ink-muted: #7e9fc0; --ink-faint: #547390;
258 --rule: #1d3a56; --rule-2: #2a4d6e;
259 }
260}
261[data-theme="dark"][data-palette="ocean"] {
262 --paper: #081726; --paper-2: #102a42;
263 --ink: #d6e8fb; --ink-soft: #aecbe8; --ink-muted: #7e9fc0; --ink-faint: #547390;
264 --rule: #1d3a56; --rule-2: #2a4d6e;
265}
266
267/* ── TEAL ── mint-turquoise → diep teal ── */
268[data-palette="teal"] {
269 --paper: #ecf7f5; --paper-2: #cfe9e4;
270 --ink: #0c2e2a; --ink-soft: #194a44; --ink-muted: #467168; --ink-faint: #84aaa2;
271 --rule: #bce0d9; --rule-2: #9bcbc1;
272}
273@media (prefers-color-scheme: dark) {
274 [data-palette="teal"]:not([data-theme="light"]) {
275 --paper: #06201d; --paper-2: #0e332e;
276 --ink: #d4f2ec; --ink-soft: #aad9d0; --ink-muted: #7aaaa0; --ink-faint: #517a72;
277 --rule: #1a4a42; --rule-2: #275e54;
278 }
279}
280[data-theme="dark"][data-palette="teal"] {
281 --paper: #06201d; --paper-2: #0e332e;
282 --ink: #d4f2ec; --ink-soft: #aad9d0; --ink-muted: #7aaaa0; --ink-faint: #517a72;
283 --rule: #1a4a42; --rule-2: #275e54;
284}
285
286/* ── CANDY ── zacht roze-rood → diep kersenrood ── */
287[data-palette="candy"] {
288 --paper: #fdf1f3; --paper-2: #f8d8de;
289 --ink: #3a1018; --ink-soft: #5e1f2a; --ink-muted: #8e4450; --ink-faint: #c2868f;
290 --rule: #f0c5cd; --rule-2: #e3a0ab;
291}
292@media (prefers-color-scheme: dark) {
293 [data-palette="candy"]:not([data-theme="light"]) {
294 --paper: #220810; --paper-2: #3a121d;
295 --ink: #fde0e6; --ink-soft: #f5b8c4; --ink-muted: #cd8b98; --ink-faint: #8e5f6a;
296 --rule: #4d1a28; --rule-2: #632536;
297 }
298}
299[data-theme="dark"][data-palette="candy"] {
300 --paper: #220810; --paper-2: #3a121d;
301 --ink: #fde0e6; --ink-soft: #f5b8c4; --ink-muted: #cd8b98; --ink-faint: #8e5f6a;
302 --rule: #4d1a28; --rule-2: #632536;
303}
304
305/* ── AMBER ── warm honing → diep amber ── */
306[data-palette="amber"] {
307 --paper: #fdf6e9; --paper-2: #f7e6c2;
308 --ink: #3a2a0c; --ink-soft: #5e451a; --ink-muted: #8e7340; --ink-faint: #c2a877;
309 --rule: #f0dca8; --rule-2: #e3c581;
310}
311@media (prefers-color-scheme: dark) {
312 [data-palette="amber"]:not([data-theme="light"]) {
313 --paper: #221a08; --paper-2: #3a2c12;
314 --ink: #fdeecb; --ink-soft: #f0d49a; --ink-muted: #c2a877; --ink-faint: #8e7340;
315 --rule: #4d3a16; --rule-2: #634d20;
316 }
317}
318[data-theme="dark"][data-palette="amber"] {
319 --paper: #221a08; --paper-2: #3a2c12;
320 --ink: #fdeecb; --ink-soft: #f0d49a; --ink-muted: #c2a877; --ink-faint: #8e7340;
321 --rule: #4d3a16; --rule-2: #634d20;
322}
323
324/* ── ROSE ── zacht roze → wijnrood ── */
325[data-palette="rose"] {
326 --paper: #fdf2f4; --paper-2: #f8dde3;
327 --ink: #2e1419; --ink-soft: #4d242c; --ink-muted: #7a4651; --ink-faint: #b3838d;
328 --rule: #f0c9d1; --rule-2: #e0a3af;
329}
330@media (prefers-color-scheme: dark) {
331 [data-palette="rose"]:not([data-theme="light"]) {
332 --paper: #1f0a0f; --paper-2: #33141c;
333 --ink: #fce4ea; --ink-soft: #f0bcc8; --ink-muted: #c98f9d; --ink-faint: #8a6470;
334 --rule: #4a1f2c; --rule-2: #5e2d3a;
335 }
336}
337[data-theme="dark"][data-palette="rose"] {
338 --paper: #1f0a0f; --paper-2: #33141c;
339 --ink: #fce4ea; --ink-soft: #f0bcc8; --ink-muted: #c98f9d; --ink-faint: #8a6470;
340 --rule: #4a1f2c; --rule-2: #5e2d3a;
341}
342
343
344/* ── TERRACOTTA ── warme klei → diep brick ── (key blijft 'mint' voor DB-compat) */
345[data-palette="mint"] {
346 --paper: #faf2ee; --paper-2: #f2ddd0;
347 --ink: #2e1a12; --ink-soft: #4a2c1e; --ink-muted: #7a5544; --ink-faint: #b3917e;
348 --rule: #eccdbb; --rule-2: #ddb49d;
349}
350@media (prefers-color-scheme: dark) {
351 [data-palette="mint"]:not([data-theme="light"]) {
352 --paper: #1f120c; --paper-2: #2e1c13;
353 --ink: #f7e6da; --ink-soft: #e0c3b0; --ink-muted: #b3917c; --ink-faint: #7a5544;
354 --rule: #4a2c1e; --rule-2: #5e3a28;
355 }
356}
357[data-theme="dark"][data-palette="mint"] {
358 --paper: #1f120c; --paper-2: #2e1c13;
359 --ink: #f7e6da; --ink-soft: #e0c3b0; --ink-muted: #b3917c; --ink-faint: #7a5544;
360 --rule: #4a2c1e; --rule-2: #5e3a28;
361}
362
363/* ── LILAC ── zacht lila → donker violet ── */
364[data-palette="lilac"] {
365 --paper: #faf4fb; --paper-2: #f0e0f3;
366 --ink: #2a1830; --ink-soft: #46294f; --ink-muted: #7a5685; --ink-faint: #b393bd;
367 --rule: #e6cdec; --rule-2: #d4abdd;
368}
369@media (prefers-color-scheme: dark) {
370 [data-palette="lilac"]:not([data-theme="light"]) {
371 --paper: #170a1c; --paper-2: #281433;
372 --ink: #f3e2f7; --ink-soft: #dabce4; --ink-muted: #b088c0; --ink-faint: #7a5a88;
373 --rule: #3d1f4a; --rule-2: #4f2b5e;
374 }
375}
376[data-theme="dark"][data-palette="lilac"] {
377 --paper: #170a1c; --paper-2: #281433;
378 --ink: #f3e2f7; --ink-soft: #dabce4; --ink-muted: #b088c0; --ink-faint: #7a5a88;
379 --rule: #3d1f4a; --rule-2: #4f2b5e;
380}
381
382/* Accent-tint wordt berekend uit --accent, zodat hij meegaat bij elke accent-keuze.
383 Wordt in template inline gezet; hier alleen een fallback. */
384:root { --accent: #c2410c; --accent-soft: #ea580c; --accent-tint: #fef2e8; }
385
386* { box-sizing: border-box; }
387html {
388 -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent;
389 overscroll-behavior-y: none;
390 /* Horizontaal clippen op ROOT-niveau: dit propageert naar de viewport (geen
391 horizontale scroll) ZONDER position:fixed te breken — html is sowieso de
392 document-scroller. De body mag GEEN overflow-x krijgen, want dan zou die een
393 tweede scroll-container worden en zou de fixed bottom-tab meeschuiven. */
394 overflow-x: hidden;
395}
396body {
397 margin: 0;
398}
399
400/* ── Thema-scrollbar: slank, afgerond, subtiel — vervangt de standaard chunky
401 browser-scrollbar (met pijltjes) die niet bij het donkere thema past.
402 Werkt op de pagina én op interne scrollers (queue, chat-thread, dropdowns). ── */
403* {
404 scrollbar-width: thin;
405 scrollbar-color: color-mix(in srgb, var(--ink) 28%, transparent) transparent;
406}
407::-webkit-scrollbar { width: 10px; height: 10px; }
408::-webkit-scrollbar-track { background: transparent; }
409::-webkit-scrollbar-thumb {
410 background-color: color-mix(in srgb, var(--ink) 24%, transparent);
411 border-radius: 999px;
412 border: 2px solid transparent; /* transparante rand → optisch slanker (≈6px) */
413 background-clip: content-box;
414}
415::-webkit-scrollbar-thumb:hover {
416 background-color: color-mix(in srgb, var(--accent) 60%, var(--ink));
417}
418::-webkit-scrollbar-corner { background: transparent; }
419
420/* Login/auth = focus-scherm. Bij htmx-navigatie naar login blijft de bottom-tab
421 uit de vorige pagina in de DOM staan → hier verbergen zodat het login-scherm
422 net zo "kaal" is als bij een full-load (waar de tab niet eens gerenderd wordt). */
423body.on-auth .bottom-tab { display: none; }
424body:not(.admin) {
425 min-height: 100dvh;
426 background: var(--paper); color: var(--ink);
427 font-family: var(--font-body); font-size: 1.0625rem; line-height: 1.65;
428 font-feature-settings: "liga" 1, "kern" 1, "onum" 1;
429 -webkit-font-smoothing: antialiased;
430 /* v9.4 — Sticky footer: body als flex-kolom, main groeit, footer onderaan */
431 display: flex;
432 flex-direction: column;
433}
434body:not(.admin) #pcms-main { flex: 1 0 auto; }
435body:not(.admin) .site-footer { flex-shrink: 0; }
436body:not(.admin)::before {
437 content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
438 /* Per-palette achtergrond-gradient: opgebouwd uit de palette's EIGEN --paper/--paper-2
439 (diagonale verloop) + een zachte accent-glow erbovenop. Werkt automatisch voor elk
440 palet, light én dark — geen per-palette regels nodig. */
441 background: radial-gradient(ellipse at top, color-mix(in srgb, var(--accent) 6%, transparent), transparent 60%),
442 radial-gradient(ellipse at bottom right, color-mix(in srgb, var(--accent) 7%, transparent), transparent 55%),
443 linear-gradient(160deg, var(--paper) 0%, var(--paper-2) 100%);
444}
445/* Klonkt-palet gebruikt nu (net als de andere paletten) de generieke accent-glow
446 hierboven → de achtergrond-gradient volgt de gekozen accentkleur. */
447::selection { background: var(--accent-tint); color: var(--accent); }
448
449h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 450; line-height: 1.15; letter-spacing: -0.015em; margin: 0; text-wrap: balance; font-variation-settings: "opsz" 144, "SOFT" 50; }
450h1 { font-size: clamp(2rem, 4vw + 1rem, 3.5rem); letter-spacing: -0.022em; }
451h2 { font-size: clamp(1.5rem, 2vw + 1rem, 2.25rem); margin: 1.5rem 0 .4rem; }
452h3 { font-size: 1.375rem; margin: 1.25rem 0 .3rem; }
453p { margin: 0 0 .85em; text-wrap: pretty; hyphens: auto; }
454a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--rule-2); text-underline-offset: 3px; transition: color var(--transition), text-decoration-color var(--transition); }
455a:hover { color: var(--accent); text-decoration-color: var(--accent); }
456/* Knop-ankers houden hun eigen tekstkleur bij hover. Zonder dit overschrijft de
457 globale a:hover{color:accent} de knop-tekst → die wordt dezelfde kleur als de
458 accent-achtergrond en valt weg (vooral op sites met een felle accent). */
459a.btn:hover, a.epk-btn:hover { color: var(--ink); }
460a.btn-primary:hover, a.btn-danger:hover, a.btn-success:hover, a.button:hover,
461a.epk-btn-primary:hover, a.dls-btn:hover, a.sh-tix:hover, a.dl-go:hover { color: #fff; text-decoration-color: currentColor; }
462a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; text-decoration: none; }
463blockquote { margin: 2em 0; padding: .25em 0 .25em 1.25em; border-left: 2px solid var(--accent); font-family: var(--font-display); font-style: italic; font-size: 1.15em; color: var(--ink-soft); }
464code { font-family: var(--font-mono); font-size: .9em; padding: .15em .4em; background: var(--paper-2); border: 1px solid var(--rule); border-radius: 4px; }
465pre { font-family: var(--font-mono); font-size: .85rem; padding: 1.25em 1.5em; background: var(--paper-2); border: 1px solid var(--rule); border-radius: var(--radius); overflow-x: auto; line-height: 1.5; }
466pre code { padding: 0; background: none; border: none; }
467hr { border: 0; border-top: 1px solid var(--rule); margin: 3em auto; max-width: 4rem; }
468
469/* Container — schaalt mee met schermgrootte.
470 42rem default (optimale leesbreedte voor tekst),
471 breder op HD/2.5K/4K waar je meer ademruimte hebt.
472 Grid-view heeft z'n eigen bredere variant (zie .feed-grid). */
473/* Container max-width schaalt per schermformaat.
474 42rem is optimale leesbreedte voor tekst. Op grotere schermen
475 ademruimte geven zonder uitwaaieren. Grid apart geregeld. */
476.container { max-width: 42rem; margin-inline: auto; padding-inline: clamp(1.25rem, 4vw, 2rem); }
477
478/* Post-/leesbreedte. Globaal + hogere specificity (.container.post-page) zodat 'ie
479 de responsive .container-breakpoints (56/72rem) overal overrult — posts én de
480 downloads-pagina krijgen zo exact dezelfde 720px-breedte. */
481.container.post-page { max-width: 720px; margin: 0.5rem auto 2rem; padding: 0 1rem; }
482
483/* Laptop / Full HD — 1440px viewport of groter */
484@media (min-width: 1440px) {
485 .container { max-width: 56rem; }
486}
487/* Desktop / QHD 2.5K — 1920px viewport of groter */
488@media (min-width: 1920px) {
489 .container { max-width: 72rem; }
490}
491/* 4K UHD — 2560px viewport of groter */
492@media (min-width: 2560px) {
493 .container { max-width: 88rem; }
494}
495
496/* Grid-view tiles: Instagram-feel. Max-width schaalt met gekozen kolommen zodat
497 tegels niet te groot of te klein worden. */
498@media (min-width: 1200px) {
499 body[data-grid-cols="4"] .feed-grid.container { max-width: 80rem; }
500 body[data-grid-cols="3"] .feed-grid.container { max-width: 64rem; }
501 body[data-grid-cols="2"] .feed-grid.container { max-width: 48rem; }
502}
503@media (min-width: 1920px) {
504 body[data-grid-cols="4"] .feed-grid.container { max-width: 96rem; }
505}
506
507.icon { width: 1.125em; height: 1.125em; flex-shrink: 0; vertical-align: -0.15em; stroke: currentColor; fill: none; display: inline-block; }
508
509/* Masthead */
510.masthead {
511 border-bottom: 1px solid var(--rule);
512 padding: calc(1rem + var(--ios-safe-top, 0px)) 0 1rem;
513 /* GEEN backdrop-filter hier: een voorouder met backdrop-filter blokkeert de
514 frosted blur van het user-menu-dropdown eronder. Masthead is toch bijna
515 dekkend, dus solide bg i.p.v. blur. */
516 background: color-mix(in srgb, var(--paper) 94%, transparent);
517 position: sticky; top: 0; z-index: 20;
518}
519.masthead-inner { display: flex; align-items: center; justify-content: flex-end; gap: 1.5rem; }
520.masthead-brand { min-width: 0; flex: 1 1 auto; overflow: hidden; }
521.site-title { font-family: var(--font-display); font-size: 1.5rem; font-weight: 500; font-variation-settings: "opsz" 72, "SOFT" 100; letter-spacing: -0.015em; text-decoration: none; color: var(--ink); line-height: 1; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
522.site-title:hover { color: var(--accent); }
523/* v9.0 — nav-home icon (replaces site-title text in nav) */
524.nav-home {
525 display: inline-flex; align-items: center; justify-content: center;
526 width: 2.25rem; height: 2.25rem;
527 color: var(--ink-soft); text-decoration: none;
528 border-radius: 6px; border: 1px solid transparent;
529 transition: all var(--transition);
530}
531.nav-home:hover { color: var(--ink); background: var(--paper-2); border-color: var(--rule); }
532.nav-home .icon { width: 1.15rem; height: 1.15rem; }
533.top-nav { display: flex; align-items: center; gap: .25rem; flex-shrink: 0; }
534.nav-link, .nav-btn {
535 font-family: var(--font-ui); font-size: .875rem; font-weight: 500;
536 color: var(--ink-soft); text-decoration: none;
537 padding: .5rem .85rem; border-radius: 6px;
538 background: transparent; border: 1px solid transparent;
539 cursor: pointer;
540 display: inline-flex; align-items: center; gap: .4rem;
541 min-height: 2.25rem;
542 transition: all var(--transition);
543}
544.nav-link:hover, .nav-btn:hover { color: var(--ink); background: var(--paper-2); border-color: var(--rule); }
545.nav-link-muted { color: var(--ink-muted); }
546.nav-label { display: none; }
547@media (min-width: 560px) { .nav-label { display: inline; } }
548
549/* Article */
550main { padding-top: 2rem; }
551.page { padding-block: 2rem 3rem; }
552page-container { display: block; font-size: 1.125rem; line-height: 1.75; }
553page-container h1 { font-size: clamp(2rem, 5vw + 1rem, 3.75rem); margin-bottom: 1rem; letter-spacing: -0.025em; }
554page-container img, page-container iframe, page-container video { max-width: 100%; height: auto; display: block; margin: 2rem auto; border-radius: var(--radius); }
555page-container audio { width: 100%; margin: 1.5rem 0; }
556page-container figure { margin: 2rem 0; }
557page-container figcaption { font-family: var(--font-ui); font-size: .85rem; color: var(--ink-muted); text-align: center; margin-top: .5rem; }
558page-container[contenteditable="true"] { outline: 2px dashed var(--accent); outline-offset: .5rem; border-radius: var(--radius); }
559
560.empty { text-align: center; padding: 4rem 1rem; }
561/* Moved account (FEP-7628 slice 3): the signpost above the feed. Loud on
562 purpose — a visitor must not read a stale timeline thinking it is current. */
563.moved-banner {
564 text-align: center;
565 margin: 1.5rem 0 2rem;
566 padding: 1.75rem 1.25rem;
567 border: 2px solid var(--accent, #e8b04b);
568 border-radius: var(--radius, 10px);
569 background: color-mix(in srgb, var(--accent, #e8b04b) 10%, var(--paper-2, var(--paper)));
570}
571.moved-banner-lead { margin: 0 0 .5rem; font-size: 1rem; opacity: .85; }
572.moved-banner-link {
573 display: inline-block;
574 font-family: var(--font-display);
575 font-size: clamp(1.5rem, 5vw, 2.4rem);
576 font-weight: 700;
577 line-height: 1.15;
578 overflow-wrap: anywhere;
579}
580.moved-banner-hint { margin: .75rem 0 0; font-size: .85rem; color: var(--ink-soft); }
581.empty h1 { font-size: 2rem; }
582.empty p { color: var(--ink-muted); }
583
584/* Embeds */
585.pcms-embed, .folio-embed { margin: 2rem 0; }
586.pcms-embed iframe, .pcms-embed video, .pcms-embed audio,
587.folio-embed iframe, .folio-embed video, .folio-embed audio { width: 100%; display: block; border: 0; border-radius: var(--radius); }
588
589/* Article footer */
590.article-footer { display: flex; justify-content: center; padding-top: 2rem; margin-top: 2rem; border-top: 1px solid var(--rule); }
591
592/* Buttons */
593.button {
594 font-family: var(--font-ui); font-size: .875rem; font-weight: 500;
595 padding: .6rem 1.1rem; min-height: 2.5rem;
596 display: inline-flex; align-items: center; gap: .5rem;
597 background: var(--ink); color: var(--paper);
598 border: 1px solid var(--ink);
599 border-radius: 6px;
600 text-decoration: none; cursor: pointer;
601 transition: all var(--transition);
602}
603.button:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
604.button-ghost { background: transparent; color: var(--ink); border-color: var(--rule-2); }
605.button-ghost:hover { background: var(--paper-2); border-color: var(--accent); color: var(--accent); }
606
607/* Pagination */
608.pagination {
609 display: grid;
610 grid-template-columns: 1fr 1fr;
611 gap: 1rem;
612 padding-block: 2rem 4rem;
613 margin-top: 2rem;
614 border-top: 1px solid var(--rule);
615}
616a.pagination-item {
617 display: flex; align-items: center; gap: 1rem; padding: 1rem 1.25rem;
618 border: 2px solid var(--rule-2, var(--rule));
619 border-radius: var(--radius);
620 text-decoration: none; transition: all .15s ease; font-size: .9rem;
621 background: var(--paper-2);
622 min-height: 5rem;
623 min-width: 0;
624 overflow: hidden;
625 cursor: pointer;
626 box-shadow: 0 1px 0 rgba(0,0,0,.02);
627 -webkit-tap-highlight-color: color-mix(in srgb, var(--accent) 30%, transparent);
628 position: relative;
629}
630.pagination-item:not(a) {
631 display: flex; align-items: center; gap: 1rem; padding: 1rem 1.25rem;
632 border: 1px dashed var(--rule); border-radius: var(--radius);
633 font-size: .9rem; background: transparent; min-height: 5rem;
634 min-width: 0; overflow: hidden;
635}
636.pagination-next { flex-direction: row-reverse; text-align: right; }
637a.pagination-item:hover {
638 border-color: var(--accent);
639 background: color-mix(in srgb, var(--accent) 5%, var(--paper-2));
640 transform: translateY(-2px);
641 box-shadow: 0 4px 12px rgba(0,0,0,.08);
642}
643a.pagination-item:active {
644 transform: translateY(0);
645 transition-duration: .05s;
646}
647.pagination-disabled { opacity: .5; cursor: default; font-style: italic; }
648.pagination-body { display: flex; flex-direction: column; gap: .15rem; min-width: 0; flex: 1; }
649.pagination-label {
650 display: block; font-family: var(--font-ui); font-size: .7rem; font-weight: 700;
651 letter-spacing: .08em; text-transform: uppercase; color: var(--accent);
652 margin-bottom: .15rem;
653}
654.pagination-slug {
655 display: block; font-size: .72rem; color: var(--ink-muted);
656 opacity: .75; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
657 font-style: italic;
658}
659.pagination-title {
660 display: block; font-family: var(--font-display); font-weight: 600; color: var(--ink);
661 font-variation-settings: "opsz" 72; font-style: normal;
662 line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
663 font-size: 1.05rem;
664 text-decoration: underline;
665 text-decoration-color: var(--rule-2, var(--rule));
666 text-decoration-thickness: 1px;
667 text-underline-offset: 3px;
668 transition: text-decoration-color .15s;
669}
670a.pagination-item:hover .pagination-title {
671 text-decoration-color: var(--accent);
672}
673.pagination .icon {
674 width: 1.75em; height: 1.75em; flex-shrink: 0;
675 color: var(--accent);
676 transition: transform .15s;
677}
678a.pagination-prev:hover .icon { transform: translateX(-4px); }
679a.pagination-next:hover .icon { transform: translateX(4px); }
680
681/* Pinned-targets → accent-tint en dikkere rand voor duidelijkheid */
682.pagination-item.is-pinned {
683 background: color-mix(in srgb, var(--accent) 8%, var(--paper-2));
684 border-color: color-mix(in srgb, var(--accent) 45%, var(--rule));
685}
686a.pagination-item.is-pinned:hover {
687 background: color-mix(in srgb, var(--accent) 15%, var(--paper-2));
688 border-color: var(--accent);
689}
690
691@media (max-width: 800px) { .pagination { grid-template-columns: 1fr; } }
692
693/* Footer */
694.site-footer { padding: 2rem 0 calc(2rem + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--rule); margin-top: 4rem; font-family: var(--font-ui); font-size: .85rem; color: var(--ink-muted); }
695.site-footer-inner { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; align-items: center; }
696.site-footer a { color: var(--ink-muted); text-decoration-color: var(--rule); }
697.site-footer a:hover { color: var(--accent); }
698.site-footer-links { display: flex; gap: 1rem; }
699.sep { color: var(--ink-faint); margin: 0 .5rem; }
700.footer-tagline { font-style: italic; opacity: .8; }
701
702@media print {
703 .masthead, .pagination, .site-footer, .article-footer, .top-nav { display: none; }
704 body { background: #fff; color: #000; }
705}
706@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; animation: none !important; } }
707
708/* ============================================================
709 V7 ADDITIONS — post metadata UI + audio player + post-images
710 ============================================================ */
711
712/* Post header */
713.post-header { margin: 0 0 2rem; }
714.post-cover {
715 margin: 0 0 1.5rem;
716 border-radius: 8px;
717 overflow: hidden;
718 background: color-mix(in srgb, var(--fg) 4%, transparent);
719}
720.post-cover img {
721 display: block;
722 width: 100%;
723 height: auto; /* volledige (niet-bijgesneden) hoogte */
724 /* geen max-height/object-fit: cover meer → de hele afbeelding toont,
725 volle containerbreedte, zijkanten gerespecteerd. */
726}
727.post-title { font-size: clamp(1.6rem, 4vw, 2.6rem); line-height: 1.15; margin: 0 0 .5rem; letter-spacing: -.02em; }
728
729/* Pin banner — duidelijk zichtbaar dat een post gepind is */
730.post-pin-banner {
731 display: inline-flex;
732 align-items: center;
733 gap: .5rem;
734 background: color-mix(in srgb, var(--accent) 14%, transparent);
735 border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
736 color: var(--accent);
737 padding: .4rem .8rem;
738 border-radius: 999px;
739 font-size: .75rem;
740 font-weight: 600;
741 text-transform: uppercase;
742 letter-spacing: .08em;
743 margin: 0 0 .85rem;
744 line-height: 1;
745}
746.post-pin-banner-icon { font-size: 1rem; line-height: 1; }
747.post-pin-banner-sep { opacity: .5; }
748.post-pin-banner-pos { opacity: .85; }
749.post-date {
750 color: var(--fg-muted, #888);
751 font-size: .85rem;
752 letter-spacing: .05em;
753 text-transform: uppercase;
754 margin: 0 0 1.5rem;
755}
756
757/* Post-images in content (resize + align) */
758.post-image {
759 margin: 1.5rem 0;
760}
761.post-image img {
762 display: block;
763 height: auto;
764 border-radius: 6px;
765 max-width: 100%;
766}
767.post-image figcaption {
768 font-size: .85rem;
769 color: var(--fg-muted, #888);
770 margin-top: .5rem;
771 font-style: italic;
772 text-align: center;
773}
774.post-image--small img { max-width: 240px; }
775.post-image--medium img { max-width: 440px; }
776.post-image--large img { max-width: 720px; }
777.post-image--full img { width: 100%; max-width: 100%; }
778.post-image--left { text-align: left; }
779.post-image--left img { margin-left: 0; margin-right: auto; }
780.post-image--center { text-align: center; }
781.post-image--center img { margin-left: auto; margin-right: auto; }
782.post-image--right { text-align: right; }
783.post-image--right img { margin-left: auto; margin-right: 0; }
784
785/* Afbeeldingen in de post-tekst vullen ALTIJD de containerbreedte en houden hun
786 natuurlijke (niet-bijgesneden) hoogte — geen vaste hoogte / geen crop. */
787.post-content img {
788 display: block;
789 width: 100%;
790 max-width: 100%;
791 height: auto;
792 border-radius: 6px;
793 margin: 1.4em auto;
794}
795
796/* Video in de post-tekst (C2S-uploads uit Shaer): dezelfde maatvoering als
797 afbeeldingen, plus een plafond. Zonder deze regel rendert een <video> op
798 zijn natuurlijke maat en duwt een portret-video de hele kolom vol; met
799 object-fit blijft het beeld heel binnen het kader. Geldt ook voor de
800 Krant/Berichten (tl-content), waar dezelfde content langskomt. */
801.post-content video, .tl-content video {
802 display: block;
803 width: 100%;
804 max-width: 100%;
805 max-height: min(70vh, 540px);
806 height: auto;
807 object-fit: contain;
808 background: #000;
809 border-radius: 6px;
810 margin: 1.4em auto;
811}
812.post-content audio, .tl-content audio { width: 100%; display: block; margin: 1em auto; }
813
814/* Mini-player + bottom-tab clearance: applied to .pcms-main so the footer
815 is NOT pushed below the viewport bottom. The bars are position:fixed so
816 they overlap whatever they sit on top of; the padding ensures the last
817 content row isn't covered. Footer naturally follows main and now sits
818 exactly at viewport-bottom on short pages (sticky footer flex). */
819.pcms-main { padding-bottom: 5.5rem; }
820body.has-audio-player .pcms-main { padding-bottom: 5.5rem; }
821
822/* Mobiel toetsenbord open (body.kb-open, gezet via visualViewport in shell.ejs):
823 verberg de vaste onderbalken — anders zweven de bottom-tab + mini-speler boven
824 het toetsenbord of overlappen ze het invoerveld. Ze komen terug zodra het
825 toetsenbord sluit. De extra clearance onderaan is dan ook niet meer nodig. */
826body.kb-open .bottom-tab,
827body.kb-open .audio-player { display: none !important; }
828body.kb-open .pcms-main { padding-bottom: 1rem; }
829
830/* Mobiel & tablet (touch): scrollbalken verbergen — scrollen blijft werken, alleen
831 de balk-UI is weg (geen verwarrende/dubbele balken). */
832@media (pointer: coarse) {
833 * { scrollbar-width: none; -ms-overflow-style: none; }
834 *::-webkit-scrollbar { width: 0; height: 0; display: none; }
835}
836
837/* Afbeeldingen lastiger op te slaan (frictie, geen echte beveiliging): geen
838 sleep-naar-bureaublad, geen iOS long-press-bewaarmenu. Rechtsklik wordt in JS
839 geblokkeerd (zie shell.ejs). Covers/hero zijn al CSS-achtergronden = niet via
840 "afbeelding opslaan" te pakken. */
841img { -webkit-user-drag: none; -khtml-user-drag: none; -moz-user-drag: none; user-drag: none; -webkit-touch-callout: none; }
842
843/* ============================================================
844 AUDIO PLAYER v7.1 — SVG iconen, thema-aware, geen emoji
845 ============================================================ */
846.audio-player {
847 position: fixed;
848 left: 0; right: 0; bottom: 0;
849 background: color-mix(in srgb, var(--paper, #fff) 96%, var(--ink, #000));
850 border-top: 1px solid var(--rule, rgba(0,0,0,.1));
851 color: var(--ink, #222);
852 z-index: 1000;
853 backdrop-filter: blur(12px);
854 -webkit-backdrop-filter: blur(12px);
855 box-shadow: 0 -4px 20px rgba(0,0,0,.04);
856}
857[data-theme="dark"] .audio-player {
858 background: color-mix(in srgb, var(--paper, #1a1a1a) 92%, #fff);
859 box-shadow: 0 -4px 20px rgba(0,0,0,.3);
860}
861.audio-player-inner {
862 display: grid;
863 grid-template-columns: minmax(0, 1.6fr) auto minmax(180px, 2fr) auto;
864 gap: 1rem;
865 align-items: center;
866 padding: .65rem 1.25rem;
867 max-width: 1400px;
868 margin: 0 auto;
869}
870
871/* v9.0 — Clickable trigger dat cover + meta combineert zodat klikken erop sheet opent */
872.audio-player-expand-trigger {
873 display: flex;
874 align-items: center;
875 gap: .65rem;
876 min-width: 0;
877 background: none;
878 border: none;
879 padding: 0;
880 margin: 0;
881 cursor: pointer;
882 color: inherit;
883 text-align: left;
884 font: inherit;
885 border-radius: 6px;
886 transition: background .15s;
887}
888.audio-player-expand-trigger:hover {
889 background: color-mix(in srgb, var(--ink, #000) 6%, transparent);
890}
891
892/* Cover art */
893.audio-player-cover {
894 width: 48px; height: 48px;
895 border-radius: 4px;
896 background: linear-gradient(135deg,
897 var(--accent, #c2410c),
898 color-mix(in srgb, var(--accent, #c2410c) 55%, #000));
899 background-size: cover;
900 background-position: center;
901 flex-shrink: 0;
902 position: relative;
903}
904.audio-player-cover.has-image {
905 background-blend-mode: normal;
906}
907
908/* Meta — titel + artiest */
909.audio-player-meta {
910 min-width: 0;
911 display: flex;
912 flex-direction: column;
913 gap: 2px;
914}
915.audio-player-title-wrap {
916 overflow: hidden;
917 white-space: nowrap;
918 position: relative;
919}
920.audio-player-title {
921 display: inline-block;
922 font-family: var(--font-ui, inherit);
923 font-weight: 600;
924 font-size: .9rem;
925 line-height: 1.3;
926 letter-spacing: -.01em;
927 color: var(--ink, inherit);
928 white-space: nowrap;
929}
930.audio-player-title.is-overflowing {
931 animation: audio-title-marquee 6s ease-in-out infinite alternate;
932}
933@keyframes audio-title-marquee {
934 0%, 8% { transform: translateX(0); }
935 92%, 100% { transform: translateX(calc(-1 * var(--audio-shift, 0px))); }
936}
937.audio-player-artist {
938 font-family: var(--font-ui, inherit);
939 font-size: .74rem;
940 line-height: 1.3;
941 color: var(--ink-muted, rgba(0,0,0,.55));
942 white-space: nowrap;
943 overflow: hidden;
944 text-overflow: ellipsis;
945}
946
947/* Controls — prev / play / next */
948.audio-player-controls {
949 display: flex;
950 gap: .1rem;
951 align-items: center;
952}
953.audio-btn {
954 background: transparent;
955 border: none;
956 color: inherit;
957 cursor: pointer;
958 padding: .5rem;
959 border-radius: 50%;
960 transition: background .15s, color .15s, transform .08s;
961 display: inline-flex;
962 align-items: center;
963 justify-content: center;
964 position: relative;
965}
966.audio-btn svg {
967 width: 18px; height: 18px;
968 display: block;
969}
970.audio-btn:hover { background: color-mix(in srgb, var(--ink, #000) 8%, transparent); }
971.audio-btn:active { transform: scale(.94); }
972.audio-btn-play {
973 background: var(--accent, #c2410c);
974 color: #fff;
975 width: 38px; height: 38px;
976 padding: 0;
977}
978.audio-btn-play:hover {
979 background: color-mix(in srgb, var(--accent, #c2410c) 85%, #fff);
980}
981[data-theme="dark"] .audio-btn-play:hover {
982 background: color-mix(in srgb, var(--accent, #c2410c) 85%, #000);
983}
984.audio-btn-play svg { width: 16px; height: 16px; }
985.audio-btn-play .icon-pause { display: none; }
986.audio-player.is-playing .audio-btn-play .icon-play { display: none; }
987.audio-player.is-playing .audio-btn-play .icon-pause { display: block; }
988
989/* Progress bar */
990.audio-player-progress {
991 display: flex;
992 gap: .75rem;
993 align-items: center;
994 min-width: 0;
995}
996.audio-time {
997 font-size: .7rem;
998 color: var(--ink-muted, rgba(0,0,0,.55));
999 font-variant-numeric: tabular-nums;
1000 min-width: 2.7em;
1001 text-align: center;
1002}
1003.audio-seek {
1004 flex: 1;
1005 min-width: 0;
1006 cursor: pointer;
1007 padding: .5rem 0;
1008 position: relative;
1009}
1010.audio-seek-bar {
1011 height: 3px;
1012 background: color-mix(in srgb, var(--ink, #000) 12%, transparent);
1013 border-radius: 2px;
1014 overflow: hidden;
1015 transition: height .15s;
1016}
1017.audio-seek:hover .audio-seek-bar { height: 5px; }
1018.audio-seek-fill {
1019 height: 100%;
1020 background: var(--accent, #c2410c);
1021 width: 0%;
1022 transition: width .1s linear;
1023}
1024
1025/* Volume — popup on hover */
1026.audio-player-volume {
1027 position: relative;
1028 display: flex;
1029 align-items: center;
1030}
1031.audio-btn .icon-mute { display: none; }
1032.audio-player.is-muted .audio-btn .icon-vol { display: none; }
1033.audio-player.is-muted .audio-btn .icon-mute { display: block; }
1034.audio-volume-popup {
1035 position: absolute;
1036 bottom: calc(100% + 8px);
1037 left: 50%;
1038 transform: translateX(-50%) scale(.95);
1039 transform-origin: bottom center;
1040 background: var(--paper, #fff);
1041 border: 1px solid var(--rule, rgba(0,0,0,.1));
1042 border-radius: 8px;
1043 padding: .75rem .5rem;
1044 box-shadow: 0 4px 16px rgba(0,0,0,.15);
1045 opacity: 0;
1046 pointer-events: none;
1047 transition: opacity .15s, transform .15s, visibility .15s;
1048 height: 110px;
1049 visibility: hidden;
1050}
1051/* Invisible bridge tussen knop en popup zodat hover niet breekt */
1052.audio-volume-popup::after {
1053 content: '';
1054 position: absolute;
1055 bottom: -12px;
1056 left: -10px;
1057 right: -10px;
1058 height: 16px;
1059}
1060[data-theme="dark"] .audio-volume-popup {
1061 background: color-mix(in srgb, var(--paper, #111) 92%, #fff);
1062}
1063.audio-player-volume:hover .audio-volume-popup,
1064.audio-player-volume:focus-within .audio-volume-popup,
1065.audio-player-volume.is-open .audio-volume-popup {
1066 opacity: 1;
1067 visibility: visible;
1068 transform: translateX(-50%) scale(1);
1069 pointer-events: auto;
1070}
1071.audio-volume-popup input[type=range] {
1072 /* Standard vertical slider — replaces deprecated -webkit-appearance: slider-vertical. */
1073 writing-mode: vertical-lr;
1074 direction: rtl;
1075 width: 6px;
1076 height: 90px;
1077 accent-color: var(--accent, #c2410c);
1078 /* Grotere hit-area via pseudo-touch padding */
1079 padding: 0 12px;
1080 box-sizing: content-box;
1081 cursor: pointer;
1082}
1083
1084/* Mobile */
1085@media (max-width: 720px) {
1086 .audio-player-inner {
1087 /* v9.0 — 3 cols: expand-trigger (cover+meta) | controls | volume */
1088 grid-template-columns: minmax(0, 1fr) auto auto;
1089 gap: .5rem;
1090 padding: .5rem .8rem;
1091 }
1092 .audio-player-progress {
1093 display: none;
1094 }
1095 .audio-player-cover { width: 40px; height: 40px; }
1096 .audio-btn-play { width: 34px; height: 34px; }
1097 body.has-audio-player .pcms-main { padding-bottom: 4.2rem; }
1098 /* Verberg titel-artiest op bijzonder smal, verstop volume op mobile */
1099 .audio-player-volume { display: none; }
1100}
1101
1102/* Ultra-small schermen (iPhone 4 = 320px; iPhone SE 1st gen = 320px;
1103 iPhone mini ~360px). v9.3 — alles vanaf 320px moet werken; daaronder
1104 accepteren we wel/niet. */
1105@media (max-width: 380px) {
1106 .audio-player-inner {
1107 gap: .35rem;
1108 padding: .4rem .6rem;
1109 }
1110 .audio-player-expand-trigger {
1111 gap: .45rem;
1112 }
1113 .audio-player-cover { width: 36px; height: 36px; }
1114 .audio-player-title { font-size: .88rem; }
1115 .audio-player-artist { font-size: .72rem; }
1116 .audio-player-controls { gap: .15rem; }
1117 .audio-btn { width: 30px; height: 30px; }
1118 .audio-btn svg { width: 16px; height: 16px; }
1119 .audio-btn-play { width: 32px; height: 32px; }
1120 .audio-btn-play svg { width: 18px; height: 18px; }
1121
1122 /* v9.3 — Tracklist compact op smalle telefoons.
1123 Leader 28×28, kleinere gap, padding strakker. Tijd in monoscript blijft
1124 leesbaar; titel krijgt 5-10px meer ruimte. */
1125 .pat-row {
1126 grid-template-columns: 28px 1fr auto;
1127 gap: .5rem;
1128 padding: .45rem .65rem;
1129 }
1130 .pat-row .pat-num,
1131 .pat-row .pat-cover {
1132 width: 28px;
1133 height: 28px;
1134 }
1135 .pat-row::before {
1136 left: .65rem;
1137 width: 28px; height: 28px;
1138 -webkit-mask-size: 22px 22px;
1139 mask-size: 22px 22px;
1140 }
1141 .pat-row .pat-title { font-size: .92rem; }
1142 .pat-row .pat-artist { font-size: .74rem; }
1143 .pat-row .pat-duration { font-size: .74rem; }
1144
1145 /* v9.3 — Album binnen post: cover + tracks compacter */
1146 .post-album-cover {
1147 width: 90px;
1148 height: 90px;
1149 }
1150 .post-album-header {
1151 gap: .65rem;
1152 padding: .65rem;
1153 }
1154 .post-album-title { font-size: 1rem; }
1155
1156 /* v9.3 — Profile-header bovenin compacter */
1157 .profile-photo {
1158 width: 48px !important;
1159 height: 48px !important;
1160 }
1161 .profile-content {
1162 gap: .65rem;
1163 }
1164
1165 /* v9.3 — Install-app modal: minder padding, kleinere knoppen */
1166 .install-modal-inner {
1167 padding: 1rem;
1168 }
1169 .install-app-btn {
1170 padding: .55rem 1rem;
1171 font-size: .9rem;
1172 }
1173
1174 /* v9.3 — horizontaal clippen gebeurt nu globaal op html (zie de html-regel
1175 bovenaan). Hier bewust NIETS op body zetten: body als scroll-container zou
1176 de fixed bottom-tab op mobiel laten meeschuiven. */
1177}
1178
1179/* iPhone 4 / iPhone SE 1st gen / iPhone 5/5s (320px) — strakste niveau */
1180@media (max-width: 340px) {
1181 .audio-player-inner {
1182 padding: .35rem .5rem;
1183 }
1184 .audio-player-artist { display: none; }
1185 .audio-player-cover { width: 32px; height: 32px; }
1186 body.has-audio-player .pcms-main { padding-bottom: 3.6rem; }
1187 /* Expand-sheet wat strakker */
1188 .audio-sheet-panel {
1189 padding: .5rem 1rem 1.5rem;
1190 }
1191 .audio-sheet-title { font-size: 1.2rem; }
1192 .audio-sheet-artist { font-size: .9rem; }
1193 .audio-sheet-play { width: 60px; height: 60px; }
1194 .audio-sheet-play svg { width: 26px; height: 26px; }
1195 .audio-sheet-btn { width: 42px; height: 42px; }
1196 .audio-sheet-btn svg { width: 24px; height: 24px; }
1197 .audio-sheet-controls { gap: 1rem; }
1198
1199 /* v9.3 — Op iPhone 4 (320px): tracklist nog strakker */
1200 .pat-row {
1201 grid-template-columns: 26px 1fr auto;
1202 gap: .4rem;
1203 padding: .4rem .55rem;
1204 }
1205 .pat-row .pat-num,
1206 .pat-row .pat-cover {
1207 width: 26px;
1208 height: 26px;
1209 }
1210 .pat-row::before {
1211 left: .55rem;
1212 width: 26px; height: 26px;
1213 -webkit-mask-size: 20px 20px;
1214 mask-size: 20px 20px;
1215 }
1216 .pat-row .pat-artist {
1217 display: none; /* op extreem smal weglaten ten gunste van titel */
1218 }
1219 .pat-row .pat-title { font-size: .9rem; }
1220 .pat-row .pat-duration { font-size: .72rem; }
1221
1222 /* Container padding reduceren voor max content-breedte */
1223 .container {
1224 padding-inline: 1rem !important;
1225 }
1226
1227 /* Post-cover (de grote bovenste) krimpen */
1228 .post-cover {
1229 max-height: 200px;
1230 }
1231}
1232
1233/* ============================================================
1234 v9.0 — Audio player expand-sheet (Spotify-style "now playing")
1235 Slide-up half-screen met grote cover, track info, big controls
1236 ============================================================ */
1237.audio-sheet {
1238 position: fixed;
1239 inset: 0;
1240 z-index: 1100;
1241 pointer-events: none;
1242 opacity: 0;
1243 transition: opacity .25s ease;
1244}
1245.audio-sheet.is-open {
1246 pointer-events: auto;
1247 opacity: 1;
1248}
1249.audio-sheet-backdrop {
1250 position: absolute;
1251 inset: 0;
1252 /* v9.3 — Blur en overlay-dim allebei via CSS-variabele --pcms-sheet-progress
1253 (1 = volledig, 0 = weg). JS update tijdens drag; CSS-transition pakt
1254 open/close af. Sterke blur (24px) zodat 't effect goed zichtbaar is. */
1255 background: rgb(0 0 0 / calc(.4 * var(--pcms-sheet-progress, 1)));
1256 backdrop-filter: blur(calc(24px * var(--pcms-sheet-progress, 1)));
1257 -webkit-backdrop-filter: blur(calc(24px * var(--pcms-sheet-progress, 1)));
1258 transition: background .25s ease,
1259 backdrop-filter .25s ease,
1260 -webkit-backdrop-filter .25s ease;
1261 will-change: backdrop-filter, background;
1262}
1263/* Tijdens drag: instant updates vanuit JS, geen transition */
1264.audio-sheet-panel.is-dragging ~ .audio-sheet-backdrop,
1265.audio-sheet-backdrop.is-dragging {
1266 transition: none;
1267}
1268.audio-sheet-panel {
1269 position: absolute;
1270 left: 0; right: 0; bottom: 0;
1271 background: var(--paper, #fff);
1272 border-top-left-radius: 20px;
1273 border-top-right-radius: 20px;
1274 box-shadow: 0 -20px 60px rgba(0, 0, 0, .25);
1275 padding: .75rem 1.5rem 2rem;
1276 max-height: 85vh;
1277 overflow-y: auto;
1278 /* v9.2.1 — scroll blijft binnen panel, geen chaining naar body (iOS-proof) */
1279 overscroll-behavior: contain;
1280 touch-action: pan-y;
1281 /* Hide scrollbar but keep scrollable */
1282 scrollbar-width: none; /* Firefox */
1283 -ms-overflow-style: none; /* IE/Edge */
1284 transform: translateY(100%);
1285 transition: transform .3s cubic-bezier(.22, .9, .3, 1),
1286 opacity .3s cubic-bezier(.22, .9, .3, 1);
1287 display: flex;
1288 flex-direction: column;
1289 gap: 1rem;
1290 color: var(--ink);
1291}
1292.audio-sheet-panel::-webkit-scrollbar {
1293 display: none; /* Chrome/Safari */
1294}
1295/* Body scroll lock wanneer sheet open is */
1296body.audio-sheet-locked {
1297 overflow: hidden;
1298}
1299.audio-sheet.is-open .audio-sheet-panel {
1300 transform: translateY(0);
1301}
1302
1303/* v9.3 — Drag-zone: pill bovenaan + cover. Hele strook is touch-target voor
1304 swipe-down-to-close. Pull-to-refresh is uitgeschakeld op html-niveau dus
1305 geen interferentie. Onder de cover (info/progress/controls/queue) werkt
1306 normale touch — knoppen, scroll in queue, etc. */
1307.audio-sheet-drag-zone {
1308 flex-shrink: 0;
1309 display: flex;
1310 flex-direction: column;
1311 align-items: stretch;
1312 gap: 1rem;
1313 /* v9.3 — touch-action: pan-y staat verticale scroll toe (zodat queue
1314 eronder scrollt als gebruiker omhoog veegt over cover). Onze JS
1315 detecteert intent: alleen omlaag-swipe vanaf de top sluit de sheet,
1316 omhoog-swipe = scroll. */
1317 touch-action: pan-y;
1318 user-select: none;
1319 -webkit-user-select: none;
1320 cursor: grab;
1321 margin: -.75rem -1.5rem 0;
1322 padding: .75rem 1.5rem 0;
1323}
1324.audio-sheet-drag-zone:active { cursor: grabbing; }
1325
1326/* v9.4 — Drag alleen actief op touch-only apparaten (telefoons).
1327 Desktop met muis + tablets met muis-cursor → geen drag-grip,
1328 gebruiker sluit via × pill-knop bovenin of klik op backdrop. */
1329@media (hover: hover) and (pointer: fine) {
1330 .audio-sheet-drag-zone {
1331 touch-action: auto;
1332 cursor: default;
1333 pointer-events: none;
1334 }
1335 /* Pill blijft klikbaar als sluit-knop */
1336 .audio-sheet-drag-zone .audio-sheet-handle {
1337 pointer-events: auto;
1338 cursor: pointer;
1339 }
1340}
1341
1342/* Pill (handle) gecentreerd boven de cover */
1343.audio-sheet-drag-zone .audio-sheet-handle {
1344 margin: 0 auto .25rem;
1345}
1346
1347/* Tijdens dragging: panel volgt vinger realtime; geen transition */
1348.audio-sheet-panel.is-dragging {
1349 transition: none;
1350 will-change: transform;
1351}
1352[data-theme="dark"] .audio-sheet-panel {
1353 background: var(--paper, #1a1a1a);
1354 box-shadow: 0 -20px 60px rgba(0, 0, 0, .5);
1355}
1356/* Drag-handle + close button in één */
1357.audio-sheet-handle {
1358 display: block;
1359 width: 48px;
1360 height: 4px;
1361 border-radius: 2px;
1362 background: color-mix(in srgb, var(--ink, #000) 25%, transparent);
1363 border: none;
1364 margin: 0 auto .5rem;
1365 padding: 0;
1366 cursor: pointer;
1367 flex-shrink: 0;
1368 position: relative;
1369}
1370.audio-sheet-handle::before {
1371 content: "";
1372 position: absolute;
1373 /* v9.3 — Drag-strip wrapper neemt nu de hit-area-rol over. ::before
1374 blijft als kleine extra padding voor click-comfort. */
1375 inset: -10px -20px;
1376}
1377.audio-sheet-handle:hover {
1378 background: color-mix(in srgb, var(--ink, #000) 45%, transparent);
1379}
1380/* Grote cover */
1381.audio-sheet-cover {
1382 width: 100%;
1383 max-width: 360px;
1384 aspect-ratio: 1/1;
1385 margin: 0 auto;
1386 border-radius: 12px;
1387 background: linear-gradient(135deg,
1388 var(--accent, #c2410c),
1389 color-mix(in srgb, var(--accent, #c2410c) 55%, #000));
1390 background-size: cover;
1391 background-position: center;
1392 box-shadow: 0 20px 40px rgba(0, 0, 0, .15);
1393 flex-shrink: 0;
1394}
1395[data-theme="dark"] .audio-sheet-cover {
1396 box-shadow: 0 20px 40px rgba(0, 0, 0, .4);
1397}
1398.audio-sheet-info {
1399 text-align: center;
1400 padding: 0 1rem;
1401}
1402.audio-sheet-title {
1403 font-family: var(--font-display, inherit);
1404 font-size: 1.5rem;
1405 font-weight: 600;
1406 line-height: 1.2;
1407 margin: 0 0 .25rem;
1408 letter-spacing: -0.01em;
1409 color: var(--ink);
1410}
1411.audio-sheet-artist {
1412 font-size: 1rem;
1413 color: var(--ink-muted, var(--ink));
1414 opacity: .7;
1415}
1416.audio-sheet-album {
1417 margin-top: .35rem;
1418 font-size: .82rem;
1419 opacity: .5;
1420 text-transform: uppercase;
1421 letter-spacing: .05em;
1422}
1423.audio-sheet-album:empty { display: none; }
1424/* Progress + time */
1425.audio-sheet-progress {
1426 display: grid;
1427 grid-template-columns: auto 1fr auto;
1428 gap: .6rem;
1429 align-items: center;
1430 padding: 0 .5rem;
1431}
1432.audio-sheet-progress .audio-seek { width: 100%; }
1433.audio-sheet-progress .audio-time {
1434 font-size: .78rem;
1435 opacity: .55;
1436}
1437/* Grote controls */
1438.audio-sheet-controls {
1439 display: flex;
1440 align-items: center;
1441 justify-content: center;
1442 gap: 1.75rem;
1443}
1444.audio-sheet-btn {
1445 width: 48px;
1446 height: 48px;
1447 padding: 0;
1448}
1449.audio-sheet-btn svg { width: 28px; height: 28px; }
1450.audio-sheet-play {
1451 width: 72px;
1452 height: 72px;
1453 border-radius: 50%;
1454 background: var(--accent, #c2410c);
1455 color: var(--paper, #fff);
1456 border: none;
1457 cursor: pointer;
1458 display: inline-flex;
1459 align-items: center;
1460 justify-content: center;
1461 transition: transform .1s;
1462}
1463.audio-sheet-play:hover { transform: scale(1.05); }
1464.audio-sheet-play:active { transform: scale(.95); }
1465.audio-sheet-play svg { width: 30px; height: 30px; }
1466.audio-sheet-play .icon-pause { display: none; }
1467/* v9.2.1 — sheet staat nu als sibling van .audio-player buiten de balk,
1468 dus ancestor-selector .audio-player.is-playing werkt niet meer voor de
1469 sheet. JS zet is-playing ook rechtstreeks op sheet.play (regel 551 app.js). */
1470.audio-sheet-play.is-playing .icon-play { display: none; }
1471.audio-sheet-play.is-playing .icon-pause { display: block; }
1472/* v9.2.1 — fallback: als sheet buiten alle ancestors zit, tag body */
1473body.audio-is-playing .audio-sheet-play .icon-play { display: none; }
1474body.audio-is-playing .audio-sheet-play .icon-pause { display: block; }
1475
1476/* v9.3 — Loading state: roterende spiraal (het roboburr logo).
1477 Button-achtergrond wordt transparant, play/pause svgs verbergen, en de
1478 spiraal verschijnt als roterende background via ::after pseudo-element.
1479 Cool-effect: zachte hue-drift geeft de kleur een levend gevoel. */
1480.audio-player.is-loading .audio-btn-play,
1481.audio-player.is-loading .audio-sheet-play,
1482body.audio-is-loading .audio-sheet-play,
1483.pat-row.is-loading {
1484 pointer-events: none;
1485}
1486.audio-player.is-loading .audio-btn-play,
1487.audio-player.is-loading .audio-sheet-play,
1488body.audio-is-loading .audio-sheet-play {
1489 background: transparent;
1490 box-shadow: none;
1491}
1492
1493/* v9.3 — Play/pause-icons in de player-knoppen faden smooth uit/in tijdens
1494 loading. Pseudo-spiraal faded tegelijkertijd in (geen instant pop). */
1495.audio-btn-play > svg,
1496.audio-sheet-play > svg {
1497 transition: opacity .35s cubic-bezier(.4, 0, .2, 1),
1498 transform .35s cubic-bezier(.4, 0, .2, 1);
1499}
1500.audio-player.is-loading .audio-btn-play > svg,
1501.audio-player.is-loading .audio-sheet-play > svg,
1502body.audio-is-loading .audio-sheet-play > svg {
1503 opacity: 0;
1504 transform: scale(.7);
1505}
1506.pat-row.is-loading .pat-play-icon > svg,
1507.pat-row.is-loading .pat-num,
1508.pat-row.is-loading .pat-cover {
1509 opacity: 0;
1510}
1511
1512/* v9.3 — Spiraal als pseudo-element, ALTIJD aanwezig met opacity 0 default.
1513 Bij is-loading faden 'ie smooth in (opacity + scale-bounce), bij klaar
1514 faden 'ie weer uit. Pure accent-kleur, geen hue-drift. */
1515.audio-player .audio-btn-play,
1516.audio-player .audio-sheet-play,
1517.pat-row .pat-play-icon {
1518 position: relative;
1519}
1520.audio-btn-play::after,
1521.audio-sheet-play::after {
1522 content: "";
1523 position: absolute;
1524 inset: 0;
1525 background-color: var(--accent, #c2410c);
1526 -webkit-mask: url('/assets/spinner.svg') center/contain no-repeat;
1527 mask: url('/assets/spinner.svg') center/contain no-repeat;
1528 opacity: 0;
1529 transform: scale(.6);
1530 transition: opacity .4s cubic-bezier(.4, 0, .2, 1),
1531 transform .4s cubic-bezier(.34, 1.56, .64, 1);
1532 pointer-events: none;
1533 animation: pcms-spiral-spin 1.2s linear infinite paused;
1534 will-change: opacity, transform;
1535}
1536.audio-player.is-loading .audio-btn-play::after,
1537.audio-player.is-loading .audio-sheet-play::after,
1538body.audio-is-loading .audio-sheet-play::after {
1539 opacity: 1;
1540 transform: scale(1);
1541 animation-play-state: running;
1542}
1543/* Pat-row keeps its existing ::before spinner from earlier change.
1544 Voor backward-compat als oude HTML nog .pat-play-icon::after spinner had */
1545.pat-row.is-loading .pat-play-icon::after {
1546 content: "";
1547 position: absolute;
1548 inset: 0;
1549 background-color: var(--accent, #c2410c);
1550 -webkit-mask: url('/assets/spinner.svg') center/contain no-repeat;
1551 mask: url('/assets/spinner.svg') center/contain no-repeat;
1552 opacity: 1;
1553 animation: pcms-spiral-spin 1.2s linear infinite;
1554 pointer-events: none;
1555}
1556@keyframes pcms-spiral-spin {
1557 to { transform: scale(1) rotate(-360deg); }
1558}
1559/* Queue list */
1560.audio-sheet-queue {
1561 margin-top: .5rem;
1562 padding-top: 1rem;
1563 border-top: 1px solid var(--rule);
1564}
1565.audio-sheet-queue-label {
1566 font-size: .75rem;
1567 text-transform: uppercase;
1568 letter-spacing: .08em;
1569 opacity: .5;
1570 font-weight: 600;
1571 margin-bottom: .5rem;
1572 padding: 0 .5rem;
1573}
1574.audio-sheet-queue-list {
1575 list-style: none;
1576 padding: 0;
1577 margin: 0;
1578 display: flex;
1579 flex-direction: column;
1580 gap: 2px;
1581}
1582.audio-sheet-queue-item {
1583 display: flex;
1584 align-items: center;
1585 gap: .75rem;
1586 padding: .5rem .6rem;
1587 border-radius: 6px;
1588 cursor: pointer;
1589 transition: background .15s;
1590 background: none;
1591 border: none;
1592 width: 100%;
1593 color: inherit;
1594 font: inherit;
1595 text-align: left;
1596}
1597.audio-sheet-queue-item:hover {
1598 background: color-mix(in srgb, var(--ink, #000) 6%, transparent);
1599}
1600.audio-sheet-queue-item.is-current {
1601 background: color-mix(in srgb, var(--accent, #c2410c) 14%, transparent);
1602 color: var(--accent, #c2410c);
1603}
1604.audio-sheet-queue-cover {
1605 width: 36px;
1606 height: 36px;
1607 border-radius: 4px;
1608 background-size: cover;
1609 background-position: center;
1610 background-color: var(--rule);
1611 flex-shrink: 0;
1612}
1613.audio-sheet-queue-meta {
1614 flex: 1;
1615 min-width: 0;
1616 display: flex;
1617 flex-direction: column;
1618}
1619.audio-sheet-queue-title {
1620 font-size: .9rem;
1621 font-weight: 500;
1622 overflow: hidden;
1623 text-overflow: ellipsis;
1624 white-space: nowrap;
1625}
1626.audio-sheet-queue-artist {
1627 font-size: .75rem;
1628 opacity: .55;
1629 overflow: hidden;
1630 text-overflow: ellipsis;
1631 white-space: nowrap;
1632}
1633.audio-sheet-queue-duration {
1634 font-family: ui-monospace, monospace;
1635 font-size: .72rem;
1636 opacity: .5;
1637 flex-shrink: 0;
1638}
1639
1640/* Desktop: sheet is max 480px wide and centered for bigger screens */
1641@media (min-width: 720px) {
1642 .audio-sheet-panel {
1643 left: 50%;
1644 right: auto;
1645 width: 480px;
1646 transform: translate(-50%, 100%);
1647 border-top-left-radius: 20px;
1648 border-top-right-radius: 20px;
1649 }
1650 .audio-sheet.is-open .audio-sheet-panel {
1651 transform: translate(-50%, 0);
1652 }
1653}
1654
1655/* v9.2.1 — Mobiel: sheet is fullscreen, alleen te sluiten via handle-knop bovenin.
1656 Geen rounded corners, volledige viewport, safe-area voor notches/status bar. */
1657@media (max-width: 719.98px) {
1658 .audio-sheet-panel {
1659 inset: 0;
1660 max-height: none;
1661 border-top-left-radius: 0;
1662 border-top-right-radius: 0;
1663 box-shadow: none;
1664 padding-top: calc(.75rem + var(--ios-safe-top, 0px));
1665 padding-bottom: calc(2rem + env(safe-area-inset-bottom, 0px));
1666 }
1667 /* v9.3 — Backdrop blijft visible op mobile zodat drag-gesture de blur
1668 laat doorschemeren naar helder. */
1669}
1670
1671/* ============================================================
1672 Mobile masthead + pagination
1673 ============================================================ */
1674@media (max-width: 600px) {
1675 .masthead-inner { gap: .35rem; }
1676 .site-title { font-size: 1.2rem; }
1677 .site-subtitle { display: none; }
1678 .top-nav { gap: .1rem; }
1679 .nav-label { display: none; }
1680 .nav-btn, .nav-link {
1681 padding: .4rem .45rem;
1682 min-width: 38px;
1683 justify-content: center;
1684 }
1685 .pagination { flex-direction: column; gap: .5rem; }
1686 .pagination-item { width: 100%; }
1687}
1688
1689/* Taal-keuze in de header-nav (NL/EN/DE) — eigen gestylede dropdown. */
1690.lang-menu { position: relative; }
1691.lang-menu > summary {
1692 list-style: none; cursor: pointer;
1693 display: inline-flex; align-items: center; gap: .3rem;
1694 padding: .35rem .55rem; border-radius: 999px;
1695 border: 1px solid var(--rule); background: transparent; color: var(--ink);
1696}
1697.lang-menu > summary::-webkit-details-marker { display: none; }
1698.lang-menu > summary:hover { border-color: var(--accent); }
1699.lang-toggle .lang-code { font-size: .8rem; font-weight: 700; letter-spacing: .02em; }
1700.lang-toggle .icon { width: 16px; height: 16px; opacity: .8; }
1701.lang-caret { width: 12px; height: 12px; opacity: .7; transition: transform .15s; }
1702.lang-menu[open] .lang-caret { transform: rotate(180deg); }
1703.lang-dropdown {
1704 position: absolute; top: calc(100% + 8px); right: 0;
1705 min-width: 170px; z-index: 60;
1706 background: var(--paper); border: 1px solid var(--rule);
1707 border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,.22);
1708 padding: .35rem; display: flex; flex-direction: column;
1709}
1710.lang-item {
1711 display: flex; align-items: center; gap: .6rem;
1712 padding: .5rem .65rem; border-radius: 8px;
1713 color: var(--ink); text-decoration: none; font-size: .9rem;
1714}
1715.lang-item:hover { background: var(--paper-2); }
1716.lang-item.is-active { color: var(--accent); font-weight: 600; }
1717.lang-item-code {
1718 font-size: .7rem; font-weight: 700; opacity: .6;
1719 min-width: 1.6em;
1720}
1721@media (max-width: 400px) {
1722 .masthead { padding: calc(.5rem + var(--ios-safe-top, 0px)) 0 .5rem; }
1723 .masthead-inner { gap: .25rem; }
1724 .site-title { font-size: 1.05rem; }
1725 .nav-btn, .nav-link {
1726 padding: .35rem .3rem;
1727 min-width: 34px;
1728 }
1729 .nav-btn .icon, .nav-link .icon { width: 1rem; height: 1rem; }
1730 /* PWA-install knop verbergen op klein scherm — kan ook via menu */
1731 .nav-install-btn { display: none !important; }
1732}
1733@media (max-width: 340px) {
1734 .site-title { font-size: .95rem; }
1735 .nav-btn, .nav-link { padding: .3rem .25rem; min-width: 30px; }
1736 /* Theme-toggle verbergen op ultra-klein — kan via user-menu */
1737 [data-pcms-theme-toggle] { display: none; }
1738}
1739
1740.post-date-ago {
1741 margin-left: .5em;
1742 opacity: .6;
1743 font-size: .9em;
1744 font-style: italic;
1745 text-transform: none;
1746 letter-spacing: 0;
1747}
1748
1749/* HTMX loading indicator — subtle progress-bar aan de top van de content */
1750.pcms-loading {
1751 position: fixed;
1752 top: 0; left: 0;
1753 height: 2px;
1754 width: 0%;
1755 background: var(--accent, #c2410c);
1756 z-index: 9999;
1757 transition: width .3s ease, opacity .2s;
1758 opacity: 0;
1759 pointer-events: none;
1760}
1761.pcms-loading.htmx-request {
1762 opacity: 1;
1763 width: 80%;
1764 transition: width 6s cubic-bezier(.1,.9,.2,1), opacity .2s;
1765}
1766/* subtile fade wanneer content gewisseld wordt */
1767#pcms-main.htmx-swapping {
1768 opacity: .5;
1769 transition: opacity .15s;
1770}
1771
1772/* Masthead brand group (site-title + optional tagline) */
1773.masthead-brand { display: flex; flex-direction: column; gap: 2px; }
1774.site-subtitle {
1775 font-family: var(--font-ui, inherit);
1776 font-size: .75rem;
1777 color: var(--ink-muted, rgba(0,0,0,.55));
1778 letter-spacing: 0;
1779 line-height: 1.3;
1780}
1781
1782/* ============================================================
1783 v7.3 — TAGS, TYPE, ARCHIEF, ZOEK, RELATED, SPECIAL VIEWS
1784 ============================================================ */
1785
1786/* Post-meta row — datum + type-chip + tags samen onder de titel */
1787.post-meta {
1788 display: flex;
1789 flex-wrap: wrap;
1790 align-items: center;
1791 gap: .5rem .85rem;
1792 margin-top: .5rem;
1793}
1794.post-meta .post-date { margin: 0; }
1795
1796/* Type-chip (groter, als een label) */
1797.post-type-chip {
1798 display: inline-flex;
1799 align-items: center;
1800 padding: .2rem .6rem;
1801 background: color-mix(in srgb, var(--accent) 10%, transparent);
1802 color: var(--accent);
1803 border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
1804 border-radius: 999px;
1805 font-family: var(--font-ui);
1806 font-size: .72rem;
1807 font-weight: 600;
1808 text-decoration: none;
1809 letter-spacing: .04em;
1810 text-transform: uppercase;
1811 transition: background .15s;
1812}
1813.post-type-chip:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
1814
1815/* Tag-chips — klein, subtiel, gelinkt */
1816.post-tags, .post-list-tags {
1817 display: inline-flex;
1818 flex-wrap: wrap;
1819 gap: .3rem;
1820}
1821.tag-chip {
1822 display: inline-block;
1823 padding: .15rem .55rem;
1824 background: color-mix(in srgb, var(--ink) 5%, transparent);
1825 color: var(--ink-muted);
1826 border-radius: 999px;
1827 font-family: var(--font-ui);
1828 font-size: .72rem;
1829 font-weight: 500;
1830 text-decoration: none;
1831 transition: background .15s, color .15s;
1832}
1833.tag-chip:hover {
1834 background: color-mix(in srgb, var(--accent) 12%, transparent);
1835 color: var(--accent);
1836}
1837
1838/* ============================================================
1839 SPECIAL PAGES (archief, tag, type, zoek)
1840 ============================================================ */
1841.special-page .post-header { margin-bottom: 2rem; }
1842.special-eyebrow {
1843 font-size: .72rem;
1844 color: var(--ink-muted);
1845 text-transform: uppercase;
1846 letter-spacing: .12em;
1847 margin: 0 0 .25rem;
1848}
1849
1850/* Archief: jaar-overzicht */
1851.archive-years {
1852 display: flex;
1853 flex-direction: column;
1854 gap: 2rem;
1855}
1856.archive-year h2 {
1857 font-family: var(--font-display);
1858 font-size: 1.75rem;
1859 margin: 0 0 .75rem;
1860}
1861.archive-year h2 a {
1862 color: var(--ink);
1863 text-decoration: none;
1864}
1865.archive-year h2 a:hover { color: var(--accent); }
1866.archive-year h2 .admin-muted { font-size: .9rem; font-weight: normal; color: var(--ink-muted); }
1867.archive-months {
1868 list-style: none;
1869 padding: 0; margin: 0;
1870 display: grid;
1871 grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
1872 gap: .5rem;
1873}
1874.archive-months li {
1875 display: flex;
1876 align-items: center;
1877 gap: .4rem;
1878 padding: .55rem .75rem;
1879 background: color-mix(in srgb, var(--ink) 3%, transparent);
1880 border-radius: var(--radius);
1881 transition: background .15s;
1882}
1883.archive-months li:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
1884.archive-months a {
1885 color: var(--ink);
1886 text-decoration: none;
1887 font-weight: 500;
1888 font-size: .95rem;
1889}
1890.archive-months .admin-muted { font-size: .75rem; color: var(--ink-muted); margin-left: auto; }
1891
1892/* Archief: jaar drill-down */
1893.archive-month-list {
1894 display: flex;
1895 flex-direction: column;
1896 gap: 2rem;
1897}
1898.archive-month h2 {
1899 font-family: var(--font-display);
1900 font-size: 1.35rem;
1901 margin: 0 0 .5rem;
1902 padding-bottom: .35rem;
1903 border-bottom: 1px solid var(--rule);
1904}
1905.archive-month h2 a {
1906 color: var(--ink);
1907 text-decoration: none;
1908}
1909.archive-month h2 a:hover { color: var(--accent); }
1910
1911/* Compact post-list (gebruikt in archief drill-down per maand) */
1912.post-compact-list {
1913 list-style: none;
1914 padding: 0; margin: 0;
1915 display: flex;
1916 flex-direction: column;
1917}
1918.post-compact-list li {
1919 border-bottom: 1px solid var(--rule);
1920}
1921.post-compact-list li:last-child { border-bottom: none; }
1922.post-compact-list a {
1923 display: grid;
1924 grid-template-columns: 3em 1fr;
1925 gap: .75rem;
1926 padding: .6rem .2rem;
1927 text-decoration: none;
1928 color: var(--ink);
1929 transition: background .12s;
1930 border-radius: 4px;
1931}
1932.post-compact-list a:hover {
1933 background: color-mix(in srgb, var(--accent) 6%, transparent);
1934}
1935.post-compact-date {
1936 color: var(--ink-muted);
1937 font-size: .8rem;
1938 align-self: center;
1939}
1940.post-compact-title { font-weight: 500; font-size: 1rem; align-self: center; }
1941.post-compact-list.large a { grid-template-columns: 4em 1fr; padding: .8rem .3rem; }
1942.post-compact-list.large .post-compact-title { font-family: var(--font-display); font-size: 1.15rem; }
1943.post-compact-excerpt {
1944 grid-column: 2;
1945 color: var(--ink-muted);
1946 font-size: .88rem;
1947 line-height: 1.5;
1948}
1949
1950/* Post-list (gebruikt in tag / type / zoek pagina's) */
1951.post-list {
1952 list-style: none;
1953 padding: 0; margin: 0;
1954 display: flex;
1955 flex-direction: column;
1956 gap: 1.5rem;
1957}
1958.post-list-item {
1959 display: grid;
1960 grid-template-columns: 120px 1fr;
1961 gap: 1.25rem;
1962 padding-bottom: 1.5rem;
1963 border-bottom: 1px solid var(--rule);
1964}
1965.post-list-item:last-child { border-bottom: none; }
1966.post-list-item:has(> .post-list-cover + .post-list-body) { grid-template-columns: 120px 1fr; }
1967.post-list-item:not(:has(.post-list-cover)) { grid-template-columns: 1fr; }
1968.post-list-cover {
1969 display: block;
1970 aspect-ratio: 1;
1971 overflow: hidden;
1972 border-radius: var(--radius);
1973 /* Accent-gradient placeholder while the cover image loads (image fades in over it). */
1974 background-image: linear-gradient(135deg,
1975 color-mix(in srgb, var(--accent, #888) 22%, var(--paper-2, var(--paper))) 0%,
1976 color-mix(in srgb, var(--accent, #888) 6%, var(--paper-2, var(--paper))) 100%);
1977}
1978.post-list-cover img, .post-list-cover video {
1979 width: 100%; height: 100%;
1980 object-fit: cover; /* fill the square, keep aspect, crop the overflow, centered */
1981 object-position: center;
1982 display: block;
1983 background-color: #fff; /* transparent logos sit on white once loaded */
1984 transition: opacity .4s ease;
1985}
1986.post-list-cover img.is-loading { opacity: 0; }
1987.post-list-body { display: flex; flex-direction: column; gap: .35rem; }
1988.post-list-meta {
1989 color: var(--ink-muted);
1990 font-size: .75rem;
1991 display: flex; gap: .35rem; align-items: center;
1992}
1993.post-list-title {
1994 font-family: var(--font-display);
1995 font-size: 1.3rem;
1996 font-weight: 450;
1997 margin: 0;
1998 line-height: 1.2;
1999}
2000.post-list-title a {
2001 color: var(--ink);
2002 text-decoration: none;
2003}
2004.post-list-title a:hover { color: var(--accent); }
2005.post-list-excerpt {
2006 color: var(--ink-muted);
2007 font-size: .95rem;
2008 margin: 0;
2009 line-height: 1.5;
2010}
2011
2012/* Zoek-formulier binnen special view */
2013.search-form {
2014 display: flex;
2015 gap: .5rem;
2016 margin-bottom: 2rem;
2017}
2018.search-form input[type=search] {
2019 flex: 1;
2020 padding: .75rem 1rem;
2021 background: var(--paper-2);
2022 border: 1px solid var(--rule);
2023 border-radius: var(--radius);
2024 font: inherit;
2025 color: var(--ink);
2026}
2027.search-form input[type=search]:focus {
2028 outline: none;
2029 border-color: var(--accent);
2030 box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
2031}
2032
2033/* ============================================================
2034 SEARCH OVERLAY (masthead → full-screen zoekbalk)
2035 ============================================================ */
2036.search-overlay {
2037 position: fixed;
2038 inset: 0;
2039 background: color-mix(in srgb, var(--ink) 50%, transparent);
2040 z-index: 1500;
2041 display: flex;
2042 justify-content: center;
2043 align-items: flex-start;
2044 padding-top: 10vh;
2045 backdrop-filter: blur(6px);
2046 -webkit-backdrop-filter: blur(6px);
2047}
2048.search-overlay[hidden] { display: none; }
2049.search-overlay-form {
2050 width: min(90vw, 600px);
2051 background: var(--paper);
2052 border-radius: calc(var(--radius) * 1.5);
2053 padding: .75rem 1rem;
2054 display: flex;
2055 align-items: center;
2056 gap: .6rem;
2057 box-shadow: 0 20px 60px rgba(0,0,0,.3);
2058 border: 1px solid var(--rule);
2059}
2060.search-overlay-form .icon {
2061 width: 1.2rem; height: 1.2rem;
2062 color: var(--ink-muted);
2063 flex-shrink: 0;
2064}
2065.search-overlay-form input[type=search] {
2066 flex: 1;
2067 background: transparent;
2068 border: none;
2069 font-family: var(--font-body);
2070 font-size: 1.1rem;
2071 color: var(--ink);
2072 padding: .5rem 0;
2073}
2074.search-overlay-form input[type=search]:focus { outline: none; }
2075.search-overlay-form .nav-btn {
2076 background: transparent;
2077 border: none;
2078 color: var(--ink-muted);
2079 cursor: pointer;
2080 font-size: 1.25rem;
2081 line-height: 1;
2082 padding: .3rem .5rem;
2083}
2084.search-overlay-form .nav-btn:hover { color: var(--ink); }
2085body.search-open { overflow: hidden; }
2086
2087/* ============================================================
2088 RELATED POSTS (onder elke post-view)
2089 ============================================================ */
2090.related-posts {
2091 margin-top: 2.5rem;
2092 padding-top: 2rem;
2093 border-top: 1px solid var(--rule);
2094}
2095.related-title {
2096 font-family: var(--font-mono);
2097 font-size: .7rem;
2098 color: var(--ink-muted);
2099 text-transform: uppercase;
2100 letter-spacing: .12em;
2101 margin: 0 0 1rem;
2102}
2103.related-grid {
2104 list-style: none;
2105 padding: 0; margin: 0;
2106 display: grid;
2107 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
2108 gap: 1rem;
2109}
2110.related-grid a {
2111 display: flex;
2112 flex-direction: column;
2113 gap: .5rem;
2114 text-decoration: none;
2115 color: var(--ink);
2116 border: 1px solid var(--rule);
2117 border-radius: var(--radius);
2118 overflow: hidden;
2119 transition: border-color .15s, transform .15s;
2120 height: 100%;
2121}
2122.related-grid a:hover {
2123 border-color: color-mix(in srgb, var(--accent) 50%, var(--rule));
2124 transform: translateY(-2px);
2125}
2126.related-cover {
2127 display: block;
2128 aspect-ratio: 16/10;
2129 background-size: cover;
2130 background-position: center;
2131 background-color: var(--paper-2);
2132}
2133
2134/* v9.4 — Wanneer related-cover ook .grid-tile-gradient class heeft (lege cover
2135 fallback), erft 'ie automatisch de hue/gradient/title styling van grid-tiles.
2136 Geen aparte related-cover-gradient meer nodig. */
2137.related-body {
2138 display: flex;
2139 flex-direction: column;
2140 gap: .25rem;
2141 padding: .6rem .75rem .9rem;
2142}
2143.related-meta { color: var(--ink-muted); font-size: .7rem; }
2144.related-name {
2145 font-family: var(--font-display);
2146 font-weight: 450;
2147 font-size: 1rem;
2148 line-height: 1.25;
2149}
2150
2151/* ============================================================
2152 MOBILE TWEAKS voor v7.3
2153 ============================================================ */
2154@media (max-width: 600px) {
2155 .post-list-item { grid-template-columns: 1fr; }
2156 .post-list-cover { aspect-ratio: 16/9; }
2157 .archive-months { grid-template-columns: repeat(2, 1fr); }
2158 .related-grid { grid-template-columns: 1fr; }
2159 .search-overlay { padding-top: 4vh; }
2160}
2161
2162/* ============================================================
2163 v7.4 — PROFILE HEADER + VIEW SWITCHER + GRID + POST-TYPE LAYOUTS
2164 MOBILE-FIRST — belangrijkste eis
2165 ============================================================ */
2166
2167/* -------- PROFILE HEADER (Optie C: full op home, compact op post/special) -------- */
2168
2169.profile-header {
2170 background: var(--paper-2, var(--paper));
2171 border-bottom: 1px solid var(--rule);
2172 transition: padding .25s ease, background .2s;
2173}
2174/* Mobiel default = full-mode padding */
2175.profile-header { padding: 1.25rem 0; }
2176.profile-header-inner {
2177 display: flex;
2178 gap: .9rem;
2179 align-items: flex-start;
2180}
2181
2182.profile-photo {
2183 flex-shrink: 0;
2184 width: 80px;
2185 height: 80px;
2186 border-radius: 50%;
2187 overflow: hidden;
2188 display: block;
2189 background: var(--paper);
2190 border: 2px solid var(--rule);
2191 transition: width .25s ease, height .25s ease, border-color .2s;
2192 text-decoration: none;
2193}
2194.profile-photo:hover { border-color: var(--accent); }
2195.profile-photo img {
2196 width: 100%; height: 100%;
2197 object-fit: cover;
2198 display: block;
2199}
2200.profile-photo-fallback {
2201 display: flex;
2202 align-items: center;
2203 justify-content: center;
2204 width: 100%; height: 100%;
2205 background: var(--accent);
2206 color: var(--paper);
2207 font-family: var(--font-display);
2208 font-weight: 700;
2209 font-size: 2.2rem;
2210 text-transform: uppercase;
2211 line-height: 1;
2212 transition: font-size .25s ease;
2213}
2214
2215.profile-info {
2216 flex: 1;
2217 min-width: 0;
2218 display: flex;
2219 flex-direction: column;
2220 gap: .3rem;
2221}
2222.profile-name {
2223 margin: 0;
2224 font-family: var(--font-display);
2225 font-size: 1.2rem;
2226 font-weight: 500;
2227 line-height: 1.15;
2228 transition: font-size .25s ease;
2229}
2230.profile-name a {
2231 color: var(--ink);
2232 text-decoration: none;
2233}
2234.profile-name a:hover { color: var(--accent); }
2235.profile-bio {
2236 margin: 0;
2237 color: var(--ink-muted);
2238 font-size: .92rem;
2239 line-height: 1.45;
2240 max-height: 200px;
2241 overflow: hidden;
2242 transition: max-height .25s ease, margin .25s ease, opacity .2s;
2243}
2244.profile-links {
2245 list-style: none;
2246 padding: 0;
2247 margin: .15rem 0 0;
2248 display: flex;
2249 flex-wrap: wrap;
2250 gap: .4rem;
2251 max-height: 50px;
2252 overflow: hidden;
2253 transition: max-height .25s ease, margin .25s ease, opacity .2s;
2254}
2255.profile-links li { margin: 0; }
2256.profile-links a,
2257.profile-links .profile-fedi-link {
2258 display: inline-flex;
2259 align-items: center;
2260 justify-content: center;
2261 width: 40px;
2262 height: 40px;
2263 border-radius: 50%;
2264 background: color-mix(in srgb, var(--ink) 5%, transparent);
2265 color: var(--ink-muted);
2266 text-decoration: none;
2267 transition: background .15s, color .15s;
2268}
2269.profile-links a:hover,
2270.profile-links .profile-fedi-link:hover {
2271 background: color-mix(in srgb, var(--accent) 15%, transparent);
2272 color: var(--accent);
2273}
2274.profile-links .icon { width: 1.1rem; height: 1.1rem; }
2275
2276/* -------- COMPACT MODE: op post + special pages -------- */
2277
2278.on-post .profile-header,
2279.on-special .profile-header {
2280 padding: .5rem 0;
2281}
2282.on-post .profile-photo,
2283.on-special .profile-photo {
2284 width: 36px;
2285 height: 36px;
2286 border-width: 1px;
2287}
2288.on-post .profile-photo-fallback,
2289.on-special .profile-photo-fallback {
2290 font-size: 1rem;
2291}
2292.on-post .profile-name,
2293.on-special .profile-name {
2294 font-size: 1rem;
2295 align-self: center;
2296}
2297.on-post .profile-bio,
2298.on-special .profile-bio,
2299.on-post .profile-links,
2300.on-special .profile-links {
2301 display: none;
2302}
2303.on-post .profile-header-inner,
2304.on-special .profile-header-inner {
2305 align-items: center;
2306 justify-content: flex-start;
2307 gap: .6rem;
2308}
2309.on-post .profile-info,
2310.on-special .profile-info {
2311 flex: 0 1 auto;
2312 min-width: 0;
2313}
2314.on-post .profile-name,
2315.on-special .profile-name {
2316 text-align: left;
2317}
2318
2319/* -------- DESKTOP: meer ruimte, grotere foto -------- */
2320@media (min-width: 768px) {
2321 .profile-header { padding: 2rem 0; }
2322 .profile-photo { width: 120px; height: 120px; }
2323 .profile-photo-fallback { font-size: 3.2rem; }
2324 .profile-name { font-size: 1.5rem; }
2325 .profile-bio { font-size: 1rem; }
2326 .profile-header-inner { gap: 1.5rem; }
2327 .on-post .profile-header,
2328 .on-special .profile-header { padding: .6rem 0; }
2329 .on-post .profile-photo,
2330 .on-special .profile-photo { width: 42px; height: 42px; }
2331 /* Compact mode: gap blijft klein, ook op desktop */
2332 .on-post .profile-header-inner,
2333 .on-special .profile-header-inner { gap: .75rem; }
2334 .on-post .profile-name,
2335 .on-special .profile-name { font-size: 1.1rem; }
2336}
2337
2338/* -------- VIEW SWITCHER -------- */
2339.view-switcher-wrap {
2340 border-bottom: 1px solid var(--rule);
2341 background: var(--paper);
2342 /* Bottom margin gives breathing room between the switcher bar and the
2343 first post. margin-top staat in shared-styles.ejs (de winnende inline
2344 <style>) — symmetrisch op 1.5rem. */
2345 margin-bottom: 1.5rem;
2346}
2347.view-switcher {
2348 display: flex;
2349 justify-content: center;
2350 gap: .35rem;
2351 /* Slimmer bar — was .6rem, now .35rem so the bar reads as a divider
2352 rather than a row of weight equal to the header. */
2353 padding: .35rem 0;
2354}
2355.view-switch-btn {
2356 display: inline-flex;
2357 align-items: center;
2358 gap: .4rem;
2359 padding: .35rem .85rem; /* was .55rem 1rem */
2360 min-height: 32px; /* was 44px */
2361 background: transparent;
2362 border: 1px solid var(--rule);
2363 border-radius: 999px;
2364 color: var(--ink-muted);
2365 font-family: var(--font-ui);
2366 font-size: .82rem; /* was .85rem */
2367 cursor: pointer;
2368 transition: all .15s;
2369}
2370.view-switch-btn:hover {
2371 color: var(--ink);
2372 border-color: var(--ink-muted);
2373}
2374/* Uitgeschakeld (Lezen in de cirkel) wint van :hover -- anders licht een knop
2375 op die je niet kunt indrukken, en dat is precies de verkeerde belofte. */
2376.view-switch-btn[disabled],
2377.view-switch-btn[disabled]:hover {
2378 opacity: .38;
2379 cursor: not-allowed;
2380 color: var(--ink-muted);
2381 border-color: var(--rule);
2382}
2383/* Actieve state: puur afgeleid van body[data-feed-view]. Geen .is-active class meer.
2384 Niet op de Archive-pagina: daar is geen "huidige" timeline/grid-keuze, dus beide
2385 knoppen blijven neutraal (grijs). */
2386/* Actieve Tijdlijn/Grid-state wordt nu gestuurd via [aria-selected="true"] (gezet
2387 door syncAria, die óók op htmx:afterSettle draait) i.p.v. body[data-feed-view].
2388 Reden: body-class/attribuut-CSS pakte niet betrouwbaar na een htmx-OOB-swap;
2389 een element-attribuut wel. De witte active-regel staat in shared-styles. */
2390.view-switch-btn .icon { width: 1rem; height: 1rem; }
2391
2392/* -------- FEED TOGGLE VIA BODY --------
2393 Grid-modus werkt op de feed (home, cirkel). Het ARCHIEF negeert de grid-keuze
2394 bewust: dat is altijd de chronologische jaar/maand-lijst (de switcher is daar
2395 grijs en springt bij klik terug naar de feed). Post-pagina's tonen de
2396 feed-timeline article sowieso altijd. Alle andere body-classes negeren
2397 data-feed-view en houden timeline visible. */
2398body.on-home[data-feed-view="grid"] .feed-timeline,
2399body.on-home[data-feed-view="grid"] .pagination,
2400body.on-home[data-feed-view="grid"] .related-posts,
2401body.on-home[data-feed-view="grid"] .article-footer,
2402body.on-cirkel[data-feed-view="grid"] .feed-timeline {
2403 display: none !important;
2404}
2405/* Grid-sectie alleen zichtbaar op de feed in grid-mode (NIET op archief) */
2406.feed-grid { display: none; }
2407body.on-home[data-feed-view="grid"] .feed-grid,
2408body.on-cirkel[data-feed-view="grid"] .feed-grid { display: block; }
2409
2410/* -------- LEESWEERGAVE (data-feed-view="reader") --------
2411 Dezelfde berichten als de tijdlijn, maar heel: één per scherm, grenzen door
2412 scroll-snapping. GEEN eigen route en geen eigen shell -- de balken blijven
2413 staan, want je bent nog op de feed en moet terug kunnen schakelen.
2414 Pagina's zonder .feed-reader (cirkel, archief, post) negeren de keuze en
2415 houden hun tijdlijn: er is hieronder simpelweg geen regel die die daar
2416 verbergt. Zo is Lezen nooit een leeg scherm. */
2417.feed-reader { display: none; }
2418body.on-home[data-feed-view="reader"] .feed-reader { display: block; }
2419/* :has(.feed-reader) is hier de LEGE STAAT, niet een truc. Zonder berichten
2420 rendert de leessectie niet, en dan mag de tijdlijn niet verborgen worden --
2421 want dáár staat "nog niets geschreven" met de knop om te beginnen. Zonder deze
2422 voorwaarde is Lezen op een verse site een wit scherm. Hetzelfde geldt voor
2423 feeds die (nog) geen leessectie hebben. */
2424body.on-home[data-feed-view="reader"]:has(.feed-reader) .feed-timeline,
2425body.on-home[data-feed-view="reader"]:has(.feed-reader) .feed-grid,
2426body.on-home[data-feed-view="reader"]:has(.feed-reader) .pagination,
2427body.on-home[data-feed-view="reader"]:has(.feed-reader) .related-posts,
2428body.on-home[data-feed-view="reader"]:has(.feed-reader) .article-footer {
2429 display: none !important;
2430}
2431
2432/* -------- AUTO (data-feed-view="auto") --------
2433 Lezen op mobiel, Tijdlijn op desktop. GEEN JavaScript: de CSS kiest, dus geen
2434 resize-afhandeling en geen flikkering waarin je eerst de verkeerde weergave
2435 ziet. De tijdlijn is de terugval, dus boven de grens hoeft hier niets te
2436 staan -- die is vanzelf al zichtbaar.
2437 De :has(.feed-reader)-voorwaarde staat er om dezelfde reden als hierboven:
2438 zonder berichten is er geen leessectie, en dan moet de tijdlijn blijven staan
2439 met "nog niets geschreven" erin. */
2440@media (max-width: 767px) {
2441 body.on-home[data-feed-view="auto"] .feed-reader { display: block; }
2442 body.on-home[data-feed-view="auto"]:has(.feed-reader) .feed-timeline,
2443 body.on-home[data-feed-view="auto"]:has(.feed-reader) .feed-grid,
2444 body.on-home[data-feed-view="auto"]:has(.feed-reader) .pagination,
2445 body.on-home[data-feed-view="auto"]:has(.feed-reader) .related-posts,
2446 body.on-home[data-feed-view="auto"]:has(.feed-reader) .article-footer {
2447 display: none !important;
2448 }
2449}
2450/* In de auto-stand staan BEIDE knoppen in de HTML en laat de mediaquery er een
2451 zien. Zo verspringt de naam van de knop niet terwijl je ernaar kijkt, en is er
2452 geen JavaScript nodig dat op resize moet luisteren. */
2453.view-switch-btn.vs-alleen-mobiel { display: none; }
2454@media (max-width: 767px) {
2455 .view-switch-btn.vs-alleen-mobiel { display: inline-flex; }
2456 .view-switch-btn.vs-alleen-desktop { display: none; }
2457}
2458
2459/* GEWOON SCROLLEN (Robin, 30-9: "Reader view normaal scrollen, Lenis mag
2460 helemaal uit klonkt"). Hier stond scroll-snap-type op het document, en Lenis
2461 deed het snappen erbovenop. Er snapt niets meer: de reader scrolt zoals elke
2462 andere pagina. De .read-anker-punten hieronder staan er nog, maar zonder
2463 snap-container doen ze niets. Terug willen? Zie de vault, "Klonkt Reader
2464 Scroll": native snappen had bekende nadelen, en die staan daar. */
2465/* Scroll anchoring uit: als Load more er berichten bij zet, schuiven Chrome en
2466 Firefox anders zelf mee. Stond er voor het snappen, maar ook zonder snappen
2467 hoort wat je leest niet te verspringen. */
2468html:has(body.on-home[data-feed-view="reader"]),
2469.feed-reader .read-stream { overflow-anchor: none; }
2470
2471.feed-reader .read-post {
2472 /* Snap naar de BOVENkant van een bericht, zacht (proximity op html) en nooit
2473 dwingend (stop: normal). Een korte post vult het scherm, zodat hij niet als
2474 snipper tussen twee lijnen hangt.
2475 De uitzondering staat NIET hier maar in mod/read.js: een bericht dat langer
2476 is dan het scherm krijgt scroll-snap-align:none, anders trekt de browser je
2477 tijdens het lezen terug naar zijn bovenrand en lijkt het of het scrollen
2478 stopt. "Past het op het scherm?" is een vraag over gemeten hoogte, en die
2479 kan CSS niet stellen. */
2480 /* Je LANDT op de bovenkant van een bericht -- dat is wat "snap to post"
2481 hoort te betekenen. (align:end probeerde ik ook; dan land je op de
2482 ONDERKANT van het vorige bericht, en bij een lang bericht zie je dus zijn
2483 laatste schermvol in plaats van de kop van het volgende.)
2484 Wanneer die bovenkant een snappunt IS, staat in mod/read.js: alleen zolang
2485 je er nog niet voorbij bent. Eenmaal in een bericht snapt er niets meer,
2486 ook niet naar zijn eigen bovenrand -- anders trekt proximity je terug
2487 zodra je een paar regels verder leest. */
2488 /* Het snappunt zit op .read-anker hierna, niet hier. Zie de toelichting
2489 daar: een snapgebied groter dan het scherm heeft een BEREIK aan geldige
2490 posities, en de bovenste daarvan zet het volgende bericht op de onderrand. */
2491 scroll-snap-align: none;
2492 scroll-snap-stop: normal;
2493 /* GEEN min-height meer. Die rekte elk bericht tot een vol scherm, en bij een
2494 kort bericht leverde dat een halve pagina leegte op onder "Replies and
2495 reactions" -- je zat naar niets te kijken en moest verder scrollen om te
2496 ontdekken dat er nog iets kwam. Robins vondst (20-8), met een
2497 schermafbeelding erbij.
2498 Zonder die hoogte is de stroom gewoon bericht na bericht: je ziet het
2499 volgende meteen onder het vorige beginnen. Het snappen doet nog steeds
2500 zijn werk aan de grens -- dat hangt aan scroll-snap-align en niet aan de
2501 hoogte -- dus de streep lijnt nog altijd uit met de bovenkant van het
2502 volgende bericht. */
2503 max-width: 46rem;
2504 margin: 0 auto;
2505 /* De onderrand loopt achter de vaste tabbalk door; deze padding houdt de
2506 TEKST erboven. De BOVENmarge is verdubbeld (Robin, 20-8: 2rem -> 4rem):
2507 na een snap staat de scheidingslijn op de bovenrand, en de tekst
2508 daaronder krijgt meer lucht. MOET GELIJK BLIJVEN aan scroll-margin-top
2509 op .read-anker hieronder -- lopen die uit elkaar, dan landt de lijn niet
2510 meer precies op de bovenrand. */
2511 padding: 4rem 1.2rem 4rem;
2512 box-sizing: border-box;
2513}
2514/* ONDERIN GEBEURT ER NIETS (Robin, 20-8: "enkel de bovenkant mag snappen, niet
2515 de onderkant; onderin mag er niks met de zone gebeuren").
2516 Het laatste bericht heeft een snappunt dat de browser NIET KAN BEREIKEN: er
2517 zit niet genoeg pagina onder om zijn bovenkant naar de bovenrand te brengen.
2518 Gemeten op dev: snappunt 2548, maar de scroll gaat maar tot 2356. De spec
2519 laat de browser dan naar de dichtstbijzijnde BEREIKBARE positie snappen, en
2520 dat is de bodem -- waardoor het laatste bericht ergens middenin het scherm
2521 blijft hangen en het lijkt of er aan de onderkant wordt uitgelijnd.
2522 Zonder snappunt scrolt dat laatste stuk gewoon vrij uit tot het einde. */
2523/* HET SNAPPUNT. Nul hoog, dus precies EEN geldige positie: deze bovenrand aan de
2524 bovenrand van het scherm. Zat de uitlijning op het artikel zelf, dan gold voor
2525 elk bericht dat hoger is dan het scherm een heel BEREIK aan geldige posities
2526 (spec: een snapgebied groter dan de snapport mag overal staan waar het de
2527 snapport nog vult). Gemeten op dev: bericht van 1009px op een scherm van 812
2528 -> geldig van 700 tot 897, en op 867 bleef hij dus gewoon staan. Die bovenste
2529 stand, 897, is de onderkant van dat bericht op de onderrand -- oftewel de
2530 bovenkant van het VOLGENDE bericht op de onderrand. Dat is wat er onderin
2531 leek te snappen, en het kwam van de browser en niet uit onze code. */
2532.feed-reader .read-anker {
2533 display: block;
2534 height: 0;
2535 scroll-snap-align: start;
2536 /* Het anker staat BINNEN de padding van het artikel, dus een padding-top
2537 onder de streep. Zonder deze correctie landt de snap net zoveel te laag
2538 en staat de scheidingslijn boven beeld -- gemeten: 733 waar 700 hoort.
2539 scroll-margin verschuift alleen het SNAPPUNT en niet de opmaak, dus het
2540 anker blijft waar het staat en de uitlijning klopt met de lijn.
2541 GELIJK aan de padding-top van .read-post hierboven; die ging 20-8 van
2542 2rem naar 4rem, dus deze ook. */
2543 scroll-margin-top: 4rem;
2544}
2545/* HET LAATSTE BERICHT SNAPT OOK (Robin, 20-8). Dat kon eerst niet: zijn
2546 bovenkant was onbereikbaar -- er zat te weinig pagina onder om hem naar de
2547 bovenrand te brengen (gemeten: snappunt 2548, maxScroll 2356), en de browser
2548 viel dan terug op de dichtstbijzijnde bereikbare positie: de bodem. Het
2549 snappunt weghalen was de vorige oplossing; dit is de echte.
2550 Een volle schermhoogte op het LAATSTE bericht maakt die bovenkant precies
2551 bereikbaar. Korter dan het scherm: het bericht wordt even hoog en het snappunt
2552 valt exact op maxScroll. Langer dan het scherm: dan was het al haalbaar en
2553 doet min-height niets.
2554 100lvh en niet 100svh: op een telefoon met een verdwijnende adresbalk is het
2555 zichtbare scherm soms de GROTE viewport, en dan komt svh net tekort.
2556 Zichtbaar kost dit niets: berichten hebben geen kader, alleen een scheidings-
2557 lijn erboven, dus het laatste bericht krijgt hooguit witruimte eronder.
2558 SINDS 30-9 WEG: zonder snappen hoeft die bovenkant nergens meer heen, en dan is
2559 de volle schermhoogte alleen nog een leeg scherm aan het eind van de stroom. */
2560
2561/* ELK BERICHT EEN EIGEN SCHERM, als de site dat wil (Instellingen -> Weergave).
2562 Aan: een kort bericht vult het scherm en je ziet het volgende pas na een snap.
2563 Uit (standaard): de stroom is bericht na bericht, zonder lege ruimte eronder.
2564 Dit stond eerst vast op AAN, is toen vast op UIT gezet omdat een kort bericht
2565 een halve pagina leegte gaf onder "Replies and reactions", en is nu een keuze
2566 -- want welke van de twee prettiger leest hangt af van wat je schrijft. */
2567body[data-reader-pages="1"] .feed-reader .read-post { min-height: 100svh; }
2568/* OP MOBIEL ALTIJD, wat de instelling ook zegt (Robin, 20-8). Op een telefoon is
2569 een bericht per scherm de vorm die werkt -- je veegt van bericht naar bericht.
2570 Op desktop blijft het een keuze, want daar lees je in een kolom naast veel
2571 witruimte en is een doorlopende stroom vaak prettiger. */
2572/* SINDS 30-9 NIET MEER OP MOBIEL AFGEDWONGEN. Een scherm per bericht hoorde bij
2573 vegen van bericht naar bericht, en dat vegen was het snappen. Zonder snappen
2574 gaf het onder een kort bericht een leeg scherm -- dezelfde leegte waar Robin
2575 op 20-8 op desktop over viel. De instelling hierboven geldt nu overal: wie een
2576 scherm per bericht wil, zet hem aan. */
2577
2578/* -------- PAGINAMODUS --------
2579 Elk bericht is een paneel van precies een scherm dat ZELF scrollt; tussen
2580 berichten ga je met knoppen. Daarmee doet het scrollgevoel er niet meer toe:
2581 geen momentum om na te bouwen en geen snap die vangt.
2582 De stroom zelf scrolt niet: dat deed lenis.stop(), en sinds Lenis weg is
2583 (30-9) de regel hieronder op het document. Het paneel scrolt zelf.
2584 Geldt waar een bericht een eigen scherm heeft: op mobiel altijd, op desktop
2585 als de site dat zo instelt. */
2586html:has(body.is-paged) { overflow: hidden; }
2587body.is-paged .feed-reader .read-post {
2588 height: 100svh;
2589 min-height: 0;
2590 overflow-y: auto;
2591 overscroll-behavior: contain; /* raakt het einde, dan niet doorschuiven */
2592 scroll-snap-align: none;
2593 /* Ruimte voor de twee balken, zodat er nooit tekst onder een knop verdwijnt.
2594 Het verloop laat een regel wel zacht wegvagen, maar wat je moet KUNNEN
2595 lezen hoort er niet achter te komen -- vandaar padding en niet alleen die
2596 fade. --read-balk staat hieronder bij .read-nav, zodat de twee niet uit
2597 elkaar kunnen lopen. */
2598 padding-top: calc(var(--read-balk) + .5rem);
2599 padding-bottom: calc(var(--read-balk) + .5rem);
2600 /* BOVENAAN beginnen, niet gecentreerd. Ik had de inhoud een ronde lang
2601 verticaal gecentreerd om het gat onder een kort bericht te verdelen, maar
2602 dan hangt zo'n bericht halverwege het scherm en begint niet elk paneel op
2603 dezelfde plek -- dat leest onrustig. Robin ving het (20-8). Elk bericht
2604 begint nu bovenaan, en de ruimte die overblijft valt onderaan. */
2605}
2606body.is-paged .feed-reader .read-anker { scroll-snap-align: none; }
2607
2608/* DE DYNAMIC ISLAND, en met opzet alleen op iOS (Robin, 20-8: "op android is het
2609 een ander verhaal").
2610 In paginamodus vult een bericht precies het scherm, dus de eerste regel komt
2611 bovenaan -- en op een iPhone ligt daar de island of de notch overheen.
2612 env(safe-area-inset-top) geeft daar de vrije ruimte; viewport-fit=cover staat
2613 al in de viewport-meta, dus die waarde is echt.
2614 De @supports-regel is de iOS-Safari-toets: -webkit-touch-callout bestaat alleen
2615 daar. Op Android is env() vaak 0 of betekent het iets anders (camera-uitsparing,
2616 statusbalk die al meegerekend is), en dan zou dit een gat bovenin maken dat er
2617 niet hoort. Vandaar afgeschermd in plaats van env() overal toepassen.
2618 De hoogte krimpt mee, anders duwt de padding het paneel groter dan het scherm
2619 en krijg je alsnog scroll tussen de berichten. */
2620@supports (-webkit-touch-callout: none) {
2621 body.is-paged .feed-reader .read-post {
2622 padding-top: calc(var(--read-balk) + .5rem + env(safe-area-inset-top, 0px));
2623 height: calc(100svh - env(safe-area-inset-top, 0px));
2624 }
2625 /* De bovenbalk houdt zijn knop al vrij van de island via padding-top
2626 hierboven; hier hoeft niets extra's meer. */
2627}
2628
2629/* De knop onderaan een bericht: naar het volgende. */
2630
2631
2632/* Omhoog en omlaag: twee balken, boven en onder, die BLIJVEN staan terwijl je in
2633 een bericht scrolt (Robin, 20-8). Het verloop van paginakleur naar doorzichtig
2634 laat de tekst eronderdoor wegvagen -- zonder dat zou een regel halverwege hard
2635 achter de knop verdwijnen en oogt het als een afgeknipte pagina.
2636 De BALK vangt geen aanrakingen (pointer-events: none), alleen de knop zelf --
2637 anders zou een strook van 5rem boven- en onderaan je scroll opeten. */
2638/* TWEE BALKEN over de volle breedte, boven en onder. Eerder waren dit losse
2639 cirkels (te rommelig naast de tabbalk en de speler) en daarna onzichtbare
2640 tikzones -- maar die werkten niet, omdat de onderste strook onder de
2641 mini-speler en de tabbalk ligt en die de tik opvangen. Een balk met een eigen
2642 z-index kan daar wel overheen.
2643 Slank gehouden: ze eten hooguit 2.6rem aan elke kant, en het paneel houdt daar
2644 al ruimte voor vrij. */
2645:root { --read-balk: 2.6rem; }
2646.read-bar {
2647 position: fixed; left: 0; right: 0; z-index: 1060; /* boven tabbalk (1050) */
2648 display: flex; align-items: center;
2649 /* Pijlen LINKS in de balk, niet gecentreerd (Robin, 20-8). */
2650 justify-content: flex-start;
2651 padding-left: max(1.1rem, env(safe-area-inset-left, 0px));
2652 box-sizing: border-box;
2653 height: var(--read-balk);
2654 border: 0;
2655 border-top: 1px solid transparent;
2656 background: color-mix(in srgb, var(--paper) 90%, var(--ink));
2657 backdrop-filter: blur(14px) saturate(140%);
2658 -webkit-backdrop-filter: blur(14px) saturate(140%);
2659 color: var(--ink);
2660 cursor: pointer;
2661 transition: opacity .15s ease;
2662}
2663.read-bar--top {
2664 top: 0;
2665 height: calc(var(--read-balk) + env(safe-area-inset-top, 0px));
2666 padding-top: env(safe-area-inset-top, 0px);
2667 border-bottom: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
2668}
2669.read-bar--bottom {
2670 /* De waarde komt uit mod/read.js: die meet hoe hoog de tabbalk en de
2671 mini-speler werkelijk staan. Geraden rem-waarden lieten hem zweven -- een
2672 kier boven de speler, of er half achter. Dit is de terugval voor de eerste
2673 frame en voor als de module niet laadt. */
2674 bottom: 0;
2675 border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
2676}
2677.read-bar:active { background: color-mix(in srgb, var(--paper) 78%, var(--ink)); }
2678.read-bar[hidden], body:not(.is-paged) .read-bar { display: none; }
2679
2680/* Een lijn tussen twee berichten: zonder grens is een stroom een lange brij. */
2681.feed-reader .read-post + .read-post { border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent); }
2682
2683/* De cover vult de leeskolom. Eigen regel, niet .post-cover hergebruikt: die
2684 heeft een marge van 2rem die hier tegen de scroll-snap aan duwt.
2685 De hoogtebegrenzing is er met opzet. Een paneel is min-height:100svh en snapt,
2686 dus een staande foto op volle breedte zou de titel en de eerste alinea onder
2687 de vouw duwen -- dan begint elk bericht met een plaat waar je langs moet
2688 scrollen om te zien wat het IS. */
2689.feed-reader .read-cover {
2690 /* Volle kolombreedte: de afbeelding erin neemt die breedte, dus de figure
2691 hoeft niet meer om het beeld te krimpen. */
2692 width: 100%;
2693 margin: 0 0 1.2rem;
2694 border-radius: 10px;
2695 overflow: hidden;
2696}
2697.feed-reader .read-cover img,
2698.feed-reader .read-cover video {
2699 display: block;
2700 /* Neemt de VOLLE kolombreedte, met de verhouding intact (object-fit
2701 weggelaten, dus geen bijsnijden). Een cover die van zichzelf kleiner is
2702 wordt netjes tot de kolom opgeschaald in plaats van als postzegeltje te
2703 blijven staan -- dat was te klein. De hoogtegrens houdt een staande foto
2704 nog binnen de perken zodat titel + eerste alinea boven de vouw blijven;
2705 hij krimpt dan mee in plaats van bijgesneden te worden. */
2706 display: block;
2707 width: 100%;
2708 height: auto;
2709 max-height: 70svh;
2710 object-fit: contain;
2711 /* Een logo met doorzichtige achtergrond zweeft anders op de paginakleur;
2712 dezelfde keuze als bij .post-list-cover in de tijdlijn. */
2713 background-color: #fff;
2714}
2715
2716.feed-reader .read-head { padding-bottom: .5rem; }
2717.feed-reader .read-title { font-size: clamp(1.6rem, 4vw, 2.4rem); line-height: 1.15; margin: 0 0 .3rem; }
2718/* Het pin-icoon voor de titel: meeschalend met de kop, maar duidelijk kleiner --
2719 het is een markering en geen deel van de titel. */
2720.feed-reader .read-pin {
2721 display: inline-block;
2722 vertical-align: baseline;
2723 width: .62em; height: .62em;
2724 margin-right: .35em;
2725 color: var(--ink-muted);
2726}
2727.feed-reader .read-pin svg { width: 100%; height: 100%; display: block; }
2728/* De titel is een link, maar mag er niet als een link uitzien: dit is een
2729 leesscherm, geen index. */
2730.feed-reader .read-title a { color: inherit; text-decoration: none; }
2731.feed-reader .read-title a:hover,
2732.feed-reader .read-title a:focus-visible { text-decoration: underline; }
2733.feed-reader .read-when { font-size: .85rem; opacity: .6; margin: 0; }
2734
2735.feed-reader .read-foot { margin-top: 2rem; }
2736.feed-reader .read-open {
2737 display: inline-block; font-size: .9rem; opacity: .75;
2738 text-decoration: none; border-bottom: 1px solid currentColor; padding-bottom: 1px;
2739}
2740.feed-reader .read-open:hover,
2741.feed-reader .read-open:focus-visible { opacity: 1; }
2742
2743/* Terug naar boven. Rechtsonder, want daar zit de duim -- en boven de tabbalk,
2744 die op mobiel 1050 is en anders overheen zou vallen. Onzichtbaar tot je ver
2745 genoeg bent, zodat hij niet meteen in de weg staat bij het eerste bericht. */
2746.read-top {
2747 position: fixed;
2748 right: max(1rem, env(safe-area-inset-right, 0));
2749 bottom: calc(env(safe-area-inset-bottom, 0) + 4.75rem);
2750 z-index: 1040;
2751 display: grid;
2752 place-items: center;
2753 width: 44px;
2754 height: 44px; /* 44px = de kleinste maat die een duim betrouwbaar raakt */
2755 border-radius: 999px;
2756 border: 1px solid var(--rule);
2757 background: color-mix(in srgb, var(--paper) 88%, transparent);
2758 backdrop-filter: blur(14px) saturate(140%);
2759 -webkit-backdrop-filter: blur(14px) saturate(140%);
2760 color: var(--ink);
2761 box-shadow: 0 2px 12px color-mix(in srgb, var(--ink) 12%, transparent);
2762 cursor: pointer;
2763 opacity: 0;
2764 pointer-events: none;
2765 transition: opacity .18s ease, transform .18s ease;
2766 transform: translateY(6px);
2767}
2768.read-top.is-zichtbaar { opacity: 1; pointer-events: auto; transform: none; }
2769/* Buiten de leesweergave bestaat hij niet -- home.ejs rendert alle drie de
2770 secties, dus zonder deze regel zweeft hij ook boven Tijdlijn en Grid. */
2771body:not([data-feed-view="reader"]) .read-top,
2772body:not(.on-home) .read-top { display: none; }
2773/* Op desktop is er geen tabbalk, dus mag hij lager staan. */
2774@media (min-width: 768px) {
2775 .read-top { bottom: 1.5rem; right: 1.5rem; }
2776}
2777/* Staat de mini-speler onderin, dan wijkt de knop daarvoor -- anders komt hij er
2778 bovenop te liggen (Robins schermafbeelding, 20-8). body.has-audio-player wordt
2779 door audio-player.js gezet zodra de speler zichtbaar is, en weer weggehaald
2780 als hij verdwijnt, dus dit volgt vanzelf.
2781 Op mobiel staat de speler BOVEN de tabbalk gestapeld, dus daar komt zijn
2782 hoogte bovenop de ruimte die er al gereserveerd was. */
2783body.has-audio-player .read-top { bottom: calc(env(safe-area-inset-bottom, 0) + 4.75rem + 4.25rem); }
2784@media (min-width: 768px) {
2785 body.has-audio-player .read-top { bottom: calc(1.5rem + 4.25rem); }
2786}
2787@media (prefers-reduced-motion: reduce) {
2788 .read-top { transition: none; transform: none; }
2789}
2790
2791@media (prefers-reduced-motion: reduce) {
2792 html:has(body.on-home[data-feed-view="reader"]) {
2793 scroll-behavior: auto;
2794 }
2795}
2796
2797/* -------- GRID VIEW TILES (Instagram-stijl) -------- */
2798.feed-grid {
2799 padding: 1rem 0 2rem;
2800}
2801.grid-tiles {
2802 display: grid;
2803 gap: 4px;
2804 grid-template-columns: repeat(2, 1fr);
2805}
2806.grid-tile {
2807 position: relative;
2808 display: block;
2809 aspect-ratio: 1;
2810 background-size: cover;
2811 background-position: center;
2812 background-color: var(--paper-2);
2813 overflow: hidden;
2814 text-decoration: none;
2815 color: var(--ink);
2816 transition: transform .2s;
2817 cursor: pointer;
2818 -webkit-tap-highlight-color: rgba(0,0,0,.1);
2819}
2820
2821/* Transparent covers (PNG logos etc.) sit on white so they don't blend into a dark
2822 theme. The white is on the IMG, so the tile's accent-gradient placeholder shows
2823 while the image is still loading. */
2824.post-cover img,
2825.post-list-cover img,
2826.grid-tile-img { background-color: #fff; }
2827/* Accent-gradient placeholder shown while the cover image loads (the image fades in
2828 over it), instead of a white flash on cover tiles. */
2829.grid-tile:not(.grid-tile-gradient) {
2830 background-image: linear-gradient(135deg,
2831 color-mix(in srgb, var(--accent, #888) 22%, var(--paper-2, var(--paper))) 0%,
2832 color-mix(in srgb, var(--accent, #888) 6%, var(--paper-2, var(--paper))) 100%);
2833}
2834/* Cover as a real <img> (not background-image): browsers downsample <img> with the
2835 high-quality image pipeline, so high-res line-art covers don't go jagged when shrunk.
2836 Fades in on load (.is-loading removed) so the placeholder shows meanwhile. */
2837.grid-tile-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; transition: opacity .4s ease; }
2838.grid-tile-img.is-loading { opacity: 0; }
2839/* Audio tile (Shaer, Robins vraag 30-7): the waveform PNG is white on
2840 transparent and floats OVER the tile's own hue gradient, so every audio
2841 post keeps its color and gets its shape. Wave and title each get their
2842 own band (wave upper, title lower): centering both stacked them right
2843 through each other (Robins schermafdruk). */
2844.grid-tile-img.grid-tile-wave {
2845 background-color: transparent;
2846 object-fit: contain;
2847 /* explicit box, not auto: an absolutely positioned img falls back to its
2848 intrinsic size when width/height are auto, collapsing the band */
2849 top: 8%; left: 8%; right: auto; bottom: auto;
2850 width: 84%; height: 46%;
2851 opacity: .85;
2852}
2853.grid-tile-audio .grid-tile-title { position: absolute; left: 10%; right: 10%; top: 60%; }
2854.grid-tile-audio .grid-tile-gradient-src { position: absolute; left: 10%; right: 10%; top: 76%; }
2855/* Cover videos are decorative, not players: let clicks + right-click pass through to the
2856 tile link so you get the normal link menu (not the browser's <video> menu/controls).
2857 Autoplay is independent of pointer-events, so the video still plays. */
2858video.grid-tile-img,
2859.post-list-cover video,
2860.post-cover video { pointer-events: none; }
2861.grid-tile:hover { transform: scale(0.98); }
2862.grid-tile-overlay {
2863 position: absolute;
2864 inset: 0;
2865 padding: .6rem;
2866 display: flex;
2867 flex-direction: column;
2868 justify-content: flex-end;
2869 align-items: flex-start;
2870 background: linear-gradient(to top, rgba(0,0,0,.65) 0%, rgba(0,0,0,0) 50%);
2871 opacity: 0;
2872 transition: opacity .2s;
2873 pointer-events: none; /* klik moet naar de <a> eronder gaan, niet naar de overlay */
2874}
2875/* Bron-site (cirkel-feed): toont bij hover "van <site>" onder de titel. */
2876/* Source label: small text that wraps to 2 lines if needed, with a big chain icon
2877 beside it (the text was 1.44rem — bigger than the title — and overflowed on mobile). */
2878.grid-tile-overlay-src {
2879 color: #fff; opacity: .85;
2880 font-size: .72rem; line-height: 1.2; margin-top: .25rem;
2881 display: flex; align-items: center; gap: .3rem; width: 100%;
2882}
2883.grid-tile-overlay-src::before { content: "⛓"; font-size: 1.3rem; line-height: 1; flex: 0 0 auto; }
2884.grid-tile-gradient-src {
2885 margin-top: .35rem; font-size: .72rem; line-height: 1.2; opacity: .85;
2886 color: var(--ink-soft);
2887 display: flex; align-items: center; gap: .3rem; width: 100%;
2888}
2889.grid-tile-gradient-src::before { content: "⛓"; font-size: 1.3rem; line-height: 1; flex: 0 0 auto; }
2890.grid-tile-overlay strong {
2891 color: #fff;
2892 font-family: var(--font-display);
2893 font-size: .9rem;
2894 font-weight: 500;
2895 line-height: 1.15;
2896 display: -webkit-box;
2897 -webkit-line-clamp: 3;
2898 -webkit-box-orient: vertical;
2899 overflow: hidden;
2900}
2901@media (hover: hover) {
2902 .grid-tile:hover .grid-tile-overlay { opacity: 1; }
2903}
2904@media (hover: none) {
2905 /* Op touch-devices altijd overlay tonen zodat titels zichtbaar zijn */
2906 .grid-tile-overlay { opacity: 1; background: linear-gradient(to top, rgba(0,0,0,.7) 0%, rgba(0,0,0,0) 70%); }
2907}
2908
2909.grid-tile-gradient {
2910 background-image: none;
2911 background-color: hsl(var(--tile-hue, 30), 40%, 85%);
2912 background-image: linear-gradient(135deg, hsl(var(--tile-hue, 30), 45%, 88%), hsl(calc(var(--tile-hue, 30) + 30), 35%, 75%));
2913 display: flex;
2914 flex-direction: column;
2915 align-items: center;
2916 justify-content: center;
2917 padding: 1rem;
2918 text-align: center;
2919}
2920[data-theme="dark"] .grid-tile-gradient,
2921.grid-tile-gradient[data-theme="dark"] {
2922 background-image: linear-gradient(135deg, hsl(var(--tile-hue, 30), 30%, 18%), hsl(calc(var(--tile-hue, 30) + 30), 25%, 12%));
2923}
2924.grid-tile-title {
2925 font-family: var(--font-display);
2926 font-size: 1rem;
2927 font-weight: 500;
2928 color: var(--ink);
2929 line-height: 1.2;
2930 display: -webkit-box;
2931 -webkit-line-clamp: 4;
2932 -webkit-box-orient: vertical;
2933 overflow: hidden;
2934 z-index: 1;
2935 /* Federated link posts can have a long bare URL as their title — break it so it wraps
2936 inside the tile and clamps cleanly instead of overflowing past both edges. */
2937 max-width: 100%;
2938 overflow-wrap: anywhere;
2939 word-break: break-word;
2940 hyphens: auto;
2941}
2942
2943.grid-tile-pin {
2944 position: absolute;
2945 top: .5rem;
2946 right: .5rem;
2947 display: inline-flex;
2948 align-items: center;
2949 gap: .25rem;
2950 padding: .2rem .55rem;
2951 background: var(--accent);
2952 color: #fff;
2953 border-radius: 999px;
2954 font-family: var(--font-ui);
2955 font-size: .65rem;
2956 font-weight: 700;
2957 text-transform: uppercase;
2958 letter-spacing: .06em;
2959 z-index: 3;
2960 box-shadow: 0 2px 6px rgba(0,0,0,.3);
2961 pointer-events: none;
2962}
2963.grid-tile-pin svg {
2964 width: .95em;
2965 height: .95em;
2966 margin-top: -1px;
2967}
2968/* Pinned/boosted tile: accent ring. An outline on the tile is painted UNDER the absolute
2969 cover <img>, so draw the ring as a pseudo-element overlay ABOVE the image (z-index above
2970 the img, below the corner badges + nsfw veil). */
2971.grid-tile.is-pinned { position: relative; z-index: 1; }
2972.grid-tile.is-pinned::after {
2973 content: '';
2974 position: absolute;
2975 inset: 0;
2976 border: 3px solid var(--accent);
2977 border-radius: inherit;
2978 pointer-events: none;
2979 z-index: 1;
2980}
2981.grid-tile-type {
2982 position: absolute;
2983 top: .4rem;
2984 left: .4rem;
2985 padding: .15rem .5rem;
2986 background: rgba(0,0,0,.55);
2987 color: #fff;
2988 border-radius: 999px;
2989 font-family: var(--font-ui);
2990 font-size: .6rem;
2991 font-weight: 600;
2992 text-transform: uppercase;
2993 letter-spacing: .05em;
2994 z-index: 2;
2995}
2996
2997@media (min-width: 600px) {
2998 .grid-tiles { grid-template-columns: repeat(3, 1fr); }
2999}
3000@media (min-width: 900px) {
3001 .grid-tiles { grid-template-columns: repeat(3, 1fr); gap: 8px; }
3002}
3003
3004/* -------- POST-TYPE SPECIFIEKE LAYOUTS -------- */
3005
3006/* Foto-post: beeld prominent, geen titel bovenaan, caption onder */
3007.feed-timeline[data-post-type="foto"] .post-title {
3008 font-size: 1.1rem;
3009 color: var(--ink-muted);
3010 font-weight: 400;
3011}
3012.feed-timeline[data-post-type="foto"] .post-cover {
3013 margin: 0 -1rem 1rem;
3014 border-radius: 0;
3015}
3016.feed-timeline[data-post-type="foto"] .post-cover img {
3017 width: 100%;
3018 border-radius: 0;
3019}
3020
3021/* Post-caption onder de cover */
3022.post-caption {
3023 font-family: var(--font-body);
3024 font-size: 1rem;
3025 color: var(--ink);
3026 line-height: 1.5;
3027 margin: 0 0 1.25rem;
3028 padding: 0 0 1rem;
3029 border-bottom: 1px solid var(--rule);
3030 white-space: pre-wrap;
3031}
3032.feed-timeline[data-post-type="foto"] .post-caption {
3033 font-size: 1.05rem;
3034 margin-top: -.5rem;
3035 padding-bottom: 1.25rem;
3036}
3037
3038/* Audio/video posts — type-chip helder, content eronder zoals normaal */
3039.feed-timeline[data-post-type="audio"] .post-title,
3040.feed-timeline[data-post-type="video"] .post-title {
3041 font-size: 1.5rem;
3042}
3043
3044@media (min-width: 768px) {
3045 .feed-timeline[data-post-type="foto"] .post-cover {
3046 margin: 0 0 1.5rem;
3047 border-radius: var(--radius);
3048 }
3049}
3050
3051/* -------- ADMIN EDIT-BUTTON op post-view (alleen zichtbaar voor ingelogde admin) -------- */
3052.post-edit-btn {
3053 position: absolute;
3054 top: 1rem;
3055 right: 1rem;
3056 display: inline-flex;
3057 align-items: center;
3058 justify-content: center;
3059 width: 44px;
3060 height: 44px;
3061 background: var(--paper);
3062 border: 1px solid var(--rule);
3063 border-radius: 50%;
3064 color: var(--ink-muted);
3065 text-decoration: none;
3066 box-shadow: 0 2px 8px rgba(0,0,0,.08);
3067 transition: background .15s, color .15s, transform .15s, border-color .15s;
3068 z-index: 10;
3069}
3070.post-edit-btn:hover {
3071 background: var(--accent);
3072 border-color: var(--accent);
3073 color: var(--paper);
3074 transform: scale(1.05);
3075}
3076.post-edit-btn .icon {
3077 width: 1.1rem;
3078 height: 1.1rem;
3079}
3080/* Zorg dat de article relative is zodat absolute positionering werkt */
3081.feed-timeline { position: relative; }
3082
3083@media (min-width: 768px) {
3084 .post-edit-btn {
3085 top: 1.5rem;
3086 right: 1.5rem;
3087 }
3088}
3089
3090/* ============================================================
3091 v8.0 — ACCENT SPAN + POST-TABLE (published posts)
3092 ============================================================ */
3093.post span.accent,
3094.post-content span.accent,
3095article span.accent,
3096page-container span.accent {
3097 color: var(--accent);
3098 font-weight: 500;
3099}
3100
3101.post-table,
3102article table.post-table {
3103 width: 100%;
3104 max-width: 100%;
3105 border-collapse: collapse;
3106 margin: 1.25rem 0;
3107 font-size: .95rem;
3108 display: block;
3109 overflow-x: auto;
3110 -webkit-overflow-scrolling: touch;
3111}
3112.post-table thead { background: color-mix(in srgb, var(--accent) 10%, transparent); }
3113.post-table th,
3114.post-table td {
3115 padding: .6rem .8rem;
3116 text-align: left;
3117 border: 1px solid var(--rule);
3118 vertical-align: top;
3119}
3120.post-table th {
3121 font-family: var(--font-ui);
3122 font-weight: 600;
3123 font-size: .88rem;
3124 color: var(--ink);
3125 letter-spacing: .02em;
3126}
3127.post-table tbody tr:nth-child(even) {
3128 background: color-mix(in srgb, var(--ink) 3%, transparent);
3129}
3130@media (min-width: 700px) {
3131 .post-table { display: table; overflow: visible; }
3132}
3133
3134/* ============================================================
3135 v8.0 fix — grid-cols picker (desktop only) + grid col overrides
3136 ============================================================ */
3137
3138/* Switcher-wrap bevat nu ook de cols-picker; layout side-by-side */
3139.view-switcher-wrap .container {
3140 display: flex;
3141 align-items: center;
3142 justify-content: center;
3143 gap: 1rem;
3144 flex-wrap: wrap;
3145}
3146
3147/* Solo / Cirkel scope-switcher (alleen in circle-modus) — segmented toggle, centraal.
3148 Hoge specificiteit (+ !important op tekst) zodat 'ie globale link-stijlen wint. */
3149.view-switcher-wrap .feed-scope {
3150 display: inline-flex;
3151 background: var(--paper-2);
3152 border: 1px solid var(--rule);
3153 border-radius: 999px;
3154 padding: 3px;
3155}
3156.view-switcher-wrap .feed-scope-btn {
3157 display: inline-flex;
3158 align-items: center;
3159 padding: .32rem .95rem;
3160 min-height: 30px;
3161 border: none !important;
3162 border-radius: 999px !important;
3163 background: transparent !important;
3164 color: var(--ink-muted) !important;
3165 font-family: var(--font-ui);
3166 font-size: .82rem;
3167 font-weight: 500 !important;
3168 text-decoration: none !important;
3169 cursor: pointer;
3170 transition: all .15s;
3171}
3172.view-switcher-wrap .feed-scope-btn:hover { color: var(--ink) !important; }
3173.view-switcher-wrap .feed-scope-btn.is-active {
3174 background: var(--ink) !important;
3175 color: var(--paper) !important;
3176}
3177
3178.grid-cols-picker {
3179 display: none; /* default hidden — desktop + grid-active maakt 'm zichtbaar */
3180 gap: 2px;
3181 padding: 0;
3182}
3183.grid-cols-btn {
3184 display: inline-flex;
3185 align-items: center;
3186 justify-content: center;
3187 min-width: 36px;
3188 height: 32px;
3189 padding: 0 .6rem;
3190 background: transparent;
3191 border: 1px solid var(--rule);
3192 color: var(--ink-muted);
3193 font-family: var(--font-ui);
3194 font-size: .85rem;
3195 font-weight: 500;
3196 cursor: pointer;
3197 transition: all .12s;
3198}
3199.grid-cols-btn:first-child { border-radius: 999px 0 0 999px; }
3200.grid-cols-btn:last-child { border-radius: 0 999px 999px 0; }
3201.grid-cols-btn:not(:first-child):not(:last-child) { border-left: none; border-right: none; }
3202.grid-cols-btn:hover { color: var(--ink); }
3203/* Active state puur via body[data-grid-cols] — geen is-active-class meer. */
3204body[data-grid-cols="2"] .grid-cols-btn[data-cols="2"],
3205body[data-grid-cols="3"] .grid-cols-btn[data-cols="3"],
3206body[data-grid-cols="4"] .grid-cols-btn[data-cols="4"] {
3207 background: var(--ink);
3208 color: var(--paper);
3209 border-color: var(--ink);
3210}
3211
3212/* Desktop + grid-view → toon cols-picker (op de home-feed; niet op archief) */
3213@media (min-width: 600px) {
3214 body.on-home[data-feed-view="grid"] .grid-cols-picker {
3215 display: inline-flex;
3216 }
3217}
3218
3219/* Kolom-overrides op desktop. Mobiel negeert dit (staat vast op 2). Geldt op ELKE
3220 feed met grid (home én cirkel/tag/type/user) — scope op .feed-grid .grid-tiles. */
3221@media (min-width: 600px) {
3222 body[data-grid-cols="2"] .feed-grid .grid-tiles { grid-template-columns: repeat(2, 1fr); }
3223 body[data-grid-cols="3"] .feed-grid .grid-tiles { grid-template-columns: repeat(3, 1fr); }
3224 body[data-grid-cols="4"] .feed-grid .grid-tiles { grid-template-columns: repeat(4, 1fr); }
3225}
3226
3227/* Gradient-tile: iets meer contrast voor light + dark mode zodat titel altijd leesbaar is */
3228.grid-tile-gradient {
3229 background-image: linear-gradient(135deg,
3230 hsl(var(--tile-hue, 30), 55%, 72%),
3231 hsl(calc(var(--tile-hue, 30) + 40), 45%, 60%));
3232}
3233[data-theme="dark"] .grid-tile-gradient {
3234 background-image: linear-gradient(135deg,
3235 hsl(var(--tile-hue, 30), 40%, 30%),
3236 hsl(calc(var(--tile-hue, 30) + 40), 30%, 18%));
3237}
3238.grid-tile-gradient .grid-tile-title {
3239 color: #fff;
3240 text-shadow: 0 1px 3px rgba(0,0,0,.25);
3241}
3242[data-theme="dark"] .grid-tile-gradient .grid-tile-title {
3243 color: #fff;
3244 text-shadow: 0 1px 2px rgba(0,0,0,.5);
3245}
3246
3247/* ============================================================
3248 v8.0.2 — COMPACT DESIGN OVERRIDES
3249 Tighter masthead, padding, margins overall. Profiel-header
3250 collapse/expand blijft zoals 'ie was.
3251 ============================================================ */
3252
3253/* Masthead: compacter */
3254.masthead { padding: calc(.55rem + var(--ios-safe-top, 0px)) 0 .55rem; }
3255.masthead-inner { gap: 1rem; }
3256.site-title { font-size: 1.25rem; }
3257.nav-link, .nav-btn { padding: .4rem .7rem; min-height: 2rem; font-size: .82rem; }
3258
3259/* Main content padding */
3260main { padding-top: 1rem; }
3261.page { padding-block: 1rem 1.75rem; }
3262
3263/* View switcher-bar strakker */
3264.view-switcher { padding: .4rem 0; gap: .3rem; }
3265.view-switch-btn { padding: .4rem .85rem; min-height: 38px; font-size: .8rem; }
3266.grid-cols-btn { min-width: 32px; height: 28px; padding: 0 .5rem; font-size: .8rem; }
3267
3268/* Post header spacing terug */
3269.post-header { margin: 0 0 1.25rem; }
3270.post-title { margin-bottom: .35rem; }
3271.post-meta { font-size: .78rem; }
3272.post-tags { margin-top: .6rem; gap: .3rem; }
3273.post-tag { font-size: .7rem; padding: .15rem .55rem; }
3274
3275/* Article footer + pagination compacter */
3276.article-footer { padding-top: 1.25rem; margin-top: 1.5rem; }
3277.pagination { padding-block: 1.25rem 2rem; margin-top: 1.5rem; }
3278.pagination-item { padding: .75rem .95rem; min-height: 4rem; }
3279.pagination-title { font-size: .95rem; }
3280
3281/* Site footer compacter */
3282.site-footer { padding: 1.25rem 0 calc(1.25rem + env(safe-area-inset-bottom, 0px)); margin-top: 2rem; font-size: .78rem; }
3283.site-footer-inner { gap: .75rem; }
3284
3285/* Profiel-header: full mode iets minder riant (compact mode blijft hetzelfde) */
3286.profile-header { padding: 1rem 0; }
3287.profile-photo { width: 72px; height: 72px; }
3288.profile-photo-fallback { font-size: 1.8rem; }
3289.profile-name { font-size: 1.2rem; }
3290.profile-bio { font-size: .85rem; margin-top: .15rem; }
3291.profile-links { margin-top: .5rem; gap: .4rem; }
3292.profile-links a,
3293.profile-links .profile-fedi-link { width: 32px; height: 32px; }
3294
3295@media (min-width: 768px) {
3296 .profile-header { padding: 1.35rem 0; }
3297 .profile-photo { width: 92px; height: 92px; }
3298 .profile-photo-fallback { font-size: 2.4rem; }
3299 .profile-name { font-size: 1.4rem; }
3300 .profile-bio { font-size: .95rem; }
3301 .profile-header-inner { gap: 1.1rem; }
3302 /* Compact-mode op desktop blijft klein zoals we hadden afgesproken */
3303 .on-post .profile-photo,
3304 .on-special .profile-photo { width: 42px; height: 42px; }
3305}
3306
3307/* Grid-tegels iets strakker gaten */
3308.grid-tiles { gap: 3px; }
3309
3310/* Post-cover margin */
3311.post-cover { margin: 0 0 1rem; }
3312
3313/* Audio player wat lichter */
3314#pcms-audio-bar { padding: .55rem 1rem; }
3315
3316/* v8.1 — pagination CTA + mobile tap target */
3317.pagination-cta {
3318 display: inline-block;
3319 font-family: var(--font-ui);
3320 font-size: .72rem;
3321 font-weight: 600;
3322 color: var(--accent);
3323 margin-top: .35rem;
3324 letter-spacing: .02em;
3325 opacity: .9;
3326}
3327a.pagination-item:hover .pagination-cta { opacity: 1; }
3328
3329/* Mobiel: nog meer tap-feedback + iets prominenter */
3330@media (max-width: 640px) {
3331 a.pagination-item {
3332 padding: 1.1rem 1rem;
3333 min-height: 5.5rem;
3334 border-width: 2px;
3335 }
3336 a.pagination-item:active {
3337 background: color-mix(in srgb, var(--accent) 10%, var(--paper-2));
3338 transform: scale(.98);
3339 }
3340 .pagination-title { font-size: 1rem; }
3341 .pagination-cta { font-size: .75rem; }
3342 .pagination .icon { width: 1.5em; height: 1.5em; }
3343}
3344
3345
3346
3347/* v8.3 / v9.4 — PWA install button (alleen zichtbaar als browser install ondersteunt) */
3348.nav-install-btn[hidden] { display: none; }
3349.nav-install-btn {
3350 position: relative;
3351 display: inline-flex;
3352 align-items: center;
3353 justify-content: center;
3354 gap: .35rem;
3355 background: linear-gradient(135deg,
3356 var(--accent),
3357 color-mix(in srgb, var(--accent) 80%, #000));
3358 color: #fff;
3359 border: none;
3360 border-radius: .65rem;
3361 padding: .45rem .85rem;
3362 font-size: .8rem;
3363 font-weight: 600;
3364 line-height: 1;
3365 cursor: pointer;
3366 overflow: hidden;
3367 isolation: isolate;
3368 box-shadow:
3369 0 1px 0 rgba(255,255,255,.15) inset,
3370 0 2px 6px color-mix(in srgb, var(--accent) 35%, transparent);
3371 transition:
3372 transform .15s cubic-bezier(.4, 0, .2, 1),
3373 box-shadow .2s ease,
3374 filter .15s ease;
3375}
3376.nav-install-btn::before {
3377 content: "";
3378 position: absolute;
3379 top: 0;
3380 left: -100%;
3381 width: 60%;
3382 height: 100%;
3383 background: linear-gradient(110deg,
3384 transparent 30%,
3385 rgba(255,255,255,.22) 50%,
3386 transparent 70%);
3387 z-index: -1;
3388 animation: pcmsInstallShimmer 4s ease-in-out infinite;
3389 pointer-events: none;
3390}
3391.nav-install-btn:hover {
3392 transform: translateY(-1px);
3393 filter: brightness(1.08);
3394 box-shadow:
3395 0 1px 0 rgba(255,255,255,.2) inset,
3396 0 4px 10px color-mix(in srgb, var(--accent) 50%, transparent);
3397}
3398.nav-install-btn:active { transform: translateY(0); }
3399.nav-install-btn .icon {
3400 flex-shrink: 0;
3401 transition: transform .25s cubic-bezier(.34, 1.56, .64, 1);
3402}
3403.nav-install-btn:hover .icon { transform: translateY(-1px); }
3404@media (prefers-reduced-motion: reduce) {
3405 .nav-install-btn::before { animation: none; left: -100%; }
3406 .nav-install-btn:hover { transform: none; }
3407 .nav-install-btn:hover .icon { transform: none; }
3408}
3409/* Als PWA al draait (standalone mode) — verberg install-prompt knop volledig */
3410@media all and (display-mode: standalone) {
3411 #pwa-install-btn { display: none !important; }
3412}
3413
3414/* ========== v8.9.9 — Privé-post indicators ========== */
3415.post-private-banner {
3416 display: flex;
3417 align-items: center;
3418 gap: .6rem;
3419 padding: .75rem 1rem;
3420 background: color-mix(in srgb, #eab308 12%, var(--paper, #fff));
3421 border: 1px solid color-mix(in srgb, #eab308 40%, transparent);
3422 border-radius: 8px;
3423 margin: 0 0 1rem;
3424 font-size: .88rem;
3425 color: var(--ink, #1a1a1a);
3426}
3427.post-private-banner strong {
3428 color: #b45309;
3429}
3430
3431.grid-tile-private {
3432 position: absolute;
3433 top: 8px;
3434 left: 8px;
3435 padding: 3px 8px;
3436 background: rgba(0,0,0,0.7);
3437 color: #fde68a;
3438 font-size: .7rem;
3439 font-weight: 700;
3440 border-radius: 999px;
3441 z-index: 3;
3442 backdrop-filter: blur(4px);
3443 -webkit-backdrop-filter: blur(4px);
3444}
3445
3446/* ========== v8.9.10 — Social chip (inline in post-body) ========== */
3447.social-chip {
3448 display: inline-flex;
3449 align-items: center;
3450 gap: .45rem;
3451 padding: .35rem .75rem;
3452 background: color-mix(in srgb, var(--accent) 10%, transparent);
3453 border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
3454 border-radius: 999px;
3455 color: var(--ink, #1a1a1a);
3456 text-decoration: none;
3457 font-family: var(--font-ui, system-ui);
3458 font-size: .85rem;
3459 font-weight: 600;
3460 line-height: 1.2;
3461 transition: all .15s;
3462 vertical-align: middle;
3463 margin: 0 .1rem;
3464}
3465.social-chip:hover {
3466 background: var(--accent);
3467 color: #fff;
3468 border-color: var(--accent);
3469 text-decoration: none;
3470 transform: translateY(-1px);
3471 box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 40%, transparent);
3472}
3473.social-chip .icon {
3474 width: 16px;
3475 height: 16px;
3476 flex-shrink: 0;
3477}
3478.social-chip-icon-only {
3479 width: 36px;
3480 height: 36px;
3481 padding: 0;
3482 justify-content: center;
3483 border-radius: 50%;
3484}
3485.social-chip-icon-only .icon {
3486 width: 18px;
3487 height: 18px;
3488}
3489
3490/* v8.9.34 — player hidden state + post audio blocks
3491 v9.3 — Hide-rules uitgeschakeld: mini-player blijft permanent zichtbaar
3492 zodat 'ie niet meer verdwijnt bij browser-back of navigatie. */
3493.audio-player.is-hidden {
3494 /* display: none !important; */
3495}
3496body:not(.has-audio-player) #pcms-audio-player.is-hidden {
3497 /* display: none !important; */
3498}
3499
3500/* Post audio track — single track row in een post */
3501/* Lokale audio-track — visueel consistent met .post-audio-external */
3502.post-audio-track {
3503 display: flex;
3504 align-items: center;
3505 gap: .75rem;
3506 padding: .65rem .85rem;
3507 background: var(--paper-2, color-mix(in srgb, var(--ink) 4%, transparent));
3508 border: 1px solid var(--rule, rgba(0,0,0,.08));
3509 border-radius: 10px;
3510 margin: .75rem 0;
3511 transition: background .15s, border-color .15s;
3512}
3513.post-audio-track:hover {
3514 border-color: var(--accent, #c2410c);
3515 background: color-mix(in srgb, var(--accent, #c2410c) 5%, var(--paper-2, transparent));
3516}
3517/* Korte highlight wanneer je via de mini-speler naar deze track springt. */
3518.post-audio-track.pat-flash,
3519.post-album-track-compact.pat-flash {
3520 animation: patFlash 1.6s ease-out;
3521 border-radius: 8px;
3522}
3523/* Blijvende highlight op de track die NU actief is in de speler. */
3524.post-audio-track.pat-playing,
3525.post-album-track-compact.pat-playing {
3526 background: color-mix(in srgb, var(--accent, #c2410c) 12%, var(--paper-2, transparent));
3527 box-shadow: inset 3px 0 0 0 var(--accent, #c2410c);
3528 border-radius: 8px;
3529}
3530@keyframes patFlash {
3531 0%, 30% { border-color: var(--accent, #c2410c); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #c2410c) 35%, transparent); }
3532 100% { box-shadow: 0 0 0 0 transparent; }
3533}
3534.post-audio-track .pat-play {
3535 flex-shrink: 0;
3536 width: 42px;
3537 height: 42px;
3538 border-radius: 8px; /* zelfde rounded-square als pae-icon */
3539 border: none;
3540 background: var(--accent, #c2410c);
3541 color: var(--paper, #fff);
3542 cursor: pointer;
3543 display: inline-flex;
3544 align-items: center;
3545 justify-content: center;
3546 transition: transform .1s, background .15s;
3547 padding: 0;
3548 position: relative;
3549 overflow: hidden;
3550}
3551.post-audio-track .pat-play:hover { transform: scale(1.04); }
3552.post-audio-track .pat-play:active { transform: scale(.96); }
3553.post-audio-track .pat-play svg,
3554.post-audio-track .pat-play .pat-sitelogo {
3555 width: 100%; height: 100%;
3556 position: absolute;
3557 top: 0; left: 0;
3558 transition: opacity .15s;
3559}
3560.post-audio-track .pat-play .icon-play,
3561.post-audio-track .pat-play .icon-pause {
3562 width: 18px; height: 18px;
3563 position: absolute;
3564 top: 50%; left: 50%;
3565 transform: translate(-50%, -50%);
3566 fill: currentColor;
3567 opacity: 0; /* verborgen tot hover of is-current */
3568}
3569/* Site logo: object-cover binnen 42x42 box */
3570.post-audio-track .pat-play .pat-sitelogo {
3571 object-fit: cover;
3572 opacity: 1;
3573}
3574.post-audio-track .pat-play .pat-sitelogo-fallback {
3575 padding: 11px; /* SVG music-note iets kleiner in box */
3576 opacity: 1;
3577}
3578/* Bij hover: site logo faded, play-icoon verschijnt */
3579.post-audio-track:hover .pat-play .pat-sitelogo { opacity: 0.25; }
3580.post-audio-track:hover .pat-play .icon-play { opacity: 1; }
3581/* Bij current + playing: toon pause-icon */
3582.post-audio-track.is-current .pat-play .pat-sitelogo { opacity: 0.25; }
3583.post-audio-track.is-current .pat-play .icon-play { opacity: 1; }
3584.post-audio-track.is-current.is-playing .pat-play .icon-play { opacity: 0; }
3585.post-audio-track.is-current.is-playing .pat-play .icon-pause { opacity: 1; }
3586.post-audio-track.is-current {
3587 border-color: var(--accent, #c2410c);
3588 background: color-mix(in srgb, var(--accent, #c2410c) 10%, transparent);
3589}
3590.post-audio-track .pat-meta {
3591 flex: 1;
3592 min-width: 0;
3593 display: flex;
3594 flex-direction: column;
3595 gap: 2px;
3596}
3597.post-audio-track .pat-title {
3598 display: block;
3599 font-weight: 600;
3600 color: var(--ink);
3601 font-size: .95rem;
3602 overflow: hidden;
3603 text-overflow: ellipsis;
3604 white-space: nowrap;
3605}
3606.post-audio-track .pat-artist {
3607 display: block;
3608 font-size: .78rem;
3609 color: var(--ink-muted, var(--ink));
3610 opacity: .65;
3611 overflow: hidden;
3612 text-overflow: ellipsis;
3613 white-space: nowrap;
3614}
3615.post-audio-track .pat-duration {
3616 flex-shrink: 0;
3617 font-family: ui-monospace, monospace;
3618 font-size: .8rem;
3619 opacity: .55;
3620}
3621
3622/* Album block in een post */
3623.post-album {
3624 position: relative;
3625 border: 1px solid var(--rule, rgba(0,0,0,.1));
3626 border-radius: 14px;
3627 overflow: hidden;
3628 margin: 1.25rem 0;
3629 background: var(--paper-2, color-mix(in srgb, var(--ink) 3%, transparent));
3630}
3631.post-album-header {
3632 display: flex;
3633 gap: .75rem;
3634 padding: .55rem .7rem;
3635 align-items: center;
3636}
3637.post-album-cover {
3638 width: 120px;
3639 height: 120px;
3640 border-radius: 8px;
3641 background-size: cover;
3642 background-position: center;
3643 background-color: var(--rule);
3644 flex-shrink: 0;
3645}
3646/* v9.0 — Collage van track-covers als album-cover leeg is */
3647.post-album-cover-collage {
3648 display: grid;
3649 grid-template-columns: 1fr 1fr;
3650 grid-template-rows: 1fr 1fr;
3651 gap: 0;
3652 overflow: hidden;
3653 background: transparent;
3654}
3655.post-album-cover-collage .post-album-cover-tile {
3656 background-size: cover;
3657 background-position: center;
3658 width: 100%;
3659 height: 100%;
3660}
3661/* Empty state: gradient met muziek-noot */
3662.post-album-cover-empty {
3663 display: flex;
3664 align-items: center;
3665 justify-content: center;
3666 background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 25%, var(--paper-2)), color-mix(in srgb, var(--accent) 10%, var(--paper)));
3667 color: var(--accent, #c2410c);
3668}
3669.post-album-cover-empty svg {
3670 width: 42%;
3671 height: 42%;
3672 opacity: .55;
3673}
3674.post-album-info {
3675 flex: 1;
3676 min-width: 0;
3677}
3678.post-album-label {
3679 text-transform: uppercase;
3680 font-size: .68rem;
3681 letter-spacing: .1em;
3682 opacity: .55;
3683 margin: 0 0 .15rem;
3684 font-weight: 700;
3685}
3686.post-album-title {
3687 font-family: var(--font-display);
3688 font-size: 1.1rem;
3689 font-weight: 600;
3690 margin: 0 0 .1rem;
3691 line-height: 1.15;
3692 letter-spacing: -0.01em;
3693}
3694.post-album-artist {
3695 font-size: .88rem;
3696 opacity: .8;
3697 margin: 0 0 .12rem;
3698 line-height: 1.25;
3699}
3700.post-album-meta {
3701 font-size: .74rem;
3702 opacity: .55;
3703 margin: 0;
3704 line-height: 1.2;
3705}
3706.post-album-playall {
3707 margin-top: .75rem;
3708 padding: .45rem .9rem;
3709 background: var(--accent, #c2410c);
3710 color: var(--paper, #fff);
3711 border: none;
3712 border-radius: 6px;
3713 font-weight: 600;
3714 cursor: pointer;
3715 font-size: .88rem;
3716 display: inline-flex;
3717 align-items: center;
3718 gap: .35rem;
3719}
3720.post-album-playall:hover { filter: brightness(1.08); }
3721
3722/* ============================================================
3723 v9.0 — Album cover clickable button
3724 ============================================================ */
3725.post-album-cover-btn {
3726 position: relative;
3727 background: none;
3728 border: none;
3729 padding: 0;
3730 margin: 0;
3731 cursor: pointer;
3732 flex-shrink: 0;
3733 border-radius: 8px;
3734 overflow: hidden;
3735 display: block;
3736 width: auto;
3737 /* v9.1 — voorkom mobile 300ms double-tap zoom delay, anders zou 2× snel klikken
3738 een zoom triggeren i.p.v. 2× click-events */
3739 touch-action: manipulation;
3740 -webkit-tap-highlight-color: transparent;
3741}
3742.post-album-cover-btn .post-album-cover {
3743 display: block;
3744 width: 76px;
3745 height: 76px;
3746 border-radius: 7px;
3747 background-size: cover;
3748 background-position: center;
3749 background-color: var(--rule);
3750 transition: transform .25s ease;
3751}
3752.post-album-cover-btn:hover .post-album-cover {
3753 transform: scale(1.04);
3754}
3755.post-album-cover-btn .post-album-cover-empty {
3756 background: linear-gradient(135deg,
3757 color-mix(in srgb, var(--accent, #c2410c) 35%, var(--paper)),
3758 color-mix(in srgb, var(--accent, #c2410c) 10%, var(--paper-2)));
3759 display: flex;
3760 align-items: center;
3761 justify-content: center;
3762}
3763.post-album-cover-btn .post-album-cover-empty svg {
3764 width: 28px;
3765 height: 28px;
3766 color: color-mix(in srgb, var(--accent, #c2410c) 60%, transparent);
3767}
3768/* Play overlay op hover */
3769.post-album-cover-play {
3770 position: absolute;
3771 inset: 0;
3772 display: flex;
3773 align-items: center;
3774 justify-content: center;
3775 background: rgba(0, 0, 0, .35);
3776 opacity: 0;
3777 transition: opacity .2s ease;
3778 border-radius: 8px;
3779 pointer-events: none;
3780}
3781.post-album-cover-play svg {
3782 width: 48px;
3783 height: 48px;
3784 color: #fff;
3785 filter: drop-shadow(0 2px 8px rgba(0,0,0,.4));
3786}
3787.post-album-cover-btn:hover .post-album-cover-play,
3788.post-album-cover-btn:focus-visible .post-album-cover-play {
3789 opacity: 1;
3790}
3791
3792/* ============================================================
3793 v1 P52 — Admin actions overlay (edit / delete)
3794 Only rendered when the viewer has god role. Sits top-right
3795 of the card so it doesn't compete with the cover play affordance.
3796 ============================================================ */
3797.post-album-actions {
3798 position: absolute;
3799 top: .4rem;
3800 right: .4rem;
3801 display: flex;
3802 gap: .25rem;
3803 z-index: 2;
3804}
3805.post-album-action {
3806 width: 28px;
3807 height: 28px;
3808 border-radius: 6px;
3809 border: 1px solid var(--rule);
3810 background: color-mix(in srgb, var(--paper) 78%, transparent);
3811 backdrop-filter: blur(8px);
3812 -webkit-backdrop-filter: blur(8px);
3813 color: var(--ink);
3814 display: inline-flex;
3815 align-items: center;
3816 justify-content: center;
3817 cursor: pointer;
3818 text-decoration: none;
3819 opacity: .65;
3820 padding: 0;
3821 transition: opacity .15s ease, transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease;
3822 -webkit-tap-highlight-color: transparent;
3823}
3824.post-album-action:hover,
3825.post-album-action:focus-visible {
3826 opacity: 1;
3827 transform: translateY(-1px);
3828 outline: none;
3829}
3830.post-album-action.is-danger:hover,
3831.post-album-action.is-danger:focus-visible {
3832 background: color-mix(in srgb, #c2410c 18%, var(--paper));
3833 border-color: color-mix(in srgb, #c2410c 45%, var(--rule));
3834 color: #c2410c;
3835}
3836.post-album-action svg {
3837 width: 15px;
3838 height: 15px;
3839 display: block;
3840}
3841
3842/* ============================================================
3843 v9.0 — Compacte tracklist (informatief, niet-klikbaar)
3844 ============================================================ */
3845.post-album-tracks {
3846 list-style: none;
3847 padding: 0;
3848 margin: 0;
3849 border-top: 1px solid var(--rule);
3850}
3851/* v9.4 — Playlist insert modal rows */
3852.pli-row {
3853 display: flex;
3854 align-items: center;
3855 gap: .75rem;
3856 padding: .65rem .5rem;
3857 border-bottom: 1px solid var(--border);
3858}
3859.pli-row:last-child { border-bottom: none; }
3860.pli-row-cover {
3861 width: 44px;
3862 height: 44px;
3863 border-radius: 6px;
3864 background-size: cover;
3865 background-position: center;
3866 background-color: color-mix(in srgb, var(--ink, #000) 8%, transparent);
3867 flex-shrink: 0;
3868 display: flex;
3869 align-items: center;
3870 justify-content: center;
3871 font-size: 1.4rem;
3872}
3873.pli-row-info {
3874 flex: 1;
3875 min-width: 0;
3876}
3877.pli-row-title {
3878 font-weight: 600;
3879 overflow: hidden;
3880 text-overflow: ellipsis;
3881 white-space: nowrap;
3882}
3883.pli-row-meta {
3884 font-size: .82rem;
3885 overflow: hidden;
3886 text-overflow: ellipsis;
3887 white-space: nowrap;
3888}
3889.post-playlist-placeholder {
3890 margin: 1rem 0;
3891 user-select: none;
3892}
3893
3894/* v9.4 — Generic modal classes (gebruikt door playlist-editor en andere modals) */
3895.pcms-modal-backdrop {
3896 position: fixed;
3897 inset: 0;
3898 background: rgba(0, 0, 0, .6);
3899 display: flex;
3900 align-items: center;
3901 justify-content: center;
3902 z-index: 9000;
3903 padding: 1rem;
3904 box-sizing: border-box;
3905 overflow-y: auto;
3906}
3907.pcms-modal {
3908 background: var(--paper, #fff);
3909 color: var(--ink, #000);
3910 border-radius: 12px;
3911 width: 100%;
3912 max-width: 640px;
3913 max-height: calc(100vh - 2rem);
3914 display: flex;
3915 flex-direction: column;
3916 overflow: hidden;
3917 box-shadow: 0 20px 60px rgba(0, 0, 0, .3);
3918}
3919.pcms-modal-header {
3920 display: flex;
3921 align-items: center;
3922 justify-content: space-between;
3923 padding: 1rem 1.25rem;
3924 border-bottom: 1px solid var(--border);
3925 flex-shrink: 0;
3926}
3927.pcms-modal-header h3 { font-size: 1.1rem; }
3928.pcms-modal-close {
3929 background: transparent;
3930 border: none;
3931 font-size: 1.6rem;
3932 line-height: 1;
3933 cursor: pointer;
3934 padding: .25rem .5rem;
3935 color: var(--fg-muted, #666);
3936 border-radius: 4px;
3937}
3938.pcms-modal-close:hover {
3939 background: color-mix(in srgb, var(--ink, #000) 8%, transparent);
3940}
3941.pcms-modal-body {
3942 padding: 1.25rem;
3943 overflow-y: auto;
3944 overscroll-behavior: contain;
3945 flex: 1;
3946 min-height: 0;
3947}
3948.pcms-modal-footer {
3949 display: flex;
3950 gap: .5rem;
3951 justify-content: flex-end;
3952 padding: .75rem 1.25rem;
3953 border-top: 1px solid var(--border);
3954 flex-shrink: 0;
3955 background: color-mix(in srgb, var(--ink, #000) 3%, transparent);
3956}
3957
3958/* On mobile: full-screen modal */
3959@media (max-width: 600px) {
3960 .pcms-modal-backdrop {
3961 padding: 0;
3962 }
3963 .pcms-modal {
3964 border-radius: 0;
3965 max-width: 100%;
3966 max-height: 100vh;
3967 height: 100vh;
3968 }
3969}
3970
3971/* v9.4 — Media → Playlists tab */
3972.playlists-page-header {
3973 display: flex;
3974 flex-wrap: wrap;
3975 gap: 1rem;
3976 align-items: center;
3977 justify-content: space-between;
3978 margin-bottom: 1.25rem;
3979}
3980.playlists-page-header > p { flex: 1; min-width: 200px; }
3981.playlists-grid {
3982 display: grid;
3983 grid-template-columns: 1fr;
3984 gap: .75rem;
3985}
3986@media (min-width: 720px) {
3987 .playlists-grid { grid-template-columns: repeat(2, 1fr); }
3988}
3989@media (min-width: 1100px) {
3990 .playlists-grid { grid-template-columns: repeat(3, 1fr); }
3991}
3992.playlist-card {
3993 display: grid;
3994 grid-template-columns: 64px 1fr auto;
3995 gap: .75rem;
3996 align-items: center;
3997 padding: .75rem;
3998 border: 1px solid var(--border);
3999 border-radius: var(--radius);
4000 background: var(--paper, #fff);
4001}
4002.playlist-card-cover {
4003 width: 64px;
4004 height: 64px;
4005 border-radius: 8px;
4006 overflow: hidden;
4007 background-color: color-mix(in srgb, var(--ink, #000) 6%, transparent);
4008 display: flex;
4009 align-items: center;
4010 justify-content: center;
4011}
4012.playlist-card-cover img {
4013 width: 100%;
4014 height: 100%;
4015 object-fit: cover;
4016}
4017.playlist-card-cover-empty { font-size: 1.6rem; opacity: .5; }
4018.playlist-card-info { min-width: 0; }
4019.playlist-card-title {
4020 font-weight: 600;
4021 overflow: hidden;
4022 text-overflow: ellipsis;
4023 white-space: nowrap;
4024}
4025.playlist-card-artist,
4026.playlist-card-meta {
4027 font-size: .82rem;
4028 overflow: hidden;
4029 text-overflow: ellipsis;
4030 white-space: nowrap;
4031}
4032.playlist-card-actions {
4033 display: flex;
4034 gap: .25rem;
4035 flex-shrink: 0;
4036}
4037
4038/* v9.4 — Playlist editor modal (mobile-friendly, single-column) */
4039.pl-editor {
4040 /* Erft van .pcms-modal */
4041}
4042.pl-editor-meta {
4043 display: grid;
4044 grid-template-columns: 1fr;
4045 gap: .75rem;
4046 margin-bottom: 1rem;
4047}
4048@media (min-width: 560px) {
4049 .pl-editor-meta {
4050 grid-template-columns: 1fr 1fr;
4051 }
4052 .pl-editor-meta .pl-editor-field-full { grid-column: 1 / -1; }
4053}
4054.pl-editor-field {
4055 display: flex;
4056 flex-direction: column;
4057 gap: .25rem;
4058 min-width: 0;
4059}
4060.pl-editor-field span {
4061 font-size: .82rem;
4062 color: var(--fg-muted, #666);
4063 font-weight: 500;
4064}
4065.pl-editor-field input,
4066.pl-editor-field select {
4067 width: 100%;
4068 box-sizing: border-box;
4069 padding: .5rem .65rem;
4070 font-size: .95rem;
4071 border: 1px solid var(--border);
4072 border-radius: 6px;
4073 background: var(--paper, #fff);
4074 color: var(--ink, #000);
4075}
4076.pl-editor-field input:focus,
4077.pl-editor-field select:focus {
4078 outline: 2px solid var(--accent, #5a8a5a);
4079 outline-offset: -1px;
4080}
4081.pl-editor-section-title {
4082 margin: 1.25rem 0 .5rem;
4083 font-size: 1rem;
4084 font-weight: 600;
4085}
4086.pl-tracks-selected {
4087 border: 1px solid var(--border);
4088 border-radius: var(--radius);
4089 background: var(--paper, #fff);
4090 min-height: 60px;
4091 margin-bottom: 1rem;
4092 padding: .25rem;
4093}
4094.pl-tracks-empty {
4095 padding: 1.5rem;
4096 text-align: center;
4097 color: var(--fg-muted, #666);
4098 font-size: .9rem;
4099}
4100.pl-track-row {
4101 display: grid;
4102 grid-template-columns: 32px 36px 1fr auto;
4103 gap: .5rem;
4104 align-items: center;
4105 padding: .5rem;
4106 border-bottom: 1px solid var(--border);
4107 background: var(--paper, #fff);
4108 user-select: none;
4109 touch-action: pan-x;
4110}
4111.pl-track-row:last-child { border-bottom: none; }
4112.pl-track-row.is-dragging {
4113 opacity: .55;
4114 transform: scale(.98);
4115 background: color-mix(in srgb, var(--accent, #5a8a5a) 10%, transparent);
4116 border-radius: 6px;
4117}
4118.pl-track-row.drop-target-above {
4119 box-shadow: inset 0 2px 0 0 var(--accent, #5a8a5a);
4120}
4121.pl-track-row.drop-target-below {
4122 box-shadow: inset 0 -2px 0 0 var(--accent, #5a8a5a);
4123}
4124.pl-track-handle {
4125 cursor: grab;
4126 font-size: 1.2rem;
4127 line-height: 1;
4128 color: var(--fg-muted, #666);
4129 display: flex;
4130 align-items: center;
4131 justify-content: center;
4132 height: 32px;
4133 width: 32px;
4134 border-radius: 4px;
4135 flex-shrink: 0;
4136}
4137.pl-track-handle:active { cursor: grabbing; }
4138.pl-track-handle:hover {
4139 background: color-mix(in srgb, var(--ink, #000) 8%, transparent);
4140}
4141.pl-track-num {
4142 text-align: center;
4143 color: var(--fg-muted, #666);
4144 font-size: .85rem;
4145 width: 36px;
4146}
4147.pl-track-info {
4148 min-width: 0;
4149 overflow: hidden;
4150}
4151.pl-track-title {
4152 font-weight: 500;
4153 overflow: hidden;
4154 text-overflow: ellipsis;
4155 white-space: nowrap;
4156}
4157.pl-track-artist {
4158 font-size: .78rem;
4159 color: var(--fg-muted, #666);
4160 overflow: hidden;
4161 text-overflow: ellipsis;
4162 white-space: nowrap;
4163}
4164.pl-track-remove {
4165 background: transparent;
4166 border: none;
4167 cursor: pointer;
4168 padding: .35rem;
4169 border-radius: 4px;
4170 color: var(--fg-muted, #666);
4171 font-size: 1.2rem;
4172 line-height: 1;
4173}
4174.pl-track-remove:hover {
4175 background: color-mix(in srgb, #c00 12%, transparent);
4176 color: #c00;
4177}
4178
4179/* Available tracks panel */
4180.pl-tracks-available-panel {
4181 border: 1px solid var(--border);
4182 border-radius: var(--radius);
4183 background: var(--paper, #fff);
4184}
4185.pl-tracks-available-search {
4186 padding: .5rem;
4187 border-bottom: 1px solid var(--border);
4188}
4189.pl-tracks-available-search input {
4190 width: 100%;
4191 box-sizing: border-box;
4192}
4193.pl-tracks-available-list {
4194 max-height: 30vh;
4195 overflow-y: auto;
4196}
4197.pl-track-row-available {
4198 display: grid;
4199 grid-template-columns: 1fr auto;
4200 gap: .5rem;
4201 align-items: center;
4202 padding: .5rem;
4203 border-bottom: 1px solid var(--border);
4204 cursor: pointer;
4205}
4206.pl-track-row-available:last-child { border-bottom: none; }
4207.pl-track-row-available:hover {
4208 background: color-mix(in srgb, var(--accent, #5a8a5a) 8%, transparent);
4209}
4210.pl-track-row-available.is-already-added {
4211 opacity: .4;
4212 pointer-events: none;
4213}
4214.pl-track-add-btn {
4215 background: transparent;
4216 border: 1px solid var(--border);
4217 border-radius: 4px;
4218 padding: .25rem .5rem;
4219 cursor: pointer;
4220 font-size: .85rem;
4221 color: var(--accent, #5a8a5a);
4222}
4223
4224/* v9.3 — Tracklist items zijn nu klikbare <button class="pat-row"> binnen <li>.
4225 De LI is puur een container zonder visuele styling; alles gebeurt op de button. */
4226.post-album-track-compact {
4227 padding: 0;
4228 margin: 0;
4229 border-bottom: 1px solid var(--rule);
4230 list-style: none;
4231}
4232.post-album-track-compact:last-child { border-bottom: none; }
4233
4234.pat-row {
4235 display: grid;
4236 grid-template-columns: 32px 1fr auto;
4237 gap: .65rem;
4238 align-items: center;
4239 width: 100%;
4240 padding: .5rem .85rem;
4241 background: transparent;
4242 border: none;
4243 color: inherit;
4244 font: inherit;
4245 font-size: .82rem;
4246 text-align: left;
4247 cursor: pointer;
4248 position: relative;
4249 transition: background .15s ease;
4250}
4251.pat-row:hover {
4252 background: color-mix(in srgb, var(--ink, #000) 6%, transparent);
4253}
4254.pat-row:active { transform: scale(.995); }
4255[data-theme="dark"] .pat-row:hover {
4256 background: color-mix(in srgb, var(--paper, #fff) 10%, transparent);
4257}
4258
4259/* Leader: track-nummer OF cover-thumb (afhankelijk van kind).
4260 v9.3 — pat-leader is altijd 32×32, met overgang voor spinner-vervanging. */
4261.pat-row .pat-num,
4262.pat-row .pat-cover {
4263 width: 32px;
4264 height: 32px;
4265 border-radius: 4px;
4266 flex-shrink: 0;
4267 transition: opacity .25s ease, transform .25s ease;
4268}
4269.pat-row .pat-num {
4270 font-family: ui-monospace, monospace;
4271 font-size: .8rem;
4272 opacity: .5;
4273 display: flex;
4274 align-items: center;
4275 justify-content: center;
4276}
4277.pat-row .pat-cover {
4278 background-size: cover;
4279 background-position: center;
4280 background-color: color-mix(in srgb, var(--ink, #000) 8%, transparent);
4281}
4282
4283/* v9.3 — Loading-spiraal als overlay op de leader-kolom.
4284 pat-num/pat-cover faden uit, spinner faded in via ::before. */
4285.pat-row::before {
4286 content: "";
4287 position: absolute;
4288 left: .85rem; top: 50%;
4289 width: 32px; height: 32px;
4290 transform: translateY(-50%);
4291 color: var(--accent, #c2410c);
4292 background-color: currentColor;
4293 -webkit-mask: url('/assets/spinner.svg') center/26px 26px no-repeat;
4294 mask: url('/assets/spinner.svg') center/26px 26px no-repeat;
4295 opacity: 0;
4296 transition: opacity .25s ease;
4297 pointer-events: none;
4298 animation: pat-spinner-rotate 1.2s linear infinite;
4299 animation-play-state: paused;
4300}
4301.pat-row.is-loading::before {
4302 opacity: 1;
4303 animation-play-state: running;
4304}
4305.pat-row.is-loading .pat-num,
4306.pat-row.is-loading .pat-cover {
4307 opacity: 0;
4308 transform: scale(.85);
4309}
4310@keyframes pat-spinner-rotate {
4311 from { transform: translateY(-50%) rotate(0deg); }
4312 to { transform: translateY(-50%) rotate(-360deg); }
4313}
4314
4315/* Meta-kolom */
4316.pat-row .pat-meta {
4317 display: flex;
4318 flex-direction: column;
4319 min-width: 0;
4320 gap: 1px;
4321 overflow: hidden;
4322}
4323.pat-row .pat-title {
4324 font-weight: 500;
4325 overflow: hidden;
4326 text-overflow: ellipsis;
4327 white-space: nowrap;
4328}
4329.pat-row .pat-artist {
4330 font-size: .72rem;
4331 opacity: .6;
4332 overflow: hidden;
4333 text-overflow: ellipsis;
4334 white-space: nowrap;
4335}
4336.pat-row .pat-duration {
4337 font-family: ui-monospace, monospace;
4338 font-size: .72rem;
4339 opacity: .55;
4340 font-variant-numeric: tabular-nums;
4341 white-space: nowrap;
4342}
4343.pat-row .pat-duration-empty {
4344 opacity: .25;
4345}
4346
4347/* is-current — actieve track in de player */
4348.pat-row.is-current {
4349 background: color-mix(in srgb, var(--accent, #c2410c) 12%, transparent);
4350}
4351.pat-row.is-current .pat-title,
4352.pat-row.is-current .pat-artist { color: var(--accent, #c2410c); }
4353
4354/* v9.3 — Marquee bij actief afspelen: titel scrollt heen-en-weer.
4355 --pat-shift wordt door JS gezet (= overflow in pixels). Bij 0 → geen
4356 beweging want translateX(0) blijft 0.
4357 Snelle cyclus (6s) met korte rust aan beide uiteinden (8%) zodat 't
4358 vlot voelt zonder onleesbaar te zijn. */
4359.pat-row.is-current.is-playing .pat-title {
4360 animation: pat-title-marquee 6s ease-in-out infinite alternate;
4361 text-overflow: clip;
4362 overflow: visible;
4363 will-change: transform;
4364}
4365@keyframes pat-title-marquee {
4366 0%, 8% { transform: translateX(0); }
4367 92%, 100% { transform: translateX(calc(-1 * var(--pat-shift, 0px))); }
4368}
4369
4370/* ============================================================
4371 v9.0 — CSS-migratie voor OUDE opgeslagen albums
4372 Oude posts hebben <li class="post-audio-track"> met play-knoppen + cover-spans.
4373 We renderen die VISUEEL als compact, zonder de HTML aan te passen.
4374 ============================================================ */
4375.post-album-tracks .post-audio-track {
4376 display: grid;
4377 grid-template-columns: 28px 1fr auto;
4378 gap: .75rem;
4379 align-items: center;
4380 padding: .5rem .85rem;
4381 margin: 0;
4382 border: none;
4383 border-bottom: 1px solid var(--rule);
4384 background: transparent;
4385 border-radius: 0;
4386 cursor: default;
4387 font-size: .92rem;
4388}
4389.post-album-tracks .post-audio-track:last-child { border-bottom: none; }
4390/* Verberg de cover-thumb en play-button in OUDE saved posts */
4391.post-album-tracks .post-audio-track .pat-cover,
4392.post-album-tracks .post-audio-track .pat-play {
4393 display: none !important;
4394}
4395.post-album-tracks .post-audio-track .pat-num {
4396 font-family: ui-monospace, monospace;
4397 font-size: .8rem;
4398 opacity: .45;
4399 text-align: center;
4400 width: auto;
4401}
4402.post-album-tracks .post-audio-track .pat-meta {
4403 display: flex;
4404 flex-direction: column;
4405 min-width: 0;
4406 gap: 1px;
4407}
4408.post-album-tracks .post-audio-track .pat-title {
4409 font-weight: 500;
4410 overflow: hidden;
4411 text-overflow: ellipsis;
4412 white-space: nowrap;
4413}
4414.post-album-tracks .post-audio-track .pat-artist {
4415 font-size: .78rem;
4416 opacity: .6;
4417}
4418.post-album-tracks .post-audio-track .pat-duration {
4419 font-family: ui-monospace, monospace;
4420 font-size: .78rem;
4421 opacity: .55;
4422}
4423.post-album-tracks .post-audio-track.is-current {
4424 background: color-mix(in srgb, var(--accent, #c2410c) 10%, transparent);
4425}
4426.post-album-tracks .post-audio-track.is-current .pat-num,
4427.post-album-tracks .post-audio-track.is-current .pat-title {
4428 color: var(--accent);
4429}
4430
4431@media (max-width: 540px) {
4432 .post-album-header { flex-direction: column; }
4433 .post-album-cover-btn .post-album-cover { width: 100%; max-width: 240px; height: auto; aspect-ratio: 1/1; }
4434}
4435@media (max-width: 380px) {
4436 .post-album-header { padding: .75rem; gap: .75rem; }
4437 .post-album-cover-btn .post-album-cover { max-width: 180px; }
4438 .post-album-title { font-size: 1.15rem; }
4439 .post-album-meta { font-size: .8rem; }
4440 .post-album-track-compact,
4441 .post-album-tracks .post-audio-track {
4442 grid-template-columns: 24px 1fr auto;
4443 gap: .5rem;
4444 padding: .4rem .6rem;
4445 font-size: .85rem;
4446 }
4447 .post-album-track-compact .pat-duration,
4448 .post-album-tracks .post-audio-track .pat-duration {
4449 font-size: .72rem;
4450 }
4451 /* Queue items in expand-sheet */
4452 .audio-sheet-queue-item { padding: .4rem .45rem; gap: .55rem; }
4453 .audio-sheet-queue-cover { width: 32px; height: 32px; }
4454 .audio-sheet-queue-title { font-size: .85rem; }
4455 .audio-sheet-queue-artist { font-size: .7rem; }
4456}
4457
4458/* External track (SoundCloud / YouTube / Spotify / etc) — klikbaar maar link-naar-platform */
4459.post-audio-external {
4460 display: flex;
4461 align-items: center;
4462 gap: .75rem;
4463 padding: .65rem .85rem;
4464 background: var(--paper-2, color-mix(in srgb, var(--ink) 4%, transparent));
4465 border: 1px solid var(--rule, rgba(0,0,0,.08));
4466 border-radius: 10px;
4467 margin: .75rem 0;
4468 text-decoration: none;
4469 color: var(--ink);
4470 transition: background .15s, border-color .15s;
4471}
4472.post-audio-external:hover {
4473 border-color: var(--accent);
4474 background: color-mix(in srgb, var(--accent) 5%, var(--paper-2, transparent));
4475}
4476.post-audio-external .pae-icon {
4477 flex-shrink: 0;
4478 width: 42px;
4479 height: 42px;
4480 border-radius: 8px;
4481 display: inline-flex;
4482 align-items: center;
4483 justify-content: center;
4484 background: var(--platform-color, #555);
4485 color: #fff;
4486 font-size: 1.2rem;
4487}
4488.post-audio-external .pae-icon-svg svg {
4489 width: 60%;
4490 height: 60%;
4491 fill: currentColor;
4492}
4493/* Per-platform vult de box net wat anders — brand-specifiek geoptimaliseerd */
4494.post-audio-external[data-platform="soundcloud"] .pae-icon-svg svg { width: 72%; height: auto; }
4495.post-audio-external[data-platform="spotify"] .pae-icon-svg svg { width: 72%; height: 72%; }
4496.post-audio-external[data-platform="youtube"] .pae-icon-svg svg { width: 68%; height: auto; }
4497.post-audio-external[data-platform="bandcamp"] .pae-icon-svg svg { width: 62%; height: auto; }
4498.post-audio-external[data-platform="apple"] .pae-icon-svg svg { width: 58%; height: 58%; }
4499.post-audio-external[data-platform="tidal"] .pae-icon-svg svg { width: 68%; height: auto; }
4500.post-audio-external[data-platform="deezer"] .pae-icon-svg svg { width: 65%; height: auto; }
4501.post-audio-external[data-platform="mixcloud"] .pae-icon-svg svg { width: 75%; height: auto; }
4502.post-audio-external[data-platform="soundcloud"] .pae-icon { background: #ff5500; }
4503.post-audio-external[data-platform="spotify"] .pae-icon { background: #1db954; }
4504.post-audio-external[data-platform="youtube"] .pae-icon { background: #ff0000; }
4505.post-audio-external[data-platform="bandcamp"] .pae-icon { background: #1da0c3; }
4506.post-audio-external[data-platform="apple"] .pae-icon { background: #fa243c; }
4507.post-audio-external[data-platform="tidal"] .pae-icon { background: #000; }
4508.post-audio-external[data-platform="deezer"] .pae-icon { background: #a238ff; }
4509.post-audio-external[data-platform="mixcloud"] .pae-icon { background: #314359; }
4510.post-audio-external[data-platform="other"] .pae-icon { background: #555; }
4511.post-audio-external .pae-meta {
4512 flex: 1;
4513 min-width: 0;
4514 display: flex;
4515 flex-direction: column;
4516 gap: 2px;
4517}
4518.post-audio-external .pae-title {
4519 display: block;
4520 font-weight: 600;
4521 overflow: hidden;
4522 text-overflow: ellipsis;
4523 white-space: nowrap;
4524 color: var(--ink);
4525}
4526.post-audio-external .pae-platform {
4527 display: block;
4528 font-size: .78rem;
4529 opacity: .65;
4530 text-transform: capitalize;
4531}
4532.post-audio-external .pae-go {
4533 flex-shrink: 0;
4534 opacity: .5;
4535 font-size: 1.1rem;
4536}
4537
4538/* =========================================================================
4539 v9.0 — Video player (inline)
4540 ========================================================================= */
4541
4542.post-video-figure {
4543 margin: 1.25rem 0;
4544 max-width: 100%;
4545}
4546
4547.post-video {
4548 display: block;
4549 width: 100%;
4550 max-width: 100%;
4551 height: auto;
4552 border-radius: 10px;
4553 background: #000;
4554 box-shadow: 0 4px 20px rgba(0,0,0,.15);
4555}
4556
4557.post-video-caption {
4558 margin-top: .5rem;
4559 font-size: .88rem;
4560 color: var(--ink-muted, color-mix(in srgb, var(--ink) 60%, transparent));
4561 text-align: center;
4562 line-height: 1.5;
4563}
4564
4565.post-video-caption strong {
4566 display: inline;
4567 font-weight: 600;
4568 color: var(--ink);
4569}
4570
4571@media (max-width: 640px) {
4572 .post-video-figure { margin: .75rem -.5rem; }
4573 .post-video { border-radius: 6px; }
4574}
4575
4576/* v9.3 — Floating "Bewerken" knop voor album-blokken in de post-editor.
4577 Verschijnt bij hover over een .post-album binnen #visual en wordt via JS
4578 geplaatst op de rechterbovenhoek van het blok. Niet in de opgeslagen HTML. */
4579.pcms-album-edit-overlay {
4580 position: absolute;
4581 z-index: 1000;
4582 transform: translateX(-100%);
4583 display: inline-flex;
4584 align-items: center;
4585 gap: .35rem;
4586 padding: .35rem .7rem;
4587 background: var(--accent, #c2410c);
4588 color: #fff;
4589 border: none;
4590 border-radius: 6px;
4591 font-size: .82rem;
4592 font-weight: 600;
4593 cursor: pointer;
4594 box-shadow: 0 4px 12px rgba(0, 0, 0, .18);
4595 transition: transform .12s ease, box-shadow .12s ease;
4596}
4597.pcms-album-edit-overlay:hover {
4598 transform: translateX(-100%) translateY(-1px);
4599 box-shadow: 0 6px 16px rgba(0, 0, 0, .24);
4600}
4601.pcms-album-edit-overlay:active {
4602 transform: translateX(-100%) translateY(0);
4603 box-shadow: 0 2px 6px rgba(0, 0, 0, .18);
4604}
4605
4606/* v9.3 — Drag-and-drop in de album-builder selected-lijst */
4607.ab-selected-row { cursor: default; }
4608.ab-selected-row .ab-drag-handle {
4609 display: inline-flex;
4610 align-items: center;
4611 justify-content: center;
4612 width: 18px;
4613 height: 100%;
4614 color: color-mix(in srgb, currentColor 40%, transparent);
4615 cursor: grab;
4616 user-select: none;
4617 font-size: .9rem;
4618 letter-spacing: -2px;
4619 flex-shrink: 0;
4620}
4621.ab-selected-row:hover .ab-drag-handle {
4622 color: color-mix(in srgb, currentColor 70%, transparent);
4623}
4624.ab-selected-row.is-dragging {
4625 opacity: .35;
4626 cursor: grabbing;
4627}
4628.ab-selected-row.is-dragging .ab-drag-handle { cursor: grabbing; }
4629/* Drop-indicator lijnen boven/onder de target rij */
4630.ab-selected-row.is-drag-target-before {
4631 box-shadow: 0 -2px 0 0 var(--accent, #c2410c) inset, 0 2px 0 0 var(--accent, #c2410c);
4632 /* Gebruik outline voor cross-browser zichtbaarheid */
4633 position: relative;
4634}
4635.ab-selected-row.is-drag-target-before::before {
4636 content: "";
4637 position: absolute;
4638 top: -1px; left: 0; right: 0;
4639 height: 2px;
4640 background: var(--accent, #c2410c);
4641 pointer-events: none;
4642}
4643.ab-selected-row.is-drag-target-after::after {
4644 content: "";
4645 position: absolute;
4646 bottom: -1px; left: 0; right: 0;
4647 height: 2px;
4648 background: var(--accent, #c2410c);
4649 pointer-events: none;
4650}
4651.ab-selected-row.is-drag-target-after { position: relative; }
4652
4653/* v9.3 — Playlist fallback states (missing of empty playlist reference in post) */
4654.post-playlist-missing, .post-playlist-empty {
4655 padding: 1rem;
4656 margin: .75rem 0;
4657 background: color-mix(in srgb, var(--accent, #c2410c) 8%, transparent);
4658 border: 1px dashed color-mix(in srgb, var(--accent, #c2410c) 30%, transparent);
4659 border-radius: 8px;
4660 font-size: .9rem;
4661}
4662.post-playlist-missing em, .post-playlist-empty em {
4663 opacity: .8;
4664}
4665
4666/* v9.3 — Install-app footer-knop */
4667.site-footer-install {
4668 display: flex;
4669 justify-content: center;
4670 padding: 1rem 1rem 1.25rem;
4671 border-top: 1px solid color-mix(in srgb, currentColor 8%, transparent);
4672 margin-top: .5rem;
4673}
4674
4675/* v9.4 — Cooler install-knop: gradient + shimmer + perfect centering.
4676 Twee kleur-stops voor diepte, animated shimmer-streep om aandacht te trekken,
4677 icon perfect verticaal gecentreerd via flex (was: net iets te hoog tov tekst). */
4678.install-app-btn {
4679 position: relative;
4680 display: inline-flex;
4681 align-items: center;
4682 justify-content: center;
4683 gap: .55rem;
4684 padding: .8rem 1.6rem;
4685 background: linear-gradient(135deg,
4686 var(--accent, #c2410c),
4687 color-mix(in srgb, var(--accent, #c2410c) 80%, #000));
4688 color: #fff;
4689 border: none;
4690 border-radius: 999px;
4691 font: inherit;
4692 font-weight: 600;
4693 font-size: .95rem;
4694 line-height: 1;
4695 letter-spacing: .01em;
4696 cursor: pointer;
4697 overflow: hidden;
4698 isolation: isolate;
4699 box-shadow:
4700 0 1px 0 rgba(255,255,255,.15) inset,
4701 0 -2px 0 rgba(0,0,0,.1) inset,
4702 0 4px 14px color-mix(in srgb, var(--accent, #c2410c) 40%, transparent),
4703 0 1px 3px rgba(0,0,0,.18);
4704 transition:
4705 transform .15s cubic-bezier(.4, 0, .2, 1),
4706 box-shadow .2s ease,
4707 filter .15s ease;
4708}
4709.install-app-btn svg {
4710 flex-shrink: 0;
4711 /* subtle bounce-on-hover trigger, separate animation */
4712 transition: transform .25s cubic-bezier(.34, 1.56, .64, 1);
4713}
4714.install-app-btn span {
4715 line-height: 1;
4716}
4717
4718/* Shimmer-streep die langzaam over de knop loopt — "alive" effect */
4719.install-app-btn::before {
4720 content: "";
4721 position: absolute;
4722 top: 0;
4723 left: -100%;
4724 width: 60%;
4725 height: 100%;
4726 background: linear-gradient(110deg,
4727 transparent 30%,
4728 rgba(255,255,255,.25) 50%,
4729 transparent 70%);
4730 z-index: -1;
4731 animation: pcmsInstallShimmer 4s ease-in-out infinite;
4732 pointer-events: none;
4733}
4734@keyframes pcmsInstallShimmer {
4735 0%, 35% { left: -100%; }
4736 65%, 100% { left: 200%; }
4737}
4738
4739.install-app-btn:hover {
4740 transform: translateY(-2px) scale(1.02);
4741 filter: brightness(1.06);
4742 box-shadow:
4743 0 1px 0 rgba(255,255,255,.2) inset,
4744 0 -2px 0 rgba(0,0,0,.1) inset,
4745 0 8px 24px color-mix(in srgb, var(--accent, #c2410c) 55%, transparent),
4746 0 2px 6px rgba(0,0,0,.2);
4747}
4748.install-app-btn:hover svg {
4749 transform: translateY(-2px);
4750}
4751.install-app-btn:active {
4752 transform: translateY(0) scale(.98);
4753 transition-duration: .05s;
4754}
4755.install-app-btn:focus-visible {
4756 outline: 2px solid var(--accent, #c2410c);
4757 outline-offset: 3px;
4758}
4759
4760/* Reduced motion: shimmer uit, alleen statisch */
4761@media (prefers-reduced-motion: reduce) {
4762 .install-app-btn::before { animation: none; left: -100%; }
4763 .install-app-btn:hover { transform: none; }
4764 .install-app-btn:hover svg { transform: none; }
4765}
4766
4767/* Install-instructions modal */
4768.install-modal {
4769 position: fixed;
4770 inset: 0;
4771 z-index: 10000;
4772 display: flex;
4773 align-items: center;
4774 justify-content: center;
4775 padding: 1rem;
4776}
4777.install-modal-backdrop {
4778 position: absolute;
4779 inset: 0;
4780 background: rgba(0,0,0,.55);
4781 backdrop-filter: blur(3px);
4782}
4783.install-modal-panel {
4784 position: relative;
4785 z-index: 1;
4786 background: var(--paper);
4787 color: var(--ink);
4788 border: 1px solid var(--rule);
4789 border-radius: 14px;
4790 padding: 1.5rem;
4791 max-width: 440px;
4792 width: 100%;
4793 max-height: 90vh;
4794 overflow-y: auto;
4795 box-shadow: 0 20px 60px rgba(0,0,0,.4);
4796}
4797.install-modal-panel h3 { font-size: 1.15rem; color: var(--ink); }
4798.install-modal-panel h4 {
4799 margin: 1rem 0 .4rem;
4800 font-size: .92rem;
4801 letter-spacing: .03em;
4802 text-transform: uppercase;
4803 opacity: .7;
4804 color: var(--ink);
4805}
4806.install-modal-panel p { color: var(--ink); }
4807.install-steps {
4808 margin: 0;
4809 padding-left: 1.35rem;
4810 display: flex;
4811 flex-direction: column;
4812 gap: .45rem;
4813 font-size: .95rem;
4814 line-height: 1.45;
4815 color: var(--ink);
4816}
4817.install-steps li::marker { color: var(--accent); font-weight: 700; }
4818.install-apk-block {
4819 margin-top: 1.25rem;
4820 padding-top: 1rem;
4821 border-top: 1px dashed var(--rule);
4822}
4823.install-apk-link {
4824 display: inline-flex;
4825 align-items: center;
4826 margin-top: .4rem;
4827 padding: .6rem 1rem;
4828 background: var(--accent);
4829 color: #fff;
4830 border-radius: 8px;
4831 text-decoration: none;
4832 font-weight: 600;
4833}
4834.install-apk-link:hover { filter: brightness(1.08); }
4835.install-modal-actions {
4836 display: flex;
4837 justify-content: flex-end;
4838 margin-top: 1.25rem;
4839}
4840.install-modal-actions .btn {
4841 padding: .5rem 1rem;
4842 background: var(--paper-2);
4843 border: 1px solid var(--rule);
4844 border-radius: 8px;
4845 color: var(--ink);
4846 cursor: pointer;
4847 font: inherit;
4848 font-weight: 500;
4849}
4850.install-modal-actions .btn:hover {
4851 background: color-mix(in srgb, var(--accent) 10%, var(--paper-2));
4852 border-color: var(--accent);
4853 color: var(--accent);
4854}
4855
4856@media (max-width: 520px) {
4857 .install-modal-panel { padding: 1.2rem; }
4858 .install-app-btn { width: 100%; justify-content: center; }
4859}
4860
4861/* v9.3 — Install-modal: choice-blocks met voor/nadelen */
4862.install-choice {
4863 margin-top: 1.25rem;
4864 padding: 1rem;
4865 border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
4866 border-radius: 10px;
4867 background: color-mix(in srgb, currentColor 3%, transparent);
4868}
4869.install-choice + .install-choice { margin-top: 1rem; }
4870.install-choice h4 {
4871 margin: 0 0 .75rem;
4872 font-size: 1rem;
4873 letter-spacing: 0;
4874 text-transform: none;
4875 opacity: 1;
4876}
4877.install-pros-cons {
4878 display: flex;
4879 gap: 1rem;
4880 margin: .5rem 0 1rem;
4881}
4882.install-pros,
4883.install-cons {
4884 flex: 1;
4885 min-width: 0;
4886 font-size: .85rem;
4887 line-height: 1.45;
4888}
4889.install-pros strong,
4890.install-cons strong {
4891 display: block;
4892 margin-bottom: .35rem;
4893 font-size: .82rem;
4894 letter-spacing: .02em;
4895}
4896.install-pros strong { color: #16a34a; }
4897.install-cons strong { color: #dc2626; }
4898.install-pros ul,
4899.install-cons ul {
4900 margin: 0;
4901 padding-left: 1.1rem;
4902 display: flex;
4903 flex-direction: column;
4904 gap: .25rem;
4905}
4906.install-pros li::marker { color: #16a34a; }
4907.install-cons li::marker { color: #dc2626; }
4908
4909.install-choice-cta {
4910 display: inline-flex;
4911 align-items: center;
4912 justify-content: center;
4913 gap: .35rem;
4914 margin-top: .5rem;
4915 padding: .7rem 1.1rem;
4916 background: var(--accent, #c2410c);
4917 color: #fff;
4918 border: none;
4919 border-radius: 8px;
4920 text-decoration: none;
4921 font: inherit;
4922 font-weight: 600;
4923 font-size: .95rem;
4924 cursor: pointer;
4925 transition: filter .12s ease;
4926}
4927.install-choice-cta:hover { filter: brightness(1.08); }
4928.install-choice-note {
4929 margin: .5rem 0 0;
4930 padding: .6rem .8rem;
4931 background: color-mix(in srgb, currentColor 4%, transparent);
4932 border-left: 3px solid var(--accent, #c2410c);
4933 border-radius: 4px;
4934 font-size: .9rem;
4935 line-height: 1.45;
4936}
4937
4938@media (max-width: 520px) {
4939 .install-pros-cons { flex-direction: column; gap: .75rem; }
4940}
4941
4942/* v9.4 — Soft fade-overgang tussen posts bij navigatie.
4943 Fade-out is rustig (450ms): oude content vervaagt geleidelijk terwijl
4944 de scroll-naar-top loopt. Fade-in is snel (180ms) met een lichte blur-
4945 onscherp → scherp animatie zodat de nieuwe content niet abrupt verschijnt. */
4946#pcms-main {
4947 transition: opacity .45s ease-out;
4948}
4949#pcms-main.is-pcms-transitioning-out {
4950 opacity: 0;
4951}
4952#pcms-main.is-pcms-transitioning-in {
4953 opacity: 0;
4954 filter: blur(6px);
4955 animation: pcmsFadeIn .18s ease-out forwards;
4956 will-change: opacity, filter;
4957}
4958@keyframes pcmsFadeIn {
4959 from { opacity: 0; filter: blur(6px); }
4960 to { opacity: 1; filter: blur(0); }
4961}
4962
4963/* Respecteer reduced-motion voorkeur (iOS/Android accessibility setting) */
4964@media (prefers-reduced-motion: reduce) {
4965 #pcms-main {
4966 transition: opacity .15s ease-out;
4967 }
4968 #pcms-main.is-pcms-transitioning-in {
4969 filter: none;
4970 animation: none;
4971 opacity: 1;
4972 }
4973}
4974
4975/* === Fediverse interactions — inherits the native .comment styling === */
4976.post-fediverse .fedi-stats { display: flex; gap: .45rem; margin: .35rem 0 1.5rem; align-items: center; }
4977.post-fediverse .fedi-stat {
4978 display: inline-flex; align-items: center; gap: .4rem;
4979 padding: .32rem .7rem; border-radius: 999px; font-size: .85rem; line-height: 1;
4980 font-variant-numeric: tabular-nums;
4981 color: var(--ink-muted); background: transparent;
4982 border: 1px solid var(--rule);
4983}
4984.post-fediverse .fedi-stat svg { width: 15px; height: 15px; }
4985.post-fediverse .fedi-stat-btn { cursor: pointer; font: inherit; line-height: 1; transition: color .15s, border-color .15s, background .15s; }
4986.post-fediverse .fedi-stat-btn:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, var(--rule)); background: var(--accent-tint); }
4987.post-fediverse .fedi-stat-btn:hover svg { fill: color-mix(in srgb, var(--accent) 22%, transparent); }
4988.post-fediverse .fedi-handle { color: var(--ink-soft, #888); font-size: .82rem; }
4989.post-fediverse .comment-content p { margin: .2rem 0; }
4990.post-fediverse .comment-content p:first-child { margin-top: 0; }
4991.post-fediverse .comment-content p:last-child { margin-bottom: 0; }
4992/* reply toggle: a plain "Reageer" action like the comment buttons (no triangle) */
4993.post-fediverse .fedi-reply-toggle { list-style: none; cursor: pointer; display: inline-block; }
4994.post-fediverse .fedi-reply-toggle::-webkit-details-marker { display: none; }
4995.post-fediverse details[open] .fedi-reply-toggle { margin-bottom: .5rem; }
4996
4997/* Reply-via-fediverse CTA on post pages */
4998.post-fediverse-cta { margin: 1.75rem 0; }
4999.fedi-remote-reply-btn { display: inline-flex; align-items: center; gap: .45rem; }
5000.post-fediverse-cta .fedi-remote-reply-btn {
5001 border: 1px solid color-mix(in srgb, var(--accent, #888) 55%, transparent);
5002 background: color-mix(in srgb, var(--accent, #888) 10%, transparent);
5003 color: var(--ink, inherit); border-radius: 999px; padding: .5rem 1.05rem;
5004 font-weight: 600; cursor: pointer; transition: background .15s ease;
5005}
5006.post-fediverse-cta .fedi-remote-reply-btn:hover { background: color-mix(in srgb, var(--accent, #888) 22%, transparent); }
5007.post-fediverse-cta .fedi-remote-reply-btn svg { color: var(--accent, currentColor); }
5008.post-fediverse .comment-actions .fedi-remote-reply-btn { gap: .3rem; }
5009
5010/* Inline "reply via fediverse" address form (replaces the browser prompt) */
5011.fedi-remote-form {
5012 position: absolute; z-index: 60; display: inline-flex; align-items: center; gap: .4rem;
5013 padding: .4rem; border-radius: 999px;
5014 background: var(--paper-2, var(--paper, #fff));
5015 border: 1px solid color-mix(in srgb, var(--ink, #000) 12%, transparent);
5016 box-shadow: 0 10px 30px rgba(0,0,0,.28);
5017}
5018.fedi-remote-form input {
5019 height: 2.2rem; padding: 0 .9rem; border-radius: 999px; font: inherit; min-width: 230px;
5020 border: 1px solid color-mix(in srgb, var(--ink, #000) 18%, transparent);
5021 background: color-mix(in srgb, var(--ink, #000) 4%, transparent); color: var(--ink, #000);
5022}
5023.fedi-remote-form input::placeholder { color: var(--ink-soft, #999); }
5024.fedi-remote-form input:focus { outline: none; border-color: color-mix(in srgb, var(--accent, #888) 70%, transparent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #888) 22%, transparent); }
5025.fedi-remote-form .fedi-remote-go {
5026 height: 2.2rem; min-width: 2.2rem; padding: 0 .85rem; border-radius: 999px;
5027 display: inline-flex; align-items: center; justify-content: center; font-size: 1rem;
5028}
5029.fedi-remote-form .fedi-remote-cancel { background: none; border: none; color: var(--ink-soft, #888); cursor: pointer; font-size: 1.1rem; line-height: 1; padding: .25rem .35rem; }
5030.fedi-remote-form .fedi-remote-cancel:hover { color: var(--ink, #000); }
5031
5032/* ── NSFW / sensitive content — blur + click to reveal ───────────────── */
5033.nsfw-media { position: relative; }
5034.nsfw-veil {
5035 position: absolute; inset: 0; z-index: 4; cursor: pointer; border-radius: inherit;
5036 display: flex; flex-direction: column; align-items: center; justify-content: center;
5037 gap: .5rem; padding: .75rem; text-align: center;
5038 background: color-mix(in srgb, var(--paper, #14141a) 50%, transparent);
5039 backdrop-filter: blur(22px) saturate(.6); -webkit-backdrop-filter: blur(22px) saturate(.6);
5040}
5041.nsfw-media.is-shown > .nsfw-veil { display: none; }
5042.nsfw-veil-label { font-weight: 700; font-size: .82rem; color: var(--ink, #f3f1ea); text-shadow: 0 1px 2px rgba(0,0,0,.5); }
5043.nsfw-veil-btn {
5044 font-size: .72rem; font-weight: 600; padding: .3rem .8rem; border-radius: 999px;
5045 border: 1px solid color-mix(in srgb, var(--ink, #fff) 55%, transparent);
5046 background: color-mix(in srgb, var(--paper, #000) 35%, transparent); color: var(--ink, #f3f1ea);
5047}
5048/* Post page: blur cover + body behind a content-warning banner until revealed */
5049.nsfw-gate .post-cover,
5050.nsfw-gate .post-content { filter: blur(26px); pointer-events: none; user-select: none; transition: filter .2s ease; }
5051.nsfw-gate.is-shown .post-cover,
5052.nsfw-gate.is-shown .post-content { filter: none; pointer-events: auto; user-select: auto; }
5053.nsfw-banner {
5054 display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; margin: 0 0 1.5rem;
5055 padding: .8rem 1rem; border-radius: 12px;
5056 border: 1px solid color-mix(in srgb, var(--accent, #e8b04b) 45%, transparent);
5057 background: color-mix(in srgb, var(--accent, #e8b04b) 9%, transparent);
5058}
5059.nsfw-gate.is-shown .nsfw-banner { display: none; }
5060.nsfw-banner-icon { font-size: 1.2rem; }
5061.nsfw-banner-text { font-weight: 600; font-size: .92rem; color: var(--ink); }
5062.nsfw-banner-btn {
5063 margin-left: auto; cursor: pointer; font: inherit; font-size: .82rem; font-weight: 600;
5064 padding: .4rem 1rem; border-radius: 999px; border: 0;
5065 background: var(--accent, #e8b04b); color: #1a1205;
5066}
5067
5068/* ── Het bandje (mixtape) ──────────────────────────────────────────────
5069 Een cassette, getekend als SVG op de echte maat: 100,4 x 63,8 mm, dus een
5070 viewBox van 314x200. De vorige versie tekende twee schijfjes met CSS; dit is
5071 een replica -- behuizing, labelvlak, venster, twee spoelen met tandjes, de
5072 schroefjes in de hoeken en de openingen voor de kop en de capstans onderin.
5073
5074 Alle kleuren komen uit de bestaande variabelen, dus licht, donker en het
5075 accent van de site volgen vanzelf. */
5076.post-tape {
5077 position: relative;
5078 border: 1px solid var(--rule, rgba(0,0,0,.1));
5079 border-radius: 14px;
5080 overflow: hidden;
5081 margin: 1.25rem 0;
5082 background: var(--paper-2, color-mix(in srgb, var(--ink) 3%, transparent));
5083}
5084
5085.tape-shell { position: relative; padding: .8rem .8rem .3rem; }
5086.tape-svg { display: block; width: 100%; height: auto; }
5087
5088/* De behuizing. Twee lagen zodat er een randje omheen zit, zoals bij een echt
5089 doosje waar de bovenschaal iets uitsteekt. */
5090.tape-body { fill: color-mix(in srgb, var(--ink) 82%, var(--paper, #fff)); }
5091.tape-body-inner { fill: color-mix(in srgb, var(--ink) 70%, var(--paper, #fff)); }
5092.tape-labelplate { fill: color-mix(in srgb, var(--paper, #fff) 92%, var(--accent)); }
5093.tape-glass {
5094 fill: color-mix(in srgb, var(--ink) 88%, transparent);
5095 stroke: color-mix(in srgb, var(--paper, #fff) 25%, transparent);
5096 stroke-width: 1.5;
5097}
5098.tape-pack { fill: color-mix(in srgb, var(--ink) 55%, var(--paper, #fff)); }
5099.tape-ribbon { fill: color-mix(in srgb, var(--ink) 45%, var(--paper, #fff)); }
5100.tape-hub { fill: var(--paper, #fff); }
5101.tape-tooth { fill: color-mix(in srgb, var(--ink) 60%, var(--paper, #fff)); }
5102.tape-screw { fill: color-mix(in srgb, var(--ink) 55%, var(--paper, #fff)); }
5103.tape-slot { fill: color-mix(in srgb, var(--ink) 92%, transparent); }
5104.tape-slot--capstan { fill: color-mix(in srgb, var(--ink) 96%, transparent); }
5105
5106/* Draaien. De tandjes zitten in dezelfde groep als de naaf, dus de hele spoel
5107 draait als een geheel -- daar gaat het om, want aan die tandjes zie je pas
5108 DAT hij draait. Het label blijft stil, anders wordt de titel onleesbaar. */
5109.post-tape.is-playing .tape-reel { animation: tape-spin 3s linear infinite; }
5110.post-tape.is-playing .tape-reel--right { animation-duration: 2.2s; }
5111/* Spoelen: veel sneller, en achteruit ook echt achteruit. */
5112.post-tape.is-winding .tape-reel { animation-duration: .45s; }
5113.post-tape.is-winding-back .tape-reel { animation-direction: reverse; }
5114@keyframes tape-spin { to { transform: rotate(360deg); } }
5115@media (prefers-reduced-motion: reduce) {
5116 .post-tape.is-playing .tape-reel,
5117 .post-tape.is-winding .tape-reel { animation: none; }
5118}
5119
5120/* De tekst staat OP het labelvlak van de cassette. Percentages volgen de
5121 viewBox, dus het blijft kloppen op elke breedte. */
5122.tape-label {
5123 position: absolute;
5124 left: 8.4%; right: 8.4%;
5125 top: calc(.8rem + 11%);
5126 height: 31%;
5127 padding: .1rem .4rem;
5128 color: color-mix(in srgb, var(--ink) 92%, transparent);
5129 overflow: hidden;
5130}
5131.tape-kind {
5132 margin: 0;
5133 font-size: clamp(.55rem, 1.6vw, .68rem);
5134 letter-spacing: .14em;
5135 text-transform: uppercase;
5136 opacity: .7;
5137}
5138.tape-title {
5139 margin: 0;
5140 font-size: clamp(.85rem, 2.6vw, 1.1rem);
5141 line-height: 1.15;
5142 white-space: nowrap;
5143 overflow: hidden;
5144 text-overflow: ellipsis;
5145}
5146.tape-artist { margin: 0; font-size: clamp(.65rem, 1.9vw, .8rem); opacity: .8; }
5147.tape-meta { margin: 0; font-size: clamp(.6rem, 1.7vw, .72rem); opacity: .6; }
5148
5149/* De knoppen: terugspoelen, spelen, vooruitspoelen. Meer heeft een cassette
5150 niet, en dat is de bedoeling -- er is geen zoekbalk omdat je op een bandje
5151 niet kunt zoeken, alleen spoelen. */
5152.tape-controls {
5153 display: flex;
5154 align-items: center;
5155 justify-content: center;
5156 gap: .6rem;
5157 padding: .5rem 0 .6rem;
5158}
5159.tape-btn {
5160 display: grid;
5161 place-items: center;
5162 width: 40px; height: 40px;
5163 border-radius: 50%;
5164 border: 1px solid var(--rule, rgba(0,0,0,.15));
5165 background: var(--paper, #fff);
5166 color: var(--ink);
5167 cursor: pointer;
5168 padding: 0;
5169 touch-action: manipulation; /* vasthouden mag geen zoom of scroll worden */
5170 user-select: none;
5171}
5172.tape-btn svg { width: 18px; height: 18px; }
5173.tape-btn:hover { border-color: var(--accent); color: var(--accent); }
5174.tape-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
5175.tape-btn--play {
5176 width: 52px; height: 52px;
5177 background: var(--accent);
5178 border-color: var(--accent);
5179 color: var(--paper, #fff);
5180}
5181.tape-btn--play svg { width: 22px; height: 22px; }
5182.post-tape.is-winding .tape-btn--back,
5183.post-tape.is-winding .tape-btn--fwd { border-color: var(--accent); color: var(--accent); }
5184
5185.tape-now {
5186 margin: 0;
5187 padding: 0 .8rem .4rem;
5188 text-align: center;
5189 font-size: .8rem;
5190 opacity: .8;
5191 min-height: 1.2em;
5192}
5193
5194/* De nummers staan er als INHOUD, niet als knoppen. Geen cursor, geen hover,
5195 geen aanwijzing dat je erop kunt drukken: op een cassette kun je dat niet. */
5196.tape-tracks {
5197 margin: 0;
5198 padding: 0 .8rem .8rem;
5199 list-style: none;
5200 counter-reset: tape;
5201 display: grid;
5202 gap: .1rem;
5203}
5204.tape-track {
5205 counter-increment: tape;
5206 display: flex;
5207 gap: .5rem;
5208 align-items: baseline;
5209 font-size: .85rem;
5210 padding: .18rem .3rem;
5211 border-radius: 6px;
5212 opacity: .75;
5213}
5214.tape-track::before {
5215 content: counter(tape) ".";
5216 opacity: .55;
5217 min-width: 1.4em;
5218 font-variant-numeric: tabular-nums;
5219}
5220.tape-track-title { flex: 1; }
5221.tape-track-dur { opacity: .6; font-variant-numeric: tabular-nums; }
5222.tape-track.is-current {
5223 opacity: 1;
5224 background: color-mix(in srgb, var(--accent) 12%, transparent);
5225}
5226
5227
5228/* ── De speler in bandmodus ────────────────────────────────────────────
5229 Zelfde speler, ander gedrag: op een cassette kies je geen nummer. De lijst
5230 blijft staan zodat je ziet wat erop staat, maar zonder de opmaak die belooft
5231 dat je erin kunt prikken. */
5232.audio-sheet-queue-list.is-tape .audio-sheet-queue-item {
5233 cursor: default;
5234 pointer-events: none;
5235}
5236.audio-sheet-queue-list.is-tape .audio-sheet-queue-item:hover {
5237 background: none;
5238}
5239/* Het lopende nummer blijft wel gemarkeerd -- dat is de enige plek waar je op
5240 een bandje leest waar je bent. */
5241.audio-sheet-queue-list.is-tape .audio-sheet-queue-item.is-current {
5242 opacity: 1;
5243}
5244/* Spoelknoppen mogen niet als "volgende" voelen: vasthouden hoort geen tekst te
5245 selecteren of de pagina te laten zoomen. */
5246.audio-btn.is-wind {
5247 touch-action: manipulation;
5248 user-select: none;
5249}
Note: See TracBrowser for help on using the repository browser.