:root{
  --bg: rgb(202,132,230); --ink:#17110d; --ink-a: rgba(23,17,13,.16); --slab:#17110d; --slab-text:#f3e3fb;
  --paper: rgba(255,255,255,.14); --tex: none; --tex-o: .5; --tex-blend: multiply; --tex-size: 100px;
  --display: "Bodoni 72","Bodoni MT","Didot","Bodoni Moda",Georgia,serif;
  --text: "Iowan Old Style","Palatino Linotype",Palatino,"Book Antiqua",Georgia,serif;
  --mono: ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --initial-r: -12deg; --initial-x: 55%; --stamp-r: -7deg;
}
*{box-sizing:border-box}
html{background:var(--bg);color:var(--ink);-webkit-text-size-adjust:100%;overflow-x:clip}
body{margin:0;min-height:100vh;font-family:var(--text);font-size:clamp(1.02rem,.95rem + .35vw,1.2rem);
  line-height:1.58;background:var(--bg);color:var(--ink);overflow-x:clip}   /* clip, not hidden: hidden would make body a scroll container, and the photo strips' scroll timelines would track it instead of the page */
/* the texture and spotlight scroll with the page rather than being fixed to the screen: Safari 26 tints its bars from
   fixed elements on the screen edges, and a full-screen fixed layer with no colour of its own freezes them on the
   first colour (confirmed on iPhone with lab/tint.html tests A and E, 13 Nov 2026) */
body{position:relative}
.tex,.spot{position:absolute;inset:0;pointer-events:none;z-index:0}
.tex{background-image:var(--tex);background-size:var(--tex-size);opacity:var(--tex-o);mix-blend-mode:var(--tex-blend)}
.spot{bottom:auto;height:900px;background:url(/assets/bg_spot.png) no-repeat 50% 0/1600px 900px;opacity:.55;mix-blend-mode:soft-light}
.wrap{overflow-x:visible;overflow-y:clip;--gutter:clamp(16px,7vw,4.5rem);position:relative;z-index:1;max-width:72rem;margin:0 auto;padding:0 var(--gutter) 4rem}
@media (min-width:1100px){.wrap{padding-left:max(var(--gutter),7.5rem)}}

/* masthead: the name runs down the left edge on wide screens */
.mast{display:flex;align-items:baseline;gap:1rem;padding:1.2rem 0 0}
.mast{align-items:center}
.mast a.name{display:block;color:var(--tint,var(--ink));line-height:0}
.mast a.name svg{fill:currentColor;display:block}
.mast a.name .h{height:2.1rem;width:auto}
.mast a.name .v{display:none}
.mast .est{font-variant:small-caps;letter-spacing:.12em;font-size:.85rem;opacity:.75}
@media (min-width:1100px){
  .mast{position:fixed;left:1.2rem;top:0;bottom:0;flex-direction:column;justify-content:space-between;padding:1.6rem 0;z-index:2}
  .mast a.name .h{display:none}
  .mast a.name .v{display:block;width:2.6rem;height:auto}
  .mast .est{writing-mode:vertical-rl;transform:rotate(180deg)}
}

/* the post */
article{position:relative;padding-top:clamp(2rem,6vw,5rem)}
.ghost{position:absolute;inset:0 calc(-1*var(--gutter)) auto calc(-1*var(--gutter));height:100%;overflow-x:clip;overflow-y:visible;z-index:-1;pointer-events:none}
.initial{position:absolute;top:-.08em;left:var(--initial-x);font-family:var(--display);font-size:clamp(18rem,52vw,44rem);
  line-height:.8;color:transparent;-webkit-text-stroke:2px var(--ink-a);transform:rotate(var(--initial-r));user-select:none}
/* a post's byline and tags: tags are outlined, unlike the solid header links */
.kicker{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem .45rem;font-variant:small-caps;letter-spacing:.12em;font-size:.82rem}
.kicker .by{margin-right:.4rem}
.kicker a.tag{color:inherit;text-decoration:none;border:1px solid var(--ink);padding:.18em .7em .26em;border-radius:10px 0 10px 0;line-height:1.2}
.kicker a.tag:hover,.kicker a.tag:focus-visible{background:var(--ink);color:var(--bg)}
h1{font-family:var(--display);font-weight:400;font-size:clamp(2.7rem,1.4rem + 6.5vw,7rem);line-height:1.04;letter-spacing:-.025em;
  margin:.25em 0 .35em;max-width:14ch;text-wrap:balance}
