Completes U7 with its fourth component: a jump-to-group rail derived from filename prefixes, replacing the subfolder sections ROADMAP named. The scope departure was ratified by the operator 2026-09-22; this commit deletes test_no_group_rail_is_shipped_yet, the guard that held it back, in the same change that builds what it guarded against. All seven v1 capabilities are now landed. The 1.0 cut is a decision, not a dependency, and it is the operator's — no version bump here, because a commit is not a release. THE RULE CHANGED AT IMPLEMENTATION, ON MEASURED GROUNDS. The contract specified `strip ONE trailing run of digits`; run against the live set that yields 24 groups for sindra-bakeoff's 40 images and 27 for sindra's 30 — a rail with a row per tile — because it keys on the END of the stem, where the instance number lives. The contract's own table claimed 5 and 1 for those two booths and neither reproduces; the numbers are reachable only by two OTHER heuristics, so the table that justified the design was assembled from more than one rule. Its own worked example contradicts it in plain sight. The shipped rule keys on the first separator-delimited segment, where the family lives, destemming only when the stem has no separator at all — so `ac01` -> `ac` while `v30-seed8302` and `v35-seed8302` stay apart. Re-measured across all 17 live booths; the table is in the contract. INV-3 GAINED ITS SECOND DEGENERACY. The contract guarded one group for everything (sc-iso-spread: DSC0001-DSC0006). The live set's actual failure is the opposite — pewpew-ui-brief yields 23 groups for 34 items, dfa-concepts 13 for 20 — and the contract as written would have shipped a rail that is a second copy of the grid. The rail now renders only when grouping is informative: two or more groups, and the middle group holding more than one item. That predicate gets all 17 booths right. Grouping is a VIEW. The grid stays sorted(rel) and the zoom ring stays that order filtered to images; the group fixture interleaves across subdirectories precisely so a (group, rel) re-sort goes red. Groups are derived from the RENDERED list, not the full gallery, so no anchor points at a filtered-out tile. booth/items.py _group_of + Item.group, derived in the resolver (INV-1) booth/app.py _groups() builds the rail rows; build_gallery carries it booth/templates/ the rail-groups nav and its CSS tests/ +16 tests; 639 green Every new falsifier was proved by running its defeating change (12/12). Three were vacuous first time out: one fixture's positional order happened to be alphabetical, one assertion miscounted elements, and the harness itself certified a broken test twice — no green baseline, and byte-identical mutations silently defeated by the pyc cache's one-second mtime granularity.
555 lines
37 KiB
HTML
555 lines
37 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>{% block title %}The Booth{% endblock %}</title>
|
||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%23171a23'/%3E%3Ccircle cx='16' cy='16' r='6' fill='none' stroke='%2342dcd1' stroke-width='2.5'/%3E%3Ccircle cx='16' cy='16' r='2.2' fill='%2342dcd1'/%3E%3C/svg%3E">
|
||
<style>
|
||
/* ============================================================
|
||
The Booth — Corviduo "Australis" theme (aurora accents, dark-first).
|
||
Token values adopted from ratatoskr-web (colors_and_type.css canonical
|
||
palette). Self-contained, no webfont CDN — system fallback stacks.
|
||
============================================================ */
|
||
:root{
|
||
/* ---- Australis raw palette ---- */
|
||
--aus-black:#222531; --aus-white:#a9bcc3; --aus-bright-white:#cce7ec; --aus-bright-black:#373b46;
|
||
--aus-dark-30:#414751; --aus-dark-40:#565f69; --aus-dark-50:#6e7882; --aus-dark-60:#86929d;
|
||
--aus-bright-70:#9daeb6; --aus-bright-80:#b3cbcf;
|
||
--aus-blue:#6388d8; --aus-bright-blue:#a4c4ff;
|
||
--aus-cyan:#00b1a8; --aus-bright-cyan:#42dcd1;
|
||
--aus-green:#16b866; --aus-bright-green:#51e08a;
|
||
--aus-red:#ff491a; --aus-bright-red:#ff854f;
|
||
--aus-yellow:#e1c631; --aus-bright-yellow:#ffe14e;
|
||
--aus-magenta:#9d78ff; --aus-bright-magenta:#d8adff;
|
||
|
||
/* ---- semantic surface / foreground ---- */
|
||
--border-subtle:var(--aus-dark-30); --border-default:var(--aus-dark-40); --border-strong:var(--aus-dark-50);
|
||
--fg-0:var(--aus-bright-white); --fg-1:var(--aus-white); --fg-2:var(--aus-bright-70);
|
||
--fg-3:var(--aus-dark-60); --fg-muted:var(--aus-dark-50); --fg-on-accent:var(--aus-black);
|
||
|
||
/* ---- ratatoskr console surfaces ---- */
|
||
--rk-deep:#14161d; --rk-canvas:#171a23; --rk-well:#1b1e28; --rk-panel:#1d2029; --rk-ghost:#2c3040;
|
||
|
||
/* ---- type ---- */
|
||
--font-display:"Space Grotesk","Inter",ui-sans-serif,system-ui,sans-serif;
|
||
--font-sans:"Inter",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
|
||
--font-mono:"Berkeley Mono","JetBrains Mono","IBM Plex Mono",ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;
|
||
|
||
/* ---- radius / shadow / motion ---- */
|
||
--radius-sm:4px; --radius-md:6px; --radius-lg:10px; --radius-pill:9999px;
|
||
--shadow-2:0 2px 6px rgba(10,12,18,.45),0 1px 2px rgba(10,12,18,.4);
|
||
--shadow-3:0 8px 24px rgba(10,12,18,.5),0 2px 6px rgba(10,12,18,.4);
|
||
--glow-cyan:0 0 0 3px rgba(66,220,209,.26);
|
||
--ease-out:cubic-bezier(.2,.8,.2,1);
|
||
--ease-aurora:cubic-bezier(.65,0,.35,1);
|
||
}
|
||
@media (prefers-color-scheme: light){
|
||
:root{
|
||
--rk-deep:#e5edef; --rk-canvas:#eef4f6; --rk-well:#ffffff; --rk-panel:#f6fafb; --rk-ghost:#cad8dd;
|
||
--border-subtle:#d3dfe3; --border-default:#bccad0; --border-strong:#9fb0b7;
|
||
--fg-0:#10151c; --fg-1:#26313a; --fg-2:#48555f; --fg-3:#64727c; --fg-muted:#8a99a1; --fg-on-accent:#ffffff;
|
||
--aus-bright-cyan:#0a938b; --aus-cyan:#0a938b; --aus-bright-green:#12925a; --aus-green:#12925a;
|
||
--aus-bright-blue:#3f66bd; --aus-blue:#4a6fc0; --aus-bright-magenta:#7a52d8; --aus-magenta:#7a52d8;
|
||
--aus-bright-red:#d63a15; --aus-red:#d63a15; --aus-bright-yellow:#a9820a; --aus-yellow:#b98f0c;
|
||
--aus-dark-40:#b0bec4;
|
||
--shadow-3:0 8px 24px rgba(30,50,60,.16),0 2px 6px rgba(30,50,60,.1);
|
||
--glow-cyan:0 0 0 3px rgba(10,147,139,.2);
|
||
}
|
||
}
|
||
|
||
*{box-sizing:border-box}
|
||
html,body{margin:0}
|
||
body{background:var(--rk-canvas);color:var(--fg-1);font-family:var(--font-sans);
|
||
line-height:1.5;-webkit-font-smoothing:antialiased;min-height:100vh;display:flex;flex-direction:column;
|
||
transition:background-color .32s var(--ease-aurora),color .32s var(--ease-aurora)}
|
||
a{color:var(--aus-bright-cyan);text-decoration:none}
|
||
a:hover{color:var(--aus-cyan);text-decoration:underline}
|
||
code{font-family:var(--font-mono);font-size:.84em;background:var(--rk-well);color:var(--fg-2);
|
||
padding:.12em .42em;border-radius:var(--radius-sm);border:1px solid var(--border-subtle)}
|
||
|
||
.topbar{display:flex;align-items:center;gap:1.1rem;flex-wrap:wrap;
|
||
padding:1rem 1.5rem;border-bottom:1px solid var(--border-subtle);
|
||
background:linear-gradient(180deg,var(--rk-panel),transparent)}
|
||
.brand{display:inline-flex;align-items:center;gap:.6rem;color:var(--fg-0)}
|
||
.brand:hover{text-decoration:none}
|
||
.brand .dot{width:.62rem;height:.62rem;border-radius:50%;background:var(--aus-bright-cyan);
|
||
box-shadow:0 0 0 4px rgba(66,220,209,.15),0 0 12px rgba(66,220,209,.5);
|
||
animation:pulse 2.8s var(--ease-aurora) infinite}
|
||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||
.brand .name{font-family:var(--font-display);font-size:1.22rem;font-weight:600;letter-spacing:-.01em}
|
||
.tagline{color:var(--aus-cyan);font-size:.66rem;font-family:var(--font-mono);
|
||
letter-spacing:.2em;text-transform:uppercase}
|
||
|
||
main{flex:1;width:100%;max-width:1240px;margin:0 auto;padding:1.7rem 1.5rem 3rem}
|
||
|
||
/* ---- upload / pickup ---- */
|
||
.uploader{display:flex;gap:.9rem;align-items:stretch;margin-bottom:1.7rem;flex-wrap:wrap}
|
||
.drop{flex:1 1 300px;position:relative;display:flex;flex-direction:column;align-items:center;
|
||
justify-content:center;gap:.15rem;text-align:center;cursor:pointer;padding:1.1rem 1rem;
|
||
border:1.5px dashed var(--border-default);border-radius:var(--radius-lg);background:var(--rk-well);
|
||
transition:border-color .16s var(--ease-out),background .16s var(--ease-out),box-shadow .16s var(--ease-out)}
|
||
.drop:hover{border-color:var(--aus-cyan)}
|
||
.drop.over{border-color:var(--aus-bright-cyan);background:rgba(66,220,209,.06);box-shadow:var(--glow-cyan)}
|
||
.drop.has{border-style:solid;border-color:var(--aus-cyan)}
|
||
.drop input[type=file]{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}
|
||
.drop-icon{font-size:1.2rem;color:var(--aus-bright-cyan)}
|
||
.drop-main{font-family:var(--font-display);font-weight:600;color:var(--fg-0);font-size:.98rem}
|
||
.drop-sub{font-family:var(--font-mono);font-size:.7rem;letter-spacing:.03em;color:var(--fg-3)}
|
||
.up-go{flex:0 0 auto;align-self:stretch;cursor:pointer;font-family:var(--font-mono);font-size:.74rem;
|
||
letter-spacing:.1em;text-transform:uppercase;padding:0 1.15rem;border-radius:var(--radius-lg);
|
||
background:transparent;border:1px solid var(--aus-cyan);color:var(--aus-bright-cyan);
|
||
transition:.14s var(--ease-out)}
|
||
.up-go:hover{background:var(--aus-bright-cyan);color:var(--fg-on-accent);border-color:var(--aus-bright-cyan)}
|
||
|
||
.badge{display:inline-block;font-family:var(--font-mono);font-size:.6rem;letter-spacing:.1em;
|
||
text-transform:uppercase;font-weight:600;color:var(--fg-on-accent);background:var(--aus-bright-cyan);
|
||
padding:.08rem .42rem;border-radius:var(--radius-pill);vertical-align:middle}
|
||
.card .thumb{position:relative}
|
||
.thumb .badge{position:absolute;top:.5rem;left:.5rem;box-shadow:var(--shadow-2)}
|
||
|
||
/* Kept lane. The accent is a 2px TOP edge in aurora blue — the one accent
|
||
border Australis sanctions (never a coloured left border), and it marks the
|
||
card as featured without changing its fill, so kept and ephemeral still
|
||
read as the same family of object. */
|
||
.lane-head{margin:1.9rem 0 .8rem;font-family:var(--font-mono);font-size:.68rem;font-weight:600;
|
||
letter-spacing:.14em;text-transform:uppercase;color:var(--aus-bright-cyan);
|
||
display:flex;align-items:center;gap:.7rem}
|
||
.lane-head::after{content:"";flex:1;height:1px;background:var(--border-subtle)}
|
||
.lane-note{font-weight:400;letter-spacing:.06em;color:var(--fg-3);text-transform:none}
|
||
.lane-note code{font-size:.95em;color:var(--fg-2)}
|
||
.kept-grid{margin-bottom:.4rem}
|
||
.card-kept{border-top:2px solid var(--aus-blue)}
|
||
.card-kept:hover{border-color:var(--aus-blue);border-top-color:var(--aus-bright-blue)}
|
||
.badge-kept{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||
/* Release sits where the ephemeral card's × sits, but reads as a word rather
|
||
than a destructive glyph — it is not the delete, it is what unlocks it. */
|
||
/* One positioned row holds BOTH kept-card controls. They used to pin
|
||
themselves to the same corner independently and the later sibling won. */
|
||
.kept-actions{position:absolute;top:.4rem;right:.4rem;display:flex;gap:.3rem;
|
||
align-items:center;opacity:0;transition:opacity .12s}
|
||
.card-kept:hover .kept-actions,.kept-actions:focus-within{opacity:1}
|
||
.kept-actions form{position:static;opacity:1;margin:0}
|
||
.release{opacity:0;transition:opacity .12s}
|
||
.card-kept:hover .release,.release:focus-within{opacity:1}
|
||
.release button{font:inherit;font-size:.72rem;line-height:1;padding:.22rem .45rem;
|
||
border-radius:.3rem;cursor:pointer;border:1px solid var(--aus-blue);
|
||
background:var(--rk-panel);color:var(--aus-blue)}
|
||
.release button:hover{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||
|
||
/* ---- the standing link board ------------------------------------------
|
||
Rows, not a markdown blob. Dense enough that thirty entries stay
|
||
scannable, with provenance de-emphasised so the description leads and the
|
||
× only surfaces on hover — destructive controls should not compete for
|
||
attention with the thing you came to read. */
|
||
.board{border:1px solid var(--border-subtle);border-radius:.5rem;overflow:hidden;
|
||
background:var(--rk-panel);margin:.6rem 0 1rem}
|
||
.board-head{display:flex;align-items:center;gap:.6rem;padding:.45rem .75rem;
|
||
border-bottom:1px solid var(--border-subtle);background:var(--rk-well)}
|
||
.board-selall{display:inline-flex;align-items:center;cursor:pointer;flex:0 0 auto}
|
||
.board-title{font-weight:600;font-size:.85rem}
|
||
.board-note{font-size:.72rem;color:var(--fg-3)}
|
||
.board-spacer{flex:1 1 auto}
|
||
/* Bulk delete: quiet until something is selected. Red-outline danger word,
|
||
matching the .wipe-lg language — fills on hover, greys out when disabled. */
|
||
.board-del-sel{flex:0 0 auto;cursor:pointer;font-family:var(--font-mono);font-size:.7rem;
|
||
letter-spacing:.06em;padding:.28rem .6rem;border-radius:var(--radius-sm);
|
||
background:transparent;border:1px solid var(--aus-red);color:var(--aus-bright-red);
|
||
transition:.14s var(--ease-out)}
|
||
.board-del-sel:hover:not(:disabled){background:var(--aus-red);border-color:var(--aus-red);color:#fff}
|
||
.board-del-sel:disabled{opacity:.4;cursor:default;border-color:var(--border-default);color:var(--fg-3)}
|
||
|
||
.board-row{display:flex;align-items:center;gap:.6rem;padding:.45rem .75rem;
|
||
border-bottom:1px solid var(--border-subtle);transition:background .1s}
|
||
.board-row:last-child{border-bottom:0}
|
||
.board-row:hover{background:var(--rk-well)}
|
||
/* Pinned rows: a subtle cyan wash + a filled star. NOT a coloured left border
|
||
(Australis forbids it); the tint is enough to group them, and the ★ carries
|
||
the state at the row level. */
|
||
.board-row.is-pinned{background:rgba(66,220,209,.05)}
|
||
.board-row.is-pinned:hover{background:rgba(66,220,209,.09)}
|
||
.board-check{flex:0 0 auto;width:1rem;height:1rem;cursor:pointer;accent-color:var(--aus-bright-cyan)}
|
||
.board-selall input{width:1rem;height:1rem;cursor:pointer;accent-color:var(--aus-bright-cyan)}
|
||
/* ★ favorite toggle. Always visible (unlike ×) because it carries state you
|
||
need to see; dim-hollow when off, warm-gold and filled when pinned. */
|
||
.board-pin{flex:0 0 auto;border:0;background:none;cursor:pointer;line-height:1;
|
||
font-size:1.02rem;padding:.05rem .2rem;border-radius:.25rem;color:var(--fg-3);
|
||
transition:color .12s,transform .12s var(--ease-out)}
|
||
.board-pin:hover{color:var(--aus-bright-yellow);transform:scale(1.15)}
|
||
.board-pin.on{color:var(--aus-bright-yellow)}
|
||
.board-main{flex:1 1 auto;min-width:0}
|
||
.board-link{font-size:.9rem;text-decoration:none;font-weight:500}
|
||
.board-link:hover{text-decoration:underline}
|
||
.board-url{font-size:.7rem;color:var(--fg-3);opacity:.8;overflow:hidden;text-overflow:ellipsis;
|
||
white-space:nowrap;font-family:var(--font-mono)}
|
||
.board-meta{flex:0 0 auto;display:flex;flex-direction:column;align-items:flex-end;
|
||
gap:.05rem;font-size:.68rem;color:var(--fg-3);white-space:nowrap}
|
||
.board-who{font-weight:600}
|
||
.board-copy,.board-rm-btn{opacity:0;transition:opacity .12s;flex:0 0 auto}
|
||
.board-row:hover .board-copy,.board-row:hover .board-rm-btn,
|
||
.board-copy:focus,.board-rm-btn:focus{opacity:1}
|
||
.board-rm-btn{font:inherit;font-size:1rem;line-height:1;padding:.1rem .35rem;
|
||
border:0;background:none;cursor:pointer;color:var(--fg-2);border-radius:.25rem}
|
||
.board-rm-btn:hover{background:var(--aus-red);color:#fff}
|
||
@media (max-width:600px){
|
||
/* No hover on touch — controls must be permanently visible or unreachable. */
|
||
.board-copy,.board-rm-btn{opacity:1}
|
||
.board-meta{display:none}
|
||
}
|
||
|
||
.pickup-note{margin:-.5rem 0 1.5rem;padding:.6rem .85rem;border:1px solid var(--border-subtle);
|
||
border-left:3px solid var(--aus-bright-cyan);border-radius:var(--radius-md);background:var(--rk-well);
|
||
font-family:var(--font-mono);font-size:.78rem;color:var(--fg-2)}
|
||
.copy-btn{cursor:pointer;font-family:var(--font-mono);font-size:.68rem;letter-spacing:.04em;
|
||
padding:.12rem .5rem;margin:0 .25rem;border:1px solid var(--border-default);border-radius:var(--radius-sm);
|
||
background:transparent;color:var(--aus-bright-cyan);vertical-align:middle;transition:.12s var(--ease-out)}
|
||
.copy-btn:hover{border-color:var(--aus-cyan);background:rgba(66,220,209,.08)}
|
||
.copy-btn.copied{border-color:var(--aus-green);color:var(--aus-bright-green)}
|
||
.dl-link{color:var(--aus-bright-cyan);text-decoration:none;margin-right:.4rem;font-size:.95em}
|
||
.dl-link:hover{color:var(--aus-cyan)}
|
||
.cap-text{color:var(--fg-2)}
|
||
|
||
/* ---- image viewer (fixed full-viewport overlay) ---- */
|
||
.viewer{position:fixed;inset:0;z-index:50;background:var(--rk-canvas);display:flex;flex-direction:column}
|
||
.vbar{display:flex;align-items:center;gap:.7rem;padding:.5rem .8rem;
|
||
border-bottom:1px solid var(--border-subtle);background:var(--rk-panel)}
|
||
.vname{font-family:var(--font-mono);font-size:.8rem;color:var(--fg-2);
|
||
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:50vw}
|
||
.vspacer{flex:1}
|
||
.vbtn{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;
|
||
padding:0 .55rem;border:1px solid var(--border-default);border-radius:var(--radius-md);
|
||
color:var(--fg-1);background:transparent;font-family:var(--font-mono);font-size:.9rem;
|
||
transition:.14s var(--ease-out)}
|
||
.vbtn:hover{border-color:var(--aus-cyan);color:var(--aus-bright-cyan);text-decoration:none}
|
||
.vx:hover{border-color:var(--aus-red);color:#fff;background:var(--aus-red)}
|
||
.vtoggle{border:1px solid var(--border-default);border-radius:var(--radius-md);overflow:hidden}
|
||
.vseg{cursor:pointer;border:0;background:transparent;color:var(--fg-3);font-family:var(--font-mono);
|
||
font-size:.72rem;letter-spacing:.06em;padding:.4rem .75rem;transition:.14s var(--ease-out)}
|
||
.vseg+.vseg{border-left:1px solid var(--border-default)}
|
||
.vseg:hover{color:var(--fg-1)}
|
||
.vseg.on{background:var(--aus-bright-cyan);color:var(--fg-on-accent)}
|
||
.vstage{flex:1;min-height:0;background:var(--rk-deep)}
|
||
.vstage.fit{display:flex;align-items:center;justify-content:center;overflow:hidden;padding:1rem}
|
||
.vstage.fit img{max-width:100%;max-height:100%;width:auto;height:auto;box-shadow:var(--shadow-3)}
|
||
.vstage.one{overflow:auto;text-align:center}
|
||
.vstage.one img{max-width:none;max-height:none;margin:auto}
|
||
|
||
.foot{border-top:1px solid var(--border-subtle);color:var(--fg-muted);
|
||
font-size:.72rem;font-family:var(--font-mono);letter-spacing:.04em;padding:1rem 1.5rem;text-align:center}
|
||
|
||
.empty{border:1px dashed var(--border-default);border-radius:var(--radius-lg);
|
||
padding:3rem 1.5rem;text-align:center;color:var(--fg-3);background:var(--rk-well);font-family:var(--font-mono);font-size:.92rem}
|
||
|
||
/* index grid */
|
||
.grid{display:grid;gap:1.1rem;grid-template-columns:repeat(auto-fill,minmax(248px,1fr))}
|
||
.card{position:relative;background:var(--rk-panel);border:1px solid var(--border-subtle);
|
||
border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-2);
|
||
transition:transform .16s var(--ease-out),border-color .16s var(--ease-out),box-shadow .16s var(--ease-out)}
|
||
.card:hover{transform:translateY(-3px);border-color:var(--aus-cyan);box-shadow:var(--shadow-3),var(--glow-cyan)}
|
||
.card .thumb{display:block;aspect-ratio:16/10;background:var(--rk-deep);overflow:hidden}
|
||
.card .thumb img{width:100%;height:100%;object-fit:cover;display:block}
|
||
.card .ph{width:100%;height:100%;display:flex;align-items:center;justify-content:center;
|
||
color:var(--fg-3);font-family:var(--font-mono);font-size:.82rem;letter-spacing:.16em;text-transform:uppercase}
|
||
.card .meta{padding:.7rem .85rem .85rem}
|
||
.card .name{display:block;font-family:var(--font-display);font-weight:600;color:var(--fg-0);word-break:break-word}
|
||
.card .name:hover{text-decoration:none;color:var(--aus-bright-cyan)}
|
||
.card .sub{color:var(--fg-3);font-size:.72rem;font-family:var(--font-mono);letter-spacing:.03em;margin-top:.3rem}
|
||
|
||
/* THE ANNOUNCEMENT — who posted this booth and why (U5). Same size and
|
||
rhythm as .sub above it, because it is the same class of information: a
|
||
second line of card metadata, not a heading. The handle carries the only
|
||
colour, so a scan down the index reads as a column of posters. */
|
||
.prov{margin-top:.28rem;font-size:.72rem;font-family:var(--font-mono);
|
||
letter-spacing:.03em;color:var(--fg-3);line-height:1.45;
|
||
overflow-wrap:anywhere}
|
||
.prov-who{color:var(--fg-2)}
|
||
.prov-why{color:var(--fg-3)}
|
||
/* Quiet on purpose. 26 booths arrived before this convention existed and
|
||
rsync keeps making more, so the marker has to be visible-if-you-look and
|
||
never a badge shouting 26 times. `unreadable` gets the warning tint
|
||
because, unlike `unannounced`, it is something somebody has to fix. */
|
||
.prov-none{color:var(--fg-muted);font-style:italic}
|
||
.prov-broken{color:var(--aus-bright-yellow,#e8c547);font-style:italic;cursor:help}
|
||
|
||
.wipe{position:absolute;top:.5rem;right:.5rem;margin:0}
|
||
/* ★ keep, mirroring .wipe on the other shoulder of the card. Same
|
||
hover-to-reveal language as .release in the kept lane. */
|
||
.keepit{position:absolute;top:.5rem;left:.5rem;margin:0;opacity:0;transition:opacity .12s}
|
||
/* × on a KEPT card. Same shoulder as the ephemeral ×, deliberately tinted so
|
||
it does not read as the same weight of action. */
|
||
/* positioned by .kept-actions, not by itself */
|
||
.wipe-kept button{font:inherit;line-height:1;cursor:pointer;border:1px solid var(--line);
|
||
border-radius:.3rem;padding:.02rem .3rem;background:var(--bg);color:var(--muted)}
|
||
.wipe-kept button:hover{background:var(--aus-red,#ff6b6b);color:var(--fg-on-accent,#fff);
|
||
border-color:var(--aus-red,#ff6b6b)}
|
||
/* keep / release from inside a booth, beside "Wipe now" */
|
||
.keep-lg{display:inline-block;margin:0 .4rem 0 0}
|
||
.keep-lg button{font:inherit;font-size:.8rem;line-height:1;padding:.32rem .6rem;
|
||
cursor:pointer;border:1px solid var(--line);border-radius:.3rem;
|
||
background:var(--bg);color:var(--fg)}
|
||
.keep-lg button:hover{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||
.card:hover .keepit,.keepit:focus-within{opacity:1}
|
||
.keepit button{font:inherit;font-size:.9rem;line-height:1;padding:.1rem .34rem;
|
||
cursor:pointer;border:1px solid var(--line);border-radius:.3rem;
|
||
background:var(--bg);color:var(--fg)}
|
||
.keepit button:hover{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||
|
||
/* ⚠ Blur is COSMETIC. The file is still served at its own URL and still in
|
||
the zip. This hides an item from a glance, nothing more. */
|
||
.item.blurred img,.item.blurred video,
|
||
.item.blurred .doc-body,.item.blurred .textview{filter:blur(22px);transition:filter .15s}
|
||
.item.blurred.revealed img,.item.blurred.revealed video,
|
||
.item.blurred.revealed .doc-body,.item.blurred.revealed .textview{filter:none}
|
||
.item.blurred{position:relative}
|
||
.item.blurred .reveal{position:absolute;top:.5rem;left:.5rem;z-index:2;
|
||
font:inherit;font-size:.72rem;line-height:1;padding:.24rem .5rem;cursor:pointer;
|
||
border:1px solid var(--line);border-radius:.3rem;background:var(--bg);color:var(--fg)}
|
||
.item.blurred .reveal:hover{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||
/* Legible on purpose. v1 was a bare muted glyph with no border and no label,
|
||
and the operator's reaction to it was "no UI option to blur/unblur?" — a
|
||
control nobody can find is a control that is not there. */
|
||
.blurtoggle{display:inline-block;margin:0}
|
||
.blurtoggle button{font:inherit;font-size:.7rem;line-height:1;padding:.2rem .4rem;
|
||
cursor:pointer;border:1px solid var(--line);border-radius:.3rem;
|
||
background:var(--bg);color:var(--fg);white-space:nowrap}
|
||
.blurtoggle button:hover{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||
/* In the doc bar it sits beside ⤢ ⬇ ✕ and should not out-shout them. */
|
||
.doc-bar .blurtoggle{margin-left:.35rem}
|
||
.doc-bar .blurtoggle button{font-size:.66rem;padding:.14rem .34rem}
|
||
/* Cover thumbs on the index inherit the blur so the front page cannot undo it. */
|
||
.blurred-thumb{filter:blur(16px)}
|
||
/* opaque dark control-scrim + always-light glyph — legible over any thumbnail
|
||
AND in both themes (glyph must NOT follow --fg-*, which flips dark on light). */
|
||
.wipe button{cursor:pointer;border:1px solid rgba(255,255,255,.16);background:rgba(16,18,25,.86);
|
||
color:#e9eef0;width:1.9rem;height:1.9rem;border-radius:var(--radius-md);font-size:1.1rem;line-height:1;
|
||
backdrop-filter:blur(6px);transition:.14s var(--ease-out)}
|
||
.wipe button:hover{border-color:var(--aus-red);color:#fff;background:var(--aus-red)}
|
||
|
||
/* Asks — a session's multiple-choice question awaiting the operator.
|
||
Amber = "needs you" while open (the one colour the page does not otherwise
|
||
use for state), green check once answered; the accent is a TOP edge, per
|
||
Australis, never a coloured left border. */
|
||
.badge-mark{background:var(--aus-bright-yellow);color:var(--fg-on-accent)}
|
||
/* U4: the lifetime line's HELD states. Marked rather than styled into
|
||
invisibility — the whole safety argument for an unbounded hold is that
|
||
a booth which stopped counting down says so where the countdown was. */
|
||
.held{color:var(--aus-bright-yellow)}
|
||
.held-broken{color:var(--fg-3);text-decoration:underline dotted}
|
||
.thumb .badge+.badge-mark{top:2.2rem}
|
||
.marks{display:flex;flex-direction:column;gap:.9rem;margin:.2rem 0 1.4rem}
|
||
.mark{border:1px solid var(--border-subtle);border-top:2px solid var(--aus-bright-yellow);
|
||
border-radius:.5rem;background:var(--rk-panel);overflow:hidden}
|
||
.mark.is-answered{border-top-color:var(--aus-bright-green)}
|
||
/* Partial: answered SOME questions. Not a failure and not done — blanks are a
|
||
legal outcome (operator ruling 2026-09-09), so it gets its own state rather
|
||
than being forced into one of the other two. */
|
||
.mark.is-partial{border-top-color:var(--aus-bright-blue)}
|
||
.mark.is-partial .mark-state{color:var(--aus-bright-blue)}
|
||
.mark-part{font-family:var(--font-mono);font-size:.68rem;color:var(--aus-bright-blue);font-weight:700}
|
||
.mark-answer-choice.is-skipped{opacity:.55;font-style:italic}
|
||
.mark-answer-choice.is-skipped::before{content:"— ";color:var(--fg-3)}
|
||
.mark.is-broken{border-top-color:var(--aus-bright-red)}
|
||
.mark-head{display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;
|
||
border-bottom:1px solid var(--border-subtle);background:var(--rk-well);
|
||
font-family:var(--font-mono);font-size:.68rem;color:var(--fg-3)}
|
||
.mark-state{font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--aus-bright-yellow)}
|
||
.mark.is-answered .mark-state{color:var(--aus-bright-green)}
|
||
.mark.is-broken .mark-state{color:var(--aus-bright-red)}
|
||
.mark-when{white-space:nowrap}
|
||
/* ---- marks: the per-item controls and the note slots ------------------- */
|
||
.mark-state-note{color:var(--aus-bright-cyan,#42dcd1)}
|
||
.mark-state-flag{color:var(--aus-bright-green)}
|
||
.mark-text,.vnote pre,.item-note pre{margin:.3rem 0;padding:.5rem .7rem;white-space:pre-wrap;
|
||
background:var(--bg-2,rgba(128,140,160,.10));border-radius:6px;font-size:.82rem}
|
||
.mark-flaglist{margin:.3rem 0 .2rem;padding-left:1.1rem;font-size:.84rem}
|
||
.mark-add,.vaddnote,.item-addnote form{display:flex;gap:.5rem;align-items:flex-start;margin:.6rem 0}
|
||
.mark-add textarea,.vaddnote textarea,.item-addnote textarea{flex:1;min-width:0}
|
||
.mark-x{background:none;border:0;color:var(--fg-3);cursor:pointer;font-size:1rem;line-height:1;padding:0 .3rem}
|
||
.mark-x:hover{color:var(--aus-bright-red)}
|
||
.mark-undo{margin-left:.4rem}
|
||
.flagtoggle button{background:none;border:1px solid var(--border-subtle);border-radius:6px;
|
||
padding:.12rem .45rem;font-size:.72rem;color:var(--fg-2);cursor:pointer}
|
||
.flagtoggle button:hover{border-color:var(--aus-bright-green);color:var(--aus-bright-green)}
|
||
.item.is-flagged{outline:2px solid var(--aus-bright-green);outline-offset:2px}
|
||
.item-note{display:flex;align-items:flex-start;gap:.3rem}
|
||
.item-addnote summary{cursor:pointer;font-size:.72rem;color:var(--fg-3);padding:.15rem 0}
|
||
.vmarks{margin:.6rem auto 0;max-width:min(92vw,900px);display:flex;flex-direction:column;gap:.4rem}
|
||
.vflag{display:flex}
|
||
.vbtn.is-flagged{color:var(--aus-bright-green);border-color:var(--aus-bright-green)}
|
||
.vnote{display:flex;align-items:flex-start;gap:.3rem}
|
||
.mark-title{margin:.8rem .9rem -.35rem;font-family:var(--font-mono);font-size:.7rem;
|
||
letter-spacing:.08em;text-transform:uppercase;color:var(--fg-3)}
|
||
.mark-prompt{margin:.85rem .9rem .5rem;font-size:1.02rem;font-weight:600;color:var(--fg-0);white-space:pre-wrap}
|
||
.mark-error{margin:.8rem .9rem;color:var(--aus-bright-red);font-size:.85rem}
|
||
.mark-answer{margin:.2rem .9rem .6rem;padding:.55rem .75rem;border:1px solid var(--border-subtle);
|
||
border-radius:var(--radius-md);background:rgba(81,224,138,.06)}
|
||
.mark-answer-choice{font-weight:600;color:var(--fg-0)}
|
||
.mark-answer-choice::before{content:"✓ ";color:var(--aus-bright-green)}
|
||
.mark-answer-notes{margin:.4rem 0 0;white-space:pre-wrap;font-family:var(--font-sans);font-size:.86rem;
|
||
color:var(--fg-1)}
|
||
.mark-answer-file{display:block;margin-top:.35rem;font-family:var(--font-mono);font-size:.68rem;color:var(--fg-3)}
|
||
.mark-answer-file a{color:var(--fg-2)}
|
||
.mark-formwrap{margin:0 .9rem .8rem}
|
||
.mark-change{cursor:pointer;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;
|
||
text-transform:uppercase;color:var(--fg-3);list-style:none;user-select:none}
|
||
.mark-change::-webkit-details-marker{display:none}
|
||
.mark-formwrap[open]>.mark-change{margin-bottom:.4rem}
|
||
.mark-formwrap:not([open])>.mark-change{color:var(--aus-bright-cyan)}
|
||
.mark-q{border:0;margin:0 0 .7rem;padding:0;min-width:0}
|
||
.mark-q:last-of-type{margin-bottom:0}
|
||
.mark-q-prompt{padding:0;margin:0 0 .35rem;font-size:.9rem;font-weight:600;color:var(--fg-0)}
|
||
.mark-qnotes{margin-top:.35rem;font-size:.82rem}
|
||
.mark-answer-q{padding:.3rem 0;border-bottom:1px dashed var(--border-subtle)}
|
||
.mark-answer-q:last-of-type{border-bottom:0}
|
||
.mark-answer-qprompt{display:block;font-size:.76rem;color:var(--fg-3)}
|
||
.mark-options{display:flex;flex-direction:column;gap:.35rem}
|
||
.mark-opt{display:flex;align-items:flex-start;gap:.6rem;padding:.5rem .65rem;cursor:pointer;
|
||
border:1px solid var(--border-subtle);border-radius:var(--radius-md);background:var(--rk-well);
|
||
transition:border-color .12s,background .12s}
|
||
.mark-opt:hover{border-color:var(--border-strong)}
|
||
.mark-opt:has(input:checked){border-color:var(--aus-bright-cyan);background:rgba(66,220,209,.07)}
|
||
.mark-opt input{margin:.2rem 0 0;accent-color:var(--aus-bright-cyan);flex:0 0 auto}
|
||
.mark-opt-main{display:flex;flex-direction:column;gap:.1rem;min-width:0}
|
||
.mark-opt-label{font-size:.92rem;color:var(--fg-0)}
|
||
.mark-opt-detail{font-size:.76rem;color:var(--fg-3);white-space:pre-wrap}
|
||
.mark-notes{display:block;width:100%;box-sizing:border-box;margin:.6rem 0 0;padding:.5rem .6rem;
|
||
font:inherit;font-size:.88rem;color:var(--fg-0);background:var(--rk-well);
|
||
border:1px solid var(--border-subtle);border-radius:var(--radius-md);resize:vertical}
|
||
.mark-notes:focus{outline:none;border-color:var(--aus-bright-cyan);box-shadow:var(--glow-cyan)}
|
||
.mark-actions{display:flex;justify-content:flex-end;margin-top:.6rem}
|
||
.mark-submit{cursor:pointer;font-family:var(--font-mono);font-size:.74rem;letter-spacing:.06em;
|
||
padding:.42rem .9rem;border-radius:var(--radius-sm);border:1px solid var(--aus-bright-cyan);
|
||
background:var(--aus-bright-cyan);color:var(--fg-on-accent);font-weight:700;transition:.14s var(--ease-out)}
|
||
.mark-submit:hover{background:var(--aus-cyan);border-color:var(--aus-cyan)}
|
||
|
||
/* booth page */
|
||
.boothhead{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;
|
||
padding-bottom:1rem;margin-bottom:1.4rem;border-bottom:1px solid var(--border-subtle)}
|
||
.boothhead .back{font-family:var(--font-mono);font-size:.76rem;letter-spacing:.08em;color:var(--fg-3)}
|
||
.boothhead h1{margin:0;font-family:var(--font-display);font-weight:600;font-size:1.5rem;
|
||
letter-spacing:-.01em;word-break:break-word;flex:1 1 auto;color:var(--fg-0)}
|
||
.boothhead .sub{color:var(--fg-3);font-size:.74rem;font-family:var(--font-mono);letter-spacing:.06em}
|
||
/* Its own row under the title, not another chip in the flex line — a `why`
|
||
can run to WHY_MAX and would otherwise shove the zip link around. */
|
||
.boothhead .prov{flex:0 0 100%;margin-top:-.35rem}
|
||
/* The directory name beside a manifest title: quieter than the title, but
|
||
never absent — it is what the URL says and what "the third one" refers to. */
|
||
.h1-slug{font-family:var(--font-mono);font-size:.62em;font-weight:400;
|
||
letter-spacing:.06em;color:var(--fg-3);margin-left:.5rem;white-space:nowrap}
|
||
.wipe-lg{position:static}
|
||
/* red-outline danger button — legible on the dark canvas, fills on hover */
|
||
.wipe-lg button{width:auto;height:auto;padding:.42rem .85rem;border-radius:var(--radius-md);
|
||
font-size:.72rem;font-family:var(--font-mono);letter-spacing:.1em;text-transform:uppercase;
|
||
background:transparent;border-color:var(--aus-red);color:var(--aus-bright-red)}
|
||
.wipe-lg button:hover{background:var(--aus-red);border-color:var(--aus-red);color:#fff}
|
||
|
||
.gallery{display:grid;gap:1.4rem;grid-template-columns:repeat(auto-fill,minmax(320px,1fr))}
|
||
.item{margin:0;background:var(--rk-panel);border:1px solid var(--border-subtle);border-radius:var(--radius-lg);
|
||
overflow:hidden;display:flex;flex-direction:column;box-shadow:var(--shadow-2)}
|
||
.item img,.item video{width:100%;height:auto;display:block;background:var(--rk-deep)}
|
||
.item audio{width:100%;margin:1.3rem .9rem .4rem;max-width:calc(100% - 1.8rem)}
|
||
.item .dl{padding:1.4rem .9rem;font-family:var(--font-mono);font-size:.86rem;word-break:break-all}
|
||
.item figcaption{padding:.6rem .85rem .75rem;color:var(--fg-2);font-size:.78rem;
|
||
font-family:var(--font-mono);letter-spacing:.02em;border-top:1px solid var(--border-subtle);word-break:break-word}
|
||
.item-audio figcaption,.item-other figcaption{border-top:none}
|
||
|
||
/* Inline doc rendering — a .md/.txt/.log shows in place, collapsible and
|
||
closable, instead of a link to a separate page. The item spans the full
|
||
grid width so prose has a readable measure. */
|
||
.item-doc{grid-column:1 / -1}
|
||
.item-doc.is-closed{display:none}
|
||
.doc-inline{display:block}
|
||
.doc-inline > .doc-bar{list-style:none;cursor:pointer;display:flex;align-items:center;gap:.55rem;
|
||
padding:.6rem .85rem;font-family:var(--font-mono);font-size:.8rem;color:var(--fg-2);
|
||
background:var(--rk-well);border-bottom:1px solid var(--border-subtle);user-select:none}
|
||
.doc-inline > .doc-bar::-webkit-details-marker{display:none}
|
||
.doc-chevron{color:var(--fg-3);transition:transform .12s ease;font-size:.7rem}
|
||
.doc-inline[open] > .doc-bar .doc-chevron{transform:rotate(90deg)}
|
||
.doc-name{color:var(--fg-1);word-break:break-all}
|
||
.doc-spacer{flex:1}
|
||
.doc-act{color:var(--fg-3);text-decoration:none;padding:.1rem .35rem;border-radius:5px;
|
||
font-size:.9rem;line-height:1;background:none;border:0;cursor:pointer;font-family:inherit}
|
||
.doc-act:hover{color:var(--aus-bright-cyan,#42dcd1);background:var(--rk-deep)}
|
||
.doc-close:hover{color:var(--aus-red,#ff6b6b)}
|
||
.doc-body{margin:0;border:0;border-radius:0;max-height:32rem;overflow:auto;padding:1rem 1.15rem}
|
||
.doc-body.textview{background:var(--rk-panel)}
|
||
|
||
/* Shared doc typography — used by the inline body above AND the full-page
|
||
doc view (doc.html). Kept here so both surfaces render identically. */
|
||
.textview{white-space:pre-wrap;word-break:break-word;font-family:var(--font-mono);
|
||
font-size:.86rem;line-height:1.5;color:var(--fg-1);background:var(--rk-well);
|
||
border:1px solid var(--rk-line,#252a35);border-radius:10px;padding:1rem 1.15rem;overflow-x:auto}
|
||
.markdown-body{color:var(--fg-1);line-height:1.62;font-size:.98rem;overflow-wrap:break-word}
|
||
.markdown-body h1,.markdown-body h2,.markdown-body h3{line-height:1.25;margin:1.6em 0 .5em}
|
||
.markdown-body h1{font-size:1.7em}.markdown-body h2{font-size:1.35em}.markdown-body h3{font-size:1.12em}
|
||
.markdown-body h1,.markdown-body h2{border-bottom:1px solid var(--rk-line,#252a35);padding-bottom:.3em}
|
||
.markdown-body :first-child{margin-top:0}
|
||
.markdown-body p,.markdown-body ul,.markdown-body ol,.markdown-body blockquote{margin:.7em 0}
|
||
.markdown-body a{color:var(--aus-bright-cyan,#42dcd1)}
|
||
.markdown-body code{font-family:var(--font-mono);font-size:.86em;background:var(--rk-well);
|
||
padding:.12em .38em;border-radius:5px}
|
||
.markdown-body pre{background:var(--rk-well);border:1px solid var(--rk-line,#252a35);
|
||
border-radius:10px;padding:.9rem 1.05rem;overflow-x:auto}
|
||
.markdown-body pre code{background:none;padding:0}
|
||
.markdown-body blockquote{border-left:3px solid var(--aus-bright-cyan,#42dcd1);
|
||
padding-left:1em;color:var(--fg-2);margin-left:0}
|
||
.markdown-body table{border-collapse:collapse;display:block;overflow-x:auto}
|
||
.markdown-body th,.markdown-body td{border:1px solid var(--rk-line,#252a35);padding:.4em .7em}
|
||
.markdown-body img{max-width:100%}
|
||
|
||
/* U6 — the bench registry, on the standing board's page only. */
|
||
.benches{margin:1rem 0;border:1px solid var(--line,#2a2a2a);border-radius:6px;overflow:hidden}
|
||
.bench-head{display:flex;gap:.6rem;align-items:baseline;padding:.5rem .7rem;background:rgba(255,255,255,.03)}
|
||
.bench-title{font-weight:600}
|
||
.bench-note,.bench-empty{opacity:.6;font-size:.85em}
|
||
.bench-empty{padding:.6rem .7rem}
|
||
.bench-err{padding:.6rem .7rem;color:#f2b8b5;background:rgba(242,184,181,.08)}
|
||
.bench-row{display:flex;gap:.6rem;align-items:center;padding:.45rem .7rem;border-top:1px solid var(--line,#2a2a2a)}
|
||
.bench-row.is-retired{opacity:.5}
|
||
.bench-state{font-size:.7em;text-transform:uppercase;letter-spacing:.06em;padding:.1rem .4rem;border-radius:3px;background:rgba(255,255,255,.08)}
|
||
.bench-row.is-live .bench-state{background:rgba(120,200,140,.18)}
|
||
.bench-row.is-promoted .bench-state{background:rgba(130,170,240,.18)}
|
||
.bench-main{flex:1;min-width:0}
|
||
.bench-url{font-size:.78em;opacity:.55;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.bench-meta{display:flex;flex-direction:column;align-items:flex-end;font-size:.75em;opacity:.6}
|
||
.bench-acts{display:flex;gap:.3rem}
|
||
.bench-to,.bench-rm{font-size:.75em;padding:.15rem .4rem;cursor:pointer}
|
||
.bench-add{display:flex;gap:.4rem;padding:.5rem .7rem;border-top:1px solid var(--line,#2a2a2a)}
|
||
.bench-add input[type=url]{flex:2;min-width:0}
|
||
.bench-add input[type=text]{flex:1;min-width:0}
|
||
/* A board row whose booth has been swept. Marked, never auto-removed. */
|
||
.board-row.board-dead{opacity:.45}
|
||
.board-dead-tag{font-size:.9em;color:#f2b8b5;opacity:.9}
|
||
/* U7 — the rail, and the grid cursor. */
|
||
.rail{position:sticky;top:0;z-index:5;display:flex;gap:.5rem;align-items:baseline;
|
||
padding:.4rem .6rem;margin:.6rem 0;background:var(--bg,#111);
|
||
border-bottom:1px solid var(--line,#2a2a2a);flex-wrap:wrap}
|
||
.rail-total{font-weight:600}
|
||
.rail-f{font-size:.85em;padding:.1rem .45rem;border-radius:3px;text-decoration:none;
|
||
opacity:.65;border:1px solid transparent}
|
||
.rail-f:hover{opacity:1}
|
||
.rail-f.on{opacity:1;border-color:var(--line,#2a2a2a);background:rgba(255,255,255,.06)}
|
||
/* The group row. Wraps rather than scrolls: 16 groups is the live maximum
|
||
and a horizontal scroller hides half of them behind a gesture. */
|
||
.rail-groups{display:flex;gap:.35rem;flex-wrap:wrap;align-items:baseline;
|
||
padding-left:.5rem;margin-left:.25rem;border-left:1px solid var(--line,#2a2a2a)}
|
||
.rail-g{font-size:.8em;padding:.1rem .4rem;border-radius:3px;text-decoration:none;
|
||
opacity:.6;border:1px solid transparent}
|
||
.rail-g:hover{opacity:1;border-color:var(--line,#2a2a2a)}
|
||
/* The jumped-to tile, so a fragment jump says where it landed. */
|
||
figure.item:target{outline:2px dashed #7aa2f7;outline-offset:3px}
|
||
figure.item.is-cursor{outline:2px solid #7aa2f7;outline-offset:2px}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header class="topbar">
|
||
<a class="brand" href="/"><span class="dot"></span><span class="name">The Booth</span></a>
|
||
<span class="tagline">ephemeral media · auto-wipes {{ ttl_hours }}h · kept boards don't</span>
|
||
</header>
|
||
<main>{% block content %}{% endblock %}</main>
|
||
<footer class="foot">
|
||
drop a folder into <code>{{ data_dir }}</code>{% if host %} · {{ host }}{% endif %}
|
||
</footer>
|
||
</body>
|
||
</html>
|