115 lines
5.6 KiB
HTML
115 lines
5.6 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
<title>Newbury Nights</title>
|
|
<style>
|
|
:root { color-scheme: dark; --accent:#51eaf1; --text:#e8ecff; }
|
|
html, body { margin:0; height:100%; overflow:hidden; background:#06080f; font-family:system-ui, sans-serif; color:var(--text); }
|
|
#cam { position:fixed; inset:0; width:100%; height:100%; object-fit:cover; }
|
|
#gl { position:fixed; inset:0; width:100%; height:100%; }
|
|
.hidden { display:none !important; }
|
|
|
|
/* Flow layout, not absolute positioning: the disclaimer is a normal flex child pinned
|
|
to the bottom by margin-top:auto, so it can never overlap the buttons on short
|
|
screens. Content scrolls if the viewport is too small for it. */
|
|
#startScreen { position:fixed; inset:0; display:flex; flex-direction:column; align-items:center;
|
|
text-align:center; z-index:10; background-size:cover; background-position:center;
|
|
padding:max(18px, env(safe-area-inset-top)) 20px max(16px, env(safe-area-inset-bottom));
|
|
overflow-y:auto; overscroll-behavior:contain; }
|
|
#startScreen::before { content:''; position:fixed; inset:0; background:#06080f; opacity:var(--ov,.55); }
|
|
#startScreen > * { position:relative; flex:none; }
|
|
/* centres the block when there's room, without ever squashing the disclaimer */
|
|
#startInner { margin:auto 0; display:flex; flex-direction:column; align-items:center; width:100%; }
|
|
#logo { max-width:min(78vw, 420px); max-height:min(28vh, 240px); margin-bottom:16px; object-fit:contain; }
|
|
#title { font-size:clamp(1.6rem,6.5vw,2.8rem); margin:0 0 10px; letter-spacing:.06em; }
|
|
#subtitle { opacity:.85; margin:0 0 26px; max-width:34rem; line-height:1.5; font-size:clamp(.9rem,3.6vw,1rem); }
|
|
.btn { font-size:1.1rem; padding:14px 38px; border-radius:999px; border:0; cursor:pointer;
|
|
background:linear-gradient(135deg, var(--accent), #529eff); color:#04121a; font-weight:700; }
|
|
.btn.ghost { background:transparent; color:var(--text); border:1px solid rgba(255,255,255,.35);
|
|
font-size:.92rem; padding:9px 22px; font-weight:500; margin-top:12px; }
|
|
.disclaimer { font-size:.66rem; opacity:.55; line-height:1.4; max-width:36rem;
|
|
margin-top:auto; padding-top:22px; }
|
|
|
|
/* details page */
|
|
#details { position:fixed; inset:0; z-index:20; overflow:auto; background:#080b16;
|
|
padding:0 0 calc(90px + env(safe-area-inset-bottom)); }
|
|
#details .inner { max-width:44rem; margin:0 auto;
|
|
padding:max(24px, env(safe-area-inset-top)) 20px 0; }
|
|
#details h1 { font-size:1.6rem; color:var(--accent); }
|
|
#details h2, #details h3 { color:var(--accent); margin-top:1.4em; }
|
|
#details p, #details li { line-height:1.65; opacity:.9; }
|
|
#details a { color:var(--accent); }
|
|
#backBtn { position:fixed; bottom:0; left:0; right:0; border:0; font-size:1rem;
|
|
padding:14px 14px calc(14px + env(safe-area-inset-bottom));
|
|
background:#111730; color:var(--text); cursor:pointer; border-top:1px solid #1e2748; }
|
|
|
|
#hud { position:fixed; top:0; left:0; right:0; display:flex; justify-content:space-between; padding:10px 14px;
|
|
pointer-events:none; z-index:5; text-shadow:0 1px 3px #000; }
|
|
#hud span { background:rgba(6,8,15,.55); border-radius:8px; padding:5px 10px; font-size:.85rem; }
|
|
#trk.warn { color:#ffd166; }
|
|
|
|
/* shutter */
|
|
#shutter { position:fixed; bottom:calc(28px + env(safe-area-inset-bottom)); left:50%;
|
|
transform:translateX(-50%); z-index:6;
|
|
width:68px; height:68px; border-radius:50%; border:4px solid rgba(255,255,255,.85);
|
|
background:rgba(255,255,255,.22); cursor:pointer; backdrop-filter:blur(2px); }
|
|
#shutter:active { transform:translateX(-50%) scale(.92); background:rgba(255,255,255,.5); }
|
|
#flash { position:fixed; inset:0; background:#fff; opacity:0; pointer-events:none; z-index:30; transition:opacity .35s; }
|
|
#flash.on { opacity:.85; transition:opacity .05s; }
|
|
|
|
#toast { position:fixed; bottom:110px; left:50%; transform:translateX(-50%) translateY(20px);
|
|
background:rgba(20,28,55,.92); padding:10px 20px; border-radius:999px; opacity:0; transition:all .3s; z-index:6; }
|
|
#toast.show { opacity:1; transform:translateX(-50%) translateY(0); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<video id="cam" playsinline muted></video>
|
|
<canvas id="gl"></canvas>
|
|
|
|
<div id="startScreen">
|
|
<div id="startInner">
|
|
<img id="logo" class="hidden" alt="">
|
|
<h1 id="title"></h1>
|
|
<p id="subtitle"></p>
|
|
<button id="start" class="btn"></button>
|
|
<button id="detailsBtn" class="btn ghost"></button>
|
|
</div>
|
|
<div class="disclaimer" id="disc1"></div>
|
|
</div>
|
|
|
|
<div id="details" class="hidden">
|
|
<div class="inner">
|
|
<h1 id="detailsTitle"></h1>
|
|
<div id="detailsBody"></div>
|
|
<div class="disclaimer" style="position:static;margin-top:32px" id="disc2"></div>
|
|
</div>
|
|
<button id="backBtn">← Back</button>
|
|
</div>
|
|
|
|
<div id="hud" class="hidden">
|
|
<span id="trk">—</span>
|
|
<span id="cnt">—</span>
|
|
</div>
|
|
<button id="shutter" class="hidden" aria-label="Take photo"></button>
|
|
<div id="flash"></div>
|
|
<div id="toast"></div>
|
|
|
|
<!-- js-aruco2 vendor chain — load order matters: cv -> svd -> posit -> aruco -> dictionary -->
|
|
<script src="/vendor/cv.js"></script>
|
|
<script src="/vendor/svd.js"></script>
|
|
<script src="/vendor/posit1.js"></script>
|
|
<script src="/vendor/aruco.js"></script>
|
|
<script src="/vendor/dictionaries/aruco_4x4_1000.js"></script>
|
|
|
|
<script type="importmap">
|
|
{ "imports": {
|
|
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
|
|
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
|
|
} }
|
|
</script>
|
|
<script type="module" src="/js/exhibit.js"></script>
|
|
</body>
|
|
</html>
|