h1 em{font-style:italic}
.stamp{display:inline-grid;place-items:center;width:7.2rem;height:7.2rem;border:3px double var(--ink);border-radius:50%;
  transform:rotate(var(--stamp-r));text-align:center;line-height:1;font-variant:small-caps;letter-spacing:.08em;margin:0 0 2rem;float:right;
  shape-outside:circle(50%);margin-left:1rem}
.stamp b{display:block;font-family:var(--display);font-size:2.6rem;font-weight:400;letter-spacing:0;font-variant:normal}
.prose{max-width:34em}
/* drop cap exactly three lines deep: the browser sizes it so its top lines up with the first line's capitals and
   its foot sits on the third line's baseline (initial-letter; Safari and Chrome). Others get a float sized to match. */
.prose.dropcap>p:first-child::first-letter{font-family:var(--display);font-style:normal;font-weight:400;float:left;font-size:5.1em;line-height:.78;margin:.04em .08em 0 0}
@supports (initial-letter:3) or (-webkit-initial-letter:3){
  .prose.dropcap>p:first-child::first-letter{float:none;font-size:inherit;line-height:inherit;margin:0 .26em 0 0;font-style:normal;font-weight:400;
    -webkit-initial-letter:3;initial-letter:3}
}
.prose a,.talk a{color:inherit;text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:.18em}
.prose a:hover,.talk a:hover{background:var(--ink);color:var(--bg);text-decoration:none}
.prose h2,.prose h3{font-family:var(--display);font-weight:400;font-size:1.9em;line-height:1;margin:1.6em 0 .4em}
.prose ul,.prose ol{padding-left:1.2em}
.prose li{margin:.35em 0}
.prose li::marker{font-family:var(--display)}
.prose strong{font-weight:700}
pre{background:var(--slab);color:var(--slab-text);font-family:var(--mono);font-size:.8em;line-height:1.5;padding:1.1em 1.2em;
  overflow-x:auto;white-space:pre;box-shadow:.55rem .55rem 0 var(--ink-a);border-radius:0;margin:1.4em 0;max-width:100%}
code{font-family:var(--mono);font-size:.88em;background:var(--ink-a);padding:.05em .3em}
pre code{background:none;padding:0;font-size:inherit}
blockquote{margin:1.2em 0;padding-left:1em;border-left:4px solid var(--ink)}
.download{display:flex;flex-wrap:wrap;gap:.6rem;margin:1.4rem 0}

/* the talk */
.talk{position:relative;margin-top:4rem;max-width:48rem}
.talk h2{font-family:var(--display);font-weight:400;font-size:clamp(2rem,1.2rem + 3vw,3.6rem);line-height:1;margin:0 0 1.5rem}
.talk h2 .count{font-size:2.2em;font-style:italic;margin-right:.12em;vertical-align:-.12em}
.talk ol{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:minmax(0,1fr);gap:1.1rem}
.c{min-width:0;overflow-wrap:anywhere;position:relative;background:var(--paper);border-radius:26px 0 26px 0;padding:1rem 1.2rem 1.1rem;box-shadow:inset 0 0 0 1px var(--ink-a)}
.c::before{content:"";position:absolute;inset:0;border-radius:inherit;background-image:var(--tex);background-size:var(--tex-size);
  opacity:calc(var(--tex-o) * .6);mix-blend-mode:var(--tex-blend);pointer-events:none}
.c header{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;margin-bottom:.35rem}
.c .who{font-variant:small-caps;letter-spacing:.1em;font-weight:700}
.c .who::before{content:"\00ab\00a0"} .c .who::after{content:"\00a0\00bb"}
.c time{font-size:.8rem;opacity:.75;white-space:nowrap}
.c p{margin:.4em 0}
.c pre{font-size:.75em}
@media (min-width:760px){ .c.side-l{margin-right:4rem} .c.side-r{margin-left:4rem} }
.c.iain{background:var(--ink);color:var(--bg);border-radius:0 26px 0 26px}
.c.iain a{color:var(--bg)} .c.iain code{background:rgba(255,255,255,.12)}
.c.iain .who::before{content:"\2192\00a0"} .c.iain .who::after{content:""}
.c.iain pre{background:var(--bg);color:var(--ink)}

