source: Klonkt/src/views/pages/post.ejs@ e390844

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

feat(post): hide 'Interact via the fediverse' CTA for the owner/admin

That button is for visitors (reply from their own server); the owner has their own
controls and it's their own post, so hide it when canManageSite.

  • Property mode set to 100644
File size: 23.4 KB
Line 
1<%
2// In hub mode the site is accessible under /user/<slug>; all site-scoped links
3// (tag, edit, delete, comments) must carry that prefix otherwise they fall back
4// to the primary site → 404 / wrong site. In solo mode _base = ''.
5const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
6%>
7<article class="container post-page">
8
9 <%# Navigation across ALL posts — ABOVE the post (shared partial). %>
10 <%- include('../partials/post-nav', { newerPost: newerPost, olderPost: olderPost }) %>
11
12 <% if (post.cover_image_url) { %>
13 <figure class="post-cover">
14 <img src="<%= post.cover_image_url %>" alt="">
15 </figure>
16 <% } %>
17
18 <header class="post-header">
19 <h1 class="post-title"><%= post.title %></h1>
20 <div class="post-meta">
21 <a class="author" href="/users/<%= encodeURIComponent(post.author_username) %>"
22 hx-get="/users/<%= encodeURIComponent(post.author_username) %>?partial=1"
23 hx-target="#pcms-main" hx-swap="innerHTML"
24 hx-push-url="/users/<%= encodeURIComponent(post.author_username) %>"
25 hx-indicator="#pcms-loading"><%= post.author_username %></a>
26 <span class="meta-sep">·</span>
27 <time datetime="<%= post.published_at || post.created_at %>"><%= formatDate(post.published_at || post.created_at) %></time>
28 <% if (post.status !== 'published') { %>
29 <span class="meta-sep">·</span>
30 <span class="status-badge status-<%= post.status %>"><%= post.status %></span>
31 <% } %>
32 </div>
33 </header>
34
35 <div class="post-content">
36 <%- post.content_html %>
37 </div>
38
39 <% if (post.tags && post.tags.length > 0) { %>
40 <div class="post-tags">
41 <% post.tags.forEach(t => { %>
42 <a class="tag" href="<%= _base %>/tag/<%= encodeURIComponent(t) %>"
43 hx-get="<%= _base %>/tag/<%= encodeURIComponent(t) %>?partial=1"
44 hx-target="#pcms-main" hx-swap="innerHTML"
45 hx-push-url="<%= _base %>/tag/<%= encodeURIComponent(t) %>"
46 hx-indicator="#pcms-loading">#<%= t %></a>
47 <% }); %>
48 </div>
49 <% } %>
50
51 <!-- Inline admin actions: only visible if user has permission -->
52 <% if (user && (permissions.canEditPost(user, post, site) || permissions.canDeletePost(user, post, site))) { %>
53 <aside class="post-actions">
54 <% if (permissions.canEditPost(user, post, site)) { %>
55 <a href="<%= _base %>/posts/<%= post.slug %>/edit" class="btn"
56 hx-get="<%= _base %>/posts/<%= post.slug %>/edit?partial=1" hx-target="#pcms-main"
57 hx-push-url="<%= _base %>/posts/<%= post.slug %>/edit" hx-indicator="#pcms-loading">
58 ✏️ Edit
59 </a>
60 <% } %>
61 <% if (permissions.canDeletePost(user, post, site)) { %>
62 <form method="post" action="<%= _base %>/posts/<%= post.slug %>/delete" style="display:inline" onsubmit="return confirm('Delete this post?')">
63 <button type="submit" class="btn btn-danger">🗑 Delete</button>
64 </form>
65 <% } %>
66 </aside>
67 <% } %>
68
69 <!-- Fediverse interactions — single place: ⭐ like (clickable) + 🔁/💬 counts + reply -->
70 <% var _postAbsUrl = (typeof ogOrigin !== 'undefined' && ogOrigin ? ogOrigin : '') + (typeof _base !== 'undefined' && _base ? _base : '') + '/' + post.slug; %>
71 <% var _fedi = (typeof fediverse !== 'undefined' && fediverse) ? fediverse : { thread: [], likeCount: 0, announceCount: 0 }; %>
72 <% if (post.status === 'published' && (typeof apEnabled === 'undefined' || apEnabled)) { %>
73 <section class="post-fediverse post-comments" id="fediverse">
74 <h2 class="comments-heading"><%= t('fedi.heading') %></h2>
75 <div class="fedi-stats">
76 <span class="fedi-stat" title="<%= t('fedi.likes') %>">
77 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
78 <span><%= _fedi.likeCount %></span>
79 </span>
80 <span class="fedi-stat" title="<%= t('fedi.boosts') %>">
81 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
82 <span><%= _fedi.announceCount %></span>
83 </span>
84 <span class="fedi-stat" title="<%= t('fedi.replies') %>">
85 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
86 <span><%= (_fedi.thread || []).length %></span>
87 </span>
88 </div>
89 <% if (_fedi.thread && _fedi.thread.length) { %>
90 <ol class="comments-list">
91 <% _fedi.thread.forEach(function(n){ %>
92 <li class="comment"><%- include('../partials/fedi-node', { n: n, t: t, canManageSite: (typeof canManageSite !== 'undefined' ? canManageSite : false), _base: _base, siteAvatar: (typeof siteAvatar !== 'undefined' ? siteAvatar : null), formatDateTime: formatDateTime, postSlug: post.slug }) %></li>
93 <% }); %>
94 </ol>
95 <% } %>
96 <%# "Interact via the fediverse" is for VISITORS (reply from their own server).
97 The owner/admin has their own controls + it's their post → hide it for them. %>
98 <% if (typeof canManageSite === 'undefined' || !canManageSite) { %>
99 <div class="post-fediverse-cta">
100 <button type="button" class="btn fedi-remote-reply-btn" data-fedi-uri="<%= _postAbsUrl %>" data-fedi-ph="<%= t('fedi.remote_ph') %>">
101 <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
102 <%= t('fedi.remote_interact') %>
103 </button>
104 </div>
105 <% } %>
106 </section>
107 <% } %>
108 <script>
109 (function(){
110 if (window.__fediRemoteWired) return; window.__fediRemoteWired = true;
111 var current = null, currentBtn = null;
112 function close(){ if (current) { current.remove(); current = null; currentBtn = null; } }
113 function go(raw, uri){
114 var d = (raw||'').trim().replace(/^@?[^@\s]*@/, '').replace(/^https?:\/\//i, '').replace(/\/.*$/, '').trim();
115 if (d) { try { localStorage.setItem('pcmsFediServer', d); } catch(e){} location.href = 'https://' + d + '/authorize_interaction?uri=' + encodeURIComponent(uri||''); }
116 }
117 function place(f, b){
118 var r = b.getBoundingClientRect();
119 f.style.top = (r.bottom + window.scrollY + 6) + 'px';
120 f.style.left = Math.max(8, Math.min(r.left + window.scrollX, window.scrollX + window.innerWidth - 340)) + 'px';
121 }
122 document.addEventListener('click', function(e){
123 if (e.target.closest && e.target.closest('.fedi-remote-cancel')) { close(); return; }
124 if (current && e.target.closest && e.target.closest('.fedi-remote-form')) return; // click inside → keep
125 var b = e.target.closest && e.target.closest('.fedi-remote-reply-btn');
126 if (b) {
127 e.preventDefault();
128 if (currentBtn === b) { close(); return; } // toggle off
129 close();
130 var f = document.createElement('form');
131 f.className = 'fedi-remote-form';
132 f.dataset.uri = b.getAttribute('data-fedi-uri') || '';
133 f.innerHTML = '<input type="text" autocomplete="off" spellcheck="false">'
134 + '<button type="submit" class="btn btn-primary fedi-remote-go" aria-label="ok">&rarr;</button>'
135 + '<button type="button" class="fedi-remote-cancel" aria-label="x">&times;</button>';
136 var _inp = f.querySelector('input');
137 _inp.placeholder = b.getAttribute('data-fedi-ph') || 'mastodon.social';
138 try { var _sv = localStorage.getItem('pcmsFediServer'); if (_sv) _inp.value = _sv; } catch(e){}
139 document.body.appendChild(f); // floating popover → no layout reflow
140 place(f, b); current = f; currentBtn = b;
141 _inp.focus(); _inp.select();
142 return;
143 }
144 if (current) close(); // click anywhere else closes it
145 });
146 document.addEventListener('submit', function(e){
147 var f = e.target.closest && e.target.closest('.fedi-remote-form');
148 if (!f) return; e.preventDefault();
149 go(f.querySelector('input').value, f.dataset.uri);
150 });
151 document.addEventListener('keydown', function(e){ if (e.key === 'Escape') close(); });
152 window.addEventListener('scroll', close, true);
153 })();
154 </script>
155
156
157 <%# ── Related posts (3-card grid) ─────────────────────────────
158 Always shown if at least one related post exists. Selection logic
159 lives server-side: same-tag posts ranked by overlap, falling back
160 to most-recent if there's no tag overlap. %>
161 <% if (relatedPosts && relatedPosts.length > 0) { %>
162 <section class="post-related" aria-labelledby="related-heading">
163 <h2 class="post-related-heading" id="related-heading"><%= t('related.title') %></h2>
164 <ul class="post-related-grid">
165 <% relatedPosts.forEach(function(rp) { %>
166 <li class="post-related-card">
167 <a href="<%= rp._urlBase || '' %>/<%= rp.slug %>"<% if (!rp._urlBase) { %>
168 hx-get="/<%= rp.slug %>?partial=1" hx-target="#pcms-main"
169 hx-push-url="/<%= rp.slug %>" hx-indicator="#pcms-loading"<% } %>>
170 <% if (rp.cover_image_url) { %>
171 <span class="post-related-cover"
172 style="background-image: url('<%= rp.cover_image_url %>')"
173 aria-hidden="true"></span>
174 <% } else { %>
175 <span class="post-related-cover post-related-cover--empty" aria-hidden="true"></span>
176 <% } %>
177 <span class="post-related-meta">
178 <span class="post-related-date"><%= formatDate(rp.published_at) %></span>
179 <span class="post-related-title"><%= rp.title %></span>
180 </span>
181 </a>
182 </li>
183 <% }); %>
184 </ul>
185 </section>
186 <% } %>
187
188</article>
189<style>
190.post-page { max-width: 720px; margin: 0.5rem auto 2rem; padding: 0 1rem; }
191.post-cover { margin: 0 0 2rem; border-radius: 8px; overflow: hidden; }
192.post-cover img { width: 100%; height: auto; display: block; }
193.post-header { margin-bottom: 2rem; }
194.post-title { font-family: var(--font-display, serif); font-size: 2.5rem; line-height: 1.1; margin: 0 0 1rem; }
195@media (max-width: 600px) { .post-title { font-size: 1.85rem; } }
196.post-meta { font-size: 0.9rem; color: var(--ink-muted); }
197.author { font-weight: 500; color: var(--ink-soft); text-decoration: none; }
198.author:hover { color: var(--accent); }
199.post-content { font-family: var(--font-body, serif); font-size: 1.1rem; line-height: 1.7; color: var(--ink); }
200.post-content p { margin: 1.2em 0; }
201.like-btn {
202 display: inline-flex; align-items: center; gap: 0.45rem;
203 padding: 0.4rem 0.85rem; border: 1px solid var(--rule); border-radius: 999px;
204 background: var(--paper-2); color: var(--ink); font: inherit; font-size: 0.95rem;
205 cursor: pointer; text-decoration: none; transition: border-color 120ms, color 120ms, background 120ms;
206}
207.like-btn:hover { border-color: var(--accent); }
208.like-btn .like-heart { font-size: 1.1rem; line-height: 1; }
209.like-btn.is-liked { border-color: var(--accent); color: var(--accent); }
210.like-btn.is-liked .like-heart { color: var(--accent); }
211.like-btn .like-count { font-variant-numeric: tabular-nums; }
212.post-content h2 { font-family: var(--font-display, serif); font-size: 1.6rem; margin: 2rem 0 1rem; }
213.post-content h3 { font-family: var(--font-display, serif); font-size: 1.3rem; margin: 1.5rem 0 0.75rem; }
214.post-content a { color: var(--accent); }
215.post-content code { background: var(--paper-2); padding: 0.1em 0.4em; border-radius: 3px; font-family: var(--font-mono, monospace); font-size: 0.9em; }
216.post-content pre { background: var(--paper-2); padding: 1rem; border-radius: 6px; overflow-x: auto; }
217.post-content blockquote { border-left: 3px solid var(--accent); padding-left: 1rem; margin: 1.5rem 0; color: var(--ink-soft); font-style: italic; }
218.post-content img { max-width: 100%; height: auto; border-radius: 6px; }
219.post-tags { display: flex; gap: 0.5rem; flex-wrap: wrap; margin: 2rem 0; }
220.tag { background: var(--paper-2); color: var(--ink-soft); padding: 0.2rem 0.6rem; border-radius: 999px; font-size: 0.85rem; text-decoration: none; transition: background 120ms, color 120ms; }
221.tag:hover { background: var(--accent); color: white; }
222.post-actions { display: flex; gap: 0.5rem; margin: 2rem 0; padding: 1rem; background: var(--paper-2); border-radius: 6px; flex-wrap: wrap; }
223
224/* ============================================================
225 Comments
226 ============================================================ */
227.post-comments {
228 margin-top: 3rem;
229 padding-top: 2rem;
230 border-top: 1px solid var(--rule);
231}
232.comments-heading {
233 font-family: var(--font-display, serif);
234 font-size: 1.5rem;
235 margin: 0 0 1.5rem;
236}
237.comments-empty { color: var(--ink-muted, var(--ink-soft)); margin: 0 0 1.5rem; }
238.comments-pending-flash {
239 background: rgba(40, 160, 90, 0.15);
240 color: #2a9d5e;
241 border: 1px solid rgba(40, 160, 90, 0.3);
242 padding: 0.75rem 1rem;
243 border-radius: 6px;
244 margin-bottom: 1rem;
245 font-size: 0.9rem;
246}
247.comments-empty a { color: var(--accent); }
248
249.comments-list, .comment-replies {
250 list-style: none;
251 padding: 0;
252 margin: 0 0 2rem;
253}
254
255.comment {
256 display: flex;
257 gap: 0.85rem;
258 padding: 1rem 0;
259 border-top: 1px solid var(--rule);
260}
261.comments-list > .comment:first-child { border-top: 0; padding-top: 0; }
262
263.comment-avatar {
264 flex-shrink: 0;
265 width: 40px;
266 height: 40px;
267 border-radius: 50%;
268 background: var(--paper-2);
269 border: 1px solid var(--rule);
270 overflow: hidden;
271 display: inline-flex;
272 align-items: center;
273 justify-content: center;
274}
275.comment-avatar img { width: 100%; height: 100%; object-fit: cover; }
276.comment-avatar span {
277 font-family: var(--font-display, serif);
278 font-weight: 700;
279 color: var(--accent);
280}
281
282.comment-body { flex: 1; min-width: 0; }
283.comment-meta {
284 display: flex;
285 gap: 0.5rem;
286 align-items: baseline;
287 flex-wrap: wrap;
288 row-gap: 0.1rem;
289 font-size: 0.85rem;
290 margin-bottom: 0.3rem;
291}
292.comment-author { color: var(--ink); font-weight: 600; text-decoration: none; }
293.comment-author:hover { color: var(--accent); }
294.comment-time { color: var(--ink-muted, var(--ink-soft)); }
295.comment-meta .fedi-handle { overflow-wrap: anywhere; }
296@media (max-width: 560px) {
297 .comment-meta .comment-time { flex-basis: 100%; font-size: 0.78rem; color: var(--ink-faint, var(--ink-muted)); }
298}
299.comment-content {
300 white-space: pre-wrap;
301 word-wrap: break-word;
302 color: var(--ink);
303 line-height: 1.5;
304 font-size: 0.95rem;
305}
306
307.comment-actions {
308 display: flex;
309 gap: 0.5rem;
310 margin-top: 0.4rem;
311 flex-wrap: wrap;
312 align-items: center;
313}
314.fedi-owner-react { display: inline; }
315.fedi-owner-reply { display: inline-block; }
316.fedi-owner-reply[open] { flex-basis: 100%; }
317.comment-reply-btn, .comment-delete-btn {
318 background: none;
319 border: 0;
320 color: var(--ink-muted, var(--ink-soft));
321 font-size: 0.8rem;
322 cursor: pointer;
323 padding: 0.2rem 0;
324 font-family: inherit;
325 text-transform: uppercase;
326 letter-spacing: 0.04em;
327}
328.comment-reply-btn:hover { color: var(--accent); }
329.comment-delete-btn:hover { color: #c33; }
330
331/* Owner comment-action icons — same visual language as the News feed, compact (30px). */
332.fedi-cact { width: 30px; height: 30px; flex: 0 0 30px; border-radius: 50%; padding: 0; box-sizing: border-box;
333 display: inline-flex; align-items: center; justify-content: center; list-style: none;
334 border: 1px solid color-mix(in srgb, var(--ink, #000) 14%, transparent);
335 background: color-mix(in srgb, var(--ink, #000) 3%, transparent);
336 color: var(--ink-soft, #888); cursor: pointer; -webkit-tap-highlight-color: transparent;
337 transition: background .12s, border-color .12s, transform .1s; }
338.fedi-cact::-webkit-details-marker { display: none; }
339.fedi-cact svg { width: 15px; height: 15px; display: block; }
340.fedi-cact:active { transform: scale(.9); }
341.fedi-cact-like svg { color: #e8b04b; }
342.fedi-cact-like:hover { background: color-mix(in srgb, #e8b04b 15%, transparent); border-color: color-mix(in srgb, #e8b04b 50%, transparent); }
343.fedi-cact-like.is-on { background: color-mix(in srgb, #e8b04b 18%, transparent); border-color: color-mix(in srgb, #e8b04b 60%, transparent); }
344.fedi-cact-boost svg { color: #2fa85a; }
345.fedi-cact-boost:hover { background: color-mix(in srgb, #2fa85a 15%, transparent); border-color: color-mix(in srgb, #2fa85a 50%, transparent); }
346.fedi-cact-boost.is-on { background: color-mix(in srgb, #2fa85a 18%, transparent); border-color: color-mix(in srgb, #2fa85a 60%, transparent); }
347.fedi-cact-reply svg { color: var(--accent, #06c); }
348.fedi-cact-reply:hover { background: color-mix(in srgb, var(--accent, #888) 14%, transparent); border-color: color-mix(in srgb, var(--accent, #888) 50%, transparent); }
349
350.comment-replies {
351 margin-top: 1rem;
352 margin-left: 0;
353 border-left: 2px solid var(--rule);
354 padding-left: 1rem;
355}
356.comment-reply { padding: 0.75rem 0; }
357.comment-replies > .comment-reply:first-child { border-top: 0; padding-top: 0; }
358.comment-reply .comment-avatar { width: 32px; height: 32px; }
359.comment-reply .comment-avatar span { font-size: 0.85rem; }
360
361.comment-form, .comment-reply-form {
362 display: flex;
363 flex-direction: column;
364 gap: 0.6rem;
365 background: var(--paper-2);
366 border: 1px solid var(--rule);
367 border-radius: 8px;
368 padding: 1rem;
369 margin-top: 1rem;
370}
371.comment-form-label { display: flex; flex-direction: column; gap: 0.4rem; }
372.comment-form-label > span { font-size: 0.85rem; color: var(--ink-muted, var(--ink-soft)); }
373.comment-form textarea, .comment-reply-form textarea {
374 width: 100%;
375 resize: vertical;
376 min-height: 70px;
377 padding: 0.65rem 0.85rem;
378 border: 1px solid var(--rule);
379 border-radius: 5px;
380 background: var(--paper);
381 color: var(--ink);
382 font-family: inherit;
383 font-size: 0.95rem;
384 line-height: 1.4;
385 box-sizing: border-box;
386}
387.comment-form button { align-self: flex-start; }
388.comment-reply-form-actions { display: flex; gap: 0.5rem; }
389/* Show the reply form only AFTER clicking REPLY. The display:flex above would
390 otherwise override the [hidden] attribute → form was always open. */
391.comment-reply-form[hidden] { display: none; }
392.fedi-owner-reply summary { list-style: none; cursor: pointer; }
393.fedi-owner-reply summary::-webkit-details-marker { display: none; }
394.fedi-owner-reply[open] summary { margin-bottom: .45rem; }
395/* DELETE button is inside a <form>; display:contents makes it a direct flex-sibling
396 of REPLY → pixel-perfect alignment + equal gap. */
397.comment-actions form { display: contents; }
398
399.comments-login-cta { margin-top: 1.5rem; text-align: center; }
400
401/* ─── Related posts (3-card grid above pinned-nav) ─────────────── */
402.post-related {
403 margin: 3rem auto 1.5rem;
404 /* Break out of the 720px article column so the cards have room to breathe.
405 Cap at 1100 to match other wide content. */
406 max-width: 1100px;
407 padding: 0 1rem;
408}
409.post-related-heading {
410 font-family: var(--font-ui, system-ui), sans-serif;
411 font-size: 0.75rem;
412 font-weight: 600;
413 text-transform: uppercase;
414 letter-spacing: 0.15em;
415 color: var(--ink-muted, var(--ink-soft));
416 margin: 0 0 1rem;
417}
418.post-related-grid {
419 list-style: none;
420 padding: 0; margin: 0;
421 display: grid;
422 grid-template-columns: repeat(3, minmax(0, 1fr));
423 gap: 1rem;
424}
425@media (max-width: 880px) { .post-related-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
426@media (max-width: 540px) { .post-related-grid { grid-template-columns: 1fr; } }
427
428.post-related-card {
429 background: var(--paper);
430 border: 1px solid var(--rule);
431 border-radius: 10px;
432 overflow: hidden;
433 transition: border-color 150ms, transform 150ms;
434}
435.post-related-card:hover { border-color: var(--accent); transform: translateY(-2px); }
436.post-related-card a {
437 display: block;
438 color: inherit;
439 text-decoration: none;
440}
441.post-related-cover {
442 display: block;
443 aspect-ratio: 16 / 10;
444 background: var(--paper-2) center/cover no-repeat;
445}
446.post-related-cover--empty {
447 background-image: linear-gradient(135deg,
448 color-mix(in srgb, var(--accent) 12%, var(--paper-2)),
449 var(--paper-2));
450}
451.post-related-meta {
452 display: flex; flex-direction: column; gap: 0.4rem;
453 padding: 0.85rem 1rem 1.1rem;
454}
455.post-related-date {
456 font-size: 0.78rem;
457 color: var(--ink-muted, var(--ink-soft));
458 font-variant-numeric: tabular-nums;
459}
460.post-related-title {
461 font-family: var(--font-display, serif);
462 font-size: 1.1rem;
463 font-weight: 600;
464 color: var(--ink);
465 line-height: 1.25;
466}
467
468/* ─── Pinned navigation cards (v9 style) ───────────────────────── */
469.post-pinned-nav {
470 display: grid;
471 grid-template-columns: 1fr 1fr;
472 gap: 1rem;
473 margin: 1.5rem auto 2rem;
474 max-width: 1100px;
475 padding: 0 1rem;
476}
477@media (max-width: 600px) {
478 .post-pinned-nav { grid-template-columns: 1fr; }
479}
480
481.post-pinned-card {
482 display: flex; align-items: center; gap: 1rem;
483 padding: 1rem 1.25rem;
484 border: 1.5px solid color-mix(in srgb, var(--accent) 50%, transparent);
485 background: color-mix(in srgb, var(--accent) 6%, var(--paper));
486 border-radius: 10px;
487 text-decoration: none;
488 color: var(--ink);
489 transition: border-color 150ms, background 150ms, transform 150ms;
490 min-height: 88px;
491}
492.post-pinned-card:hover {
493 border-color: var(--accent);
494 background: color-mix(in srgb, var(--accent) 11%, var(--paper));
495 transform: translateY(-2px);
496}
497.post-pinned-arrow {
498 font-size: 1.6rem;
499 color: var(--accent);
500 flex-shrink: 0;
501 line-height: 1;
502}
503.post-pinned-card--next {
504 /* "Volgende" card aligns text to the right with the arrow on the right
505 side, mirroring v9 — the arrow is on the OPPOSITE end from the body. */
506 flex-direction: row-reverse;
507 text-align: right;
508}
509.post-pinned-body {
510 display: flex; flex-direction: column; gap: 0.25rem;
511 flex: 1; min-width: 0;
512}
513.post-pinned-label {
514 font-family: var(--font-ui, system-ui), sans-serif;
515 font-size: 0.7rem; font-weight: 600;
516 text-transform: uppercase;
517 letter-spacing: 0.1em;
518 color: var(--accent);
519}
520.post-pinned-title {
521 font-family: var(--font-display, serif);
522 font-size: 1.05rem; font-weight: 600;
523 color: var(--ink);
524 line-height: 1.25;
525 overflow: hidden;
526 text-overflow: ellipsis;
527 display: -webkit-box;
528 -webkit-line-clamp: 2;
529 -webkit-box-orient: vertical;
530}
531.post-pinned-cta {
532 font-size: 0.78rem;
533 color: var(--accent);
534 font-weight: 500;
535}
536
537/* Edge-of-stack indicator: dashed border, muted styling. */
538.post-pinned-card--edge {
539 flex-direction: column;
540 align-items: flex-start;
541 justify-content: center;
542 gap: 0.35rem;
543 border-style: dashed;
544 background: transparent;
545 color: var(--ink-muted, var(--ink-soft));
546 cursor: default;
547}
548.post-pinned-card--edge:hover {
549 /* Edge cards aren't clickable — undo the lift effect. */
550 transform: none;
551 border-color: color-mix(in srgb, var(--accent) 50%, transparent);
552 background: transparent;
553}
554.post-pinned-edge-label {
555 font-family: var(--font-ui, system-ui), sans-serif;
556 font-size: 0.72rem;
557 font-weight: 600;
558 text-transform: uppercase;
559 letter-spacing: 0.15em;
560 color: var(--accent);
561}
562.post-pinned-edge-title {
563 font-family: var(--font-display, serif);
564 font-size: 1.05rem;
565 font-weight: 600;
566 color: var(--ink);
567}
568</style>
Note: See TracBrowser for help on using the repository browser.