| [7bc636b] | 1 | <footer class="site-footer">
|
|---|
| 2 | <div class="container site-footer-inner">
|
|---|
| 3 | <div>
|
|---|
| 4 | <span>© <%= new Date().getFullYear() %> <%= (site && site.title) || 'PrutFolio' %></span>
|
|---|
| 5 | </div>
|
|---|
| 6 | <div class="footer-meta">
|
|---|
| 7 | <span>PrutFolio v1</span>
|
|---|
| 8 | <%# Mobile-only theme toggle. On desktop the topnav has its own toggle
|
|---|
| 9 | so we hide this one to avoid two visible toggles at once. The same
|
|---|
| 10 | toggleTheme handler in topnav.ejs picks this button up by id. %>
|
|---|
| 11 | <button type="button" class="footer-theme-toggle" id="theme-toggle-footer"
|
|---|
| 12 | aria-label="Thema wisselen" title="Thema wisselen">
|
|---|
| 13 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|---|
| 14 | stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|---|
| 15 | <circle cx="12" cy="12" r="4"/>
|
|---|
| 16 | <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
|
|---|
| 17 | </svg>
|
|---|
| 18 | </button>
|
|---|
| 19 | </div>
|
|---|
| 20 | </div>
|
|---|
| 21 |
|
|---|
| 22 | <%# P64 — Install-app button. JS detects platform & opens modal with steps.
|
|---|
| 23 | data-apk-url is the direct download for Android users who want the
|
|---|
| 24 | native APK route instead of the PWA. %>
|
|---|
| 25 | <div class="site-footer-install container">
|
|---|
| 26 | <button type="button" class="install-app-btn" data-pcms-install-app data-apk-url="/assets/apk/soundfabrics.apk">
|
|---|
| 27 | <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path d="M5 20h14v-2H5v2zm7-18L5.33 9h3.84v6h5.66V9h3.84L12 2z" fill="currentColor"/></svg>
|
|---|
| 28 | <span>Installeer app</span>
|
|---|
| 29 | </button>
|
|---|
| 30 | </div>
|
|---|
| 31 | </footer>
|
|---|
| 32 |
|
|---|
| 33 | <style>
|
|---|
| 34 | /* Make sure the page itself is at least viewport-tall so the flex sticky-
|
|---|
| 35 | footer logic (body display:flex, main flex:1) can push the footer down.
|
|---|
| 36 | 100dvh is dynamic viewport height — handles mobile address-bar collapse. */
|
|---|
| 37 | html { min-height: 100dvh; }
|
|---|
| 38 |
|
|---|
| 39 | .site-footer {
|
|---|
| 40 | border-top: 1px solid var(--rule);
|
|---|
| 41 | margin-top: 4rem;
|
|---|
| 42 | padding: 2rem 0;
|
|---|
| 43 | padding-bottom: calc(2rem + env(safe-area-inset-bottom, 0));
|
|---|
| 44 | font-size: 0.85rem;
|
|---|
| 45 | color: var(--ink-muted);
|
|---|
| 46 | background: var(--paper);
|
|---|
| 47 | /* When fixed bars (mini-player / bottom-tab) sit at the bottom of the
|
|---|
| 48 | viewport they overlap the footer at scroll-end. The clearance below
|
|---|
| 49 | mirrors the .pcms-main rule in audio.css — content goes at the top of
|
|---|
| 50 | the footer, the player floats over the empty padding underneath. */
|
|---|
| 51 | transition: padding-bottom .2s ease;
|
|---|
| 52 | }
|
|---|
| 53 | /* Audio player visible (desktop, no bottom-tab): clear ~80px */
|
|---|
| 54 | body.has-audio-player .site-footer {
|
|---|
| 55 | padding-bottom: calc(2rem + 80px + env(safe-area-inset-bottom, 0));
|
|---|
| 56 | }
|
|---|
| 57 | @media (max-width: 767px) {
|
|---|
| 58 | /* Bottom-tab visible alone: ~56px tab */
|
|---|
| 59 | body.has-bottom-tab .site-footer {
|
|---|
| 60 | padding-bottom: calc(2rem + 56px + env(safe-area-inset-bottom, 0));
|
|---|
| 61 | }
|
|---|
| 62 | /* Bottom-tab + audio-player stacked: ~56px tab + ~72px player */
|
|---|
| 63 | body.has-bottom-tab.has-audio-player .site-footer {
|
|---|
| 64 | padding-bottom: calc(2rem + 56px + 72px + env(safe-area-inset-bottom, 0));
|
|---|
| 65 | }
|
|---|
| 66 | }
|
|---|
| 67 | .site-footer-inner {
|
|---|
| 68 | max-width: 800px;
|
|---|
| 69 | margin: 0 auto;
|
|---|
| 70 | padding: 0 1rem;
|
|---|
| 71 | display: flex;
|
|---|
| 72 | justify-content: space-between;
|
|---|
| 73 | align-items: center;
|
|---|
| 74 | flex-wrap: wrap;
|
|---|
| 75 | gap: 1rem;
|
|---|
| 76 | }
|
|---|
| 77 | .footer-meta {
|
|---|
| 78 | font-size: 0.8rem; opacity: 0.7;
|
|---|
| 79 | display: inline-flex; align-items: center; gap: 0.75rem;
|
|---|
| 80 | }
|
|---|
| 81 | .pcms-main { min-height: 50vh; }
|
|---|
| 82 |
|
|---|
| 83 | /* Mobile theme toggle in footer. Pill-style button matching topnav nav-btn.
|
|---|
| 84 | Hidden on desktop; the topnav already has a toggle there. */
|
|---|
| 85 | .footer-theme-toggle {
|
|---|
| 86 | display: none;
|
|---|
| 87 | align-items: center; justify-content: center;
|
|---|
| 88 | width: 36px; height: 36px;
|
|---|
| 89 | padding: 0; margin: 0;
|
|---|
| 90 | background: transparent; color: var(--ink);
|
|---|
| 91 | border: 1px solid var(--rule); border-radius: 999px;
|
|---|
| 92 | cursor: pointer;
|
|---|
| 93 | transition: background-color 120ms, border-color 120ms;
|
|---|
| 94 | -webkit-tap-highlight-color: transparent;
|
|---|
| 95 | opacity: 1; /* override .footer-meta dim */
|
|---|
| 96 | }
|
|---|
| 97 | .footer-theme-toggle:hover { background: var(--paper-2); border-color: var(--accent); }
|
|---|
| 98 | .footer-theme-toggle:active { transform: scale(0.96); }
|
|---|
| 99 | .footer-theme-toggle svg { width: 18px; height: 18px; }
|
|---|
| 100 |
|
|---|
| 101 | @media (max-width: 768px) {
|
|---|
| 102 | .footer-theme-toggle { display: inline-flex; }
|
|---|
| 103 | }
|
|---|
| 104 |
|
|---|
| 105 | /* Install-app button container — sits centered under the footer-inner row */
|
|---|
| 106 | .site-footer-install {
|
|---|
| 107 | display: flex;
|
|---|
| 108 | justify-content: center;
|
|---|
| 109 | margin-top: 1.25rem;
|
|---|
| 110 | }
|
|---|
| 111 | </style>
|
|---|