footer{position:relative;z-index:1;margin-top:5rem;padding-top:1.2rem;border-top:3px double var(--ink);font-size:.9rem;
  display:flex;flex-wrap:wrap;gap:.8rem 1.6rem;align-items:center;justify-content:space-between}
footer button{font:inherit;font-variant:small-caps;letter-spacing:.12em;background:var(--ink);color:var(--bg);border:0;
  padding:.55em 1.1em;border-radius:14px 0 14px 0;cursor:pointer}
footer .moment b{font-family:var(--mono);font-weight:400}
::selection{background:var(--ink);color:var(--bg)}

/* lists: the home page, tags, docs */
.lede{font-size:1.25em;max-width:30em;margin:0 0 3rem}
.year{position:relative;margin:0 0 3.5rem}
.year h2{font-family:var(--display);font-weight:400;font-size:clamp(4rem,2rem + 9vw,9rem);line-height:.8;letter-spacing:-.04em;
  margin:0 0 .6rem;color:transparent;-webkit-text-stroke:2px var(--ink)}
.year ol,.docs ul{list-style:none;margin:0;padding:0;max-width:44rem}
.year li,.docs li{display:flex;gap:1rem;align-items:baseline;padding:.5rem 0;border-bottom:1px solid var(--ink-a)}
.year li a,.docs li a{font-family:var(--display);font-size:1.45em;line-height:1.1;color:inherit;text-decoration:none;flex:1}
.year li a:hover,.docs li a:hover{font-style:italic}
.year time{font-variant:small-caps;letter-spacing:.08em;font-size:.85rem;min-width:4.2em;opacity:.8}
.year .n{font-size:.8rem;white-space:nowrap;opacity:.8}
.docs h2{font-family:var(--display);font-weight:400;font-size:2.2em;margin:2.5rem 0 .6rem}
.docs li a{font-size:1.2em}
.tagcloud{display:flex;flex-wrap:wrap;gap:.4rem .5rem;margin:0 0 3rem;max-width:44rem}
.tagcloud a{color:inherit;text-decoration:none;border:1px solid var(--ink);padding:.15em .7em;border-radius:12px 0 12px 0;font-variant:small-caps;letter-spacing:.08em}
.tagcloud a:hover{background:var(--ink);color:var(--bg)}
/* the header links: solid blocks in the ink colour */
nav.site{display:flex;gap:.45rem;font-variant:small-caps;letter-spacing:.12em;font-size:.85rem;margin-left:auto}
nav.site a{background:var(--ink);color:var(--bg);text-decoration:none;padding:.32em .8em .4em;border-radius:10px 0 10px 0;line-height:1.2}
nav.site a:hover,nav.site a:focus-visible{background:var(--tint,var(--ink))}
@media (min-width:1100px){nav.site{position:fixed;top:1.6rem;right:2rem;z-index:2}}
.missing{opacity:.7;font-style:italic}

/* phones: just the name */
@media (max-width:599px){.mast .est{display:none}}

/* the sticky die: in the corner on every screen; steps aside when the footer, with the full details, is in view */
.dice{display:grid;place-items:center;position:fixed;right:14px;bottom:calc(14px + env(safe-area-inset-bottom));z-index:3;
  width:44px;height:44px;padding:0;border:0;border-radius:14px 0 14px 0;background:var(--ink);color:var(--bg);
  opacity:.55;cursor:pointer;transition:opacity .3s,transform .3s;-webkit-tap-highlight-color:transparent}
.dice:hover,.dice:active{opacity:.9}
.dice.away{opacity:0;transform:translateY(12px);pointer-events:none}
.dice.spin svg{animation:spin .5s ease}
@media (min-width:760px){.dice{right:24px;bottom:24px;width:52px;height:52px}.dice svg{width:26px;height:26px}}
@keyframes spin{to{transform:rotate(360deg)}}

/* where the page came from: set apart from the post */
.source{clear:both;max-width:34em;margin:3rem 0 0;padding-top:.9rem;border-top:1px solid var(--ink-a);
  font-size:.8rem;font-variant:small-caps;letter-spacing:.1em;opacity:.75}
.source a{color:inherit}
@media (max-width:599px){.source .sep{display:none}.source a{display:block;margin-top:.3rem}}

