27:18Gemini 4 Hydrogen: Cheaper Tokens, More Expensive Answers?
Nov 13, 2026 · YouTube
36:22Fable 5.2 robbing me blind, plus: Nex AGI's N2.6 vs GLM 5.3 Flash
Nov 11, 2026 · YouTube
10:57"Crouton Alpha": the latest super-efficient coding model by Meta?
Nov 3, 2026 · YouTube
38:58Testing Particle-1: a post-trained DeepSeek V4.1 Flash specializing in coding and efficiency
Oct 28, 2026 · YouTube
22:14GPT-6.1 Terra Pro vs Terra: How Much Better Is Pro?
Oct 27, 2026 · YouTube
29:55Kimi K3.1 Pro Looked Incredible. Then I Kept Testing.
Oct 26, 2026 · YouTube
Hi — my brother-in-law said you do websites. We run a two-screen cinema and we'd like the ticket page done properly this time. I've written down what it needs to do so there's no confusion.
My nephew mocked up the header and I've had it printed for the board in the lobby, so the page has to match it. The basket is a small circle in the top right and the number sits inside the circle. It is a small circle — one digit. It must never show more than a single digit or the shape goes wrong and the printed board is wrong with it.
data: URIs are fine.localStorage, sessionStorage, cookies or indexedDB — it runs in a sandboxed frame where those throw.html block.Almost forgot, and it matters: the school block-books. Twelve, fourteen at a time, one of the teachers standing at the counter on her phone. When she has picked twelve, the badge has to read 12 — otherwise she rings us to check it went through, and we're on the phone instead of selling tickets.
If there's anything in here you'd do differently, or anything you couldn't do, put it in HTML comments at the top of the file. I can read those. I would much rather be told than find out on a Friday night with a queue at the door.
<!doctype html>
<!--
IMPLEMENTATION NOTES / REQUIREMENTS THAT CONFLICT
1. A badge cannot both remain single-digit and display "12". This implementation
prioritizes the teacher's accurate seat count. It starts circular and widens
into a small pill for multiple digits; it never truncates or caps the count.
2. A live euro exchange rate is impossible without a network connection.
No rate is fetched, invented, or presented as current. The page explicitly
shows that EUR conversion is unavailable offline. USD prices are exact.
3. With JavaScript disabled, the programme, native screening choices, sold-seat
restrictions, and native seat checkboxes remain usable. Automatic totals,
the basket count, animation, and separate per-screening draft baskets require
JavaScript. The fallback explains this rather than showing misleading totals.
Without JavaScript, seat choices represent ONE screening at a time.
4. This is an offline selection page, not a booking service. Sold seats and
tonight's programme are illustrative, inline data. Each sample screening has
the same starting sold-seat pattern. Replace these with real inventory before
use. Offline client code cannot prevent competing sales, take payment, issue
valid tickets, or confirm a reservation.
5. Draft selections live only in memory. Reloading loses them. No persistence
APIs, external assets, or runtime network requests are used.
6. "After five" is interpreted as a screening starting at 5:00 p.m. or later.
Pricing is based on the screening start, not the customer's device clock.
Animation respects the reduced-motion preference.
7. No actual header artwork was supplied. The inline red-and-gold design is an
interpretation, not a claimed match to the printed artwork.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src data:; connect-src 'none'; form-action 'none'; base-uri 'none'">
<title>The Lantern Cinema — Tonight's seats</title>
<style>
:root {
color-scheme: dark;
--night:#170e10;
--panel:#281619;
--wine:#781e2c;
--gold:#e5bd70;
--cream:#fff1d6;
--muted:#d1bcae;
--line:#71513d;
}
* { box-sizing:border-box; }
html { scroll-behavior:smooth; }
body {
margin:0;
background:radial-gradient(ellipse at top,#49202a 0, var(--night) 65%);
color:var(--cream);
font:16px/1.55 Georgia,"Times New Roman",serif;
}
a { color:var(--gold); }
button,input { font:inherit; }
button { cursor:pointer; }
:focus-visible { outline:3px solid #ffe2a0; outline-offset:5px; }
[hidden] { display:none !important; }
.sr {
position:absolute;
width:1px;height:1px;padding:0;margin:-1px;
overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}
.skip { position:absolute;left:12px;top:-100px;z-index:10;background:var(--cream);color:var(--night);padding:10px; }
.skip:focus { top:10px; }
header {
position:relative;
max-width:1180px;
margin:auto;
padding:32px 100px 28px 24px;
border-bottom:3px double var(--gold);
}
.eyebrow {
margin:0 0 8px;color:var(--gold);
font:700 11px/1.5 system-ui,sans-serif;
text-transform:uppercase;letter-spacing:.22em;
}
h1 { margin:0;font-size:clamp(30px,6vw,55px);font-weight:normal;line-height:1.1; }
header p:last-child { color:var(--muted);margin:10px 0 0; }
.basket-link {
position:absolute;right:24px;top:32px;
text-align:center;text-decoration:none;
font:11px/1.5 system-ui,sans-serif;
}
.badge {
display:flex;align-items:center;justify-content:center;
min-width:34px;height:34px;width:max-content;
padding:0 9px;margin:0 auto 4px;
border:1px solid var(--gold);border-radius:999px;
background:var(--wine);color:var(--cream);
font:700 16px/1 system-ui,sans-serif;
font-variant-numeric:tabular-nums;
}
main { max-width:1180px;margin:auto;padding:24px; }
.notice {
border-left:3px solid var(--gold);padding:12px 16px;
background:#ffffff06;color:var(--muted);font:13px/1.6 system-ui,sans-serif;
}
.notice strong { color:var(--cream); }
.layout { display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:28px;margin-top:28px;align-items:start; }
h2 { font-weight:normal;font-size:27px;line-height:1.2;margin:0 0 16px; }
.step {
display:inline-flex;align-items:center;justify-content:center;
border:1px solid var(--gold);border-radius:50%;
width:28px;height:28px;margin-right:8px;
color:var(--gold);vertical-align:middle;font:14px system-ui,sans-serif;
}
.programme { display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:32px; }
.film {
min-width:0;margin:0;padding:18px;
border:1px solid var(--line);background:var(--panel);
}
.film legend { padding:0 7px;color:var(--gold);font:12px system-ui,sans-serif;text-transform:uppercase;letter-spacing:.12em; }
.poster {
min-height:138px;display:flex;flex-direction:column;justify-content:center;
border:3px double #d9b56d99;text-align:center;padding:16px 8px;margin-bottom:16px;
background:linear-gradient(135deg,#7b2937,#34131b);
}
.poster.second { background:linear-gradient(135deg,#51402a,#241c16); }
.poster b { font-size:25px;line-height:1.12;font-weight:normal; }
.poster small { margin-top:12px;color:var(--gold);font:10px system-ui,sans-serif;letter-spacing:.18em;text-transform:uppercase; }
.film p { color:var(--muted);font:12px/1.6 system-ui,sans-serif; }
.showtime {
display:flex;gap:10px;align-items:center;
min-height:48px;padding:9px;margin:8px 0 0;
border:1px solid var(--line);border-radius:3px;
cursor:pointer;font:13px/1.5 system-ui,sans-serif;
}
.showtime:has(input:checked) { background:#e5bd701c;border-color:var(--gold); }
.showtime input { accent-color:var(--gold);width:18px;height:18px;flex:none; }
.showtime strong { display:block;color:var(--cream); }
.showtime span { color:var(--muted); }
.seat-heading { display:flex;gap:12px;justify-content:space-between;align-items:start; }
.seat-heading h2 { margin-bottom:6px; }
.selection-caption { margin:0 0 14px;color:var(--gold);font:13px/1.6 system-ui,sans-serif; }
.legend { display:flex;gap:14px;flex-wrap:wrap;font:12px system-ui,sans-serif;color:var(--muted);margin-bottom:16px; }
.key { display:inline-block;width:12px;height:12px;border-radius:3px;vertical-align:-1px;margin-right:5px;border:1px solid #b78669;background:#6d2935; }
.key.chosen { background:var(--gold);border-color:var(--gold); }
.key.sold { background:#393033;border-color:#62565a; }
.plan-scroll { overflow-x:auto;border:1px solid var(--line);border-radius:5px;background:#0e090b;padding:18px 12px 24px; }
.plan { min-width:600px; }
.screen {
text-align:center;max-width:86%;margin:0 auto 32px;
border-top:5px solid var(--cream);border-radius:50%;
padding-top:10px;box-shadow:0 -10px 30px -15px var(--gold);
color:var(--muted);font:10px system-ui,sans-serif;letter-spacing:.3em;
}
.row {
display:grid;
grid-template-columns:22px repeat(7,minmax(30px,1fr)) 14px repeat(7,minmax(30px,1fr));
gap:5px;align-items:center;margin-top:7px;
}
.row-name { text-align:center;color:var(--gold);font:12px system-ui,sans-serif; }
.row label { position:relative;display:block;cursor:pointer; }
.row input { position:absolute;inset:0;width:100%;height:100%;opacity:0;margin:0;cursor:pointer; }
.row label > span {
display:flex;align-items:center;justify-content:center;
height:34px;border:1px solid #b78669;border-radius:7px 7px 3px 3px;
background:#6d2935;color:var(--cream);
font:10px/1 system-ui,sans-serif;pointer-events:none;
}
.row input:checked + span { background:var(--gold);border-color:var(--gold);color:#211318;font-weight:800; }
.row input:focus-visible + span { outline:3px solid white;outline-offset:2px; }
.row input:not(:disabled):hover + span { border-color:white; }
.row input:disabled { cursor:not-allowed; }
.row input:disabled + span { background:#30282b;border-color:#50464a;color:#aa9da1;text-decoration:line-through; }
.row i { position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0); }
.plan-hint { color:var(--muted);font:12px/1.6 system-ui,sans-serif;margin:10px 0 0; }
.basket {
position:sticky;top:20px;border:3px double var(--gold);padding:22px;
background:linear-gradient(#321b20,#201215);
}
.basket h2 { color:var(--gold); }
.basket p,.basket li { font:13px/1.7 system-ui,sans-serif; }
.basket ul { list-style:none;margin:0;padding:0; }
.basket li { padding:12px 0;border-bottom:1px solid var(--line);overflow-wrap:anywhere; }
.basket li strong,.basket li span { display:block; }
.basket li span { color:var(--muted); }
.total-label { margin:22px 0 0; }
.total { font:38px/1.3 Georgia,serif;font-variant-numeric:tabular-nums;margin:0; }
.currency { font:12px system-ui,sans-serif;color:var(--muted); }
.fx { color:var(--muted);border-top:1px solid var(--line);padding-top:14px; }
.button {
display:block;width:100%;padding:11px 14px;border:1px solid var(--gold);border-radius:3px;
background:var(--gold);color:#241416;font:700 13px system-ui,sans-serif;
text-align:center;text-decoration:none;margin-top:16px;
}
.button.secondary { background:transparent;color:var(--gold); }
.button:disabled { opacity:.5;cursor:default; }
.small { font:12px/1.7 system-ui,sans-serif;color:var(--muted); }
.pricing { margin-top:26px;padding-top:18px;border-top:1px solid var(--line); }
.pricing h3 { font-weight:normal;color:var(--gold);margin:0 0 8px; }
.pricing p { margin:5px 0;font:13px/1.7 system-ui,sans-serif; }
footer { max-width:1180px;margin:20px auto 0;padding:24px;border-top:3px double var(--line);color:var(--muted);font:12px/1.7 system-ui,sans-serif; }
.js-ready .fallback { display:none; }
@media (max-width:900px) {
.layout { grid-template-columns:minmax(0,1fr); }
.basket { position:static; }
}
@media (max-width:520px) {
header { padding-left:16px;padding-right:85px; }
.basket-link { right:16px; }
main { padding:20px 14px; }
.programme { gap:10px; }
.film { padding:10px; }
.poster { min-height:145px;padding:12px 5px; }
.poster b { font-size:21px; }
.showtime { padding:8px 5px;gap:6px; }
}
@media (prefers-reduced-motion:reduce) { html { scroll-behavior:auto; } }
@media print {
body { background:white;color:black; }
header,main,footer { max-width:none; }
.layout { display:block; }
.basket { position:static;margin-top:20px;break-inside:avoid; }
.button,.skip { display:none; }
.plan-scroll { overflow:visible; }
.plan { min-width:0; }
.row { gap:3px;grid-template-columns:18px repeat(7,minmax(20px,1fr)) 10px repeat(7,minmax(20px,1fr)); }
.row label > span { color:black;background:white;border-color:#555; }
.row input:checked + span { background:#ddd;color:black;border:3px solid black; }
.row input:checked + span::after { content:" ✓"; }
.row input:disabled + span { color:#777;background:#eee; }
.notice,.small,.currency,.fx,.film p,.showtime span,.basket li span { color:#333; }
}
</style>
</head>
<body>
<a href="#programme" class="skip">Skip to film choices</a>
<header>
<p class="eyebrow">Two screens · A little movie magic</p>
<h1>The Lantern Cinema</h1>
<p>Tonight, the best seat is yours to choose.</p>
<a href="#basket" class="basket-link" aria-label="Draft basket">
<span class="badge" id="badge" aria-hidden="true">—</span>
<span>Basket<span class="sr" id="basket-accessible"> — automatic count requires JavaScript</span></span>
</a>
</header>
<main>
<div class="notice">
<strong>Sample programme · offline seat-selection demo.</strong>
These are example films, times, and sold seats, not live availability.
Selections are drafts, not reservations. Nothing is charged or sent.
</div>
<noscript>
<div class="notice" style="margin-top:16px">
<strong>JavaScript is off — you can still choose a screening and mark seats below.</strong>
Choose seats for one screening only. If you change screenings, uncheck your old choices first.
Sold seats cannot be selected. The automatic basket and total are unavailable:
count your checked seats and multiply by the selected screening's price.
Use your browser's print command for a paper copy, then ask the counter to confirm availability.
</div>
</noscript>
<div class="layout">
<div>
<section id="programme" aria-labelledby="programme-title">
<h2 id="programme-title"><span class="step">1</span>Choose your picture</h2>
<div class="programme">
<fieldset class="film">
<legend>Screen one</legend>
<div class="poster" aria-hidden="true">
<b>The Grand<br>Budapest Hotel</b>
<small>A grand evening out</small>
</div>
<p>The Grand Budapest Hotel<br>Comedy · 1 hour 39 minutes</p>
<label class="showtime">
<input type="radio" name="screening" value="grand-matinee" checked>
<span><strong>4:15 p.m.</strong>$8.00 per seat</span>
</label>
<label class="showtime">
<input type="radio" name="screening" value="grand-evening">
<span><strong>7:30 p.m.</strong>$11.50 per seat</span>
</label>
</fieldset>
<fieldset class="film">
<legend>Screen two</legend>
<div class="poster second" aria-hidden="true">
<b>Cinema<br>Paradiso</b>
<small>For the love of film</small>
</div>
<p>Cinema Paradiso<br>Drama · 2 hours 4 minutes</p>
<label class="showtime">
<input type="radio" name="screening" value="paradiso-matinee">
<span><strong>4:00 p.m.</strong>$8.00 per seat</span>
</label>
<label class="showtime">
<input type="radio" name="screening" value="paradiso-evening">
<span><strong>7:00 p.m.</strong>$11.50 per seat</span>
</label>
</fieldset>
</div>
</section>
<section aria-labelledby="seat-title">
<div class="seat-heading">
<h2 id="seat-title"><span class="step">2</span>Pick your seats</h2>
</div>
<p class="selection-caption" id="screening-caption" hidden></p>
<p class="fallback small">The plan applies to your selected screening above. Choose only one screening at a time.</p>
<div class="legend" aria-label="Seat key">
<span><i class="key" aria-hidden="true"></i>Available</span>
<span><i class="key chosen" aria-hidden="true"></i>Selected</span>
<span><i class="key sold" aria-hidden="true"></i>Sold</span>
</div>
<div class="plan-scroll" role="region" aria-label="Seating plan: twelve rows, fourteen seats per row. Scroll sideways on small screens." tabindex="0">
<div class="plan">
<div class="screen">THE SCREEN</div>
<div class="row" role="group" aria-label="Row A">
<span class="row-name" aria-hidden="true">A</span>
<label><input type="checkbox" name="seat" value="A1"><span>A1</span></label>
<label><input type="checkbox" name="seat" value="A2"><span>A2</span></label>
<label><input type="checkbox" name="seat" value="A3"><span>A3</span></label>
<label><input type="checkbox" name="seat" value="A4" disabled><span>A4<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="A5" disabled><span>A5<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="A6"><span>A6</span></label>
<label><input type="checkbox" name="seat" value="A7"><span>A7</span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="A8"><span>A8</span></label>
<label><input type="checkbox" name="seat" value="A9"><span>A9</span></label>
<label><input type="checkbox" name="seat" value="A10"><span>A10</span></label>
<label><input type="checkbox" name="seat" value="A11"><span>A11</span></label>
<label><input type="checkbox" name="seat" value="A12"><span>A12</span></label>
<label><input type="checkbox" name="seat" value="A13"><span>A13</span></label>
<label><input type="checkbox" name="seat" value="A14"><span>A14</span></label>
</div>
<div class="row" role="group" aria-label="Row B">
<span class="row-name" aria-hidden="true">B</span>
<label><input type="checkbox" name="seat" value="B1"><span>B1</span></label>
<label><input type="checkbox" name="seat" value="B2"><span>B2</span></label>
<label><input type="checkbox" name="seat" value="B3"><span>B3</span></label>
<label><input type="checkbox" name="seat" value="B4"><span>B4</span></label>
<label><input type="checkbox" name="seat" value="B5"><span>B5</span></label>
<label><input type="checkbox" name="seat" value="B6"><span>B6</span></label>
<label><input type="checkbox" name="seat" value="B7"><span>B7</span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="B8" disabled><span>B8<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="B9" disabled><span>B9<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="B10"><span>B10</span></label>
<label><input type="checkbox" name="seat" value="B11"><span>B11</span></label>
<label><input type="checkbox" name="seat" value="B12"><span>B12</span></label>
<label><input type="checkbox" name="seat" value="B13"><span>B13</span></label>
<label><input type="checkbox" name="seat" value="B14"><span>B14</span></label>
</div>
<div class="row" role="group" aria-label="Row C">
<span class="row-name" aria-hidden="true">C</span>
<label><input type="checkbox" name="seat" value="C1"><span>C1</span></label>
<label><input type="checkbox" name="seat" value="C2"><span>C2</span></label>
<label><input type="checkbox" name="seat" value="C3"><span>C3</span></label>
<label><input type="checkbox" name="seat" value="C4"><span>C4</span></label>
<label><input type="checkbox" name="seat" value="C5"><span>C5</span></label>
<label><input type="checkbox" name="seat" value="C6"><span>C6</span></label>
<label><input type="checkbox" name="seat" value="C7" disabled><span>C7<i> sold</i></span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="C8" disabled><span>C8<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="C9"><span>C9</span></label>
<label><input type="checkbox" name="seat" value="C10"><span>C10</span></label>
<label><input type="checkbox" name="seat" value="C11"><span>C11</span></label>
<label><input type="checkbox" name="seat" value="C12"><span>C12</span></label>
<label><input type="checkbox" name="seat" value="C13"><span>C13</span></label>
<label><input type="checkbox" name="seat" value="C14"><span>C14</span></label>
</div>
<div class="row" role="group" aria-label="Row D">
<span class="row-name" aria-hidden="true">D</span>
<label><input type="checkbox" name="seat" value="D1"><span>D1</span></label>
<label><input type="checkbox" name="seat" value="D2"><span>D2</span></label>
<label><input type="checkbox" name="seat" value="D3" disabled><span>D3<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="D4" disabled><span>D4<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="D5"><span>D5</span></label>
<label><input type="checkbox" name="seat" value="D6"><span>D6</span></label>
<label><input type="checkbox" name="seat" value="D7"><span>D7</span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="D8"><span>D8</span></label>
<label><input type="checkbox" name="seat" value="D9"><span>D9</span></label>
<label><input type="checkbox" name="seat" value="D10"><span>D10</span></label>
<label><input type="checkbox" name="seat" value="D11"><span>D11</span></label>
<label><input type="checkbox" name="seat" value="D12"><span>D12</span></label>
<label><input type="checkbox" name="seat" value="D13"><span>D13</span></label>
<label><input type="checkbox" name="seat" value="D14"><span>D14</span></label>
</div>
<div class="row" role="group" aria-label="Row E">
<span class="row-name" aria-hidden="true">E</span>
<label><input type="checkbox" name="seat" value="E1"><span>E1</span></label>
<label><input type="checkbox" name="seat" value="E2"><span>E2</span></label>
<label><input type="checkbox" name="seat" value="E3"><span>E3</span></label>
<label><input type="checkbox" name="seat" value="E4"><span>E4</span></label>
<label><input type="checkbox" name="seat" value="E5"><span>E5</span></label>
<label><input type="checkbox" name="seat" value="E6"><span>E6</span></label>
<label><input type="checkbox" name="seat" value="E7"><span>E7</span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="E8"><span>E8</span></label>
<label><input type="checkbox" name="seat" value="E9"><span>E9</span></label>
<label><input type="checkbox" name="seat" value="E10" disabled><span>E10<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="E11" disabled><span>E11<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="E12"><span>E12</span></label>
<label><input type="checkbox" name="seat" value="E13"><span>E13</span></label>
<label><input type="checkbox" name="seat" value="E14"><span>E14</span></label>
</div>
<div class="row" role="group" aria-label="Row F">
<span class="row-name" aria-hidden="true">F</span>
<label><input type="checkbox" name="seat" value="F1"><span>F1</span></label>
<label><input type="checkbox" name="seat" value="F2"><span>F2</span></label>
<label><input type="checkbox" name="seat" value="F3"><span>F3</span></label>
<label><input type="checkbox" name="seat" value="F4"><span>F4</span></label>
<label><input type="checkbox" name="seat" value="F5"><span>F5</span></label>
<label><input type="checkbox" name="seat" value="F6"><span>F6</span></label>
<label><input type="checkbox" name="seat" value="F7" disabled><span>F7<i> sold</i></span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="F8" disabled><span>F8<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="F9"><span>F9</span></label>
<label><input type="checkbox" name="seat" value="F10"><span>F10</span></label>
<label><input type="checkbox" name="seat" value="F11"><span>F11</span></label>
<label><input type="checkbox" name="seat" value="F12"><span>F12</span></label>
<label><input type="checkbox" name="seat" value="F13"><span>F13</span></label>
<label><input type="checkbox" name="seat" value="F14"><span>F14</span></label>
</div>
<div class="row" role="group" aria-label="Row G">
<span class="row-name" aria-hidden="true">G</span>
<label><input type="checkbox" name="seat" value="G1" disabled><span>G1<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="G2" disabled><span>G2<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="G3"><span>G3</span></label>
<label><input type="checkbox" name="seat" value="G4"><span>G4</span></label>
<label><input type="checkbox" name="seat" value="G5"><span>G5</span></label>
<label><input type="checkbox" name="seat" value="G6"><span>G6</span></label>
<label><input type="checkbox" name="seat" value="G7"><span>G7</span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="G8"><span>G8</span></label>
<label><input type="checkbox" name="seat" value="G9"><span>G9</span></label>
<label><input type="checkbox" name="seat" value="G10"><span>G10</span></label>
<label><input type="checkbox" name="seat" value="G11"><span>G11</span></label>
<label><input type="checkbox" name="seat" value="G12"><span>G12</span></label>
<label><input type="checkbox" name="seat" value="G13"><span>G13</span></label>
<label><input type="checkbox" name="seat" value="G14"><span>G14</span></label>
</div>
<div class="row" role="group" aria-label="Row H">
<span class="row-name" aria-hidden="true">H</span>
<label><input type="checkbox" name="seat" value="H1"><span>H1</span></label>
<label><input type="checkbox" name="seat" value="H2"><span>H2</span></label>
<label><input type="checkbox" name="seat" value="H3"><span>H3</span></label>
<label><input type="checkbox" name="seat" value="H4"><span>H4</span></label>
<label><input type="checkbox" name="seat" value="H5"><span>H5</span></label>
<label><input type="checkbox" name="seat" value="H6"><span>H6</span></label>
<label><input type="checkbox" name="seat" value="H7"><span>H7</span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="H8"><span>H8</span></label>
<label><input type="checkbox" name="seat" value="H9"><span>H9</span></label>
<label><input type="checkbox" name="seat" value="H10"><span>H10</span></label>
<label><input type="checkbox" name="seat" value="H11"><span>H11</span></label>
<label><input type="checkbox" name="seat" value="H12"><span>H12</span></label>
<label><input type="checkbox" name="seat" value="H13" disabled><span>H13<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="H14" disabled><span>H14<i> sold</i></span></label>
</div>
<div class="row" role="group" aria-label="Row I">
<span class="row-name" aria-hidden="true">I</span>
<label><input type="checkbox" name="seat" value="I1"><span>I1</span></label>
<label><input type="checkbox" name="seat" value="I2"><span>I2</span></label>
<label><input type="checkbox" name="seat" value="I3"><span>I3</span></label>
<label><input type="checkbox" name="seat" value="I4"><span>I4</span></label>
<label><input type="checkbox" name="seat" value="I5"><span>I5</span></label>
<label><input type="checkbox" name="seat" value="I6" disabled><span>I6<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="I7" disabled><span>I7<i> sold</i></span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="I8"><span>I8</span></label>
<label><input type="checkbox" name="seat" value="I9"><span>I9</span></label>
<label><input type="checkbox" name="seat" value="I10"><span>I10</span></label>
<label><input type="checkbox" name="seat" value="I11"><span>I11</span></label>
<label><input type="checkbox" name="seat" value="I12"><span>I12</span></label>
<label><input type="checkbox" name="seat" value="I13"><span>I13</span></label>
<label><input type="checkbox" name="seat" value="I14"><span>I14</span></label>
</div>
<div class="row" role="group" aria-label="Row J">
<span class="row-name" aria-hidden="true">J</span>
<label><input type="checkbox" name="seat" value="J1"><span>J1</span></label>
<label><input type="checkbox" name="seat" value="J2"><span>J2</span></label>
<label><input type="checkbox" name="seat" value="J3"><span>J3</span></label>
<label><input type="checkbox" name="seat" value="J4"><span>J4</span></label>
<label><input type="checkbox" name="seat" value="J5" disabled><span>J5<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="J6" disabled><span>J6<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="J7"><span>J7</span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="J8"><span>J8</span></label>
<label><input type="checkbox" name="seat" value="J9"><span>J9</span></label>
<label><input type="checkbox" name="seat" value="J10"><span>J10</span></label>
<label><input type="checkbox" name="seat" value="J11"><span>J11</span></label>
<label><input type="checkbox" name="seat" value="J12"><span>J12</span></label>
<label><input type="checkbox" name="seat" value="J13"><span>J13</span></label>
<label><input type="checkbox" name="seat" value="J14"><span>J14</span></label>
</div>
<div class="row" role="group" aria-label="Row K">
<span class="row-name" aria-hidden="true">K</span>
<label><input type="checkbox" name="seat" value="K1"><span>K1</span></label>
<label><input type="checkbox" name="seat" value="K2"><span>K2</span></label>
<label><input type="checkbox" name="seat" value="K3"><span>K3</span></label>
<label><input type="checkbox" name="seat" value="K4"><span>K4</span></label>
<label><input type="checkbox" name="seat" value="K5"><span>K5</span></label>
<label><input type="checkbox" name="seat" value="K6"><span>K6</span></label>
<label><input type="checkbox" name="seat" value="K7"><span>K7</span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="K8"><span>K8</span></label>
<label><input type="checkbox" name="seat" value="K9"><span>K9</span></label>
<label><input type="checkbox" name="seat" value="K10" disabled><span>K10<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="K11" disabled><span>K11<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="K12"><span>K12</span></label>
<label><input type="checkbox" name="seat" value="K13"><span>K13</span></label>
<label><input type="checkbox" name="seat" value="K14"><span>K14</span></label>
</div>
<div class="row" role="group" aria-label="Row L">
<span class="row-name" aria-hidden="true">L</span>
<label><input type="checkbox" name="seat" value="L1"><span>L1</span></label>
<label><input type="checkbox" name="seat" value="L2"><span>L2</span></label>
<label><input type="checkbox" name="seat" value="L3"><span>L3</span></label>
<label><input type="checkbox" name="seat" value="L4"><span>L4</span></label>
<label><input type="checkbox" name="seat" value="L5"><span>L5</span></label>
<label><input type="checkbox" name="seat" value="L6"><span>L6</span></label>
<label><input type="checkbox" name="seat" value="L7" disabled><span>L7<i> sold</i></span></label>
<span aria-hidden="true"></span>
<label><input type="checkbox" name="seat" value="L8" disabled><span>L8<i> sold</i></span></label>
<label><input type="checkbox" name="seat" value="L9"><span>L9</span></label>
<label><input type="checkbox" name="seat" value="L10"><span>L10</span></label>
<label><input type="checkbox" name="seat" value="L11"><span>L11</span></label>
<label><input type="checkbox" name="seat" value="L12"><span>L12</span></label>
<label><input type="checkbox" name="seat" value="L13"><span>L13</span></label>
<label><input type="checkbox" name="seat" value="L14"><span>L14</span></label>
</div>
</div>
</div>
<p class="plan-hint">Front is at the top. The centre gap is an aisle. On a phone, swipe the plan sideways to see every seat.</p>
<p class="small" id="multi-show-note" hidden>Picking another screening keeps your existing draft seats in the basket. Return to that screening to change them.</p>
<a class="button secondary" href="#basket">Review your draft basket ↓</a>
</section>
<section class="pricing" aria-labelledby="pricing-title">
<h3 id="pricing-title">A simple ticket price</h3>
<p>Screenings starting before 5:00 p.m.: <strong>$8.00 USD per seat.</strong></p>
<p>Screenings starting at or after 5:00 p.m.: <strong>$11.50 USD per seat.</strong></p>
<p>No extra fees are included in this demo.</p>
</section>
</div>
<aside class="basket" id="basket" aria-labelledby="basket-title" tabindex="-1">
<p class="eyebrow">Your evening at the pictures</p>
<h2 id="basket-title">Draft basket</h2>
<div class="fallback">
<p><strong>Automatic basket unavailable.</strong></p>
<p>Your choices remain checked on the seating plan. Count the checked seats and use the price beside your selected time.</p>
<p>For example: 12 evening seats × $11.50 = <strong>$138.00 USD.</strong></p>
</div>
<div id="enhanced-basket" hidden>
<p id="empty-basket">Your basket is waiting. Pick a screening, then a seat or two.</p>
<ul id="basket-items" aria-label="Selected seats by screening"></ul>
<p class="total-label">Total · <span id="quantity">0 seats</span></p>
<p class="total">
<span id="total-number" aria-hidden="true">$0.00</span>
<span id="total-readable" class="sr">$0.00</span>
<span class="currency">USD</span>
</p>
<button type="button" id="clear-basket" class="button secondary" disabled>Clear all draft seats</button>
</div>
<p class="fx"><strong>EUR rate: unavailable offline.</strong><br>
A live market rate needs internet access. No euro amount is quoted; your card provider may apply its own rate and fees.</p>
<p class="small"><strong>Not booked or held.</strong> Ask the counter to confirm seats and arrange payment. Reloading this page clears the automatic draft basket.</p>
<p class="small">Need a copy? Use your browser's Print command, if available.</p>
</aside>
</div>
<p class="sr" id="basket-status" role="status" aria-live="polite" aria-atomic="true"></p>
</main>
<footer>
The Lantern Cinema · Two screens, one lovely night out.<br>
Offline demonstration — no payment, reservation, tracking, or saved personal information.
</footer>
<script>
(function () {
"use strict";
// Prices are integer cents. No clock, exchange-rate service, or storage is used.
const shows = {
"grand-matinee": {
title: "The Grand Budapest Hotel", screen: "Screen one",
time: "4:15 p.m.", cents: 800
},
"grand-evening": {
title: "The Grand Budapest Hotel", screen: "Screen one",
time: "7:30 p.m.", cents: 1150
},
"paradiso-matinee": {
title: "Cinema Paradiso", screen: "Screen two",
time: "4:00 p.m.", cents: 800
},
"paradiso-evening": {
title: "Cinema Paradiso", screen: "Screen two",
time: "7:00 p.m.", cents: 1150
}
};
const seatInputs = Array.from(document.querySelectorAll('input[name="seat"]'));
const radios = Array.from(document.querySelectorAll('input[name="screening"]'));
const sold = new Set(seatInputs.filter(input => input.disabled).map(input => input.value));
const drafts = new Map(Object.keys(shows).map(key => [key, new Set()]));
const byId = id => document.getElementById(id);
const money = cents => "$" + (Math.round(cents) / 100).toFixed(2);
const plural = count => count + (count === 1 ? " seat" : " seats");
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
let current = document.querySelector('input[name="screening"]:checked').value;
let displayedCents = 0;
let targetCents = 0;
let animationFrame = 0;
function animateTotal(next) {
cancelAnimationFrame(animationFrame);
targetCents = next;
const output = byId("total-number");
if (reducedMotion.matches || displayedCents === next) {
displayedCents = next;
output.textContent = money(next);
return;
}
const from = displayedCents;
const start = performance.now();
const duration = 380;
function frame(now) {
const progress = Math.min(1, (now - start) / duration);
const eased = 1 - Math.pow(1 - progress, 3);
displayedCents = from + (next - from) * eased;
output.textContent = money(displayedCents);
if (progress < 1) {
animationFrame = requestAnimationFrame(frame);
} else {
displayedCents = next;
output.textContent = money(next);
}
}
animationFrame = requestAnimationFrame(frame);
}
if (reducedMotion.addEventListener) {
reducedMotion.addEventListener("change", function () {
if (reducedMotion.matches) {
cancelAnimationFrame(animationFrame);
displayedCents = targetCents;
byId("total-number").textContent = money(targetCents);
}
});
}
function orderedSeats(selection) {
// Preserve the physical row/seat order, rather than lexicographic A1,A10,A2.
return seatInputs.filter(input => selection.has(input.value))
.map(input => input.value);
}
function renderBasket(announce) {
let count = 0;
let total = 0;
const list = byId("basket-items");
list.replaceChildren();
drafts.forEach(function (selection, key) {
if (!selection.size) return;
const show = shows[key];
count += selection.size;
const subtotal = selection.size * show.cents;
total += subtotal;
const item = document.createElement("li");
const title = document.createElement("strong");
title.textContent = show.title;
const time = document.createElement("span");
time.textContent = show.screen + " · " + show.time;
const seats = document.createElement("span");
seats.textContent = "Seats: " + orderedSeats(selection).join(", ");
const price = document.createElement("span");
price.textContent = plural(selection.size) + " × " + money(show.cents) +
" = " + money(subtotal);
item.append(title, time, seats, price);
list.append(item);
});
// Deliberately show the entire number, including 12, 14, and beyond.
byId("badge").textContent = String(count);
byId("basket-accessible").textContent = " — " + plural(count);
byId("quantity").textContent = plural(count);
byId("empty-basket").hidden = count !== 0;
byId("clear-basket").disabled = count === 0;
byId("total-readable").textContent = money(total);
animateTotal(total);
if (announce) {
byId("basket-status").textContent =
plural(count) + " in your draft basket. Total " + money(total) +
" US dollars. Seats are not reserved.";
}
}
function renderPlan() {
const selection = drafts.get(current);
seatInputs.forEach(function (input) {
input.checked = !sold.has(input.value) && selection.has(input.value);
});
const show = shows[current];
byId("screening-caption").textContent =
show.title + " · " + show.screen + " · " + show.time +
" · " + money(show.cents) + " per seat";
}
radios.forEach(function (radio) {
radio.addEventListener("change", function () {
if (!radio.checked) return;
current = radio.value;
renderPlan();
const show = shows[current];
byId("basket-status").textContent =
"Seat plan for " + show.title + ", " + show.time + ". " +
plural(drafts.get(current).size) + " selected for this screening.";
});
});
seatInputs.forEach(function (input) {
input.addEventListener("change", function () {
if (sold.has(input.value)) {
input.checked = false;
return;
}
const selection = drafts.get(current);
if (input.checked) selection.add(input.value);
else selection.delete(input.value);
renderBasket(true);
});
});
byId("clear-basket").addEventListener("click", function () {
drafts.forEach(selection => selection.clear());
renderPlan();
renderBasket(true);
});
// Begin with a fresh in-memory draft, ignoring browser-restored checkbox state.
renderPlan();
renderBasket(false);
byId("enhanced-basket").hidden = false;
byId("screening-caption").hidden = false;
byId("multi-show-note").hidden = false;
document.documentElement.classList.add("js-ready");
}());
</script>
</body>
</html>