source: Klonkt/src/assets/css/style.css@ aaa700e

main
Last change on this file since aaa700e was aaa700e, checked in by Robin <roboburr@…>, 2 weeks ago

Lezen op mobiel: proef met het native snappen, achter een vlag

De vraag die deze proef beantwoordt is er precies een: houdt de browser zich
aan scroll-snap-stop: always? Die eigenschap zegt dat een uitloop nooit over
een snappunt heen mag vliegen, en dat is woord voor woord de regel van 24-8
(voorbijgescrolld of niet, je landt op het target) maar dan uitgevoerd door
het toestel met zijn eigen momentumcurve.

Dat overvliegen was destijds de reden om Lenis op mobiel erbij te halen, en
de eigenschap is toen nooit geprobeerd: hij stond op normal, en dan ook nog
op .read-post, een element dat door scroll-snap-align:none helemaal geen
snappunt is. Het ankertje dat het snappunt WEL is had hem niet.

Achter ?nativesnap=1 (en =0 om terug te gaan), onthouden in sessionStorage
omdat een htmx-navigatie de queryreeks weggooit. Staat de vlag aan, dan
blijft Lenis van de telefoon af en snapt de browser zelf. Standaard verandert
er dus niets, voor niemand, en de Lenis-kant blijft heel tot de proef gewonnen
is.

Een detail dat anders stil zou blijven staan: zetSnappen() zette het snappen
"aan" door de inline stijl leeg te maken, oftewel terugvallen op de
stylesheet. Die zet het op mobiel juist uit. Zonder Lenis zou het snappen
daarmee nooit aangaan; nu staat er expliciet 'y proximity'. Op desktop is dat
exact wat de stylesheet daar al geeft.

MOD_V 60 en style.css v=109, want beide bestanden wijzigen.

  • Property mode set to 100644