/* images: framed like the code slabs, never wider than the column, tap to zoom */
.prose img,.talk img{max-width:100%;height:auto;vertical-align:middle}
figure.pic{margin:2.2em 0;max-width:100%}
figure.pic img{display:block;background:var(--paper);box-shadow:0 0 0 1px var(--ink-a),.55rem .55rem 0 var(--ink-a)}
figure.pic img,.prose p img{cursor:zoom-in}
figure.pic a img{cursor:pointer}
figcaption{margin-top:.9rem;font-size:.82rem;font-variant:small-caps;letter-spacing:.1em;opacity:.8}
figcaption::before{content:"\2191\00a0"}

/* the zoom view */
.lb{position:fixed;inset:0;z-index:10;background:var(--bg);display:none;overflow:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch}
.lb::before{content:"";position:fixed;inset:0;background-image:var(--tex);background-size:var(--tex-size);opacity:var(--tex-o);
  mix-blend-mode:var(--tex-blend);pointer-events:none}
.lb.open{display:grid;place-items:center}
.lb img{position:relative;display:block;max-width:calc(100vw - 32px);max-height:calc(100dvh - 96px);width:auto;height:auto;
  box-shadow:.6rem .6rem 0 var(--ink-a);cursor:zoom-in;margin:16px}
.lb.full{place-items:start}
.lb.full img{max-width:none;max-height:none;cursor:zoom-out}
.lb .close{position:fixed;top:calc(12px + env(safe-area-inset-top));right:12px;z-index:1;width:44px;height:44px;border:0;
  border-radius:14px 0 14px 0;background:var(--ink);color:var(--bg);font:1.6rem/1 var(--display);cursor:pointer}
.lb .cap{position:fixed;left:16px;right:72px;bottom:calc(14px + env(safe-area-inset-bottom));font-size:.82rem;
  font-variant:small-caps;letter-spacing:.1em}
.lb.full .cap{display:none}

/* older / newer */
.pager{clear:both;display:grid;grid-template-columns:minmax(0,1fr);gap:1rem;margin:4rem 0 0;max-width:48rem}
@media (min-width:640px){.pager{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}}
.pg{display:flex;flex-direction:column;gap:.35rem;padding:1rem 1.2rem 1.1rem;color:inherit;text-decoration:none;
  border:1px solid var(--ink);border-radius:26px 0 26px 0;transition:background .2s,color .2s}
.pg.next{text-align:right;border-radius:0 26px 0 26px}
.pg:hover,.pg:focus-visible{background:var(--ink);color:var(--bg)}
.pg.empty{border:0}
@media (max-width:639px){.pg.empty{display:none}}
.pg .dir{font-variant:small-caps;letter-spacing:.14em;font-size:.85rem}
.pg .t{font-family:var(--display);font-size:1.5em;line-height:1.05}
.pg .meta{font-size:.78rem;font-variant:small-caps;letter-spacing:.08em;opacity:.8}

/* footer link to the design page */
footer .how{color:inherit;font-variant:small-caps;letter-spacing:.1em}

/* the design page */
.live{display:grid;grid-template-columns:minmax(0,1fr);gap:1.4rem;max-width:44rem;margin:0 0 3rem}
@media (min-width:640px){.live{grid-template-columns:10rem minmax(0,1fr)}}
.live .swatch{height:10rem;border-radius:26px 0 26px 0;box-shadow:0 0 0 1px var(--ink),.55rem .55rem 0 var(--ink-a)}
.live dl{margin:0;display:grid;grid-template-columns:auto minmax(0,1fr);gap:.35rem 1rem;font-size:.95rem}
.live dt{font-variant:small-caps;letter-spacing:.1em;opacity:.8}
.live dd{margin:0}
.live b{font-family:var(--mono);font-weight:400;font-size:.92em}
.textures{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem;max-width:34em;margin:1.6rem 0}
@media (min-width:640px){.textures{grid-template-columns:repeat(4,minmax(0,1fr))}}
.textures figure{margin:0}
.tx{position:relative;aspect-ratio:1;background:var(--bg);border-radius:18px 0 18px 0;overflow:hidden;box-shadow:0 0 0 1px var(--ink-a)}
.tx::before{content:"";position:absolute;inset:0;background-image:var(--tx-img);background-size:var(--tx-size);
  opacity:var(--tx-o);mix-blend-mode:var(--tx-blend)}
.tx.on{box-shadow:0 0 0 3px var(--ink)}
.textures figcaption{margin-top:.5rem;font-family:var(--mono);font-size:.8rem;font-variant:normal;letter-spacing:0}
.textures figcaption::before{content:none}

