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
>
<!--
NOTES FOR THE BOARD — please read before Friday. Plain English, numbered.
You asked to be told rather than find out with a queue at the door, so here
is everything I could not do exactly as written, and every assumption.
1) THE BADGE: "one digit" versus "12". These cannot both be true — 12 is two
characters. Your nephew's printed board says the little circle must never
hold more than a single digit; the P.S. says the teacher MUST see 12 or
she phones the counter. I kept the circle exactly the same small diameter
(34px, one-digit proportions) and simply set the type a bit smaller once
the count reaches 10, so "12" sits inside the SAME circle. The printed
shape does not change by a pixel. If you really want one character I can
show "9+" above nine — but then the teacher is back on the phone, so I
did not do that. Say the word and I will swap it.
2) THE LIVE EURO RATE. Requirement 6 (today's rate "off the live market")
conflicts with "no network at runtime / machine is not on the internet":
a live rate needs a request. I baked in a DATED SNAPSHOT, 1 USD = 0.92 EUR,
and labelled it on the page as "snapshot, not live" so nobody is misled.
To refresh each morning: edit RATE in the script AND the pre-rendered euro
figures in the marked "no-JS price ladder" below (they sit together), or
better, have the office machine regenerate this file nightly. On a real
till I would do this differently: let the till push the rate, not a person.
3) SOLD SEATS are a hand-typed snapshot for tonight (19 of them, marked Sold).
There is ONE 12 x 14 plan shared by both screens. I could not give each
screen its own sold map without a live feed or a rebuild per screening —
with JavaScript off there is no way to swap the map safely. Edit the
`disabled` seats at shift change. Same story: the till should own this.
4) WITH JAVASCRIPT OFF the page genuinely works: films, times, seat picking,
sold seats, the basket badge (including 12), the seat count and the money
are all pure HTML + CSS. Exact dollar AND euro totals are pre-rendered for
0–20 seats, which covers your 12- and 14-at-a-time school bookings with
headroom. If someone somehow ticks 21+ with JavaScript off, the total line
honestly reads "21+ seats — please total at the counter" rather than
printing a wrong figure. With JavaScript on, totals are exact for ANY
number of seats. This is the one place I would rather over-explain.
5) THE SMOOTH COUNT-UP (item 8) only exists when JavaScript is on. With it
off the correct figure simply appears. That is the only feature that
quietly drops out.
6) I read "$11.50 after five / $8.00 before" as the price PER TICKET by
showing time. Both films therefore have an early and an evening showing,
so both price tiers are reachable and visible. If you meant a flat
surcharge on the basket instead, tell me and it is a small change.
7) "Tonight's" date is written in locally when JavaScript is on; otherwise
the page just says "Tonight". No server clock is assumed. Rows run
A B C D E F G H J K L M — twelve rows, skipping I so it cannot be misread
as a 1. Fourteen seats to a row, aisle after seat 7.
8) CONFIRMED: one file, everything inline; no CDN, external stylesheet,
script, font or image; no fetch / XHR / WebSocket / @import at runtime;
no localStorage, sessionStorage, cookies or indexedDB (never touched);
no webfonts. Even the favicon is an inline data: URI so it makes no
request. Needs a modern browser for CSS :has() (2023-onward Chrome, Edge,
Safari, Firefox).
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<title>The Marlowe — Tonight's Tickets</title>
<script>document.documentElement.classList.add('js');</script>
<style>
:root{
--red:#5b0d15; --red2:#8c1420; --red3:#3b0a10;
--gold:#d9ad5b; --gold2:#f0d38a; --cream:#f6e7c8;
--ink:#2a1010; --sold:#241719;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
background:
repeating-linear-gradient(90deg,#4a0b12 0 13px,#6d0f18 13px 22px,#3f0810 22px 32px) fixed,
#4a0b12;
color:var(--cream);
font:16px/1.5 Georgia,"Times New Roman",serif;
-webkit-print-color-adjust:exact; print-color-adjust:exact;
}
.page{
counter-reset:picked 0; /* the basket/ledger read this AFTER the plan */
position:relative; max-width:1000px; margin:0 auto; padding:0 26px 60px;
background:
radial-gradient(120% 60% at 50% -10%,#7a121d 0%,transparent 70%),
linear-gradient(#2b070d,#1a0509 60%,#2b070d);
border-inline:3px solid var(--gold);
box-shadow:0 0 0 1px #000,0 0 60px #0009;
min-height:100vh;
}
/* ---------- marquee ---------- */
header.marquee{text-align:center;padding:26px 12px 20px;position:relative}
header.marquee::before,header.marquee::after{
content:"";position:absolute;left:0;right:0;height:12px;
background-image:radial-gradient(circle,#ffd77a 0 3px,#b8862f 3px 4px,transparent 5px);
background-size:28px 12px;opacity:.9;
}
header.marquee::before{top:2px} header.marquee::after{bottom:0}
.kicker{
font-size:11px;letter-spacing:.42em;text-transform:uppercase;color:var(--gold);
margin:0 0 8px;
}
h1{
margin:0;font-size:clamp(30px,6vw,54px);font-weight:400;letter-spacing:.14em;
text-transform:uppercase;color:var(--gold2);
text-shadow:0 2px 0 #6d3c14,0 0 22px #d9ad5b55;
}
.strap{
margin:8px 0 0;font-size:13px;letter-spacing:.28em;text-transform:uppercase;
color:#e9cf9e;
}
.strap time{font-style:italic;text-transform:none;letter-spacing:.06em}
.rule{
height:1px;margin:16px auto 0;max-width:620px;
background:linear-gradient(90deg,transparent,var(--gold),transparent);
position:relative;
}
.rule::after{
content:"\2726";position:absolute;left:50%;top:-11px;transform:translateX(-50%);
color:var(--gold);background:#3a0810;padding:0 10px;font-size:14px;
}
/* ---------- films ---------- */
form{margin:0}
fieldset.films{border:0;padding:18px 0 0;margin:0}
fieldset.films legend{
padding:0;font-size:12px;letter-spacing:.3em;text-transform:uppercase;
color:var(--gold);text-align:center;width:100%;
}
.pricelegend{
text-align:center;margin:2px 0 16px;font-size:13px;color:#d8bd8c;font-style:italic;
}
.screens{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.card{
border:1px solid #b98b3f66;border-radius:3px;padding:16px 18px 14px;
background:linear-gradient(#5a1019,#3d0a12);
box-shadow:inset 0 1px 0 #f0d38a22,0 6px 18px #0005;
}
.card .scr{font-size:10px;letter-spacing:.36em;text-transform:uppercase;color:var(--gold);margin:0}
.card h2{margin:6px 0 2px;font-size:25px;font-weight:400;color:#fbecd0;letter-spacing:.03em}
.card p.blurb{margin:0 0 14px;font-size:13px;font-style:italic;color:#d5b68c}
.times{display:grid;gap:8px}
label.t{
display:flex;align-items:center;gap:12px;cursor:pointer;padding:9px 11px;
border:1px dashed #d9ad5b55;border-radius:2px;background:#2a070d88;
transition:background .2s,border-color .2s,transform .2s;
}
label.t:hover{border-color:var(--gold)}
label.t:has(input:checked){
background:linear-gradient(#8a5f22,#6b4517);border:1px solid var(--gold2);
transform:translateY(-1px);box-shadow:0 0 0 2px #d9ad5b33;
}
label.t input{accent-color:var(--gold);width:16px;height:16px;margin:0;flex:0 0 auto}
label.t .when{font-size:19px;color:var(--gold2);letter-spacing:.06em;min-width:64px}
label.t .how{font-size:12px;font-style:italic;color:#e3c894;margin-left:auto;text-align:right}
.cue{
text-align:center;margin:20px 0 6px;font-size:14px;font-style:italic;color:var(--gold2);
letter-spacing:.04em;
}
.page:has(.show:checked) .cue{color:#9c8258}
/* ---------- seating plan ---------- */
.screenbar{
margin:14px auto 8px;height:34px;max-width:76%;
border-top:2px solid var(--gold);
border-radius:0 0 50% 50%/0 0 100% 100%;
background:linear-gradient(#d9ad5b22,transparent);
text-align:center;font-size:10px;letter-spacing:.4em;text-transform:uppercase;
color:var(--gold);padding-top:7px;
}
.screenbar::after{content:"SCREEN"}
.page:has(#f1m:checked) .screenbar::after,
.page:has(#f1e:checked) .screenbar::after{content:"SCREEN I \00b7 THE LAST LANTERN"}
.page:has(#f2m:checked) .screenbar::after,
.page:has(#f2e:checked) .screenbar::after{content:"SCREEN II \00b7 A WALTZ IN SEPIA"}
.plan{
display:grid;grid-template-columns:24px repeat(14,minmax(0,1fr));
gap:5px 4px;margin:0 auto;max-width:820px;padding:14px 10px 18px;
border-top:1px solid #d9ad5b33;border-bottom:1px solid #d9ad5b33;
position:relative;
opacity:.48;filter:saturate(.5);pointer-events:none;
transition:opacity .3s,filter .3s;
}
.page:has(.show:checked) .plan{opacity:1;filter:none;pointer-events:auto}
.plan > .s{position:absolute;width:1px;height:1px;opacity:0;margin:0;pointer-events:none}
.plan > .rl{
grid-column:1;align-self:center;text-align:center;font-size:12px;
color:var(--gold);letter-spacing:.1em;
}
.plan > label{
display:grid;place-items:center;min-width:0;height:26px;cursor:pointer;
font:11px/1 Georgia,serif;color:#e8d3ab;
border:1px solid #d9ad5b55;border-bottom:3px solid #d9ad5b33;
border-radius:6px 6px 2px 2px;
background:linear-gradient(#8f1d27,#5e1019);
box-shadow:inset 0 3px 0 #f0d38a18;
transition:transform .12s,background .12s,color .12s;
user-select:none;
}
.plan > label:hover{border-color:var(--gold2);transform:translateY(-2px)}
.plan > label:nth-of-type(14n+7){margin-inline-end:15px} /* the aisle */
.s:checked + label{
background:linear-gradient(#f3d691,#d5a64c);color:#3a0a10;
border-color:#ffe6ab;border-bottom-color:#a97c2e;
box-shadow:0 0 10px #d9ad5b77,inset 0 3px 0 #fff6;
transform:translateY(-2px);font-weight:700;
}
.s:focus-visible + label{outline:2px solid var(--gold2);outline-offset:2px}
.s:checked + label{counter-increment:picked 1}
.plan > label.gone{
background:repeating-linear-gradient(45deg,#241719,#241719 4px,#2e1d20 4px,#2e1d20 8px);
border-color:#5a3a3a;color:#7d6060;cursor:not-allowed;pointer-events:none;
box-shadow:none;transform:none;font-weight:400;
}
.plan > label.gone::after{content:"\00d7";font-size:13px;color:#a2705f}
.plan > label.gone{font-size:0}
.seatkey{
text-align:center;margin:10px 0 0;font-size:12px;color:#c6a878;font-style:italic;
}
.seatkey b{color:var(--gold2);font-weight:400}
/* ---------- ledger ---------- */
.ledger{
margin:26px auto 0;max-width:520px;padding:18px 22px;
border:1px solid var(--gold);border-radius:3px;
background:linear-gradient(#4a0c14,#30080e);
box-shadow:inset 0 0 0 4px #0004,0 8px 24px #0006;
}
.lrow{display:flex;justify-content:space-between;align-items:baseline;font-size:14px;color:#dcc096}
.lbl{letter-spacing:.24em;text-transform:uppercase;font-size:11px;color:var(--gold)}
.total{
display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;
border-top:1px solid #d9ad5b55;border-bottom:1px solid #d9ad5b55;
margin:12px 0 10px;padding:12px 0;
}
.total .lbl{align-self:center}
.sum{
font-size:34px;font-weight:400;color:var(--gold2);letter-spacing:.02em;
font-variant-numeric:tabular-nums;text-shadow:0 0 18px #d9ad5b44;
}
.sum::after{content:var(--sum,"$0.00")}
.eur{margin-left:auto;font-size:17px;color:#e0c591;font-variant-numeric:tabular-nums}
.eur::after{content:var(--eur,"\20AC0.00")}
.rateline{font-size:12px;color:#b99a70;line-height:1.7}
.rate::after{content:var(--per,"choose a showing")}
.rateline .snap{color:var(--gold);white-space:nowrap}
.printbtn{
display:block;margin:20px auto 0;padding:11px 26px;cursor:pointer;
font:14px Georgia,serif;letter-spacing:.18em;text-transform:uppercase;
color:#2a0a0e;background:linear-gradient(#f3d691,#c99a40);
border:1px solid #ffe6ab;border-radius:2px;box-shadow:0 4px 0 #7a521c;
}
.printbtn:active{transform:translateY(2px);box-shadow:0 2px 0 #7a521c}
noscript .nsnote{
display:block;margin:14px auto 0;max-width:520px;text-align:center;
font-size:13px;font-style:italic;color:#d8bd8c;
}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
/* ---------- the small circle, top right ---------- */
.basket{
position:fixed;top:18px;right:18px;z-index:50;
width:34px;height:34px;border-radius:50%;
display:grid;place-items:center;
background:radial-gradient(circle at 34% 28%,#a3232e,#5b0d15 70%);
border:2px solid var(--gold2);
box-shadow:0 0 0 3px #3b0a10,0 4px 14px #0009,inset 0 0 8px #0006;
font:700 18px/1 Georgia,serif;color:#fff3d8;
font-variant-numeric:tabular-nums;
}
.basket::before{ /* little bail handle */
content:"";position:absolute;top:-7px;left:9px;right:9px;height:8px;
border:2px solid var(--gold);border-bottom:0;border-radius:8px 8px 0 0;
}
.basket-count::after{content:counter(picked)}
.js .basket-count::after{content:none}
.seatcount::after{content:counter(picked)}
.js .seatcount::after{content:none}
/* keep "12" inside the SAME one-digit-sized circle */
.page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked) .basket{
font-size:13px;letter-spacing:-1px;
}
.basket[data-digits="2"]{font-size:13px;letter-spacing:-1px}
.basket[data-digits="3"]{font-size:11px;letter-spacing:-1.4px}
/* ================================================================
NO-JAVASCRIPT PRICE LADDER — exact totals for 0–20 seats.
Both tiers live in one rule each; the tier rule below picks one.
Snapshot rate 1 USD = 0.92 EUR -> $8.00 = 7.36 / $11.50 = 10.58
Each line means "at least N seats ticked"; later lines win.
================================================================ */
html:not(.js) .page:has(.mat:checked){--sum:var(--m,"$0.00");--eur:var(--me,"\20AC0.00");--per:"$8.00 per seat \00b7 before five";}
html:not(.js) .page:has(.eve:checked){--sum:var(--e,"$0.00");--eur:var(--ee,"\20AC0.00");--per:"$11.50 per seat \00b7 after five";}
/* n=1 */ html:not(.js) .page:has(.s:checked){--m:"$8.00";--e:"$11.50";--me:"\20AC7.36";--ee:"\20AC10.58";}
/* n=2 */ html:not(.js) .page:has(.s:checked~.s:checked){--m:"$16.00";--e:"$23.00";--me:"\20AC14.72";--ee:"\20AC21.16";}
/* n=3 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked){--m:"$24.00";--e:"$34.50";--me:"\20AC22.08";--ee:"\20AC31.74";}
/* n=4 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked){--m:"$32.00";--e:"$46.00";--me:"\20AC29.44";--ee:"\20AC42.32";}
/* n=5 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$40.00";--e:"$57.50";--me:"\20AC36.80";--ee:"\20AC52.90";}
/* n=6 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$48.00";--e:"$69.00";--me:"\20AC44.16";--ee:"\20AC63.48";}
/* n=7 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$56.00";--e:"$80.50";--me:"\20AC51.52";--ee:"\20AC74.06";}
/* n=8 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$64.00";--e:"$92.00";--me:"\20AC58.88";--ee:"\20AC84.64";}
/* n=9 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$72.00";--e:"$103.50";--me:"\20AC66.24";--ee:"\20AC95.22";}
/* n=10 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$80.00";--e:"$115.00";--me:"\20AC73.60";--ee:"\20AC105.80";}
/* n=11 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$88.00";--e:"$126.50";--me:"\20AC80.96";--ee:"\20AC116.38";}
/* n=12 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$96.00";--e:"$138.00";--me:"\20AC88.32";--ee:"\20AC126.96";}
/* n=13 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$104.00";--e:"$149.50";--me:"\20AC95.68";--ee:"\20AC137.54";}
/* n=14 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$112.00";--e:"$161.00";--me:"\20AC103.04";--ee:"\20AC148.12";}
/* n=15 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$120.00";--e:"$172.50";--me:"\20AC110.40";--ee:"\20AC158.70";}
/* n=16 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$128.00";--e:"$184.00";--me:"\20AC117.76";--ee:"\20AC169.28";}
/* n=17 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$136.00";--e:"$195.50";--me:"\20AC125.12";--ee:"\20AC179.86";}
/* n=18 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$144.00";--e:"$207.00";--me:"\20AC132.48";--ee:"\20AC190.44";}
/* n=19 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$152.00";--e:"$218.50";--me:"\20AC139.84";--ee:"\20AC201.02";}
/* n=20 */ html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){--m:"$160.00";--e:"$230.00";--me:"\20AC147.20";--ee:"\20AC211.60";}
/* n=21+ : never print a wrong number */
html:not(.js) .page:has(.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked~.s:checked){
--m:"21+ seats \2014 please total at the counter";
--e:"21+ seats \2014 please total at the counter";
--me:"\2014";--ee:"\2014";
}
@media (max-width:720px){
.screens{grid-template-columns:1fr}
.plan{gap:3px 2px;padding:10px 2px}
.plan > label{height:22px;font-size:9px}
.plan > label:nth-of-type(14n+7){margin-inline-end:8px}
.basket{top:10px;right:10px}
.sum{font-size:27px}
}
@media print{
body{background:#fff}
.page{box-shadow:none;max-width:none}
.cue,.printbtn,noscript{display:none}
}
</style>
</head>
<body>
<div class="page">
<header class="marquee">
<p class="kicker">Two screens · one ticket desk</p>
<h1>The Marlowe</h1>
<p class="strap">Tonight · <time id="tonight">Tonight</time> · Box office</p>
<div class="rule"></div>
</header>
<form id="f" action="" method="get" novalidate>
<fieldset class="films">
<legend>Choose your film & time</legend>
<p class="pricelegend">Before five $8.00 a seat · after five $11.50 a seat</p>
<div class="screens">
<article class="card">
<p class="scr">Screen I</p>
<h2>The Last Lantern</h2>
<p class="blurb">A lantern-maker, a long winter, and one last reel.</p>
<div class="times">
<label class="t"><input class="show mat" type="radio" name="show" id="f1m"><span class="when">16:00</span><span class="how">$8.00 · before five</span></label>
<label class="t"><input class="show eve" type="radio" name="show" id="f1e"><span class="when">19:30</span><span class="how">$11.50 · after five</span></label>
</div>
</article>
<article class="card">
<p class="scr">Screen II</p>
<h2>A Waltz in Sepia</h2>
<p class="blurb">Two strangers, a rainy station, a borrowed waltz.</p>
<div class="times">
<label class="t"><input class="show mat" type="radio" name="show" id="f2m"><span class="when">16:45</span><span class="how">$8.00 · before five</span></label>
<label class="t"><input class="show eve" type="radio" name="show" id="f2e"><span class="when">20:15</span><span class="how">$11.50 · after five</span></label>
</div>
</article>
</div>
</fieldset>
<p class="cue">Choose a showing above, then pick your seats below.</p>
<div class="screenbar"></div>
<!-- 12 rows (A–M, skipping I) x 14 seats. "disabled" = already sold. -->
<div class="plan">
<span class="rl">A</span><input class="s" id="A1" type="checkbox"><label for="A1">1</label><input class="s" id="A2" type="checkbox"><label for="A2">2</label><input class="s" id="A3" type="checkbox"><label for="A3">3</label><input class="s" id="A4" type="checkbox"><label for="A4">4</label><input class="s" id="A5" type="checkbox"><label for="A5">5</label><input class="s" id="A6" type="checkbox"><label for="A6">6</label><input class="s" id="A7" type="checkbox" disabled><label class="gone" for="A7" title="Sold">7</label><input class="s" id="A8" type="checkbox" disabled><label class="gone" for="A8" title="Sold">8</label><input class="s" id="A9" type="checkbox"><label for="A9">9</label><input class="s" id="A10" type="checkbox"><label for="A10">10</label><input class="s" id="A11" type="checkbox"><label for="A11">11</label><input class="s" id="A12" type="checkbox"><label for="A12">12</label><input class="s" id="A13" type="checkbox"><label for="A13">13</label><input class="s" id="A14" type="checkbox"><label for="A14">14</label>
<span class="rl">B</span><input class="s" id="B1" type="checkbox"><label for="B1">1</label><input class="s" id="B2" type="checkbox"><label for="B2">2</label><input class="s" id="B3" type="checkbox" disabled><label class="gone" for="B3" title="Sold">3</label><input class="s" id="B4" type="checkbox"><label for="B4">4</label><input class="s" id="B5" type="checkbox"><label for="B5">5</label><input class="s" id="B6" type="checkbox"><label for="B6">6</label><input class="s" id="B7" type="checkbox"><label for="B7">7</label><input class="s" id="B8" type="checkbox"><label for="B8">8</label><input class="s" id="B9" type="checkbox"><label for="B9">9</label><input class="s" id="B10" type="checkbox"><label for="B10">10</label><input class="s" id="B11" type="checkbox"><label for="B11">11</label><input class="s" id="B12" type="checkbox"><label for="B12">12</label><input class="s" id="B13" type="checkbox"><label for="B13">13</label><input class="s" id="B14" type="checkbox"><label for="B14">14</label>
<span class="rl">C</span><input class="s" id="C1" type="checkbox"><label for="C1">1</label><input class="s" id="C2" type="checkbox"><label for="C2">2</label><input class="s" id="C3" type="checkbox"><label for="C3">3</label><input class="s" id="C4" type="checkbox"><label for="C4">4</label><input class="s" id="C5" type="checkbox"><label for="C5">5</label><input class="s" id="C6" type="checkbox"><label for="C6">6</label><input class="s" id="C7" type="checkbox"><label for="C7">7</label><input class="s" id="C8" type="checkbox"><label for="C8">8</label><input class="s" id="C9" type="checkbox"><label for="C9">9</label><input class="s" id="C10" type="checkbox"><label for="C10">10</label><input class="s" id="C11" type="checkbox"><label for="C11">11</label><input class="s" id="C12" type="checkbox" disabled><label class="gone" for="C12" title="Sold">12</label><input class="s" id="C13" type="checkbox" disabled><label class="gone" for="C13" title="Sold">13</label><input class="s" id="C14" type="checkbox"><label for="C14">14</label>
<span class="rl">D</span><input class="s" id="D1" type="checkbox"><label for="D1">1</label><input class="s" id="D2" type="checkbox"><label for="D2">2</label><input class="s" id="D3" type="checkbox"><label for="D3">3</label><input class="s" id="D4" type="checkbox"><label for="D4">4</label><input class="s" id="D5" type="checkbox" disabled><label class="gone" for="D5" title="Sold">5</label><input class="s" id="D6" type="checkbox"><label for="D6">6</label><input class="s" id="D7" type="checkbox"><label for="D7">7</label><input class="s" id="D8" type="checkbox"><label for="D8">8</label><input class="s" id="D9" type="checkbox"><label for="D9">9</label><input class="s" id="D10" type="checkbox"><label for="D10">10</label><input class="s" id="D11" type="checkbox"><label for="D11">11</label><input class="s" id="D12" type="checkbox"><label for="D12">12</label><input class="s" id="D13" type="checkbox"><label for="D13">13</label><input class="s" id="D14" type="checkbox"><label for="D14">14</label>
<span class="rl">E</span><input class="s" id="E1" type="checkbox"><label for="E1">1</label><input class="s" id="E2" type="checkbox"><label for="E2">2</label><input class="s" id="E3" type="checkbox"><label for="E3">3</label><input class="s" id="E4" type="checkbox"><label for="E4">4</label><input class="s" id="E5" type="checkbox"><label for="E5">5</label><input class="s" id="E6" type="checkbox"><label for="E6">6</label><input class="s" id="E7" type="checkbox"><label for="E7">7</label><input class="s" id="E8" type="checkbox"><label for="E8">8</label><input class="s" id="E9" type="checkbox" disabled><label class="gone" for="E9" title="Sold">9</label><input class="s" id="E10" type="checkbox" disabled><label class="gone" for="E10" title="Sold">10</label><input class="s" id="E11" type="checkbox" disabled><label class="gone" for="E11" title="Sold">11</label><input class="s" id="E12" type="checkbox"><label for="E12">12</label><input class="s" id="E13" type="checkbox"><label for="E13">13</label><input class="s" id="E14" type="checkbox"><label for="E14">14</label>
<span class="rl">F</span><input class="s" id="F1" type="checkbox" disabled><label class="gone" for="F1" title="Sold">1</label><input class="s" id="F2" type="checkbox"><label for="F2">2</label><input class="s" id="F3" type="checkbox"><label for="F3">3</label><input class="s" id="F4" type="checkbox"><label for="F4">4</label><input class="s" id="F5" type="checkbox"><label for="F5">5</label><input class="s" id="F6" type="checkbox"><label for="F6">6</label><input class="s" id="F7" type="checkbox"><label for="F7">7</label><input class="s" id="F8" type="checkbox"><label for="F8">8</label><input class="s" id="F9" type="checkbox"><label for="F9">9</label><input class="s" id="F10" type="checkbox"><label for="F10">10</label><input class="s" id="F11" type="checkbox"><label for="F11">11</label><input class="s" id="F12" type="checkbox"><label for="F12">12</label><input class="s" id="F13" type="checkbox"><label for="F13">13</label><input class="s" id="F14" type="checkbox"><label for="F14">14</label>
<span class="rl">G</span><input class="s" id="G1" type="checkbox"><label for="G1">1</label><input class="s" id="G2" type="checkbox"><label for="G2">2</label><input class="s" id="G3" type="checkbox"><label for="G3">3</label><input class="s" id="G4" type="checkbox"><label for="G4">4</label><input class="s" id="G5" type="checkbox"><label for="G5">5</label><input class="s" id="G6" type="checkbox" disabled><label class="gone" for="G6" title="Sold">6</label><input class="s" id="G7" type="checkbox" disabled><label class="gone" for="G7" title="Sold">7</label><input class="s" id="G8" type="checkbox"><label for="G8">8</label><input class="s" id="G9" type="checkbox"><label for="G9">9</label><input class="s" id="G10" type="checkbox"><label for="G10">10</label><input class="s" id="G11" type="checkbox"><label for="G11">11</label><input class="s" id="G12" type="checkbox"><label for="G12">12</label><input class="s" id="G13" type="checkbox"><label for="G13">13</label><input class="s" id="G14" type="checkbox"><label for="G14">14</label>
<span class="rl">H</span><input class="s" id="H1" type="checkbox"><label for="H1">1</label><input class="s" id="H2" type="checkbox"><label for="H2">2</label><input class="s" id="H3" type="checkbox"><label for="H3">3</label><input class="s" id="H4" type="checkbox"><label for="H4">4</label><input class="s" id="H5" type="checkbox"><label for="H5">5</label><input class="s" id="H6" type="checkbox"><label for="H6">6</label><input class="s" id="H7" type="checkbox"><label for="H7">7</label><input class="s" id="H8" type="checkbox"><label for="H8">8</label><input class="s" id="H9" type="checkbox"><label for="H9">9</label><input class="s" id="H10" type="checkbox"><label for="H10">10</label><input class="s" id="H11" type="checkbox"><label for="H11">11</label><input class="s" id="H12" type="checkbox"><label for="H12">12</label><input class="s" id="H13" type="checkbox"><label for="H13">13</label><input class="s" id="H14" type="checkbox" disabled><label class="gone" for="H14" title="Sold">14</label>
<span class="rl">J</span><input class="s" id="J1" type="checkbox"><label for="J1">1</label><input class="s" id="J2" type="checkbox" disabled><label class="gone" for="J2" title="Sold">2</label><input class="s" id="J3" type="checkbox" disabled><label class="gone" for="J3" title="Sold">3</label><input class="s" id="J4" type="checkbox"><label for="J4">4</label><input class="s" id="J5" type="checkbox"><label for="J5">5</label><input class="s" id="J6" type="checkbox"><label for="J6">6</label><input class="s" id="J7" type="checkbox"><label for="J7">7</label><input class="s" id="J8" type="checkbox"><label for="J8">8</label><input class="s" id="J9" type="checkbox"><label for="J9">9</label><input class="s" id="J10" type="checkbox"><label for="J10">10</label><input class="s" id="J11" type="checkbox"><label for="J11">11</label><input class="s" id="J12" type="checkbox"><label for="J12">12</label><input class="s" id="J13" type="checkbox"><label for="J13">13</label><input class="s" id="J14" type="checkbox"><label for="J14">14</label>
<span class="rl">K</span><input class="s" id="K1" type="checkbox"><label for="K1">1</label><input class="s" id="K2" type="checkbox"><label for="K2">2</label><input class="s" id="K3" type="checkbox"><label for="K3">3</label><input class="s" id="K4" type="checkbox"><label for="K4">4</label><input class="s" id="K5" type="checkbox"><label for="K5">5</label><input class="s" id="K6" type="checkbox"><label for="K6">6</label><input class="s" id="K7" type="checkbox"><label for="K7">7</label><input class="s" id="K8" type="checkbox" disabled><label class="gone" for="K8" title="Sold">8</label><input class="s" id="K9" type="checkbox"><label for="K9">9</label><input class="s" id="K10" type="checkbox"><label for="K10">10</label><input class="s" id="K11" type="checkbox"><label for="K11">11</label><input class="s" id="K12" type="checkbox"><label for="K12">12</label><input class="s" id="K13" type="checkbox"><label for="K13">13</label><input class="s" id="K14" type="checkbox"><label for="K14">14</label>
<span class="rl">L</span><input class="s" id="L1" type="checkbox"><label for="L1">1</label><input class="s" id="L2" type="checkbox"><label for="L2">2</label><input class="s" id="L3" type="checkbox"><label for="L3">3</label><input class="s" id="L4" type="checkbox" disabled><label class="gone" for="L4" title="Sold">4</label><input class="s" id="L5" type="checkbox" disabled><label class="gone" for="L5" title="Sold">5</label><input class="s" id="L6" type="checkbox"><label for="L6">6</label><input class="s" id="L7" type="checkbox"><label for="L7">7</label><input class="s" id="L8" type="checkbox"><label for="L8">8</label><input class="s" id="L9" type="checkbox"><label for="L9">9</label><input class="s" id="L10" type="checkbox"><label for="L10">10</label><input class="s" id="L11" type="checkbox"><label for="L11">11</label><input class="s" id="L12" type="checkbox"><label for="L12">12</label><input class="s" id="L13" type="checkbox"><label for="L13">13</label><input class="s" id="L14" type="checkbox"><label for="L14">14</label>
<span class="rl">M</span><input class="s" id="M1" type="checkbox"><label for="M1">1</label><input class="s" id="M2" type="checkbox"><label for="M2">2</label><input class="s" id="M3" type="checkbox"><label for="M3">3</label><input class="s" id="M4" type="checkbox"><label for="M4">4</label><input class="s" id="M5" type="checkbox"><label for="M5">5</label><input class="s" id="M6" type="checkbox"><label for="M6">6</label><input class="s" id="M7" type="checkbox"><label for="M7">7</label><input class="s" id="M8" type="checkbox"><label for="M8">8</label><input class="s" id="M9" type="checkbox"><label for="M9">9</label><input class="s" id="M10" type="checkbox"><label for="M10">10</label><input class="s" id="M11" type="checkbox" disabled><label class="gone" for="M11" title="Sold">11</label><input class="s" id="M12" type="checkbox"><label for="M12">12</label><input class="s" id="M13" type="checkbox"><label for="M13">13</label><input class="s" id="M14" type="checkbox"><label for="M14">14</label>
</div>
<p class="seatkey"><b>Gold</b> = yours · <b>× hatch</b> = already sold · aisle after seat 7</p>
<div class="ledger">
<div class="lrow"><span class="lbl">Seats in basket</span><span class="seatcount" id="seatcount"></span></div>
<div class="total">
<span class="lbl">Total</span>
<strong class="sum" aria-hidden="true"></strong>
<span class="eur" aria-hidden="true"></span>
</div>
<div class="rateline">
<span class="rate"></span><br>
<span class="snap">1 USD = 0.92 EUR · snapshot, not live</span>
— the kiosk is offline, so this rate is printed into the page each morning, not fetched.
</div>
<button class="printbtn" id="print" type="button" onclick="window.print()">Print / show at the counter</button>
<span class="sr" id="status" role="status" aria-live="polite"></span>
</div>
<noscript><span class="nsnote">JavaScript is off — everything above still works. To take your selection to the counter, press Ctrl+P (⌘+P on a Mac): the ticks print too.</span></noscript>
<!-- LAST in the document on purpose: CSS counters only see ticks BEFORE
this point, which is how the badge reads 12 with JavaScript off. -->
<div class="basket" id="basket" title="Basket" aria-label="Basket">
<span class="basket-count" id="badge" aria-live="polite"></span>
</div>
</form>
</div>
<script>
(function(){
"use strict";
var page = document.querySelector('.page'),
form = document.getElementById('f'),
badge = document.getElementById('badge'),
count = document.getElementById('seatcount'),
basket = document.getElementById('basket'),
status = document.getElementById('status'),
tonight = document.getElementById('tonight');
/* DATED SNAPSHOT — see note 2 at the top of this file. No network call. */
var RATE = 0.92;
var shown = 0, raf = null;
function priceCents(){
var r = form.querySelector('input[name="show"]:checked');
if(!r) return 0;
return r.classList.contains('mat') ? 800 : 1150;
}
function seatCount(){ return form.querySelectorAll('.s:checked').length; }
function usd(c){ return '$' + (c/100).toFixed(2); }
function eur(c){ return '\u20AC' + (c*RATE/100).toFixed(2); }
function str(v){ return JSON.stringify(v); } /* content: needs a quoted string */
function paint(c){
page.style.setProperty('--sum', str(usd(c)));
page.style.setProperty('--eur', str(eur(c)));
}
function tween(target){
if(raf) cancelAnimationFrame(raf);
var from = shown, t0 = null, dur = 520;
function step(t){
if(t0 === null) t0 = t;
var k = Math.min(1,(t-t0)/dur), e = 1 - Math.pow(1-k,3);
var c = Math.round(from + (target-from)*e);
shown = c; paint(c);
if(k < 1) raf = requestAnimationFrame(step);
else { shown = target; paint(target); raf = null; }
}
raf = requestAnimationFrame(step);
}
function render(animate){
var n = seatCount(), p = priceCents(), target = n * p;
badge.textContent = String(n);
count.textContent = String(n);
basket.setAttribute('data-digits', String(n).length);
page.style.setProperty('--per', str(
p === 800 ? '$8.00 per seat \u00b7 before five' :
p === 1150 ? '$11.50 per seat \u00b7 after five' :
'choose a showing'
));
status.textContent = n + ' seat' + (n===1?'':'s') +
(p ? ', total ' + usd(target) + ' (' + eur(target) + ')' : ', no showing chosen');
if(animate) tween(target);
else { if(raf) cancelAnimationFrame(raf); shown = target; paint(target); }
}
form.addEventListener('change', function(ev){
if(ev.target.name === 'show' || ev.target.classList.contains('s')) render(true);
});
/* Belt and braces: nothing here ever leaves the page. */
form.addEventListener('submit', function(ev){ ev.preventDefault(); });
try{
tonight.textContent = new Date().toLocaleDateString(undefined,
{weekday:'long', day:'numeric', month:'long'});
}catch(e){ /* keep the word "Tonight" */ }
render(false);
})();
</script>
</body>
</html>