|
2 | 2 | <html lang="en"> |
3 | 3 | <head> |
4 | 4 | <meta charset="utf-8" /> |
5 | | - <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> |
| 6 | + <meta name="theme-color" content="#131a25" /> |
| 7 | + <meta name="theme-color" content="#131a25" media="(prefers-color-scheme: dark)" /> |
| 8 | + <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" /> |
6 | 9 | <title>Wedding of Partner A & Partner B</title> |
7 | 10 | <meta name="description" content="Join us for our wedding celebration!" /> |
8 | 11 | <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
|
43 | 46 | --btn-gap: clamp(0.4rem, 0.35rem + 0.4vw, 0.8rem); |
44 | 47 | } |
45 | 48 | * { box-sizing: border-box } |
46 | | - html, body { margin:0; height:100%; background:#101010; color:var(--text); line-height:1.65; font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial, sans-serif; } |
| 49 | + html, body { margin:0; height:100%; background:#131a25; color:var(--text); line-height:1.65; font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial, sans-serif; } |
47 | 50 | html { scroll-behavior: smooth; } |
48 | 51 | body::before, body::after { |
49 | 52 | content:""; position: fixed; width: 260px; height: 260px; z-index: 1; pointer-events:none; opacity:.55; mix-blend-mode: screen; filter: drop-shadow(0 10px 20px rgba(0,0,0,.25)); |
|
52 | 55 | body::before { top: 0; left: 0; transform: translate(-10%,-10%) rotate(0deg); } |
53 | 56 | body::after { bottom: 0; right: 0; transform: translate(10%,10%) rotate(180deg); } |
54 | 57 | .bg { position: fixed; inset: 0; overflow:hidden; z-index:0; |
55 | | - /* Lighter gradient fallback so the page isn't black before photos load */ |
| 58 | + /* Configurable golden gradient fallback using CSS variables */ |
56 | 59 | background: |
57 | | - radial-gradient(1200px circle at 75% 10%, rgba(163,188,214,0.40), transparent 55%), |
58 | | - radial-gradient(900px circle at 15% 85%, rgba(247,237,220,0.35), transparent 50%), |
59 | | - linear-gradient(180deg, #1b2230, #131a25); |
| 60 | + radial-gradient(1200px circle at 75% 10%, var(--bg-r1, rgba(234, 200, 94, 0.45)), transparent 55%), |
| 61 | + radial-gradient(900px circle at 15% 85%, var(--bg-r2, rgba(255, 239, 189, 0.38)), transparent 50%), |
| 62 | + /* Deep warm base to keep good contrast with white text */ |
| 63 | + linear-gradient(180deg, var(--bg-base-top, #2d2616), var(--bg-base-bottom, #1f1a10)); |
| 64 | + } |
| 65 | + /* iOS: ensure background covers when toolbar collapses */ |
| 66 | + @supports (height: 100lvh) { |
| 67 | + .ios .bg { inset: 0; min-height: 100lvh; } |
60 | 68 | } |
61 | 69 | #content { position: relative; z-index: 2; } |
62 | 70 | .bg-slide { position:absolute; inset:0; background-position:center; background-size:cover; opacity:0; transition: opacity 1.2s ease-in-out; will-change: opacity; } |
|
68 | 76 | @supports (height: 100dvh) { |
69 | 77 | header.hero { min-height: 100dvh; } |
70 | 78 | } |
| 79 | + /* On iOS Safari prefer dynamic viewport height for exact fit; add 1px to avoid rounding gap */ |
| 80 | + @supports (height: 100dvh) { |
| 81 | + .ios header.hero { min-height: calc(100dvh + 1px); } |
| 82 | + } |
| 83 | + /* Background already covers the safe area using 100lvh; no extra offset needed */ |
71 | 84 | .hero .inner { |
72 | 85 | position:relative; z-index:2; width:min(92vw, 840px); |
73 | 86 | background: linear-gradient(180deg, rgba(255,255,255,0.1), rgba(255,255,255,0.06)); |
|
151 | 164 | .loading { text-align:center; padding:2rem; opacity:.9; } |
152 | 165 | .hide { display:none !important; } |
153 | 166 | </style> |
154 | | -</head> |
155 | | -<body> |
| 167 | + </head> |
| 168 | + <body> |
| 169 | + <script> |
| 170 | + (function(){ |
| 171 | + try { |
| 172 | + var ua = navigator.userAgent || navigator.platform || ""; |
| 173 | + var isIOS = /iPad|iPhone|iPod/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); |
| 174 | + if (isIOS) document.documentElement.classList.add('ios'); |
| 175 | + } catch {} |
| 176 | + })(); |
| 177 | + </script> |
156 | 178 | <div class="bg" id="bg"></div> |
157 | 179 | <div id="loading" class="loading">Loading details…</div> |
158 | 180 |
|
@@ -246,22 +268,32 @@ <h2>FAQ</h2> |
246 | 268 |
|
247 | 269 | // Theme colors -> CSS variables |
248 | 270 | function applyThemeColors(cfg) { |
249 | | - const colors = cfg?.ui?.colors || cfg?.colors; if (!colors) return; |
250 | 271 | const root = document.documentElement; |
251 | | - const pick = (...k) => { for (const key of k) { const v = colors[key]; if (typeof v === 'string' && v.trim()) return v.trim(); } }; |
252 | | - const map = [ |
253 | | - ['--accent-1', pick('accent1','accent-1')], |
254 | | - ['--accent-2', pick('accent2','accent-2')], |
255 | | - ['--accent-3', pick('accent3','accent-3')], |
256 | | - ['--text', pick('text')], |
257 | | - ['--ink', pick('ink')], |
258 | | - ['--bg-overlay', pick('bgOverlay','bg-overlay')], |
259 | | - ['--border', pick('border')], |
260 | | - ['--card', pick('card')], |
261 | | - ]; |
262 | | - for (const [css, val] of map) if (val) root.style.setProperty(css, val); |
263 | | - const maxw = pick('maxw','maxWidth'); if (maxw) root.style.setProperty('--maxw', typeof maxw === 'number' ? `${maxw}px` : maxw); |
264 | | - const blur = pick('blur'); if (blur) root.style.setProperty('--blur', blur); |
| 272 | + const colors = cfg?.ui?.colors || cfg?.colors || {}; |
| 273 | + const pickFrom = (obj, ...keys) => { |
| 274 | + for (const k of keys) { const v = obj?.[k]; if (typeof v === 'string' && v.trim()) return v.trim(); } |
| 275 | + }; |
| 276 | + const setVar = (name, value) => { if (typeof value === 'string' && value.trim()) root.style.setProperty(name, value.trim()); }; |
| 277 | + // Existing color variables |
| 278 | + setVar('--accent-1', pickFrom(colors, 'accent1','accent-1')); |
| 279 | + setVar('--accent-2', pickFrom(colors, 'accent2','accent-2')); |
| 280 | + setVar('--accent-3', pickFrom(colors, 'accent3','accent-3')); |
| 281 | + setVar('--text', pickFrom(colors, 'text')); |
| 282 | + setVar('--ink', pickFrom(colors, 'ink')); |
| 283 | + setVar('--bg-overlay',pickFrom(colors, 'bgOverlay','bg-overlay')); |
| 284 | + setVar('--border', pickFrom(colors, 'border')); |
| 285 | + setVar('--card', pickFrom(colors, 'card')); |
| 286 | + const maxw = pickFrom(colors, 'maxw','maxWidth'); if (maxw) root.style.setProperty('--maxw', typeof maxw === 'number' ? `${maxw}px` : maxw); |
| 287 | + const blur = pickFrom(colors, 'blur'); if (blur) root.style.setProperty('--blur', blur); |
| 288 | + |
| 289 | + // Background customization from colors or ui.background/background |
| 290 | + // Accept both flat keys in colors and nested object with keys: baseTop, baseBottom, radial1, radial2 |
| 291 | + const bgCfg = (cfg?.ui?.background || cfg?.background || {}); |
| 292 | + const bgColors = { ...colors, ...bgCfg }; |
| 293 | + setVar('--bg-base-top', pickFrom(bgColors, 'bgBaseTop','bg-base-top','baseTop','top')); |
| 294 | + setVar('--bg-base-bottom', pickFrom(bgColors, 'bgBaseBottom','bg-base-bottom','baseBottom','bottom')); |
| 295 | + setVar('--bg-r1', pickFrom(bgColors, 'bgRadial1','bg-r1','radial1','r1')); |
| 296 | + setVar('--bg-r2', pickFrom(bgColors, 'bgRadial2','bg-r2','radial2','r2')); |
265 | 297 | } |
266 | 298 |
|
267 | 299 | // Buttons |
|
0 commit comments