/* Iain's replies dock onto the comment above: no gap, square corners where they meet; the pair keeps the
   opposite-corners shape (top-left on the comment, bottom-right on the reply) */
.c.reply{margin-top:-1.1rem}
.c.replied{border-bottom-right-radius:0;border-bottom-left-radius:0}
.c.reply{border-top-left-radius:0;border-top-right-radius:0}
.c.iain.reply{border-radius:0 0 26px 0}
.c.iain.reply.replied{border-radius:0}

/* an opening quotation mark hangs in the left margin, out of the drop cap */
.prose>p.q{position:relative}
.prose>p.q::before{content:attr(data-q);position:absolute;right:100%;top:-.05em;margin-right:.08em;
  font-family:var(--display);font-size:3.4em;line-height:1}

/* the veil between pages: the next page's colour, with the mark playing on it */
.veil{position:fixed;inset:0;z-index:60;display:none;place-items:center;pointer-events:none}
.veil.on{display:grid;pointer-events:auto}
.veil svg{width:min(62vw,26rem);height:auto;overflow:visible}
.veil .pc{transform-box:fill-box;transform-origin:50% 50%}

/* home: the archive's own introduction, under the new posts */
.archive-intro{margin:5rem 0 2.5rem;padding-top:1.2rem;border-top:3px double var(--ink)}
.archive-intro h2{font-family:var(--display);font-weight:400;font-size:clamp(2.4rem,1.4rem + 4vw,4.5rem);line-height:1;margin:0 0 .8rem}
.archive-intro .lede{margin:0}

/* code, figures and tables need the full column: start them below the date stamp, not squeezed beside it */
.prose pre,.prose figure.pic,.prose table{clear:both}
pre{tab-size:4;-moz-tab-size:4}

/* the docs tree (index) and a doc page's own section */
.kicker .sep{opacity:.6}
.docs-tree,.section{max-width:44rem}
.docs-tree ul,.section ul{list-style:none;margin:0;padding:0}
.docs-tree>ul>li{border-top:1px solid var(--ink-a);padding:.7rem 0}
.docs-tree>ul>li>a{font-family:var(--display);font-size:1.6em;line-height:1.1;color:inherit;text-decoration:none}
.docs-tree ul ul{margin:.35rem 0 .2rem 1.1rem;padding-left:.9rem;border-left:2px solid var(--ink-a)}
.docs-tree ul ul li,.section li{padding:.18rem 0}
.docs-tree ul ul a,.section a{color:inherit;text-decoration:none;font-size:1.02em}
.docs-tree a:hover,.section a:hover{font-style:italic}
.docs-tree ul ul ul a{font-family:var(--mono);font-size:.82em}
.gone{opacity:.6;font-style:italic} .gone small{font-variant:small-caps;letter-spacing:.08em;font-style:normal}
.earlier{margin-top:4rem;padding-top:1.2rem;border-top:3px double var(--ink)}
.earlier h2{font-family:var(--display);font-weight:400;font-size:2.4em;margin:0 0 .4rem}
.earlier h3{font-family:var(--display);font-weight:400;font-size:1.4em;margin:2rem 0 .4rem}
.section{clear:both;margin-top:4rem;padding:1.1rem 1.3rem 1.2rem;border:1px solid var(--ink);border-radius:26px 0 26px 0}
.section h2{font-family:var(--display);font-weight:400;font-size:1.6em;margin:0 0 .5rem}
.section ul ul{margin-left:1rem}
.section a[aria-current]{font-weight:700}
.section a[aria-current]::before{content:"\2192\00a0"}

/* #links glide to their place; headings stop a little below the top of the screen */
@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}
.prose h2,.prose h3,.prose h4{scroll-margin-top:1.5rem}

/* the docs tree stacks: each name is a heading over its pages (the list-row layout above is for the other lists) */
.docs-tree li,.section li{display:block;border-bottom:0;padding-left:0;padding-right:0}
.docs-tree>ul>li>a,.docs-tree>ul>li>.gone{display:block;margin-bottom:.2rem}
.docs-tree ul ul>li:has(>ul)>a{display:block;font-family:var(--display);font-size:1.2em;margin-top:.35rem}
.docs-tree a,.section a{overflow-wrap:anywhere}