File size: 179.2 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/* Snappen doet de SCROLLCONTAINER, en dat is het document -- niet .read-stream,
2460 die heeft overflow: visible en scrollt zelf niet. Proximity en geen mandatory:
2461 in een bericht dat langer is dan het scherm moet je rustig door kunnen lezen. */
2462html:has(body.on-home[data-feed-view="reader"]) {
2463 scroll-snap-type: y proximity;
2464 /* GEEN scroll-behavior:smooth hier. Dat maakte elke beweging op deze
2465 scroller traag, ook het snappen zelf -- en een snap moet vlot zijn
2466 (Robin, 20-8: "vlot maar geanimeerd, mag niet te lang duren"). De browser
2467 animeert een snap uit zichzelf al, en korter dan zijn smooth-scroll.
2468 De terug-naar-boven-knop vraagt zijn animatie expliciet aan in
2469 mod/read.js, dus die blijft vloeiend. */
2470}
2471/* En de browser niet óók laten compenseren als Load more er berichten bij zet:
2472 Chrome en Firefox schuiven bij invoegen zelf mee (scroll anchoring), en dat
2473 vecht met het snappen. */
2474html:has(body.on-home[data-feed-view="reader"]),
2475.feed-reader .read-stream { overflow-anchor: none; }
2476
2477.feed-reader .read-post {
2478 /* Snap naar de BOVENkant van een bericht, zacht (proximity op html) en nooit
2479 dwingend (stop: normal). Een korte post vult het scherm, zodat hij niet als
2480 snipper tussen twee lijnen hangt.
2481 De uitzondering staat NIET hier maar in mod/read.js: een bericht dat langer
2482 is dan het scherm krijgt scroll-snap-align:none, anders trekt de browser je
2483 tijdens het lezen terug naar zijn bovenrand en lijkt het of het scrollen
2484 stopt. "Past het op het scherm?" is een vraag over gemeten hoogte, en die
2485 kan CSS niet stellen. */
2486 /* Je LANDT op de bovenkant van een bericht -- dat is wat "snap to post"
2487 hoort te betekenen. (align:end probeerde ik ook; dan land je op de
2488 ONDERKANT van het vorige bericht, en bij een lang bericht zie je dus zijn
2489 laatste schermvol in plaats van de kop van het volgende.)
2490 Wanneer die bovenkant een snappunt IS, staat in mod/read.js: alleen zolang
2491 je er nog niet voorbij bent. Eenmaal in een bericht snapt er niets meer,
2492 ook niet naar zijn eigen bovenrand -- anders trekt proximity je terug
2493 zodra je een paar regels verder leest. */
2494 /* Het snappunt zit op .read-anker hierna, niet hier. Zie de toelichting
2495 daar: een snapgebied groter dan het scherm heeft een BEREIK aan geldige
2496 posities, en de bovenste daarvan zet het volgende bericht op de onderrand. */
2497 scroll-snap-align: none;
2498 scroll-snap-stop: normal;
2499 /* GEEN min-height meer. Die rekte elk bericht tot een vol scherm, en bij een
2500 kort bericht leverde dat een halve pagina leegte op onder "Replies and
2501 reactions" -- je zat naar niets te kijken en moest verder scrollen om te
2502 ontdekken dat er nog iets kwam. Robins vondst (20-8), met een
2503 schermafbeelding erbij.
2504 Zonder die hoogte is de stroom gewoon bericht na bericht: je ziet het
2505 volgende meteen onder het vorige beginnen. Het snappen doet nog steeds
2506 zijn werk aan de grens -- dat hangt aan scroll-snap-align en niet aan de
2507 hoogte -- dus de streep lijnt nog altijd uit met de bovenkant van het
2508 volgende bericht. */
2509 max-width: 46rem;
2510 margin: 0 auto;
2511 /* De onderrand loopt achter de vaste tabbalk door; deze padding houdt de
2512 TEKST erboven. De BOVENmarge is verdubbeld (Robin, 20-8: 2rem -> 4rem):
2513 na een snap staat de scheidingslijn op de bovenrand, en de tekst
2514 daaronder krijgt meer lucht. MOET GELIJK BLIJVEN aan scroll-margin-top
2515 op .read-anker hieronder -- lopen die uit elkaar, dan landt de lijn niet
2516 meer precies op de bovenrand. */
2517 padding: 4rem 1.2rem 4rem;
2518 box-sizing: border-box;
2519}
2520/* ONDERIN GEBEURT ER NIETS (Robin, 20-8: "enkel de bovenkant mag snappen, niet
2521 de onderkant; onderin mag er niks met de zone gebeuren").
2522 Het laatste bericht heeft een snappunt dat de browser NIET KAN BEREIKEN: er
2523 zit niet genoeg pagina onder om zijn bovenkant naar de bovenrand te brengen.
2524 Gemeten op dev: snappunt 2548, maar de scroll gaat maar tot 2356. De spec
2525 laat de browser dan naar de dichtstbijzijnde BEREIKBARE positie snappen, en
2526 dat is de bodem -- waardoor het laatste bericht ergens middenin het scherm
2527 blijft hangen en het lijkt of er aan de onderkant wordt uitgelijnd.
2528 Zonder snappunt scrolt dat laatste stuk gewoon vrij uit tot het einde. */
2529/* HET SNAPPUNT. Nul hoog, dus precies EEN geldige positie: deze bovenrand aan de
2530 bovenrand van het scherm. Zat de uitlijning op het artikel zelf, dan gold voor
2531 elk bericht dat hoger is dan het scherm een heel BEREIK aan geldige posities
2532 (spec: een snapgebied groter dan de snapport mag overal staan waar het de
2533 snapport nog vult). Gemeten op dev: bericht van 1009px op een scherm van 812
2534 -> geldig van 700 tot 897, en op 867 bleef hij dus gewoon staan. Die bovenste
2535 stand, 897, is de onderkant van dat bericht op de onderrand -- oftewel de
2536 bovenkant van het VOLGENDE bericht op de onderrand. Dat is wat er onderin
2537 leek te snappen, en het kwam van de browser en niet uit onze code. */
2538.feed-reader .read-anker {
2539 display: block;
2540 height: 0;
2541 scroll-snap-align: start;
2542 /* Het anker staat BINNEN de padding van het artikel, dus een padding-top
2543 onder de streep. Zonder deze correctie landt de snap net zoveel te laag
2544 en staat de scheidingslijn boven beeld -- gemeten: 733 waar 700 hoort.
2545 scroll-margin verschuift alleen het SNAPPUNT en niet de opmaak, dus het
2546 anker blijft waar het staat en de uitlijning klopt met de lijn.
2547 GELIJK aan de padding-top van .read-post hierboven; die ging 20-8 van
2548 2rem naar 4rem, dus deze ook. */
2549 scroll-margin-top: 4rem;
2550 /* NOOIT OVER EEN GRENS HEEN VLIEGEN. Dit is de regel van 24-8 -- "ook al
2551 zou het target voorbijgescrolld worden, alsnog op het target landen" --
2552 maar dan als eigenschap in plaats van als JavaScript: de browser mag een
2553 uitloop niet over dit snappunt heen laten gaan, hoe hard je ook veegt.
2554 Een veeg komt daarmee altijd precies EEN bericht verder, nooit twee.
2555 Dat overvliegen was de reden om Lenis op mobiel erbij te halen, en deze
2556 eigenschap is toen nooit geprobeerd: hij stond op `normal`, en dan ook
2557 nog op .read-post hierboven -- een element dat door scroll-snap-align:
2558 none helemaal geen snappunt is, dus daar deed hij sowieso niets.
2559 Zonder native snappen (de gewone stand op mobiel, en op desktop waar
2560 Lenis het overneemt) verandert deze regel niets: er valt dan niets te
2561 stoppen. Hij telt alleen mee tijdens de proef, zie nativeSnap() in
2562 mod/read.js. */
2563 scroll-snap-stop: always;
2564}
2565/* HET LAATSTE BERICHT SNAPT OOK (Robin, 20-8). Dat kon eerst niet: zijn
2566 bovenkant was onbereikbaar -- er zat te weinig pagina onder om hem naar de
2567 bovenrand te brengen (gemeten: snappunt 2548, maxScroll 2356), en de browser
2568 viel dan terug op de dichtstbijzijnde bereikbare positie: de bodem. Het
2569 snappunt weghalen was de vorige oplossing; dit is de echte.
2570 Een volle schermhoogte op het LAATSTE bericht maakt die bovenkant precies
2571 bereikbaar. Korter dan het scherm: het bericht wordt even hoog en het snappunt
2572 valt exact op maxScroll. Langer dan het scherm: dan was het al haalbaar en
2573 doet min-height niets.
2574 100lvh en niet 100svh: op een telefoon met een verdwijnende adresbalk is het
2575 zichtbare scherm soms de GROTE viewport, en dan komt svh net tekort.
2576 Zichtbaar kost dit niets: berichten hebben geen kader, alleen een scheidings-
2577 lijn erboven, dus het laatste bericht krijgt hooguit witruimte eronder. */
2578.feed-reader .read-post:last-child { min-height: 100lvh; }
2579
2580/* ELK BERICHT EEN EIGEN SCHERM, als de site dat wil (Instellingen -> Weergave).
2581 Aan: een kort bericht vult het scherm en je ziet het volgende pas na een snap.
2582 Uit (standaard): de stroom is bericht na bericht, zonder lege ruimte eronder.
2583 Dit stond eerst vast op AAN, is toen vast op UIT gezet omdat een kort bericht
2584 een halve pagina leegte gaf onder "Replies and reactions", en is nu een keuze
2585 -- want welke van de twee prettiger leest hangt af van wat je schrijft. */
2586body[data-reader-pages="1"] .feed-reader .read-post { min-height: 100svh; }
2587/* OP MOBIEL ALTIJD, wat de instelling ook zegt (Robin, 20-8). Op een telefoon is
2588 een bericht per scherm de vorm die werkt -- je veegt van bericht naar bericht.
2589 Op desktop blijft het een keuze, want daar lees je in een kolom naast veel
2590 witruimte en is een doorlopende stroom vaak prettiger. */
2591@media (hover: none) and (pointer: coarse) {
2592 /* Op mobiel draait Lezen in paginamodus (body.is-paged), en die zet hieronder
2593 een vaste hoogte. Deze regel is de terugval voor het moment vóór de module
2594 geladen is, zodat de berichten niet eerst als losse stroom in beeld
2595 springen. */
2596 .feed-reader .read-post { min-height: 100svh; }
2597/* Geen native snap op mobiel: dat doet lenis/snap, met een instelbare vangzone
2598 en duur. Twee snapsystemen op dezelfde scroller vechten. */
2599 html:has(body.on-home[data-feed-view="reader"]) { scroll-snap-type: none; }
2600}
2601
2602/* -------- PAGINAMODUS --------
2603 Elk bericht is een paneel van precies een scherm dat ZELF scrollt; tussen
2604 berichten ga je met knoppen. Daarmee doet het scrollgevoel er niet meer toe:
2605 geen momentum om na te bouwen en geen snap die vangt.
2606 De stroom zelf vangt geen vingers meer -- dat regelt lenis.stop() in
2607 mod/read.js -- en data-lenis-prevent op het paneel houdt de scroll BINNEN een
2608 bericht ongemoeid (die controle staat in Lenis vóór de gestopt-controle).
2609 Geldt waar een bericht een eigen scherm heeft: op mobiel altijd, op desktop
2610 als de site dat zo instelt. */
2611body.is-paged .feed-reader .read-post {
2612 height: 100svh;
2613 min-height: 0;
2614 overflow-y: auto;
2615 overscroll-behavior: contain; /* raakt het einde, dan niet doorschuiven */
2616 scroll-snap-align: none;
2617 /* Ruimte voor de twee balken, zodat er nooit tekst onder een knop verdwijnt.
2618 Het verloop laat een regel wel zacht wegvagen, maar wat je moet KUNNEN
2619 lezen hoort er niet achter te komen -- vandaar padding en niet alleen die
2620 fade. --read-balk staat hieronder bij .read-nav, zodat de twee niet uit
2621 elkaar kunnen lopen. */
2622 padding-top: calc(var(--read-balk) + .5rem);
2623 padding-bottom: calc(var(--read-balk) + .5rem);
2624 /* BOVENAAN beginnen, niet gecentreerd. Ik had de inhoud een ronde lang
2625 verticaal gecentreerd om het gat onder een kort bericht te verdelen, maar
2626 dan hangt zo'n bericht halverwege het scherm en begint niet elk paneel op
2627 dezelfde plek -- dat leest onrustig. Robin ving het (20-8). Elk bericht
2628 begint nu bovenaan, en de ruimte die overblijft valt onderaan. */
2629}
2630body.is-paged .feed-reader .read-anker { scroll-snap-align: none; }
2631
2632/* DE DYNAMIC ISLAND, en met opzet alleen op iOS (Robin, 20-8: "op android is het
2633 een ander verhaal").
2634 In paginamodus vult een bericht precies het scherm, dus de eerste regel komt
2635 bovenaan -- en op een iPhone ligt daar de island of de notch overheen.
2636 env(safe-area-inset-top) geeft daar de vrije ruimte; viewport-fit=cover staat
2637 al in de viewport-meta, dus die waarde is echt.
2638 De @supports-regel is de iOS-Safari-toets: -webkit-touch-callout bestaat alleen
2639 daar. Op Android is env() vaak 0 of betekent het iets anders (camera-uitsparing,
2640 statusbalk die al meegerekend is), en dan zou dit een gat bovenin maken dat er
2641 niet hoort. Vandaar afgeschermd in plaats van env() overal toepassen.
2642 De hoogte krimpt mee, anders duwt de padding het paneel groter dan het scherm
2643 en krijg je alsnog scroll tussen de berichten. */
2644@supports (-webkit-touch-callout: none) {
2645 body.is-paged .feed-reader .read-post {
2646 padding-top: calc(var(--read-balk) + .5rem + env(safe-area-inset-top, 0px));
2647 height: calc(100svh - env(safe-area-inset-top, 0px));
2648 }
2649 /* De bovenbalk houdt zijn knop al vrij van de island via padding-top
2650 hierboven; hier hoeft niets extra's meer. */
2651}
2652
2653/* De knop onderaan een bericht: naar het volgende. */
2654
2655
2656/* Omhoog en omlaag: twee balken, boven en onder, die BLIJVEN staan terwijl je in
2657 een bericht scrolt (Robin, 20-8). Het verloop van paginakleur naar doorzichtig
2658 laat de tekst eronderdoor wegvagen -- zonder dat zou een regel halverwege hard
2659 achter de knop verdwijnen en oogt het als een afgeknipte pagina.
2660 De BALK vangt geen aanrakingen (pointer-events: none), alleen de knop zelf --
2661 anders zou een strook van 5rem boven- en onderaan je scroll opeten. */
2662/* TWEE BALKEN over de volle breedte, boven en onder. Eerder waren dit losse
2663 cirkels (te rommelig naast de tabbalk en de speler) en daarna onzichtbare
2664 tikzones -- maar die werkten niet, omdat de onderste strook onder de
2665 mini-speler en de tabbalk ligt en die de tik opvangen. Een balk met een eigen
2666 z-index kan daar wel overheen.
2667 Slank gehouden: ze eten hooguit 2.6rem aan elke kant, en het paneel houdt daar
2668 al ruimte voor vrij. */
2669:root { --read-balk: 2.6rem; }
2670.read-bar {
2671 position: fixed; left: 0; right: 0; z-index: 1060; /* boven tabbalk (1050) */
2672 display: flex; align-items: center;
2673 /* Pijlen LINKS in de balk, niet gecentreerd (Robin, 20-8). */
2674 justify-content: flex-start;
2675 padding-left: max(1.1rem, env(safe-area-inset-left, 0px));
2676 box-sizing: border-box;
2677 height: var(--read-balk);
2678 border: 0;
2679 border-top: 1px solid transparent;
2680 background: color-mix(in srgb, var(--paper) 90%, var(--ink));
2681 backdrop-filter: blur(14px) saturate(140%);
2682 -webkit-backdrop-filter: blur(14px) saturate(140%);
2683 color: var(--ink);
2684 cursor: pointer;
2685 transition: opacity .15s ease;
2686}
2687.read-bar--top {
2688 top: 0;
2689 height: calc(var(--read-balk) + env(safe-area-inset-top, 0px));
2690 padding-top: env(safe-area-inset-top, 0px);
2691 border-bottom: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
2692}
2693.read-bar--bottom {
2694 /* De waarde komt uit mod/read.js: die meet hoe hoog de tabbalk en de
2695 mini-speler werkelijk staan. Geraden rem-waarden lieten hem zweven -- een
2696 kier boven de speler, of er half achter. Dit is de terugval voor de eerste
2697 frame en voor als de module niet laadt. */
2698 bottom: 0;
2699 border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
2700}
2701.read-bar:active { background: color-mix(in srgb, var(--paper) 78%, var(--ink)); }
2702.read-bar[hidden], body:not(.is-paged) .read-bar { display: none; }
2703
2704/* Een lijn tussen twee berichten: zonder grens is een stroom een lange brij. */
2705.feed-reader .read-post + .read-post { border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent); }
2706
2707/* De cover vult de leeskolom. Eigen regel, niet .post-cover hergebruikt: die
2708 heeft een marge van 2rem die hier tegen de scroll-snap aan duwt.
2709 De hoogtebegrenzing is er met opzet. Een paneel is min-height:100svh en snapt,
2710 dus een staande foto op volle breedte zou de titel en de eerste alinea onder
2711 de vouw duwen -- dan begint elk bericht met een plaat waar je langs moet
2712 scrollen om te zien wat het IS. */
2713.feed-reader .read-cover {
2714 /* Volle kolombreedte: de afbeelding erin neemt die breedte, dus de figure
2715 hoeft niet meer om het beeld te krimpen. */
2716 width: 100%;
2717 margin: 0 0 1.2rem;
2718 border-radius: 10px;
2719 overflow: hidden;
2720}
2721.feed-reader .read-cover img,
2722.feed-reader .read-cover video {
2723 display: block;
2724 /* Neemt de VOLLE kolombreedte, met de verhouding intact (object-fit
2725 weggelaten, dus geen bijsnijden). Een cover die van zichzelf kleiner is
2726 wordt netjes tot de kolom opgeschaald in plaats van als postzegeltje te
2727 blijven staan -- dat was te klein. De hoogtegrens houdt een staande foto
2728 nog binnen de perken zodat titel + eerste alinea boven de vouw blijven;
2729 hij krimpt dan mee in plaats van bijgesneden te worden. */
2730 display: block;
2731 width: 100%;
2732 height: auto;
2733 max-height: 70svh;
2734 object-fit: contain;
2735 /* Een logo met doorzichtige achtergrond zweeft anders op de paginakleur;
2736 dezelfde keuze als bij .post-list-cover in de tijdlijn. */
2737 background-color: #fff;
2738}
2739
2740.feed-reader .read-head { padding-bottom: .5rem; }
2741.feed-reader .read-title { font-size: clamp(1.6rem, 4vw, 2.4rem); line-height: 1.15; margin: 0 0 .3rem; }
2742/* Het pin-icoon voor de titel: meeschalend met de kop, maar duidelijk kleiner --
2743 het is een markering en geen deel van de titel. */
2744.feed-reader .read-pin {
2745 display: inline-block;
2746 vertical-align: baseline;
2747 width: .62em; height: .62em;
2748 margin-right: .35em;
2749 color: var(--ink-muted);
2750}
2751.feed-reader .read-pin svg { width: 100%; height: 100%; display: block; }
2752/* De titel is een link, maar mag er niet als een link uitzien: dit is een
2753 leesscherm, geen index. */
2754.feed-reader .read-title a { color: inherit; text-decoration: none; }
2755.feed-reader .read-title a:hover,
2756.feed-reader .read-title a:focus-visible { text-decoration: underline; }
2757.feed-reader .read-when { font-size: .85rem; opacity: .6; margin: 0; }
2758
2759.feed-reader .read-foot { margin-top: 2rem; }
2760.feed-reader .read-open {
2761 display: inline-block; font-size: .9rem; opacity: .75;
2762 text-decoration: none; border-bottom: 1px solid currentColor; padding-bottom: 1px;
2763}
2764.feed-reader .read-open:hover,
2765.feed-reader .read-open:focus-visible { opacity: 1; }
2766
2767/* Terug naar boven. Rechtsonder, want daar zit de duim -- en boven de tabbalk,
2768 die op mobiel 1050 is en anders overheen zou vallen. Onzichtbaar tot je ver
2769 genoeg bent, zodat hij niet meteen in de weg staat bij het eerste bericht. */
2770.read-top {
2771 position: fixed;
2772 right: max(1rem, env(safe-area-inset-right, 0));
2773 bottom: calc(env(safe-area-inset-bottom, 0) + 4.75rem);
2774 z-index: 1040;
2775 display: grid;
2776 place-items: center;
2777 width: 44px;
2778 height: 44px; /* 44px = de kleinste maat die een duim betrouwbaar raakt */
2779 border-radius: 999px;
2780 border: 1px solid var(--rule);
2781 background: color-mix(in srgb, var(--paper) 88%, transparent);
2782 backdrop-filter: blur(14px) saturate(140%);
2783 -webkit-backdrop-filter: blur(14px) saturate(140%);
2784 color: var(--ink);
2785 box-shadow: 0 2px 12px color-mix(in srgb, var(--ink) 12%, transparent);
2786 cursor: pointer;
2787 opacity: 0;
2788 pointer-events: none;
2789 transition: opacity .18s ease, transform .18s ease;
2790 transform: translateY(6px);
2791}
2792.read-top.is-zichtbaar { opacity: 1; pointer-events: auto; transform: none; }
2793/* Buiten de leesweergave bestaat hij niet -- home.ejs rendert alle drie de
2794 secties, dus zonder deze regel zweeft hij ook boven Tijdlijn en Grid. */
2795body:not([data-feed-view="reader"]) .read-top,
2796body:not(.on-home) .read-top { display: none; }
2797/* Op desktop is er geen tabbalk, dus mag hij lager staan. */
2798@media (min-width: 768px) {
2799 .read-top { bottom: 1.5rem; right: 1.5rem; }
2800}
2801/* Staat de mini-speler onderin, dan wijkt de knop daarvoor -- anders komt hij er
2802 bovenop te liggen (Robins schermafbeelding, 20-8). body.has-audio-player wordt
2803 door audio-player.js gezet zodra de speler zichtbaar is, en weer weggehaald
2804 als hij verdwijnt, dus dit volgt vanzelf.
2805 Op mobiel staat de speler BOVEN de tabbalk gestapeld, dus daar komt zijn
2806 hoogte bovenop de ruimte die er al gereserveerd was. */
2807body.has-audio-player .read-top { bottom: calc(env(safe-area-inset-bottom, 0) + 4.75rem + 4.25rem); }
2808@media (min-width: 768px) {
2809 body.has-audio-player .read-top { bottom: calc(1.5rem + 4.25rem); }
2810}
2811@media (prefers-reduced-motion: reduce) {
2812 .read-top { transition: none; transform: none; }
2813}
2814
2815@media (prefers-reduced-motion: reduce) {
2816 html:has(body.on-home[data-feed-view="reader"]) {
2817 scroll-snap-type: none;
2818 scroll-behavior: auto;
2819 }
2820}
2821
2822/* -------- GRID VIEW TILES (Instagram-stijl) -------- */
2823.feed-grid {
2824 padding: 1rem 0 2rem;
2825}
2826.grid-tiles {
2827 display: grid;
2828 gap: 4px;
2829 grid-template-columns: repeat(2, 1fr);
2830}
2831.grid-tile {
2832 position: relative;
2833 display: block;
2834 aspect-ratio: 1;
2835 background-size: cover;
2836 background-position: center;
2837 background-color: var(--paper-2);
2838 overflow: hidden;
2839 text-decoration: none;
2840 color: var(--ink);
2841 transition: transform .2s;
2842 cursor: pointer;
2843 -webkit-tap-highlight-color: rgba(0,0,0,.1);
2844}
2845
2846/* Transparent covers (PNG logos etc.) sit on white so they don't blend into a dark
2847 theme. The white is on the IMG, so the tile's accent-gradient placeholder shows
2848 while the image is still loading. */
2849.post-cover img,
2850.post-list-cover img,
2851.grid-tile-img { background-color: #fff; }
2852/* Accent-gradient placeholder shown while the cover image loads (the image fades in
2853 over it), instead of a white flash on cover tiles. */
2854.grid-tile:not(.grid-tile-gradient) {
2855 background-image: linear-gradient(135deg,
2856 color-mix(in srgb, var(--accent, #888) 22%, var(--paper-2, var(--paper))) 0%,
2857 color-mix(in srgb, var(--accent, #888) 6%, var(--paper-2, var(--paper))) 100%);
2858}
2859/* Cover as a real <img> (not background-image): browsers downsample <img> with the
2860 high-quality image pipeline, so high-res line-art covers don't go jagged when shrunk.
2861 Fades in on load (.is-loading removed) so the placeholder shows meanwhile. */
2862.grid-tile-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; transition: opacity .4s ease; }
2863.grid-tile-img.is-loading { opacity: 0; }
2864/* Audio tile (Shaer, Robins vraag 30-7): the waveform PNG is white on
2865 transparent and floats OVER the tile's own hue gradient, so every audio
2866 post keeps its color and gets its shape. Wave and title each get their
2867 own band (wave upper, title lower): centering both stacked them right
2868 through each other (Robins schermafdruk). */
2869.grid-tile-img.grid-tile-wave {
2870 background-color: transparent;
2871 object-fit: contain;
2872 /* explicit box, not auto: an absolutely positioned img falls back to its
2873 intrinsic size when width/height are auto, collapsing the band */
2874 top: 8%; left: 8%; right: auto; bottom: auto;
2875 width: 84%; height: 46%;
2876 opacity: .85;
2877}
2878.grid-tile-audio .grid-tile-title { position: absolute; left: 10%; right: 10%; top: 60%; }
2879.grid-tile-audio .grid-tile-gradient-src { position: absolute; left: 10%; right: 10%; top: 76%; }
2880/* Cover videos are decorative, not players: let clicks + right-click pass through to the
2881 tile link so you get the normal link menu (not the browser's <video> menu/controls).
2882 Autoplay is independent of pointer-events, so the video still plays. */
2883video.grid-tile-img,
2884.post-list-cover video,
2885.post-cover video { pointer-events: none; }
2886.grid-tile:hover { transform: scale(0.98); }
2887.grid-tile-overlay {
2888 position: absolute;
2889 inset: 0;
2890 padding: .6rem;
2891 display: flex;
2892 flex-direction: column;
2893 justify-content: flex-end;
2894 align-items: flex-start;
2895 background: linear-gradient(to top, rgba(0,0,0,.65) 0%, rgba(0,0,0,0) 50%);
2896 opacity: 0;
2897 transition: opacity .2s;
2898 pointer-events: none; /* klik moet naar de <a> eronder gaan, niet naar de overlay */
2899}
2900/* Bron-site (cirkel-feed): toont bij hover "van <site>" onder de titel. */
2901/* Source label: small text that wraps to 2 lines if needed, with a big chain icon
2902 beside it (the text was 1.44rem — bigger than the title — and overflowed on mobile). */
2903.grid-tile-overlay-src {
2904 color: #fff; opacity: .85;
2905 font-size: .72rem; line-height: 1.2; margin-top: .25rem;
2906 display: flex; align-items: center; gap: .3rem; width: 100%;
2907}
2908.grid-tile-overlay-src::before { content: "⛓"; font-size: 1.3rem; line-height: 1; flex: 0 0 auto; }
2909.grid-tile-gradient-src {
2910 margin-top: .35rem; font-size: .72rem; line-height: 1.2; opacity: .85;
2911 color: var(--ink-soft);
2912 display: flex; align-items: center; gap: .3rem; width: 100%;
2913}
2914.grid-tile-gradient-src::before { content: "⛓"; font-size: 1.3rem; line-height: 1; flex: 0 0 auto; }
2915.grid-tile-overlay strong {
2916 color: #fff;
2917 font-family: var(--font-display);
2918 font-size: .9rem;
2919 font-weight: 500;
2920 line-height: 1.15;
2921 display: -webkit-box;
2922 -webkit-line-clamp: 3;
2923 -webkit-box-orient: vertical;
2924 overflow: hidden;
2925}
2926@media (hover: hover) {
2927 .grid-tile:hover .grid-tile-overlay { opacity: 1; }
2928}
2929@media (hover: none) {
2930 /* Op touch-devices altijd overlay tonen zodat titels zichtbaar zijn */
2931 .grid-tile-overlay { opacity: 1; background: linear-gradient(to top, rgba(0,0,0,.7) 0%, rgba(0,0,0,0) 70%); }
2932}
2933
2934.grid-tile-gradient {
2935 background-image: none;
2936 background-color: hsl(var(--tile-hue, 30), 40%, 85%);
2937 background-image: linear-gradient(135deg, hsl(var(--tile-hue, 30), 45%, 88%), hsl(calc(var(--tile-hue, 30) + 30), 35%, 75%));
2938 display: flex;
2939 flex-direction: column;
2940 align-items: center;
2941 justify-content: center;
2942 padding: 1rem;
2943 text-align: center;
2944}
2945[data-theme="dark"] .grid-tile-gradient,
2946.grid-tile-gradient[data-theme="dark"] {
2947 background-image: linear-gradient(135deg, hsl(var(--tile-hue, 30), 30%, 18%), hsl(calc(var(--tile-hue, 30) + 30), 25%, 12%));
2948}
2949.grid-tile-title {
2950 font-family: var(--font-display);
2951 font-size: 1rem;
2952 font-weight: 500;
2953 color: var(--ink);
2954 line-height: 1.2;
2955 display: -webkit-box;
2956 -webkit-line-clamp: 4;
2957 -webkit-box-orient: vertical;
2958 overflow: hidden;
2959 z-index: 1;
2960 /* Federated link posts can have a long bare URL as their title — break it so it wraps
2961 inside the tile and clamps cleanly instead of overflowing past both edges. */
2962 max-width: 100%;
2963 overflow-wrap: anywhere;
2964 word-break: break-word;
2965 hyphens: auto;
2966}
2967
2968.grid-tile-pin {
2969 position: absolute;
2970 top: .5rem;
2971 right: .5rem;
2972 display: inline-flex;
2973 align-items: center;
2974 gap: .25rem;
2975 padding: .2rem .55rem;
2976 background: var(--accent);
2977 color: #fff;
2978 border-radius: 999px;
2979 font-family: var(--font-ui);
2980 font-size: .65rem;
2981 font-weight: 700;
2982 text-transform: uppercase;
2983 letter-spacing: .06em;
2984 z-index: 3;
2985 box-shadow: 0 2px 6px rgba(0,0,0,.3);
2986 pointer-events: none;
2987}
2988.grid-tile-pin svg {
2989 width: .95em;
2990 height: .95em;
2991 margin-top: -1px;
2992}
2993/* Pinned/boosted tile: accent ring. An outline on the tile is painted UNDER the absolute
2994 cover <img>, so draw the ring as a pseudo-element overlay ABOVE the image (z-index above
2995 the img, below the corner badges + nsfw veil). */
2996.grid-tile.is-pinned { position: relative; z-index: 1; }
2997.grid-tile.is-pinned::after {
2998 content: '';
2999 position: absolute;
3000 inset: 0;
3001 border: 3px solid var(--accent);
3002 border-radius: inherit;
3003 pointer-events: none;
3004 z-index: 1;
3005}
3006.grid-tile-type {
3007 position: absolute;
3008 top: .4rem;
3009 left: .4rem;
3010 padding: .15rem .5rem;
3011 background: rgba(0,0,0,.55);
3012 color: #fff;
3013 border-radius: 999px;
3014 font-family: var(--font-ui);
3015 font-size: .6rem;
3016 font-weight: 600;
3017 text-transform: uppercase;
3018 letter-spacing: .05em;
3019 z-index: 2;
3020}
3021
3022@media (min-width: 600px) {
3023 .grid-tiles { grid-template-columns: repeat(3, 1fr); }
3024}
3025@media (min-width: 900px) {
3026 .grid-tiles { grid-template-columns: repeat(3, 1fr); gap: 8px; }
3027}
3028
3029/* -------- POST-TYPE SPECIFIEKE LAYOUTS -------- */
3030
3031/* Foto-post: beeld prominent, geen titel bovenaan, caption onder */
3032.feed-timeline[data-post-type="foto"] .post-title {
3033 font-size: 1.1rem;
3034 color: var(--ink-muted);
3035 font-weight: 400;
3036}
3037.feed-timeline[data-post-type="foto"] .post-cover {
3038 margin: 0 -1rem 1rem;
3039 border-radius: 0;
3040}
3041.feed-timeline[data-post-type="foto"] .post-cover img {
3042 width: 100%;
3043 border-radius: 0;
3044}
3045
3046/* Post-caption onder de cover */
3047.post-caption {
3048 font-family: var(--font-body);
3049 font-size: 1rem;
3050 color: var(--ink);
3051 line-height: 1.5;
3052 margin: 0 0 1.25rem;
3053 padding: 0 0 1rem;
3054 border-bottom: 1px solid var(--rule);
3055 white-space: pre-wrap;
3056}
3057.feed-timeline[data-post-type="foto"] .post-caption {
3058 font-size: 1.05rem;
3059 margin-top: -.5rem;
3060 padding-bottom: 1.25rem;
3061}
3062
3063/* Audio/video posts — type-chip helder, content eronder zoals normaal */
3064.feed-timeline[data-post-type="audio"] .post-title,
3065.feed-timeline[data-post-type="video"] .post-title {
3066 font-size: 1.5rem;
3067}
3068
3069@media (min-width: 768px) {
3070 .feed-timeline[data-post-type="foto"] .post-cover {
3071 margin: 0 0 1.5rem;
3072 border-radius: var(--radius);
3073 }
3074}
3075
3076/* -------- ADMIN EDIT-BUTTON op post-view (alleen zichtbaar voor ingelogde admin) -------- */
3077.post-edit-btn {
3078 position: absolute;
3079 top: 1rem;
3080 right: 1rem;
3081 display: inline-flex;
3082 align-items: center;
3083 justify-content: center;
3084 width: 44px;
3085 height: 44px;
3086 background: var(--paper);
3087 border: 1px solid var(--rule);
3088 border-radius: 50%;
3089 color: var(--ink-muted);
3090 text-decoration: none;
3091 box-shadow: 0 2px 8px rgba(0,0,0,.08);
3092 transition: background .15s, color .15s, transform .15s, border-color .15s;
3093 z-index: 10;
3094}
3095.post-edit-btn:hover {
3096 background: var(--accent);
3097 border-color: var(--accent);
3098 color: var(--paper);
3099 transform: scale(1.05);
3100}
3101.post-edit-btn .icon {
3102 width: 1.1rem;
3103 height: 1.1rem;
3104}
3105/* Zorg dat de article relative is zodat absolute positionering werkt */
3106.feed-timeline { position: relative; }
3107
3108@media (min-width: 768px) {
3109 .post-edit-btn {
3110 top: 1.5rem;
3111 right: 1.5rem;
3112 }
3113}
3114
3115/* ============================================================
3116 v8.0 — ACCENT SPAN + POST-TABLE (published posts)
3117 ============================================================ */
3118.post span.accent,
3119.post-content span.accent,
3120article span.accent,
3121page-container span.accent {
3122 color: var(--accent);
3123 font-weight: 500;
3124}
3125
3126.post-table,
3127article table.post-table {
3128 width: 100%;
3129 max-width: 100%;
3130 border-collapse: collapse;
3131 margin: 1.25rem 0;
3132 font-size: .95rem;
3133 display: block;
3134 overflow-x: auto;
3135 -webkit-overflow-scrolling: touch;
3136}
3137.post-table thead { background: color-mix(in srgb, var(--accent) 10%, transparent); }
3138.post-table th,
3139.post-table td {
3140 padding: .6rem .8rem;
3141 text-align: left;
3142 border: 1px solid var(--rule);
3143 vertical-align: top;
3144}
3145.post-table th {
3146 font-family: var(--font-ui);
3147 font-weight: 600;
3148 font-size: .88rem;
3149 color: var(--ink);
3150 letter-spacing: .02em;
3151}
3152.post-table tbody tr:nth-child(even) {
3153 background: color-mix(in srgb, var(--ink) 3%, transparent);
3154}
3155@media (min-width: 700px) {
3156 .post-table { display: table; overflow: visible; }
3157}
3158
3159/* ============================================================
3160 v8.0 fix — grid-cols picker (desktop only) + grid col overrides
3161 ============================================================ */
3162
3163/* Switcher-wrap bevat nu ook de cols-picker; layout side-by-side */
3164.view-switcher-wrap .container {
3165 display: flex;
3166 align-items: center;
3167 justify-content: center;
3168 gap: 1rem;
3169 flex-wrap: wrap;
3170}
3171
3172/* Solo / Cirkel scope-switcher (alleen in circle-modus) — segmented toggle, centraal.
3173 Hoge specificiteit (+ !important op tekst) zodat 'ie globale link-stijlen wint. */
3174.view-switcher-wrap .feed-scope {
3175 display: inline-flex;
3176 background: var(--paper-2);
3177 border: 1px solid var(--rule);
3178 border-radius: 999px;
3179 padding: 3px;
3180}
3181.view-switcher-wrap .feed-scope-btn {
3182 display: inline-flex;
3183 align-items: center;
3184 padding: .32rem .95rem;
3185 min-height: 30px;
3186 border: none !important;
3187 border-radius: 999px !important;
3188 background: transparent !important;
3189 color: var(--ink-muted) !important;
3190 font-family: var(--font-ui);
3191 font-size: .82rem;
3192 font-weight: 500 !important;
3193 text-decoration: none !important;
3194 cursor: pointer;
3195 transition: all .15s;
3196}
3197.view-switcher-wrap .feed-scope-btn:hover { color: var(--ink) !important; }
3198.view-switcher-wrap .feed-scope-btn.is-active {
3199 background: var(--ink) !important;
3200 color: var(--paper) !important;
3201}
3202
3203.grid-cols-picker {
3204 display: none; /* default hidden — desktop + grid-active maakt 'm zichtbaar */
3205 gap: 2px;
3206 padding: 0;
3207}
3208.grid-cols-btn {
3209 display: inline-flex;
3210 align-items: center;
3211 justify-content: center;
3212 min-width: 36px;
3213 height: 32px;
3214 padding: 0 .6rem;
3215 background: transparent;
3216 border: 1px solid var(--rule);
3217 color: var(--ink-muted);
3218 font-family: var(--font-ui);
3219 font-size: .85rem;
3220 font-weight: 500;
3221 cursor: pointer;
3222 transition: all .12s;
3223}
3224.grid-cols-btn:first-child { border-radius: 999px 0 0 999px; }
3225.grid-cols-btn:last-child { border-radius: 0 999px 999px 0; }
3226.grid-cols-btn:not(:first-child):not(:last-child) { border-left: none; border-right: none; }
3227.grid-cols-btn:hover { color: var(--ink); }
3228/* Active state puur via body[data-grid-cols] — geen is-active-class meer. */
3229body[data-grid-cols="2"] .grid-cols-btn[data-cols="2"],
3230body[data-grid-cols="3"] .grid-cols-btn[data-cols="3"],
3231body[data-grid-cols="4"] .grid-cols-btn[data-cols="4"] {
3232 background: var(--ink);
3233 color: var(--paper);
3234 border-color: var(--ink);
3235}
3236
3237/* Desktop + grid-view → toon cols-picker (op de home-feed; niet op archief) */
3238@media (min-width: 600px) {
3239 body.on-home[data-feed-view="grid"] .grid-cols-picker {
3240 display: inline-flex;
3241 }
3242}
3243
3244/* Kolom-overrides op desktop. Mobiel negeert dit (staat vast op 2). Geldt op ELKE
3245 feed met grid (home én cirkel/tag/type/user) — scope op .feed-grid .grid-tiles. */
3246@media (min-width: 600px) {
3247 body[data-grid-cols="2"] .feed-grid .grid-tiles { grid-template-columns: repeat(2, 1fr); }
3248 body[data-grid-cols="3"] .feed-grid .grid-tiles { grid-template-columns: repeat(3, 1fr); }
3249 body[data-grid-cols="4"] .feed-grid .grid-tiles { grid-template-columns: repeat(4, 1fr); }
3250}
3251
3252/* Gradient-tile: iets meer contrast voor light + dark mode zodat titel altijd leesbaar is */
3253.grid-tile-gradient {
3254 background-image: linear-gradient(135deg,
3255 hsl(var(--tile-hue, 30), 55%, 72%),
3256 hsl(calc(var(--tile-hue, 30) + 40), 45%, 60%));
3257}
3258[data-theme="dark"] .grid-tile-gradient {
3259 background-image: linear-gradient(135deg,
3260 hsl(var(--tile-hue, 30), 40%, 30%),
3261 hsl(calc(var(--tile-hue, 30) + 40), 30%, 18%));
3262}
3263.grid-tile-gradient .grid-tile-title {
3264 color: #fff;
3265 text-shadow: 0 1px 3px rgba(0,0,0,.25);
3266}
3267[data-theme="dark"] .grid-tile-gradient .grid-tile-title {
3268 color: #fff;
3269 text-shadow: 0 1px 2px rgba(0,0,0,.5);
3270}
3271
3272/* ============================================================
3273 v8.0.2 — COMPACT DESIGN OVERRIDES
3274 Tighter masthead, padding, margins overall. Profiel-header
3275 collapse/expand blijft zoals 'ie was.
3276 ============================================================ */
3277
3278/* Masthead: compacter */
3279.masthead { padding: calc(.55rem + var(--ios-safe-top, 0px)) 0 .55rem; }
3280.masthead-inner { gap: 1rem; }
3281.site-title { font-size: 1.25rem; }
3282.nav-link, .nav-btn { padding: .4rem .7rem; min-height: 2rem; font-size: .82rem; }
3283
3284/* Main content padding */
3285main { padding-top: 1rem; }
3286.page { padding-block: 1rem 1.75rem; }
3287
3288/* View switcher-bar strakker */
3289.view-switcher { padding: .4rem 0; gap: .3rem; }
3290.view-switch-btn { padding: .4rem .85rem; min-height: 38px; font-size: .8rem; }
3291.grid-cols-btn { min-width: 32px; height: 28px; padding: 0 .5rem; font-size: .8rem; }
3292
3293/* Post header spacing terug */
3294.post-header { margin: 0 0 1.25rem; }
3295.post-title { margin-bottom: .35rem; }
3296.post-meta { font-size: .78rem; }
3297.post-tags { margin-top: .6rem; gap: .3rem; }
3298.post-tag { font-size: .7rem; padding: .15rem .55rem; }
3299
3300/* Article footer + pagination compacter */
3301.article-footer { padding-top: 1.25rem; margin-top: 1.5rem; }
3302.pagination { padding-block: 1.25rem 2rem; margin-top: 1.5rem; }
3303.pagination-item { padding: .75rem .95rem; min-height: 4rem; }
3304.pagination-title { font-size: .95rem; }
3305
3306/* Site footer compacter */
3307.site-footer { padding: 1.25rem 0 calc(1.25rem + env(safe-area-inset-bottom, 0px)); margin-top: 2rem; font-size: .78rem; }
3308.site-footer-inner { gap: .75rem; }
3309
3310/* Profiel-header: full mode iets minder riant (compact mode blijft hetzelfde) */
3311.profile-header { padding: 1rem 0; }
3312.profile-photo { width: 72px; height: 72px; }
3313.profile-photo-fallback { font-size: 1.8rem; }
3314.profile-name { font-size: 1.2rem; }
3315.profile-bio { font-size: .85rem; margin-top: .15rem; }
3316.profile-links { margin-top: .5rem; gap: .4rem; }
3317.profile-links a,
3318.profile-links .profile-fedi-link { width: 32px; height: 32px; }
3319
3320@media (min-width: 768px) {
3321 .profile-header { padding: 1.35rem 0; }
3322 .profile-photo { width: 92px; height: 92px; }
3323 .profile-photo-fallback { font-size: 2.4rem; }
3324 .profile-name { font-size: 1.4rem; }
3325 .profile-bio { font-size: .95rem; }
3326 .profile-header-inner { gap: 1.1rem; }
3327 /* Compact-mode op desktop blijft klein zoals we hadden afgesproken */
3328 .on-post .profile-photo,
3329 .on-special .profile-photo { width: 42px; height: 42px; }
3330}
3331
3332/* Grid-tegels iets strakker gaten */
3333.grid-tiles { gap: 3px; }
3334
3335/* Post-cover margin */
3336.post-cover { margin: 0 0 1rem; }
3337
3338/* Audio player wat lichter */
3339#pcms-audio-bar { padding: .55rem 1rem; }
3340
3341/* v8.1 — pagination CTA + mobile tap target */
3342.pagination-cta {
3343 display: inline-block;
3344 font-family: var(--font-ui);
3345 font-size: .72rem;
3346 font-weight: 600;
3347 color: var(--accent);
3348 margin-top: .35rem;
3349 letter-spacing: .02em;
3350 opacity: .9;
3351}
3352a.pagination-item:hover .pagination-cta { opacity: 1; }
3353
3354/* Mobiel: nog meer tap-feedback + iets prominenter */
3355@media (max-width: 640px) {
3356 a.pagination-item {
3357 padding: 1.1rem 1rem;
3358 min-height: 5.5rem;
3359 border-width: 2px;
3360 }
3361 a.pagination-item:active {
3362 background: color-mix(in srgb, var(--accent) 10%, var(--paper-2));
3363 transform: scale(.98);
3364 }
3365 .pagination-title { font-size: 1rem; }
3366 .pagination-cta { font-size: .75rem; }
3367 .pagination .icon { width: 1.5em; height: 1.5em; }
3368}
3369
3370
3371
3372/* v8.3 / v9.4 — PWA install button (alleen zichtbaar als browser install ondersteunt) */
3373.nav-install-btn[hidden] { display: none; }
3374.nav-install-btn {
3375 position: relative;
3376 display: inline-flex;
3377 align-items: center;
3378 justify-content: center;
3379 gap: .35rem;
3380 background: linear-gradient(135deg,
3381 var(--accent),
3382 color-mix(in srgb, var(--accent) 80%, #000));
3383 color: #fff;
3384 border: none;
3385 border-radius: .65rem;
3386 padding: .45rem .85rem;
3387 font-size: .8rem;
3388 font-weight: 600;
3389 line-height: 1;
3390 cursor: pointer;
3391 overflow: hidden;
3392 isolation: isolate;
3393 box-shadow:
3394 0 1px 0 rgba(255,255,255,.15) inset,
3395 0 2px 6px color-mix(in srgb, var(--accent) 35%, transparent);
3396 transition:
3397 transform .15s cubic-bezier(.4, 0, .2, 1),
3398 box-shadow .2s ease,
3399 filter .15s ease;
3400}
3401.nav-install-btn::before {
3402 content: "";
3403 position: absolute;
3404 top: 0;
3405 left: -100%;
3406 width: 60%;
3407 height: 100%;
3408 background: linear-gradient(110deg,
3409 transparent 30%,
3410 rgba(255,255,255,.22) 50%,
3411 transparent 70%);
3412 z-index: -1;
3413 animation: pcmsInstallShimmer 4s ease-in-out infinite;
3414 pointer-events: none;
3415}
3416.nav-install-btn:hover {
3417 transform: translateY(-1px);
3418 filter: brightness(1.08);
3419 box-shadow:
3420 0 1px 0 rgba(255,255,255,.2) inset,
3421 0 4px 10px color-mix(in srgb, var(--accent) 50%, transparent);
3422}
3423.nav-install-btn:active { transform: translateY(0); }
3424.nav-install-btn .icon {
3425 flex-shrink: 0;
3426 transition: transform .25s cubic-bezier(.34, 1.56, .64, 1);
3427}
3428.nav-install-btn:hover .icon { transform: translateY(-1px); }
3429@media (prefers-reduced-motion: reduce) {
3430 .nav-install-btn::before { animation: none; left: -100%; }
3431 .nav-install-btn:hover { transform: none; }
3432 .nav-install-btn:hover .icon { transform: none; }
3433}
3434/* Als PWA al draait (standalone mode) — verberg install-prompt knop volledig */
3435@media all and (display-mode: standalone) {
3436 #pwa-install-btn { display: none !important; }
3437}
3438
3439/* ========== v8.9.9 — Privé-post indicators ========== */
3440.post-private-banner {
3441 display: flex;
3442 align-items: center;
3443 gap: .6rem;
3444 padding: .75rem 1rem;
3445 background: color-mix(in srgb, #eab308 12%, var(--paper, #fff));
3446 border: 1px solid color-mix(in srgb, #eab308 40%, transparent);
3447 border-radius: 8px;
3448 margin: 0 0 1rem;
3449 font-size: .88rem;
3450 color: var(--ink, #1a1a1a);
3451}
3452.post-private-banner strong {
3453 color: #b45309;
3454}
3455
3456.grid-tile-private {
3457 position: absolute;
3458 top: 8px;
3459 left: 8px;
3460 padding: 3px 8px;
3461 background: rgba(0,0,0,0.7);
3462 color: #fde68a;
3463 font-size: .7rem;
3464 font-weight: 700;
3465 border-radius: 999px;
3466 z-index: 3;
3467 backdrop-filter: blur(4px);
3468 -webkit-backdrop-filter: blur(4px);
3469}
3470
3471/* ========== v8.9.10 — Social chip (inline in post-body) ========== */
3472.social-chip {
3473 display: inline-flex;
3474 align-items: center;
3475 gap: .45rem;
3476 padding: .35rem .75rem;
3477 background: color-mix(in srgb, var(--accent) 10%, transparent);
3478 border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
3479 border-radius: 999px;
3480 color: var(--ink, #1a1a1a);
3481 text-decoration: none;
3482 font-family: var(--font-ui, system-ui);
3483 font-size: .85rem;
3484 font-weight: 600;
3485 line-height: 1.2;
3486 transition: all .15s;
3487 vertical-align: middle;
3488 margin: 0 .1rem;
3489}
3490.social-chip:hover {
3491 background: var(--accent);
3492 color: #fff;
3493 border-color: var(--accent);
3494 text-decoration: none;
3495 transform: translateY(-1px);
3496 box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 40%, transparent);
3497}
3498.social-chip .icon {
3499 width: 16px;
3500 height: 16px;
3501 flex-shrink: 0;
3502}
3503.social-chip-icon-only {
3504 width: 36px;
3505 height: 36px;
3506 padding: 0;
3507 justify-content: center;
3508 border-radius: 50%;
3509}
3510.social-chip-icon-only .icon {
3511 width: 18px;
3512 height: 18px;
3513}
3514
3515/* v8.9.34 — player hidden state + post audio blocks
3516 v9.3 — Hide-rules uitgeschakeld: mini-player blijft permanent zichtbaar
3517 zodat 'ie niet meer verdwijnt bij browser-back of navigatie. */
3518.audio-player.is-hidden {
3519 /* display: none !important; */
3520}
3521body:not(.has-audio-player) #pcms-audio-player.is-hidden {
3522 /* display: none !important; */
3523}
3524
3525/* Post audio track — single track row in een post */
3526/* Lokale audio-track — visueel consistent met .post-audio-external */
3527.post-audio-track {
3528 display: flex;
3529 align-items: center;
3530 gap: .75rem;
3531 padding: .65rem .85rem;
3532 background: var(--paper-2, color-mix(in srgb, var(--ink) 4%, transparent));
3533 border: 1px solid var(--rule, rgba(0,0,0,.08));
3534 border-radius: 10px;
3535 margin: .75rem 0;
3536 transition: background .15s, border-color .15s;
3537}
3538.post-audio-track:hover {
3539 border-color: var(--accent, #c2410c);
3540 background: color-mix(in srgb, var(--accent, #c2410c) 5%, var(--paper-2, transparent));
3541}
3542/* Korte highlight wanneer je via de mini-speler naar deze track springt. */
3543.post-audio-track.pat-flash,
3544.post-album-track-compact.pat-flash {
3545 animation: patFlash 1.6s ease-out;
3546 border-radius: 8px;
3547}
3548/* Blijvende highlight op de track die NU actief is in de speler. */
3549.post-audio-track.pat-playing,
3550.post-album-track-compact.pat-playing {
3551 background: color-mix(in srgb, var(--accent, #c2410c) 12%, var(--paper-2, transparent));
3552 box-shadow: inset 3px 0 0 0 var(--accent, #c2410c);
3553 border-radius: 8px;
3554}
3555@keyframes patFlash {
3556 0%, 30% { border-color: var(--accent, #c2410c); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #c2410c) 35%, transparent); }
3557 100% { box-shadow: 0 0 0 0 transparent; }
3558}
3559.post-audio-track .pat-play {
3560 flex-shrink: 0;
3561 width: 42px;
3562 height: 42px;
3563 border-radius: 8px; /* zelfde rounded-square als pae-icon */
3564 border: none;
3565 background: var(--accent, #c2410c);
3566 color: var(--paper, #fff);
3567 cursor: pointer;
3568 display: inline-flex;
3569 align-items: center;
3570 justify-content: center;
3571 transition: transform .1s, background .15s;
3572 padding: 0;
3573 position: relative;
3574 overflow: hidden;
3575}
3576.post-audio-track .pat-play:hover { transform: scale(1.04); }
3577.post-audio-track .pat-play:active { transform: scale(.96); }
3578.post-audio-track .pat-play svg,
3579.post-audio-track .pat-play .pat-sitelogo {
3580 width: 100%; height: 100%;
3581 position: absolute;
3582 top: 0; left: 0;
3583 transition: opacity .15s;
3584}
3585.post-audio-track .pat-play .icon-play,
3586.post-audio-track .pat-play .icon-pause {
3587 width: 18px; height: 18px;
3588 position: absolute;
3589 top: 50%; left: 50%;
3590 transform: translate(-50%, -50%);
3591 fill: currentColor;
3592 opacity: 0; /* verborgen tot hover of is-current */
3593}
3594/* Site logo: object-cover binnen 42x42 box */
3595.post-audio-track .pat-play .pat-sitelogo {
3596 object-fit: cover;
3597 opacity: 1;
3598}
3599.post-audio-track .pat-play .pat-sitelogo-fallback {
3600 padding: 11px; /* SVG music-note iets kleiner in box */
3601 opacity: 1;
3602}
3603/* Bij hover: site logo faded, play-icoon verschijnt */
3604.post-audio-track:hover .pat-play .pat-sitelogo { opacity: 0.25; }
3605.post-audio-track:hover .pat-play .icon-play { opacity: 1; }
3606/* Bij current + playing: toon pause-icon */
3607.post-audio-track.is-current .pat-play .pat-sitelogo { opacity: 0.25; }
3608.post-audio-track.is-current .pat-play .icon-play { opacity: 1; }
3609.post-audio-track.is-current.is-playing .pat-play .icon-play { opacity: 0; }
3610.post-audio-track.is-current.is-playing .pat-play .icon-pause { opacity: 1; }
3611.post-audio-track.is-current {
3612 border-color: var(--accent, #c2410c);
3613 background: color-mix(in srgb, var(--accent, #c2410c) 10%, transparent);
3614}
3615.post-audio-track .pat-meta {
3616 flex: 1;
3617 min-width: 0;
3618 display: flex;
3619 flex-direction: column;
3620 gap: 2px;
3621}
3622.post-audio-track .pat-title {
3623 display: block;
3624 font-weight: 600;
3625 color: var(--ink);
3626 font-size: .95rem;
3627 overflow: hidden;
3628 text-overflow: ellipsis;
3629 white-space: nowrap;
3630}
3631.post-audio-track .pat-artist {
3632 display: block;
3633 font-size: .78rem;
3634 color: var(--ink-muted, var(--ink));
3635 opacity: .65;
3636 overflow: hidden;
3637 text-overflow: ellipsis;
3638 white-space: nowrap;
3639}
3640.post-audio-track .pat-duration {
3641 flex-shrink: 0;
3642 font-family: ui-monospace, monospace;
3643 font-size: .8rem;
3644 opacity: .55;
3645}
3646
3647/* Album block in een post */
3648.post-album {
3649 position: relative;
3650 border: 1px solid var(--rule, rgba(0,0,0,.1));
3651 border-radius: 14px;
3652 overflow: hidden;
3653 margin: 1.25rem 0;
3654 background: var(--paper-2, color-mix(in srgb, var(--ink) 3%, transparent));
3655}
3656.post-album-header {
3657 display: flex;
3658 gap: .75rem;
3659 padding: .55rem .7rem;
3660 align-items: center;
3661}
3662.post-album-cover {
3663 width: 120px;
3664 height: 120px;
3665 border-radius: 8px;
3666 background-size: cover;
3667 background-position: center;
3668 background-color: var(--rule);
3669 flex-shrink: 0;
3670}
3671/* v9.0 — Collage van track-covers als album-cover leeg is */
3672.post-album-cover-collage {
3673 display: grid;
3674 grid-template-columns: 1fr 1fr;
3675 grid-template-rows: 1fr 1fr;
3676 gap: 0;
3677 overflow: hidden;
3678 background: transparent;
3679}
3680.post-album-cover-collage .post-album-cover-tile {
3681 background-size: cover;
3682 background-position: center;
3683 width: 100%;
3684 height: 100%;
3685}
3686/* Empty state: gradient met muziek-noot */
3687.post-album-cover-empty {
3688 display: flex;
3689 align-items: center;
3690 justify-content: center;
3691 background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 25%, var(--paper-2)), color-mix(in srgb, var(--accent) 10%, var(--paper)));
3692 color: var(--accent, #c2410c);
3693}
3694.post-album-cover-empty svg {
3695 width: 42%;
3696 height: 42%;
3697 opacity: .55;
3698}
3699.post-album-info {
3700 flex: 1;
3701 min-width: 0;
3702}
3703.post-album-label {
3704 text-transform: uppercase;
3705 font-size: .68rem;
3706 letter-spacing: .1em;
3707 opacity: .55;
3708 margin: 0 0 .15rem;
3709 font-weight: 700;
3710}
3711.post-album-title {
3712 font-family: var(--font-display);
3713 font-size: 1.1rem;
3714 font-weight: 600;
3715 margin: 0 0 .1rem;
3716 line-height: 1.15;
3717 letter-spacing: -0.01em;
3718}
3719.post-album-artist {
3720 font-size: .88rem;
3721 opacity: .8;
3722 margin: 0 0 .12rem;
3723 line-height: 1.25;
3724}
3725.post-album-meta {
3726 font-size: .74rem;
3727 opacity: .55;
3728 margin: 0;
3729 line-height: 1.2;
3730}
3731.post-album-playall {
3732 margin-top: .75rem;
3733 padding: .45rem .9rem;
3734 background: var(--accent, #c2410c);
3735 color: var(--paper, #fff);
3736 border: none;
3737 border-radius: 6px;
3738 font-weight: 600;
3739 cursor: pointer;
3740 font-size: .88rem;
3741 display: inline-flex;
3742 align-items: center;
3743 gap: .35rem;
3744}
3745.post-album-playall:hover { filter: brightness(1.08); }
3746
3747/* ============================================================
3748 v9.0 — Album cover clickable button
3749 ============================================================ */
3750.post-album-cover-btn {
3751 position: relative;
3752 background: none;
3753 border: none;
3754 padding: 0;
3755 margin: 0;
3756 cursor: pointer;
3757 flex-shrink: 0;
3758 border-radius: 8px;
3759 overflow: hidden;
3760 display: block;
3761 width: auto;
3762 /* v9.1 — voorkom mobile 300ms double-tap zoom delay, anders zou 2× snel klikken
3763 een zoom triggeren i.p.v. 2× click-events */
3764 touch-action: manipulation;
3765 -webkit-tap-highlight-color: transparent;
3766}
3767.post-album-cover-btn .post-album-cover {
3768 display: block;
3769 width: 76px;
3770 height: 76px;
3771 border-radius: 7px;
3772 background-size: cover;
3773 background-position: center;
3774 background-color: var(--rule);
3775 transition: transform .25s ease;
3776}
3777.post-album-cover-btn:hover .post-album-cover {
3778 transform: scale(1.04);
3779}
3780.post-album-cover-btn .post-album-cover-empty {
3781 background: linear-gradient(135deg,
3782 color-mix(in srgb, var(--accent, #c2410c) 35%, var(--paper)),
3783 color-mix(in srgb, var(--accent, #c2410c) 10%, var(--paper-2)));
3784 display: flex;
3785 align-items: center;
3786 justify-content: center;
3787}
3788.post-album-cover-btn .post-album-cover-empty svg {
3789 width: 28px;
3790 height: 28px;
3791 color: color-mix(in srgb, var(--accent, #c2410c) 60%, transparent);
3792}
3793/* Play overlay op hover */
3794.post-album-cover-play {
3795 position: absolute;
3796 inset: 0;
3797 display: flex;
3798 align-items: center;
3799 justify-content: center;
3800 background: rgba(0, 0, 0, .35);
3801 opacity: 0;
3802 transition: opacity .2s ease;
3803 border-radius: 8px;
3804 pointer-events: none;
3805}
3806.post-album-cover-play svg {
3807 width: 48px;
3808 height: 48px;
3809 color: #fff;
3810 filter: drop-shadow(0 2px 8px rgba(0,0,0,.4));
3811}
3812.post-album-cover-btn:hover .post-album-cover-play,
3813.post-album-cover-btn:focus-visible .post-album-cover-play {
3814 opacity: 1;
3815}
3816
3817/* ============================================================
3818 v1 P52 — Admin actions overlay (edit / delete)
3819 Only rendered when the viewer has god role. Sits top-right
3820 of the card so it doesn't compete with the cover play affordance.
3821 ============================================================ */
3822.post-album-actions {
3823 position: absolute;
3824 top: .4rem;
3825 right: .4rem;
3826 display: flex;
3827 gap: .25rem;
3828 z-index: 2;
3829}
3830.post-album-action {
3831 width: 28px;
3832 height: 28px;
3833 border-radius: 6px;
3834 border: 1px solid var(--rule);
3835 background: color-mix(in srgb, var(--paper) 78%, transparent);
3836 backdrop-filter: blur(8px);
3837 -webkit-backdrop-filter: blur(8px);
3838 color: var(--ink);
3839 display: inline-flex;
3840 align-items: center;
3841 justify-content: center;
3842 cursor: pointer;
3843 text-decoration: none;
3844 opacity: .65;
3845 padding: 0;
3846 transition: opacity .15s ease, transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease;
3847 -webkit-tap-highlight-color: transparent;
3848}
3849.post-album-action:hover,
3850.post-album-action:focus-visible {
3851 opacity: 1;
3852 transform: translateY(-1px);
3853 outline: none;
3854}
3855.post-album-action.is-danger:hover,
3856.post-album-action.is-danger:focus-visible {
3857 background: color-mix(in srgb, #c2410c 18%, var(--paper));
3858 border-color: color-mix(in srgb, #c2410c 45%, var(--rule));
3859 color: #c2410c;
3860}
3861.post-album-action svg {
3862 width: 15px;
3863 height: 15px;
3864 display: block;
3865}
3866
3867/* ============================================================
3868 v9.0 — Compacte tracklist (informatief, niet-klikbaar)
3869 ============================================================ */
3870.post-album-tracks {
3871 list-style: none;
3872 padding: 0;
3873 margin: 0;
3874 border-top: 1px solid var(--rule);
3875}
3876/* v9.4 — Playlist insert modal rows */
3877.pli-row {
3878 display: flex;
3879 align-items: center;
3880 gap: .75rem;
3881 padding: .65rem .5rem;
3882 border-bottom: 1px solid var(--border);
3883}
3884.pli-row:last-child { border-bottom: none; }
3885.pli-row-cover {
3886 width: 44px;
3887 height: 44px;
3888 border-radius: 6px;
3889 background-size: cover;
3890 background-position: center;
3891 background-color: color-mix(in srgb, var(--ink, #000) 8%, transparent);
3892 flex-shrink: 0;
3893 display: flex;
3894 align-items: center;
3895 justify-content: center;
3896 font-size: 1.4rem;
3897}
3898.pli-row-info {
3899 flex: 1;
3900 min-width: 0;
3901}
3902.pli-row-title {
3903 font-weight: 600;
3904 overflow: hidden;
3905 text-overflow: ellipsis;
3906 white-space: nowrap;
3907}
3908.pli-row-meta {
3909 font-size: .82rem;
3910 overflow: hidden;
3911 text-overflow: ellipsis;
3912 white-space: nowrap;
3913}
3914.post-playlist-placeholder {
3915 margin: 1rem 0;
3916 user-select: none;
3917}
3918
3919/* v9.4 — Generic modal classes (gebruikt door playlist-editor en andere modals) */
3920.pcms-modal-backdrop {
3921 position: fixed;
3922 inset: 0;
3923 background: rgba(0, 0, 0, .6);
3924 display: flex;
3925 align-items: center;
3926 justify-content: center;
3927 z-index: 9000;
3928 padding: 1rem;
3929 box-sizing: border-box;
3930 overflow-y: auto;
3931}
3932.pcms-modal {
3933 background: var(--paper, #fff);
3934 color: var(--ink, #000);
3935 border-radius: 12px;
3936 width: 100%;
3937 max-width: 640px;
3938 max-height: calc(100vh - 2rem);
3939 display: flex;
3940 flex-direction: column;
3941 overflow: hidden;
3942 box-shadow: 0 20px 60px rgba(0, 0, 0, .3);
3943}
3944.pcms-modal-header {
3945 display: flex;
3946 align-items: center;
3947 justify-content: space-between;
3948 padding: 1rem 1.25rem;
3949 border-bottom: 1px solid var(--border);
3950 flex-shrink: 0;
3951}
3952.pcms-modal-header h3 { font-size: 1.1rem; }
3953.pcms-modal-close {
3954 background: transparent;
3955 border: none;
3956 font-size: 1.6rem;
3957 line-height: 1;
3958 cursor: pointer;
3959 padding: .25rem .5rem;
3960 color: var(--fg-muted, #666);
3961 border-radius: 4px;
3962}
3963.pcms-modal-close:hover {
3964 background: color-mix(in srgb, var(--ink, #000) 8%, transparent);
3965}
3966.pcms-modal-body {
3967 padding: 1.25rem;
3968 overflow-y: auto;
3969 overscroll-behavior: contain;
3970 flex: 1;
3971 min-height: 0;
3972}
3973.pcms-modal-footer {
3974 display: flex;
3975 gap: .5rem;
3976 justify-content: flex-end;
3977 padding: .75rem 1.25rem;
3978 border-top: 1px solid var(--border);
3979 flex-shrink: 0;
3980 background: color-mix(in srgb, var(--ink, #000) 3%, transparent);
3981}
3982
3983/* On mobile: full-screen modal */
3984@media (max-width: 600px) {
3985 .pcms-modal-backdrop {
3986 padding: 0;
3987 }
3988 .pcms-modal {
3989 border-radius: 0;
3990 max-width: 100%;
3991 max-height: 100vh;
3992 height: 100vh;
3993 }
3994}
3995
3996/* v9.4 — Media → Playlists tab */
3997.playlists-page-header {
3998 display: flex;
3999 flex-wrap: wrap;
4000 gap: 1rem;
4001 align-items: center;
4002 justify-content: space-between;
4003 margin-bottom: 1.25rem;
4004}
4005.playlists-page-header > p { flex: 1; min-width: 200px; }
4006.playlists-grid {
4007 display: grid;
4008 grid-template-columns: 1fr;
4009 gap: .75rem;
4010}
4011@media (min-width: 720px) {
4012 .playlists-grid { grid-template-columns: repeat(2, 1fr); }
4013}
4014@media (min-width: 1100px) {
4015 .playlists-grid { grid-template-columns: repeat(3, 1fr); }
4016}
4017.playlist-card {
4018 display: grid;
4019 grid-template-columns: 64px 1fr auto;
4020 gap: .75rem;
4021 align-items: center;
4022 padding: .75rem;
4023 border: 1px solid var(--border);
4024 border-radius: var(--radius);
4025 background: var(--paper, #fff);
4026}
4027.playlist-card-cover {
4028 width: 64px;
4029 height: 64px;
4030 border-radius: 8px;
4031 overflow: hidden;
4032 background-color: color-mix(in srgb, var(--ink, #000) 6%, transparent);
4033 display: flex;
4034 align-items: center;
4035 justify-content: center;
4036}
4037.playlist-card-cover img {
4038 width: 100%;
4039 height: 100%;
4040 object-fit: cover;
4041}
4042.playlist-card-cover-empty { font-size: 1.6rem; opacity: .5; }
4043.playlist-card-info { min-width: 0; }
4044.playlist-card-title {
4045 font-weight: 600;
4046 overflow: hidden;
4047 text-overflow: ellipsis;
4048 white-space: nowrap;
4049}
4050.playlist-card-artist,
4051.playlist-card-meta {
4052 font-size: .82rem;
4053 overflow: hidden;
4054 text-overflow: ellipsis;
4055 white-space: nowrap;
4056}
4057.playlist-card-actions {
4058 display: flex;
4059 gap: .25rem;
4060 flex-shrink: 0;
4061}
4062
4063/* v9.4 — Playlist editor modal (mobile-friendly, single-column) */
4064.pl-editor {
4065 /* Erft van .pcms-modal */
4066}
4067.pl-editor-meta {
4068 display: grid;
4069 grid-template-columns: 1fr;
4070 gap: .75rem;
4071 margin-bottom: 1rem;
4072}
4073@media (min-width: 560px) {
4074 .pl-editor-meta {
4075 grid-template-columns: 1fr 1fr;
4076 }
4077 .pl-editor-meta .pl-editor-field-full { grid-column: 1 / -1; }
4078}
4079.pl-editor-field {
4080 display: flex;
4081 flex-direction: column;
4082 gap: .25rem;
4083 min-width: 0;
4084}
4085.pl-editor-field span {
4086 font-size: .82rem;
4087 color: var(--fg-muted, #666);
4088 font-weight: 500;
4089}
4090.pl-editor-field input,
4091.pl-editor-field select {
4092 width: 100%;
4093 box-sizing: border-box;
4094 padding: .5rem .65rem;
4095 font-size: .95rem;
4096 border: 1px solid var(--border);
4097 border-radius: 6px;
4098 background: var(--paper, #fff);
4099 color: var(--ink, #000);
4100}
4101.pl-editor-field input:focus,
4102.pl-editor-field select:focus {
4103 outline: 2px solid var(--accent, #5a8a5a);
4104 outline-offset: -1px;
4105}
4106.pl-editor-section-title {
4107 margin: 1.25rem 0 .5rem;
4108 font-size: 1rem;
4109 font-weight: 600;
4110}
4111.pl-tracks-selected {
4112 border: 1px solid var(--border);
4113 border-radius: var(--radius);
4114 background: var(--paper, #fff);
4115 min-height: 60px;
4116 margin-bottom: 1rem;
4117 padding: .25rem;
4118}
4119.pl-tracks-empty {
4120 padding: 1.5rem;
4121 text-align: center;
4122 color: var(--fg-muted, #666);
4123 font-size: .9rem;
4124}
4125.pl-track-row {
4126 display: grid;
4127 grid-template-columns: 32px 36px 1fr auto;
4128 gap: .5rem;
4129 align-items: center;
4130 padding: .5rem;
4131 border-bottom: 1px solid var(--border);
4132 background: var(--paper, #fff);
4133 user-select: none;
4134 touch-action: pan-x;
4135}
4136.pl-track-row:last-child { border-bottom: none; }
4137.pl-track-row.is-dragging {
4138 opacity: .55;
4139 transform: scale(.98);
4140 background: color-mix(in srgb, var(--accent, #5a8a5a) 10%, transparent);
4141 border-radius: 6px;
4142}
4143.pl-track-row.drop-target-above {
4144 box-shadow: inset 0 2px 0 0 var(--accent, #5a8a5a);
4145}
4146.pl-track-row.drop-target-below {
4147 box-shadow: inset 0 -2px 0 0 var(--accent, #5a8a5a);
4148}
4149.pl-track-handle {
4150 cursor: grab;
4151 font-size: 1.2rem;
4152 line-height: 1;
4153 color: var(--fg-muted, #666);
4154 display: flex;
4155 align-items: center;
4156 justify-content: center;
4157 height: 32px;
4158 width: 32px;
4159 border-radius: 4px;
4160 flex-shrink: 0;
4161}
4162.pl-track-handle:active { cursor: grabbing; }
4163.pl-track-handle:hover {
4164 background: color-mix(in srgb, var(--ink, #000) 8%, transparent);
4165}
4166.pl-track-num {
4167 text-align: center;
4168 color: var(--fg-muted, #666);
4169 font-size: .85rem;
4170 width: 36px;
4171}
4172.pl-track-info {
4173 min-width: 0;
4174 overflow: hidden;
4175}
4176.pl-track-title {
4177 font-weight: 500;
4178 overflow: hidden;
4179 text-overflow: ellipsis;
4180 white-space: nowrap;
4181}
4182.pl-track-artist {
4183 font-size: .78rem;
4184 color: var(--fg-muted, #666);
4185 overflow: hidden;
4186 text-overflow: ellipsis;
4187 white-space: nowrap;
4188}
4189.pl-track-remove {
4190 background: transparent;
4191 border: none;
4192 cursor: pointer;
4193 padding: .35rem;
4194 border-radius: 4px;
4195 color: var(--fg-muted, #666);
4196 font-size: 1.2rem;
4197 line-height: 1;
4198}
4199.pl-track-remove:hover {
4200 background: color-mix(in srgb, #c00 12%, transparent);
4201 color: #c00;
4202}
4203
4204/* Available tracks panel */
4205.pl-tracks-available-panel {
4206 border: 1px solid var(--border);
4207 border-radius: var(--radius);
4208 background: var(--paper, #fff);
4209}
4210.pl-tracks-available-search {
4211 padding: .5rem;
4212 border-bottom: 1px solid var(--border);
4213}
4214.pl-tracks-available-search input {
4215 width: 100%;
4216 box-sizing: border-box;
4217}
4218.pl-tracks-available-list {
4219 max-height: 30vh;
4220 overflow-y: auto;
4221}
4222.pl-track-row-available {
4223 display: grid;
4224 grid-template-columns: 1fr auto;
4225 gap: .5rem;
4226 align-items: center;
4227 padding: .5rem;
4228 border-bottom: 1px solid var(--border);
4229 cursor: pointer;
4230}
4231.pl-track-row-available:last-child { border-bottom: none; }
4232.pl-track-row-available:hover {
4233 background: color-mix(in srgb, var(--accent, #5a8a5a) 8%, transparent);
4234}
4235.pl-track-row-available.is-already-added {
4236 opacity: .4;
4237 pointer-events: none;
4238}
4239.pl-track-add-btn {
4240 background: transparent;
4241 border: 1px solid var(--border);
4242 border-radius: 4px;
4243 padding: .25rem .5rem;
4244 cursor: pointer;
4245 font-size: .85rem;
4246 color: var(--accent, #5a8a5a);
4247}
4248
4249/* v9.3 — Tracklist items zijn nu klikbare <button class="pat-row"> binnen <li>.
4250 De LI is puur een container zonder visuele styling; alles gebeurt op de button. */
4251.post-album-track-compact {
4252 padding: 0;
4253 margin: 0;
4254 border-bottom: 1px solid var(--rule);
4255 list-style: none;
4256}
4257.post-album-track-compact:last-child { border-bottom: none; }
4258
4259.pat-row {
4260 display: grid;
4261 grid-template-columns: 32px 1fr auto;
4262 gap: .65rem;
4263 align-items: center;
4264 width: 100%;
4265 padding: .5rem .85rem;
4266 background: transparent;
4267 border: none;
4268 color: inherit;
4269 font: inherit;
4270 font-size: .82rem;
4271 text-align: left;
4272 cursor: pointer;
4273 position: relative;
4274 transition: background .15s ease;
4275}
4276.pat-row:hover {
4277 background: color-mix(in srgb, var(--ink, #000) 6%, transparent);
4278}
4279.pat-row:active { transform: scale(.995); }
4280[data-theme="dark"] .pat-row:hover {
4281 background: color-mix(in srgb, var(--paper, #fff) 10%, transparent);
4282}
4283
4284/* Leader: track-nummer OF cover-thumb (afhankelijk van kind).
4285 v9.3 — pat-leader is altijd 32×32, met overgang voor spinner-vervanging. */
4286.pat-row .pat-num,
4287.pat-row .pat-cover {
4288 width: 32px;
4289 height: 32px;
4290 border-radius: 4px;
4291 flex-shrink: 0;
4292 transition: opacity .25s ease, transform .25s ease;
4293}
4294.pat-row .pat-num {
4295 font-family: ui-monospace, monospace;
4296 font-size: .8rem;
4297 opacity: .5;
4298 display: flex;
4299 align-items: center;
4300 justify-content: center;
4301}
4302.pat-row .pat-cover {
4303 background-size: cover;
4304 background-position: center;
4305 background-color: color-mix(in srgb, var(--ink, #000) 8%, transparent);
4306}
4307
4308/* v9.3 — Loading-spiraal als overlay op de leader-kolom.
4309 pat-num/pat-cover faden uit, spinner faded in via ::before. */
4310.pat-row::before {
4311 content: "";
4312 position: absolute;
4313 left: .85rem; top: 50%;
4314 width: 32px; height: 32px;
4315 transform: translateY(-50%);
4316 color: var(--accent, #c2410c);
4317 background-color: currentColor;
4318 -webkit-mask: url('/assets/spinner.svg') center/26px 26px no-repeat;
4319 mask: url('/assets/spinner.svg') center/26px 26px no-repeat;
4320 opacity: 0;
4321 transition: opacity .25s ease;
4322 pointer-events: none;
4323 animation: pat-spinner-rotate 1.2s linear infinite;
4324 animation-play-state: paused;
4325}
4326.pat-row.is-loading::before {
4327 opacity: 1;
4328 animation-play-state: running;
4329}
4330.pat-row.is-loading .pat-num,
4331.pat-row.is-loading .pat-cover {
4332 opacity: 0;
4333 transform: scale(.85);
4334}
4335@keyframes pat-spinner-rotate {
4336 from { transform: translateY(-50%) rotate(0deg); }
4337 to { transform: translateY(-50%) rotate(-360deg); }
4338}
4339
4340/* Meta-kolom */
4341.pat-row .pat-meta {
4342 display: flex;
4343 flex-direction: column;
4344 min-width: 0;
4345 gap: 1px;
4346 overflow: hidden;
4347}
4348.pat-row .pat-title {
4349 font-weight: 500;
4350 overflow: hidden;
4351 text-overflow: ellipsis;
4352 white-space: nowrap;
4353}
4354.pat-row .pat-artist {
4355 font-size: .72rem;
4356 opacity: .6;
4357 overflow: hidden;
4358 text-overflow: ellipsis;
4359 white-space: nowrap;
4360}
4361.pat-row .pat-duration {
4362 font-family: ui-monospace, monospace;
4363 font-size: .72rem;
4364 opacity: .55;
4365 font-variant-numeric: tabular-nums;
4366 white-space: nowrap;
4367}
4368.pat-row .pat-duration-empty {
4369 opacity: .25;
4370}
4371
4372/* is-current — actieve track in de player */
4373.pat-row.is-current {
4374 background: color-mix(in srgb, var(--accent, #c2410c) 12%, transparent);
4375}
4376.pat-row.is-current .pat-title,
4377.pat-row.is-current .pat-artist { color: var(--accent, #c2410c); }
4378
4379/* v9.3 — Marquee bij actief afspelen: titel scrollt heen-en-weer.
4380 --pat-shift wordt door JS gezet (= overflow in pixels). Bij 0 → geen
4381 beweging want translateX(0) blijft 0.
4382 Snelle cyclus (6s) met korte rust aan beide uiteinden (8%) zodat 't
4383 vlot voelt zonder onleesbaar te zijn. */
4384.pat-row.is-current.is-playing .pat-title {
4385 animation: pat-title-marquee 6s ease-in-out infinite alternate;
4386 text-overflow: clip;
4387 overflow: visible;
4388 will-change: transform;
4389}
4390@keyframes pat-title-marquee {
4391 0%, 8% { transform: translateX(0); }
4392 92%, 100% { transform: translateX(calc(-1 * var(--pat-shift, 0px))); }
4393}
4394
4395/* ============================================================
4396 v9.0 — CSS-migratie voor OUDE opgeslagen albums
4397 Oude posts hebben <li class="post-audio-track"> met play-knoppen + cover-spans.
4398 We renderen die VISUEEL als compact, zonder de HTML aan te passen.
4399 ============================================================ */
4400.post-album-tracks .post-audio-track {
4401 display: grid;
4402 grid-template-columns: 28px 1fr auto;
4403 gap: .75rem;
4404 align-items: center;
4405 padding: .5rem .85rem;
4406 margin: 0;
4407 border: none;
4408 border-bottom: 1px solid var(--rule);
4409 background: transparent;
4410 border-radius: 0;
4411 cursor: default;
4412 font-size: .92rem;
4413}
4414.post-album-tracks .post-audio-track:last-child { border-bottom: none; }
4415/* Verberg de cover-thumb en play-button in OUDE saved posts */
4416.post-album-tracks .post-audio-track .pat-cover,
4417.post-album-tracks .post-audio-track .pat-play {
4418 display: none !important;
4419}
4420.post-album-tracks .post-audio-track .pat-num {
4421 font-family: ui-monospace, monospace;
4422 font-size: .8rem;
4423 opacity: .45;
4424 text-align: center;
4425 width: auto;
4426}
4427.post-album-tracks .post-audio-track .pat-meta {
4428 display: flex;
4429 flex-direction: column;
4430 min-width: 0;
4431 gap: 1px;
4432}
4433.post-album-tracks .post-audio-track .pat-title {
4434 font-weight: 500;
4435 overflow: hidden;
4436 text-overflow: ellipsis;
4437 white-space: nowrap;
4438}
4439.post-album-tracks .post-audio-track .pat-artist {
4440 font-size: .78rem;
4441 opacity: .6;
4442}
4443.post-album-tracks .post-audio-track .pat-duration {
4444 font-family: ui-monospace, monospace;
4445 font-size: .78rem;
4446 opacity: .55;
4447}
4448.post-album-tracks .post-audio-track.is-current {
4449 background: color-mix(in srgb, var(--accent, #c2410c) 10%, transparent);
4450}
4451.post-album-tracks .post-audio-track.is-current .pat-num,
4452.post-album-tracks .post-audio-track.is-current .pat-title {
4453 color: var(--accent);
4454}
4455
4456@media (max-width: 540px) {
4457 .post-album-header { flex-direction: column; }
4458 .post-album-cover-btn .post-album-cover { width: 100%; max-width: 240px; height: auto; aspect-ratio: 1/1; }
4459}
4460@media (max-width: 380px) {
4461 .post-album-header { padding: .75rem; gap: .75rem; }
4462 .post-album-cover-btn .post-album-cover { max-width: 180px; }
4463 .post-album-title { font-size: 1.15rem; }
4464 .post-album-meta { font-size: .8rem; }
4465 .post-album-track-compact,
4466 .post-album-tracks .post-audio-track {
4467 grid-template-columns: 24px 1fr auto;
4468 gap: .5rem;
4469 padding: .4rem .6rem;
4470 font-size: .85rem;
4471 }
4472 .post-album-track-compact .pat-duration,
4473 .post-album-tracks .post-audio-track .pat-duration {
4474 font-size: .72rem;
4475 }
4476 /* Queue items in expand-sheet */
4477 .audio-sheet-queue-item { padding: .4rem .45rem; gap: .55rem; }
4478 .audio-sheet-queue-cover { width: 32px; height: 32px; }
4479 .audio-sheet-queue-title { font-size: .85rem; }
4480 .audio-sheet-queue-artist { font-size: .7rem; }
4481}
4482
4483/* External track (SoundCloud / YouTube / Spotify / etc) — klikbaar maar link-naar-platform */
4484.post-audio-external {
4485 display: flex;
4486 align-items: center;
4487 gap: .75rem;
4488 padding: .65rem .85rem;
4489 background: var(--paper-2, color-mix(in srgb, var(--ink) 4%, transparent));
4490 border: 1px solid var(--rule, rgba(0,0,0,.08));
4491 border-radius: 10px;
4492 margin: .75rem 0;
4493 text-decoration: none;
4494 color: var(--ink);
4495 transition: background .15s, border-color .15s;
4496}
4497.post-audio-external:hover {
4498 border-color: var(--accent);
4499 background: color-mix(in srgb, var(--accent) 5%, var(--paper-2, transparent));
4500}
4501.post-audio-external .pae-icon {
4502 flex-shrink: 0;
4503 width: 42px;
4504 height: 42px;
4505 border-radius: 8px;
4506 display: inline-flex;
4507 align-items: center;
4508 justify-content: center;
4509 background: var(--platform-color, #555);
4510 color: #fff;
4511 font-size: 1.2rem;
4512}
4513.post-audio-external .pae-icon-svg svg {
4514 width: 60%;
4515 height: 60%;
4516 fill: currentColor;
4517}
4518/* Per-platform vult de box net wat anders — brand-specifiek geoptimaliseerd */
4519.post-audio-external[data-platform="soundcloud"] .pae-icon-svg svg { width: 72%; height: auto; }
4520.post-audio-external[data-platform="spotify"] .pae-icon-svg svg { width: 72%; height: 72%; }
4521.post-audio-external[data-platform="youtube"] .pae-icon-svg svg { width: 68%; height: auto; }
4522.post-audio-external[data-platform="bandcamp"] .pae-icon-svg svg { width: 62%; height: auto; }
4523.post-audio-external[data-platform="apple"] .pae-icon-svg svg { width: 58%; height: 58%; }
4524.post-audio-external[data-platform="tidal"] .pae-icon-svg svg { width: 68%; height: auto; }
4525.post-audio-external[data-platform="deezer"] .pae-icon-svg svg { width: 65%; height: auto; }
4526.post-audio-external[data-platform="mixcloud"] .pae-icon-svg svg { width: 75%; height: auto; }
4527.post-audio-external[data-platform="soundcloud"] .pae-icon { background: #ff5500; }
4528.post-audio-external[data-platform="spotify"] .pae-icon { background: #1db954; }
4529.post-audio-external[data-platform="youtube"] .pae-icon { background: #ff0000; }
4530.post-audio-external[data-platform="bandcamp"] .pae-icon { background: #1da0c3; }
4531.post-audio-external[data-platform="apple"] .pae-icon { background: #fa243c; }
4532.post-audio-external[data-platform="tidal"] .pae-icon { background: #000; }
4533.post-audio-external[data-platform="deezer"] .pae-icon { background: #a238ff; }
4534.post-audio-external[data-platform="mixcloud"] .pae-icon { background: #314359; }
4535.post-audio-external[data-platform="other"] .pae-icon { background: #555; }
4536.post-audio-external .pae-meta {
4537 flex: 1;
4538 min-width: 0;
4539 display: flex;
4540 flex-direction: column;
4541 gap: 2px;
4542}
4543.post-audio-external .pae-title {
4544 display: block;
4545 font-weight: 600;
4546 overflow: hidden;
4547 text-overflow: ellipsis;
4548 white-space: nowrap;
4549 color: var(--ink);
4550}
4551.post-audio-external .pae-platform {
4552 display: block;
4553 font-size: .78rem;
4554 opacity: .65;
4555 text-transform: capitalize;
4556}
4557.post-audio-external .pae-go {
4558 flex-shrink: 0;
4559 opacity: .5;
4560 font-size: 1.1rem;
4561}
4562
4563/* =========================================================================
4564 v9.0 — Video player (inline)
4565 ========================================================================= */
4566
4567.post-video-figure {
4568 margin: 1.25rem 0;
4569 max-width: 100%;
4570}
4571
4572.post-video {
4573 display: block;
4574 width: 100%;
4575 max-width: 100%;
4576 height: auto;
4577 border-radius: 10px;
4578 background: #000;
4579 box-shadow: 0 4px 20px rgba(0,0,0,.15);
4580}
4581
4582.post-video-caption {
4583 margin-top: .5rem;
4584 font-size: .88rem;
4585 color: var(--ink-muted, color-mix(in srgb, var(--ink) 60%, transparent));
4586 text-align: center;
4587 line-height: 1.5;
4588}
4589
4590.post-video-caption strong {
4591 display: inline;
4592 font-weight: 600;
4593 color: var(--ink);
4594}
4595
4596@media (max-width: 640px) {
4597 .post-video-figure { margin: .75rem -.5rem; }
4598 .post-video { border-radius: 6px; }
4599}
4600
4601/* v9.3 — Floating "Bewerken" knop voor album-blokken in de post-editor.
4602 Verschijnt bij hover over een .post-album binnen #visual en wordt via JS
4603 geplaatst op de rechterbovenhoek van het blok. Niet in de opgeslagen HTML. */
4604.pcms-album-edit-overlay {
4605 position: absolute;
4606 z-index: 1000;
4607 transform: translateX(-100%);
4608 display: inline-flex;
4609 align-items: center;
4610 gap: .35rem;
4611 padding: .35rem .7rem;
4612 background: var(--accent, #c2410c);
4613 color: #fff;
4614 border: none;
4615 border-radius: 6px;
4616 font-size: .82rem;
4617 font-weight: 600;
4618 cursor: pointer;
4619 box-shadow: 0 4px 12px rgba(0, 0, 0, .18);
4620 transition: transform .12s ease, box-shadow .12s ease;
4621}
4622.pcms-album-edit-overlay:hover {
4623 transform: translateX(-100%) translateY(-1px);
4624 box-shadow: 0 6px 16px rgba(0, 0, 0, .24);
4625}
4626.pcms-album-edit-overlay:active {
4627 transform: translateX(-100%) translateY(0);
4628 box-shadow: 0 2px 6px rgba(0, 0, 0, .18);
4629}
4630
4631/* v9.3 — Drag-and-drop in de album-builder selected-lijst */
4632.ab-selected-row { cursor: default; }
4633.ab-selected-row .ab-drag-handle {
4634 display: inline-flex;
4635 align-items: center;
4636 justify-content: center;
4637 width: 18px;
4638 height: 100%;
4639 color: color-mix(in srgb, currentColor 40%, transparent);
4640 cursor: grab;
4641 user-select: none;
4642 font-size: .9rem;
4643 letter-spacing: -2px;
4644 flex-shrink: 0;
4645}
4646.ab-selected-row:hover .ab-drag-handle {
4647 color: color-mix(in srgb, currentColor 70%, transparent);
4648}
4649.ab-selected-row.is-dragging {
4650 opacity: .35;
4651 cursor: grabbing;
4652}
4653.ab-selected-row.is-dragging .ab-drag-handle { cursor: grabbing; }
4654/* Drop-indicator lijnen boven/onder de target rij */
4655.ab-selected-row.is-drag-target-before {
4656 box-shadow: 0 -2px 0 0 var(--accent, #c2410c) inset, 0 2px 0 0 var(--accent, #c2410c);
4657 /* Gebruik outline voor cross-browser zichtbaarheid */
4658 position: relative;
4659}
4660.ab-selected-row.is-drag-target-before::before {
4661 content: "";
4662 position: absolute;
4663 top: -1px; left: 0; right: 0;
4664 height: 2px;
4665 background: var(--accent, #c2410c);
4666 pointer-events: none;
4667}
4668.ab-selected-row.is-drag-target-after::after {
4669 content: "";
4670 position: absolute;
4671 bottom: -1px; left: 0; right: 0;
4672 height: 2px;
4673 background: var(--accent, #c2410c);
4674 pointer-events: none;
4675}
4676.ab-selected-row.is-drag-target-after { position: relative; }
4677
4678/* v9.3 — Playlist fallback states (missing of empty playlist reference in post) */
4679.post-playlist-missing, .post-playlist-empty {
4680 padding: 1rem;
4681 margin: .75rem 0;
4682 background: color-mix(in srgb, var(--accent, #c2410c) 8%, transparent);
4683 border: 1px dashed color-mix(in srgb, var(--accent, #c2410c) 30%, transparent);
4684 border-radius: 8px;
4685 font-size: .9rem;
4686}
4687.post-playlist-missing em, .post-playlist-empty em {
4688 opacity: .8;
4689}
4690
4691/* v9.3 — Install-app footer-knop */
4692.site-footer-install {
4693 display: flex;
4694 justify-content: center;
4695 padding: 1rem 1rem 1.25rem;
4696 border-top: 1px solid color-mix(in srgb, currentColor 8%, transparent);
4697 margin-top: .5rem;
4698}
4699
4700/* v9.4 — Cooler install-knop: gradient + shimmer + perfect centering.
4701 Twee kleur-stops voor diepte, animated shimmer-streep om aandacht te trekken,
4702 icon perfect verticaal gecentreerd via flex (was: net iets te hoog tov tekst). */
4703.install-app-btn {
4704 position: relative;
4705 display: inline-flex;
4706 align-items: center;
4707 justify-content: center;
4708 gap: .55rem;
4709 padding: .8rem 1.6rem;
4710 background: linear-gradient(135deg,
4711 var(--accent, #c2410c),
4712 color-mix(in srgb, var(--accent, #c2410c) 80%, #000));
4713 color: #fff;
4714 border: none;
4715 border-radius: 999px;
4716 font: inherit;
4717 font-weight: 600;
4718 font-size: .95rem;
4719 line-height: 1;
4720 letter-spacing: .01em;
4721 cursor: pointer;
4722 overflow: hidden;
4723 isolation: isolate;
4724 box-shadow:
4725 0 1px 0 rgba(255,255,255,.15) inset,
4726 0 -2px 0 rgba(0,0,0,.1) inset,
4727 0 4px 14px color-mix(in srgb, var(--accent, #c2410c) 40%, transparent),
4728 0 1px 3px rgba(0,0,0,.18);
4729 transition:
4730 transform .15s cubic-bezier(.4, 0, .2, 1),
4731 box-shadow .2s ease,
4732 filter .15s ease;
4733}
4734.install-app-btn svg {
4735 flex-shrink: 0;
4736 /* subtle bounce-on-hover trigger, separate animation */
4737 transition: transform .25s cubic-bezier(.34, 1.56, .64, 1);
4738}
4739.install-app-btn span {
4740 line-height: 1;
4741}
4742
4743/* Shimmer-streep die langzaam over de knop loopt — "alive" effect */
4744.install-app-btn::before {
4745 content: "";
4746 position: absolute;
4747 top: 0;
4748 left: -100%;
4749 width: 60%;
4750 height: 100%;
4751 background: linear-gradient(110deg,
4752 transparent 30%,
4753 rgba(255,255,255,.25) 50%,
4754 transparent 70%);
4755 z-index: -1;
4756 animation: pcmsInstallShimmer 4s ease-in-out infinite;
4757 pointer-events: none;
4758}
4759@keyframes pcmsInstallShimmer {
4760 0%, 35% { left: -100%; }
4761 65%, 100% { left: 200%; }
4762}
4763
4764.install-app-btn:hover {
4765 transform: translateY(-2px) scale(1.02);
4766 filter: brightness(1.06);
4767 box-shadow:
4768 0 1px 0 rgba(255,255,255,.2) inset,
4769 0 -2px 0 rgba(0,0,0,.1) inset,
4770 0 8px 24px color-mix(in srgb, var(--accent, #c2410c) 55%, transparent),
4771 0 2px 6px rgba(0,0,0,.2);
4772}
4773.install-app-btn:hover svg {
4774 transform: translateY(-2px);
4775}
4776.install-app-btn:active {
4777 transform: translateY(0) scale(.98);
4778 transition-duration: .05s;
4779}
4780.install-app-btn:focus-visible {
4781 outline: 2px solid var(--accent, #c2410c);
4782 outline-offset: 3px;
4783}
4784
4785/* Reduced motion: shimmer uit, alleen statisch */
4786@media (prefers-reduced-motion: reduce) {
4787 .install-app-btn::before { animation: none; left: -100%; }
4788 .install-app-btn:hover { transform: none; }
4789 .install-app-btn:hover svg { transform: none; }
4790}
4791
4792/* Install-instructions modal */
4793.install-modal {
4794 position: fixed;
4795 inset: 0;
4796 z-index: 10000;
4797 display: flex;
4798 align-items: center;
4799 justify-content: center;
4800 padding: 1rem;
4801}
4802.install-modal-backdrop {
4803 position: absolute;
4804 inset: 0;
4805 background: rgba(0,0,0,.55);
4806 backdrop-filter: blur(3px);
4807}
4808.install-modal-panel {
4809 position: relative;
4810 z-index: 1;
4811 background: var(--paper);
4812 color: var(--ink);
4813 border: 1px solid var(--rule);
4814 border-radius: 14px;
4815 padding: 1.5rem;
4816 max-width: 440px;
4817 width: 100%;
4818 max-height: 90vh;
4819 overflow-y: auto;
4820 box-shadow: 0 20px 60px rgba(0,0,0,.4);
4821}
4822.install-modal-panel h3 { font-size: 1.15rem; color: var(--ink); }
4823.install-modal-panel h4 {
4824 margin: 1rem 0 .4rem;
4825 font-size: .92rem;
4826 letter-spacing: .03em;
4827 text-transform: uppercase;
4828 opacity: .7;
4829 color: var(--ink);
4830}
4831.install-modal-panel p { color: var(--ink); }
4832.install-steps {
4833 margin: 0;
4834 padding-left: 1.35rem;
4835 display: flex;
4836 flex-direction: column;
4837 gap: .45rem;
4838 font-size: .95rem;
4839 line-height: 1.45;
4840 color: var(--ink);
4841}
4842.install-steps li::marker { color: var(--accent); font-weight: 700; }
4843.install-apk-block {
4844 margin-top: 1.25rem;
4845 padding-top: 1rem;
4846 border-top: 1px dashed var(--rule);
4847}
4848.install-apk-link {
4849 display: inline-flex;
4850 align-items: center;
4851 margin-top: .4rem;
4852 padding: .6rem 1rem;
4853 background: var(--accent);
4854 color: #fff;
4855 border-radius: 8px;
4856 text-decoration: none;
4857 font-weight: 600;
4858}
4859.install-apk-link:hover { filter: brightness(1.08); }
4860.install-modal-actions {
4861 display: flex;
4862 justify-content: flex-end;
4863 margin-top: 1.25rem;
4864}
4865.install-modal-actions .btn {
4866 padding: .5rem 1rem;
4867 background: var(--paper-2);
4868 border: 1px solid var(--rule);
4869 border-radius: 8px;
4870 color: var(--ink);
4871 cursor: pointer;
4872 font: inherit;
4873 font-weight: 500;
4874}
4875.install-modal-actions .btn:hover {
4876 background: color-mix(in srgb, var(--accent) 10%, var(--paper-2));
4877 border-color: var(--accent);
4878 color: var(--accent);
4879}
4880
4881@media (max-width: 520px) {
4882 .install-modal-panel { padding: 1.2rem; }
4883 .install-app-btn { width: 100%; justify-content: center; }
4884}
4885
4886/* v9.3 — Install-modal: choice-blocks met voor/nadelen */
4887.install-choice {
4888 margin-top: 1.25rem;
4889 padding: 1rem;
4890 border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
4891 border-radius: 10px;
4892 background: color-mix(in srgb, currentColor 3%, transparent);
4893}
4894.install-choice + .install-choice { margin-top: 1rem; }
4895.install-choice h4 {
4896 margin: 0 0 .75rem;
4897 font-size: 1rem;
4898 letter-spacing: 0;
4899 text-transform: none;
4900 opacity: 1;
4901}
4902.install-pros-cons {
4903 display: flex;
4904 gap: 1rem;
4905 margin: .5rem 0 1rem;
4906}
4907.install-pros,
4908.install-cons {
4909 flex: 1;
4910 min-width: 0;
4911 font-size: .85rem;
4912 line-height: 1.45;
4913}
4914.install-pros strong,
4915.install-cons strong {
4916 display: block;
4917 margin-bottom: .35rem;
4918 font-size: .82rem;
4919 letter-spacing: .02em;
4920}
4921.install-pros strong { color: #16a34a; }
4922.install-cons strong { color: #dc2626; }
4923.install-pros ul,
4924.install-cons ul {
4925 margin: 0;
4926 padding-left: 1.1rem;
4927 display: flex;
4928 flex-direction: column;
4929 gap: .25rem;
4930}
4931.install-pros li::marker { color: #16a34a; }
4932.install-cons li::marker { color: #dc2626; }
4933
4934.install-choice-cta {
4935 display: inline-flex;
4936 align-items: center;
4937 justify-content: center;
4938 gap: .35rem;
4939 margin-top: .5rem;
4940 padding: .7rem 1.1rem;
4941 background: var(--accent, #c2410c);
4942 color: #fff;
4943 border: none;
4944 border-radius: 8px;
4945 text-decoration: none;
4946 font: inherit;
4947 font-weight: 600;
4948 font-size: .95rem;
4949 cursor: pointer;
4950 transition: filter .12s ease;
4951}
4952.install-choice-cta:hover { filter: brightness(1.08); }
4953.install-choice-note {
4954 margin: .5rem 0 0;
4955 padding: .6rem .8rem;
4956 background: color-mix(in srgb, currentColor 4%, transparent);
4957 border-left: 3px solid var(--accent, #c2410c);
4958 border-radius: 4px;
4959 font-size: .9rem;
4960 line-height: 1.45;
4961}
4962
4963@media (max-width: 520px) {
4964 .install-pros-cons { flex-direction: column; gap: .75rem; }
4965}
4966
4967/* v9.4 — Soft fade-overgang tussen posts bij navigatie.
4968 Fade-out is rustig (450ms): oude content vervaagt geleidelijk terwijl
4969 de scroll-naar-top loopt. Fade-in is snel (180ms) met een lichte blur-
4970 onscherp → scherp animatie zodat de nieuwe content niet abrupt verschijnt. */
4971#pcms-main {
4972 transition: opacity .45s ease-out;
4973}
4974#pcms-main.is-pcms-transitioning-out {
4975 opacity: 0;
4976}
4977#pcms-main.is-pcms-transitioning-in {
4978 opacity: 0;
4979 filter: blur(6px);
4980 animation: pcmsFadeIn .18s ease-out forwards;
4981 will-change: opacity, filter;
4982}
4983@keyframes pcmsFadeIn {
4984 from { opacity: 0; filter: blur(6px); }
4985 to { opacity: 1; filter: blur(0); }
4986}
4987
4988/* Respecteer reduced-motion voorkeur (iOS/Android accessibility setting) */
4989@media (prefers-reduced-motion: reduce) {
4990 #pcms-main {
4991 transition: opacity .15s ease-out;
4992 }
4993 #pcms-main.is-pcms-transitioning-in {
4994 filter: none;
4995 animation: none;
4996 opacity: 1;
4997 }
4998}
4999
5000/* === Fediverse interactions — inherits the native .comment styling === */
5001.post-fediverse .fedi-stats { display: flex; gap: .45rem; margin: .35rem 0 1.5rem; align-items: center; }
5002.post-fediverse .fedi-stat {
5003 display: inline-flex; align-items: center; gap: .4rem;
5004 padding: .32rem .7rem; border-radius: 999px; font-size: .85rem; line-height: 1;
5005 font-variant-numeric: tabular-nums;
5006 color: var(--ink-muted); background: transparent;
5007 border: 1px solid var(--rule);
5008}
5009.post-fediverse .fedi-stat svg { width: 15px; height: 15px; }
5010.post-fediverse .fedi-stat-btn { cursor: pointer; font: inherit; line-height: 1; transition: color .15s, border-color .15s, background .15s; }
5011.post-fediverse .fedi-stat-btn:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, var(--rule)); background: var(--accent-tint); }
5012.post-fediverse .fedi-stat-btn:hover svg { fill: color-mix(in srgb, var(--accent) 22%, transparent); }
5013.post-fediverse .fedi-handle { color: var(--ink-soft, #888); font-size: .82rem; }
5014.post-fediverse .comment-content p { margin: .2rem 0; }
5015.post-fediverse .comment-content p:first-child { margin-top: 0; }
5016.post-fediverse .comment-content p:last-child { margin-bottom: 0; }
5017/* reply toggle: a plain "Reageer" action like the comment buttons (no triangle) */
5018.post-fediverse .fedi-reply-toggle { list-style: none; cursor: pointer; display: inline-block; }
5019.post-fediverse .fedi-reply-toggle::-webkit-details-marker { display: none; }
5020.post-fediverse details[open] .fedi-reply-toggle { margin-bottom: .5rem; }
5021
5022/* Reply-via-fediverse CTA on post pages */
5023.post-fediverse-cta { margin: 1.75rem 0; }
5024.fedi-remote-reply-btn { display: inline-flex; align-items: center; gap: .45rem; }
5025.post-fediverse-cta .fedi-remote-reply-btn {
5026 border: 1px solid color-mix(in srgb, var(--accent, #888) 55%, transparent);
5027 background: color-mix(in srgb, var(--accent, #888) 10%, transparent);
5028 color: var(--ink, inherit); border-radius: 999px; padding: .5rem 1.05rem;
5029 font-weight: 600; cursor: pointer; transition: background .15s ease;
5030}
5031.post-fediverse-cta .fedi-remote-reply-btn:hover { background: color-mix(in srgb, var(--accent, #888) 22%, transparent); }
5032.post-fediverse-cta .fedi-remote-reply-btn svg { color: var(--accent, currentColor); }
5033.post-fediverse .comment-actions .fedi-remote-reply-btn { gap: .3rem; }
5034
5035/* Inline "reply via fediverse" address form (replaces the browser prompt) */
5036.fedi-remote-form {
5037 position: absolute; z-index: 60; display: inline-flex; align-items: center; gap: .4rem;
5038 padding: .4rem; border-radius: 999px;
5039 background: var(--paper-2, var(--paper, #fff));
5040 border: 1px solid color-mix(in srgb, var(--ink, #000) 12%, transparent);
5041 box-shadow: 0 10px 30px rgba(0,0,0,.28);
5042}
5043.fedi-remote-form input {
5044 height: 2.2rem; padding: 0 .9rem; border-radius: 999px; font: inherit; min-width: 230px;
5045 border: 1px solid color-mix(in srgb, var(--ink, #000) 18%, transparent);
5046 background: color-mix(in srgb, var(--ink, #000) 4%, transparent); color: var(--ink, #000);
5047}
5048.fedi-remote-form input::placeholder { color: var(--ink-soft, #999); }
5049.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); }
5050.fedi-remote-form .fedi-remote-go {
5051 height: 2.2rem; min-width: 2.2rem; padding: 0 .85rem; border-radius: 999px;
5052 display: inline-flex; align-items: center; justify-content: center; font-size: 1rem;
5053}
5054.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; }
5055.fedi-remote-form .fedi-remote-cancel:hover { color: var(--ink, #000); }
5056
5057/* ── NSFW / sensitive content — blur + click to reveal ───────────────── */
5058.nsfw-media { position: relative; }
5059.nsfw-veil {
5060 position: absolute; inset: 0; z-index: 4; cursor: pointer; border-radius: inherit;
5061 display: flex; flex-direction: column; align-items: center; justify-content: center;
5062 gap: .5rem; padding: .75rem; text-align: center;
5063 background: color-mix(in srgb, var(--paper, #14141a) 50%, transparent);
5064 backdrop-filter: blur(22px) saturate(.6); -webkit-backdrop-filter: blur(22px) saturate(.6);
5065}
5066.nsfw-media.is-shown > .nsfw-veil { display: none; }
5067.nsfw-veil-label { font-weight: 700; font-size: .82rem; color: var(--ink, #f3f1ea); text-shadow: 0 1px 2px rgba(0,0,0,.5); }
5068.nsfw-veil-btn {
5069 font-size: .72rem; font-weight: 600; padding: .3rem .8rem; border-radius: 999px;
5070 border: 1px solid color-mix(in srgb, var(--ink, #fff) 55%, transparent);
5071 background: color-mix(in srgb, var(--paper, #000) 35%, transparent); color: var(--ink, #f3f1ea);
5072}
5073/* Post page: blur cover + body behind a content-warning banner until revealed */
5074.nsfw-gate .post-cover,
5075.nsfw-gate .post-content { filter: blur(26px); pointer-events: none; user-select: none; transition: filter .2s ease; }
5076.nsfw-gate.is-shown .post-cover,
5077.nsfw-gate.is-shown .post-content { filter: none; pointer-events: auto; user-select: auto; }
5078.nsfw-banner {
5079 display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; margin: 0 0 1.5rem;
5080 padding: .8rem 1rem; border-radius: 12px;
5081 border: 1px solid color-mix(in srgb, var(--accent, #e8b04b) 45%, transparent);
5082 background: color-mix(in srgb, var(--accent, #e8b04b) 9%, transparent);
5083}
5084.nsfw-gate.is-shown .nsfw-banner { display: none; }
5085.nsfw-banner-icon { font-size: 1.2rem; }
5086.nsfw-banner-text { font-weight: 600; font-size: .92rem; color: var(--ink); }
5087.nsfw-banner-btn {
5088 margin-left: auto; cursor: pointer; font: inherit; font-size: .82rem; font-weight: 600;
5089 padding: .4rem 1rem; border-radius: 999px; border: 0;
5090 background: var(--accent, #e8b04b); color: #1a1205;
5091}
5092
5093/* ── Het bandje (mixtape) ──────────────────────────────────────────────
5094 Een cassette, getekend als SVG op de echte maat: 100,4 x 63,8 mm, dus een
5095 viewBox van 314x200. De vorige versie tekende twee schijfjes met CSS; dit is
5096 een replica -- behuizing, labelvlak, venster, twee spoelen met tandjes, de
5097 schroefjes in de hoeken en de openingen voor de kop en de capstans onderin.
5098
5099 Alle kleuren komen uit de bestaande variabelen, dus licht, donker en het
5100 accent van de site volgen vanzelf. */
5101.post-tape {
5102 position: relative;
5103 border: 1px solid var(--rule, rgba(0,0,0,.1));
5104 border-radius: 14px;
5105 overflow: hidden;
5106 margin: 1.25rem 0;
5107 background: var(--paper-2, color-mix(in srgb, var(--ink) 3%, transparent));
5108}
5109
5110.tape-shell { position: relative; padding: .8rem .8rem .3rem; }
5111.tape-svg { display: block; width: 100%; height: auto; }
5112
5113/* De behuizing. Twee lagen zodat er een randje omheen zit, zoals bij een echt
5114 doosje waar de bovenschaal iets uitsteekt. */
5115.tape-body { fill: color-mix(in srgb, var(--ink) 82%, var(--paper, #fff)); }
5116.tape-body-inner { fill: color-mix(in srgb, var(--ink) 70%, var(--paper, #fff)); }
5117.tape-labelplate { fill: color-mix(in srgb, var(--paper, #fff) 92%, var(--accent)); }
5118.tape-glass {
5119 fill: color-mix(in srgb, var(--ink) 88%, transparent);
5120 stroke: color-mix(in srgb, var(--paper, #fff) 25%, transparent);
5121 stroke-width: 1.5;
5122}
5123.tape-pack { fill: color-mix(in srgb, var(--ink) 55%, var(--paper, #fff)); }
5124.tape-ribbon { fill: color-mix(in srgb, var(--ink) 45%, var(--paper, #fff)); }
5125.tape-hub { fill: var(--paper, #fff); }
5126.tape-tooth { fill: color-mix(in srgb, var(--ink) 60%, var(--paper, #fff)); }
5127.tape-screw { fill: color-mix(in srgb, var(--ink) 55%, var(--paper, #fff)); }
5128.tape-slot { fill: color-mix(in srgb, var(--ink) 92%, transparent); }
5129.tape-slot--capstan { fill: color-mix(in srgb, var(--ink) 96%, transparent); }
5130
5131/* Draaien. De tandjes zitten in dezelfde groep als de naaf, dus de hele spoel
5132 draait als een geheel -- daar gaat het om, want aan die tandjes zie je pas
5133 DAT hij draait. Het label blijft stil, anders wordt de titel onleesbaar. */
5134.post-tape.is-playing .tape-reel { animation: tape-spin 3s linear infinite; }
5135.post-tape.is-playing .tape-reel--right { animation-duration: 2.2s; }
5136/* Spoelen: veel sneller, en achteruit ook echt achteruit. */
5137.post-tape.is-winding .tape-reel { animation-duration: .45s; }
5138.post-tape.is-winding-back .tape-reel { animation-direction: reverse; }
5139@keyframes tape-spin { to { transform: rotate(360deg); } }
5140@media (prefers-reduced-motion: reduce) {
5141 .post-tape.is-playing .tape-reel,
5142 .post-tape.is-winding .tape-reel { animation: none; }
5143}
5144
5145/* De tekst staat OP het labelvlak van de cassette. Percentages volgen de
5146 viewBox, dus het blijft kloppen op elke breedte. */
5147.tape-label {
5148 position: absolute;
5149 left: 8.4%; right: 8.4%;
5150 top: calc(.8rem + 11%);
5151 height: 31%;
5152 padding: .1rem .4rem;
5153 color: color-mix(in srgb, var(--ink) 92%, transparent);
5154 overflow: hidden;
5155}
5156.tape-kind {
5157 margin: 0;
5158 font-size: clamp(.55rem, 1.6vw, .68rem);
5159 letter-spacing: .14em;
5160 text-transform: uppercase;
5161 opacity: .7;
5162}
5163.tape-title {
5164 margin: 0;
5165 font-size: clamp(.85rem, 2.6vw, 1.1rem);
5166 line-height: 1.15;
5167 white-space: nowrap;
5168 overflow: hidden;
5169 text-overflow: ellipsis;
5170}
5171.tape-artist { margin: 0; font-size: clamp(.65rem, 1.9vw, .8rem); opacity: .8; }
5172.tape-meta { margin: 0; font-size: clamp(.6rem, 1.7vw, .72rem); opacity: .6; }
5173
5174/* De knoppen: terugspoelen, spelen, vooruitspoelen. Meer heeft een cassette
5175 niet, en dat is de bedoeling -- er is geen zoekbalk omdat je op een bandje
5176 niet kunt zoeken, alleen spoelen. */
5177.tape-controls {
5178 display: flex;
5179 align-items: center;
5180 justify-content: center;
5181 gap: .6rem;
5182 padding: .5rem 0 .6rem;
5183}
5184.tape-btn {
5185 display: grid;
5186 place-items: center;
5187 width: 40px; height: 40px;
5188 border-radius: 50%;
5189 border: 1px solid var(--rule, rgba(0,0,0,.15));
5190 background: var(--paper, #fff);
5191 color: var(--ink);
5192 cursor: pointer;
5193 padding: 0;
5194 touch-action: manipulation; /* vasthouden mag geen zoom of scroll worden */
5195 user-select: none;
5196}
5197.tape-btn svg { width: 18px; height: 18px; }
5198.tape-btn:hover { border-color: var(--accent); color: var(--accent); }
5199.tape-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
5200.tape-btn--play {
5201 width: 52px; height: 52px;
5202 background: var(--accent);
5203 border-color: var(--accent);
5204 color: var(--paper, #fff);
5205}
5206.tape-btn--play svg { width: 22px; height: 22px; }
5207.post-tape.is-winding .tape-btn--back,
5208.post-tape.is-winding .tape-btn--fwd { border-color: var(--accent); color: var(--accent); }
5209
5210.tape-now {
5211 margin: 0;
5212 padding: 0 .8rem .4rem;
5213 text-align: center;
5214 font-size: .8rem;
5215 opacity: .8;
5216 min-height: 1.2em;
5217}
5218
5219/* De nummers staan er als INHOUD, niet als knoppen. Geen cursor, geen hover,
5220 geen aanwijzing dat je erop kunt drukken: op een cassette kun je dat niet. */
5221.tape-tracks {
5222 margin: 0;
5223 padding: 0 .8rem .8rem;
5224 list-style: none;
5225 counter-reset: tape;
5226 display: grid;
5227 gap: .1rem;
5228}
5229.tape-track {
5230 counter-increment: tape;
5231 display: flex;
5232 gap: .5rem;
5233 align-items: baseline;
5234 font-size: .85rem;
5235 padding: .18rem .3rem;
5236 border-radius: 6px;
5237 opacity: .75;
5238}
5239.tape-track::before {
5240 content: counter(tape) ".";
5241 opacity: .55;
5242 min-width: 1.4em;
5243 font-variant-numeric: tabular-nums;
5244}
5245.tape-track-title { flex: 1; }
5246.tape-track-dur { opacity: .6; font-variant-numeric: tabular-nums; }
5247.tape-track.is-current {
5248 opacity: 1;
5249 background: color-mix(in srgb, var(--accent) 12%, transparent);
5250}
5251
5252
5253/* ── De speler in bandmodus ────────────────────────────────────────────
5254 Zelfde speler, ander gedrag: op een cassette kies je geen nummer. De lijst
5255 blijft staan zodat je ziet wat erop staat, maar zonder de opmaak die belooft
5256 dat je erin kunt prikken. */
5257.audio-sheet-queue-list.is-tape .audio-sheet-queue-item {
5258 cursor: default;
5259 pointer-events: none;
5260}
5261.audio-sheet-queue-list.is-tape .audio-sheet-queue-item:hover {
5262 background: none;
5263}
5264/* Het lopende nummer blijft wel gemarkeerd -- dat is de enige plek waar je op
5265 een bandje leest waar je bent. */
5266.audio-sheet-queue-list.is-tape .audio-sheet-queue-item.is-current {
5267 opacity: 1;
5268}
5269/* Spoelknoppen mogen niet als "volgende" voelen: vasthouden hoort geen tekst te
5270 selecteren of de pagina te laten zoomen. */
5271.audio-btn.is-wind {
5272 touch-action: manipulation;
5273 user-select: none;
5274}
Note: See TracBrowser for help on using the repository browser.