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

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

Lezen: Lenis voor het snappen op desktop, plus twee kleine dingen

WAAROM LENIS, en dat is niet het vloeiende scrollen: de DUUR van een snap is
met native scroll-snap niet in te stellen -- die zit in de browser. Robin
vroeg om een snellere snap, en dat was declaratief onmogelijk. Lenis' snap-
pakket geeft ons duration, easing, distanceThreshold en debounce.

distanceThreshold 12% de vangzone (native: browsereigen, ~200-300px)
duration 0.4s in totaal onder de halve seconde
easing easeOutQuart -- op de helft van de tijd is 94% van de

weg af, daarna zacht uitdempen. Bewust niet
Lenis' standaard easeOutExpo: die schiet weg
en kruipt zo lang na dat het lijkt of hij niet
afmaakt.

debounce 60ms niet de standaard 500; dat voelt als te laat

ALLEEN OP DESKTOP (hover: hover en pointer: fine). Op touch doet Lenis van
zichzelf niets -- syncTouch staat uit -- en is het systeem-scrollen al
soepel. Daar blijft de native CSS-snap staan die er nu is, inclusief het
anker van nul hoog en de richtingsschakelaar. Robins keuze: "enkel voor
desktop, dat is prima, logisch dat het niet op mobiel kan".

Omhoog niet snappen gaat met Lenis via snap.stop()/start(), een stuk
schoner dan de CSS-eigenschap omzetten. Beide wegen delen dezelfde
richtingsdetectie uit wiel, vinger en toets.

NOG NIET UIT: smoothWheel. Dat kwam er na het snappen bij, en het is de kant
die oppassen vraagt -- een Mac-trackpad heeft zijn EIGEN momentum en Lenis'
demping komt daar bovenop. Vandaar lerp 0.2 in plaats van de standaard 0.1.
Robin vond dat op 20-8 nog steeds te zweverig, dus dat getal moet nog omhoog
of smoothWheel moet terug naar false. Die tweede stand werkte ook; dan doet
Lenis alleen de snap. Dat staat zo in het commentaar bij de instellingen,
zodat de volgende sessie niet hoeft te raden waar de knop zit.

Gevendored in src/assets/js/vendor (Lenis 1.3.26, MIT, 43KB samen), want de
CSP staat geen CDN toe. Dynamisch geimporteerd, dus mobiel haalt het niet
op. VENDOR_V ophogen als die bestanden wijzigen. De CSP hoefde niet aan:
strict-dynamic staat een import vanuit een vertrouwde module toe.

TWEE KLEINE DINGEN uit dezelfde ronde:

  • De terug-naar-boven-knop wijkt voor de mini-speler. Die kwam er anders bovenop te liggen (schermafbeelding). body.has-audio-player bestond al en wordt door audio-player.js gezet, dus dit volgt vanzelf.
  • Vastgepind is nu zichtbaar in de leestitel, met hetzelfde pin-icoon als in de tijdlijn. In de leesstroom staat een gepind bericht bovenaan zonder dat je ziet waarom. Nagekeken met een echte pin op dev.

style.css naar v82, MOD_V naar 19.

Suite 1169/1169.

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

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