/* videos: a player that fits the column, or, where the video is gone, a box that says so */
.player{clear:both;width:100%;margin:1.8em 0;background:var(--slab);box-shadow:.55rem .55rem 0 var(--ink-a)}
.player iframe{display:block;width:100%;height:100%;border:0}
figure.lost{clear:both;margin:2em 0;padding:2.4rem 1.4rem 2rem;border:3px solid var(--ink);border-radius:26px 0 26px 0;
  text-align:center;background:var(--paper)}
figure.lost svg{width:min(9rem,40%);height:auto;fill:var(--ink);color:var(--ink);display:block;margin:0 auto 1.2rem}
figure.lost figcaption{font-family:var(--display);font-size:clamp(1.4rem,1rem + 2vw,2.2rem);line-height:1.1;font-variant:normal;
  letter-spacing:0;opacity:1;margin:0}
figure.lost figcaption::before{content:none}

/* long inline code (disable_active_styles="yes") may break rather than jump below the date stamp */
.prose :not(pre)>code{overflow-wrap:anywhere}

/* an experiment in a post: the full width of the page, a cover until it's started, full screen on request */
.exp{clear:both;margin:2.4rem 0;max-width:64rem}
.exp-frame{position:relative;aspect-ratio:4 / 3;max-height:82vh;width:100%;background:var(--slab);color:var(--slab-text);
  border-radius:26px 0 26px 0;overflow:hidden;box-shadow:.6rem .6rem 0 var(--ink-a)}
@media (max-width:640px){.exp-frame{aspect-ratio:3 / 4}}
.exp-frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block;background:#fff}
.exp-start{position:absolute;inset:0;width:100%;display:grid;place-content:center;justify-items:center;gap:.9rem;padding:1.5rem;
  border:0;background:var(--slab);color:var(--slab-text);font:inherit;cursor:pointer;text-align:center}
.exp-start .exp-mark svg{width:7rem;height:auto;fill:currentColor;display:block}
.exp-start b{font-family:var(--display);font-weight:400;font-size:clamp(1.8rem,1.2rem + 2.5vw,3rem);line-height:1}
.exp-start .exp-go{font-variant:small-caps;letter-spacing:.14em;font-size:.9rem;border:1px solid currentColor;
  padding:.4em 1em .5em;border-radius:12px 0 12px 0}
.exp-start:not(:disabled):hover .exp-go{background:var(--slab-text);color:var(--slab)}
.exp figcaption{display:flex;flex-wrap:wrap;gap:.6rem 1rem;align-items:center;margin-top:1rem;font-variant:small-caps;letter-spacing:.1em;font-size:.85rem}
.exp figcaption::before{content:none}
.exp-full{font:inherit;letter-spacing:inherit;background:var(--ink);color:var(--bg);border:0;padding:.4em .9em .5em;
  border-radius:10px 0 10px 0;cursor:pointer}
.exp-own{color:inherit}
/* full screen where the browser can (desktops, iPads); on an iPhone the frame fills the window instead */
.exp-frame:fullscreen,.exp-frame:-webkit-full-screen{max-height:none;border-radius:0;aspect-ratio:auto}
.exp.filled .exp-frame{position:fixed;inset:0;z-index:70;height:auto;max-height:none;aspect-ratio:auto;border-radius:0;box-shadow:none}

/* wide screens: the experiment breaks out of the text column into the space either side, stopping short of the name
   up the left edge (7.5rem) and the die in the bottom-right corner (6rem), centred in what's left, at most 100rem */
@media (min-width:1100px){
  .exp{--avail:calc(100vw - 13.5rem);--w:min(var(--avail),100rem);width:var(--w);max-width:none;
    margin-left:calc((var(--avail) - var(--w)) / 2 - max(0px, (100vw - 72rem) / 2))}
}

/* the flask on an experiment's cover: a slow sway, bubbles, the odd one escaping */
.exp-start .flask{width:5.5rem;height:auto;display:block;overflow:visible}
.flask .glass{fill:none;stroke:currentColor;stroke-width:4;stroke-linejoin:round;stroke-linecap:round}
.flask .liquid{fill:currentColor;opacity:.32;animation:sway 3.2s linear infinite}
.flask .liquid.back{opacity:.16;animation-duration:4.6s;animation-direction:reverse}
.flask .bub,.flask .esc{fill:currentColor;opacity:0}
.flask .bub{animation:rise var(--t) ease-in var(--d) infinite}
.flask .esc{animation:escape 4.8s ease-out var(--d) infinite}
@keyframes sway{from{transform:translate(0,var(--rise,0px))}to{transform:translate(60px,var(--rise,0px))}}
@keyframes rise{0%{transform:none;opacity:0}15%{opacity:.75}80%{opacity:.75}100%{transform:translateY(-34px);opacity:0}}
@keyframes escape{0%{transform:none;opacity:0}10%{opacity:.6}100%{transform:translate(4px,-46px) scale(1.4);opacity:0}}
@media (prefers-reduced-motion:reduce){.flask .liquid,.flask .bub,.flask .esc{animation:none}}

/* full screen (or filling the window on an iPhone): a slim bar on top with the mark, the title and a close button */
.exp-bar{display:none}
.exp.filled .exp-bar,.exp-frame:fullscreen .exp-bar,.exp-frame:-webkit-full-screen .exp-bar{display:flex;position:absolute;
  top:0;left:0;right:0;height:36px;z-index:2;align-items:center;gap:.7rem;padding:0 4px 0 12px;background:var(--ink);color:var(--bg)}
.exp.filled iframe,.exp-frame:fullscreen iframe,.exp-frame:-webkit-full-screen iframe{top:36px;height:calc(100% - 36px)}
.exp-home{display:block;line-height:0;color:inherit}
.exp-home svg{height:16px;width:auto;fill:currentColor;display:block}
.exp-title{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant:small-caps;letter-spacing:.12em;font-size:.8rem}
.exp-close{width:30px;height:28px;border:0;padding:0;border-radius:10px 0 10px 0;background:transparent;color:inherit;
  font:1.5rem/1 var(--display);cursor:pointer}
.exp-close:hover{background:var(--bg);color:var(--ink)}
.exp-open .dice{display:none}

/* phones: the experiment runs edge to edge, past the page's side margins */
@media (max-width:640px){
  .exp{margin-left:calc(-1 * var(--gutter));margin-right:calc(-1 * var(--gutter));width:calc(100% + 2 * var(--gutter));max-width:none}
  .exp-frame{border-radius:0;box-shadow:none;aspect-ratio:auto;height:86svh;max-height:none}   /* taller: most of the screen */
  .exp figcaption{padding:0 var(--gutter)}
}

/* starting an experiment: the flask boils, the cover reports progress, then the cover lifts away */
.exp-start .exp-go{min-width:12.5rem;transition:background .2s,color .2s}
.exp-start:disabled{cursor:progress}
.exp-bar-load{position:absolute;left:0;right:0;bottom:0;height:4px;overflow:hidden;opacity:0;transition:opacity .3s}
.exp-bar-load::before{content:"";position:absolute;top:0;bottom:0;width:30%;left:-30%;background:currentColor;border-radius:0 4px 0 4px}
.exp.loading .exp-bar-load{opacity:.8}
.exp.loading .exp-bar-load::before{animation:sweep 1.1s cubic-bezier(.5,0,.5,1) infinite}
.exp.loading .flask{animation:wobble .9s ease-in-out infinite;transform-origin:50% 92%}
.exp.loading .flask .liquid{animation-duration:1.1s}
.exp.loading .flask .liquid.back{animation-duration:1.6s}
.exp.loading .flask{--rise:-14px}   /* the liquid rises as it boils */
.exp.loading .flask .bub{animation-duration:calc(var(--t) / 3);animation-delay:calc(var(--d) / 3)}
.exp.loading .flask .esc{animation-duration:1.4s;animation-delay:calc(var(--d) / 4)}
.exp.loading .exp-go{animation:breathe 1.1s ease-in-out infinite}
.exp.ready .exp-start{animation:lift .7s cubic-bezier(.6,0,.3,1) forwards;pointer-events:none}
.exp.ready .flask{animation:pop .5s cubic-bezier(.3,1.6,.5,1) forwards}
@keyframes sweep{to{left:100%}}
@keyframes wobble{0%,100%{transform:rotate(-5deg)}50%{transform:rotate(5deg)}}
@keyframes breathe{50%{opacity:.55}}
@keyframes pop{40%{transform:scale(1.25)}100%{transform:scale(.2);opacity:0}}
@keyframes lift{0%{clip-path:circle(150% at 50% 45%)}100%{clip-path:circle(0% at 50% 45%)}}
@media (prefers-reduced-motion:reduce){
  .exp.loading .flask,.exp.loading .exp-go,.exp.ready .flask,.exp.ready .exp-start{animation:none}
  .exp.loading .exp-bar-load::before{animation:none;left:0;width:100%;opacity:.4}
}

/* photo posts: a strip across the timeline, the picture sliding slowly inside it as you scroll (parallax) */
/* the same flex row as the other posts, so the strip starts where their titles do */
.year li.photo{align-items:center;padding-bottom:1rem}   /* room under the strip for its shadow */
.year li.photo a.strip{flex:1;min-width:0}
.year li.photo time{align-self:center}
a.strip{display:block;position:relative;overflow:hidden;cursor:zoom-in;border-radius:18px 0 18px 0;background:var(--ink-a);
  height:clamp(6.5rem,4rem + 10vw,11rem);box-shadow:0 0 0 1px var(--ink-a),.5rem .5rem 0 var(--ink-a)}
a.strip img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;transition:filter .3s}
/* with the script: the whole photo at the strip's width, slid up and down inside it (focal point, parallax, hover scan) */
a.strip.tall img{height:auto;bottom:auto;object-fit:fill;transform:translateY(var(--y,0px));will-change:transform}
a.strip:hover img{filter:brightness(1.06)}
/* a photo's own page: the picture leads, full width, its caption and camera details under it */
.photo-hero{clear:both;margin:1.6rem 0 2rem;max-width:64rem}
.photo-hero a.strip.hero{height:auto;border-radius:26px 0 26px 0;box-shadow:.6rem .6rem 0 var(--ink-a)}
.photo-hero a.strip.hero img{position:static;height:auto;max-height:85vh;object-fit:contain;transform:none;background:var(--ink)}
.photo-hero figcaption{margin-top:1rem;font-variant:normal;letter-spacing:0;font-size:1rem;opacity:1;max-width:34em}
.photo-hero figcaption::before{content:none}
.exif{display:block;margin-top:.5rem;font-variant:small-caps;letter-spacing:.08em;font-size:.8rem;opacity:.8}
@media (min-width:1100px){.photo-hero{--avail:calc(100vw - 13.5rem);--w:min(var(--avail),100rem);width:var(--w);max-width:none;
  margin-left:calc((var(--avail) - var(--w)) / 2 - max(0px, (100vw - 72rem) / 2))}}
@media (max-width:640px){.photo-hero{margin-left:calc(-1 * var(--gutter));margin-right:calc(-1 * var(--gutter))}
  .photo-hero a.strip.hero{border-radius:0;box-shadow:none} .photo-hero figcaption{padding:0 var(--gutter)}}
/* the full-screen photo: the whole window, caption and camera details over the foot of it */
.lb.photo{background:#0d0b09;place-items:center}
.lb.photo::before{display:none}
.lb.photo img{max-width:100vw;max-height:100dvh;margin:0;box-shadow:none;cursor:zoom-out}
.lb.photo .cap{left:12px;right:auto;bottom:calc(12px + env(safe-area-inset-bottom));max-width:min(34rem,calc(100vw - 24px));
  padding:.7rem .95rem .8rem;color:#f4efe8;background:rgba(13,11,9,.74);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  border-radius:14px 0 14px 0;font-variant:normal;letter-spacing:0;font-size:.85rem;line-height:1.4}
.lb.photo .cap b{display:block;font-family:var(--display);font-weight:400;font-size:1.35rem;line-height:1.1;margin-bottom:.15rem}
.lb.photo .cap span{display:block}
.copy{display:block;margin-top:.3rem;font-size:.75rem;opacity:.7}
@media (max-width:640px){.lb.photo .cap{font-size:.7rem;padding:.5rem .7rem .6rem}.lb.photo .cap b{font-size:1.05rem}}
.lb.photo .cap .exif{opacity:.8}
.lb.photo .close{background:#f4efe8;color:#0d0b09}

/* phones: the parallax in CSS, as a scroll-driven animation on the strip's view timeline. The browser runs it with
   its own scrolling, so it stays smooth through momentum scrolling, where a script would judder. Without support
   (Safari before 26) the photo just rests on its focal point. */
@media (hover:none) and (prefers-reduced-motion:no-preference){
  @supports (animation-timeline: view()){
    a.strip.tall:not(.hero){view-timeline:--strip block}
    a.strip.tall:not(.hero) img{animation:strip-drift linear both;animation-timeline:--strip;animation-range:cover}
  }
}
@keyframes strip-drift{from{transform:translateY(var(--y-from,0px))}to{transform:translateY(var(--y-to,0px))}}
