/* Hazel Lab — OOPSLA 2026 deck
   Fixed 1920x1080 stage, scaled to fit. Palette tracks the Hazel editor. */

:root{
  --paper:#fdfbf4;        /* Hazel editor background */
  --ink:#2b2b2b;
  --ink-soft:#6b6a63;
  --rule:#e0dcce;
  --kw:#1f3864;           /* let fun case in end type */
  --bind:#2d6cb5;         /* bound identifiers */
  --str:#c07a1e;          /* string literals */
  --typ:#7b3fa0;          /* type names */
  --unk:#d9a520;          /* the unknown type */
  --mark:#c0392b;         /* error marks */
  --good:#2f7d4f;         /* live/green */
  --hl:#efe7d0;           /* soft highlight */
  --accent:#b5541f;
}

*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;background:#15140f;overflow:hidden;
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  -webkit-font-smoothing:antialiased}

#stage{position:absolute;top:50%;left:50%;width:1920px;height:1080px;
  transform-origin:center center;background:var(--paper);color:var(--ink);overflow:hidden}

.slide{position:absolute;inset:0;padding:88px 110px;display:none;flex-direction:column}
.slide.on{display:flex}
/* A slide carrying a live Hazel embed can never be display:none — Hazel's
   font-metric ResizeObserver captures nothing without a real box, and the code
   layout comes out wrong. Keep it laid out but invisible and inert instead.
   (docs/embedding.md, "Deck-side checklist".) */
.slide.has-embed{display:flex;visibility:hidden;pointer-events:none}
.slide.has-embed.on{visibility:visible;pointer-events:auto;z-index:1}
.slide.pad-none{padding:0}

/* ---- typography ---- */
h1{font-size:92px;line-height:1.05;letter-spacing:-.02em;font-weight:700}
h2{font-size:64px;line-height:1.1;letter-spacing:-.015em;font-weight:700;margin-bottom:38px}
h3{font-size:44px;font-weight:650;margin-bottom:18px}
p,li{font-size:38px;line-height:1.42}
.lead{font-size:46px;line-height:1.35}
.big{font-size:62px;line-height:1.2;font-weight:650}
.dim{color:var(--ink-soft)}
.sub{font-size:32px;color:var(--ink-soft)}
strong{font-weight:700}
em{font-style:italic}
.accent{color:var(--accent)}
.markc{color:var(--mark)}
.goodc{color:var(--good)}
ul{list-style:none}
li{margin:0 0 22px;padding-left:44px;position:relative}
li::before{content:"";position:absolute;left:8px;top:.62em;width:14px;height:14px;
  border-radius:50%;background:var(--rule)}
li.tick::before{background:var(--good)}
li.cross::before{background:var(--mark)}

.title-rule{width:120px;height:6px;border-radius:3px;background:var(--accent)}

.kicker{font-size:26px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-soft);font-weight:650;margin-bottom:26px}

/* ---- layout helpers ---- */
.row{display:flex;gap:56px;align-items:flex-start}
.row.center{align-items:center}
.col{flex:1;min-width:0}
.spacer{flex:1}
.center-all{justify-content:center;align-items:center;text-align:center}

.card{background:#fff;border:2px solid var(--rule);border-radius:18px;padding:34px 38px}
.card h3{font-size:36px;margin-bottom:16px}
.card p,.card li{font-size:31px;line-height:1.35}
.card li{margin-bottom:14px}
/* sub-bullets inside a card: smaller type and dot, tighter, and the nested list
   sits under its parent's text rather than under its dot */
.card li ul{margin-top:6px}
.card li li{font-size:27px;margin-bottom:2px;padding-left:36px}
.card li li::before{width:10px;height:10px;top:.6em}

/* ---- code ---- */
code,pre{font-family:"SF Mono",Menlo,Consolas,"Liberation Mono",monospace}
pre.hz{font-size:34px;line-height:1.5;white-space:pre;color:var(--ink)}
pre.hz.sm{font-size:28px;line-height:1.45}
pre.hz.lg{font-size:42px;line-height:1.45}
code.inl{font-size:.92em;background:var(--hl);padding:.06em .3em;border-radius:6px}
.t-kw{color:var(--kw);font-weight:700}
.t-bind{color:var(--bind)}
.t-str{color:var(--str)}
.t-typ{color:var(--typ)}
.t-unk{color:var(--unk);font-weight:700}
.t-num{color:var(--str)}
.t-op{color:var(--ink-soft)}
.t-com{color:var(--ink-soft);font-style:italic}

/* a node annotation pinned to the right of a code line */
.ann{color:var(--typ);background:#f3ecfa;border-radius:6px;padding:0 .3em}
.ann.g{color:var(--good);background:#e8f4ec}
.ann.u{color:var(--unk);background:#fbf3d9}
.nodeno{display:inline-block;width:1.5em;color:var(--ink-soft);font-size:.8em}

/* error mark: dotted underline + ring, never color alone */
.mk{position:relative;border-bottom:4px dotted var(--mark);padding-bottom:2px}
.mk::after{content:"";position:absolute;inset:-10px -12px;border:4px solid var(--mark);
  border-radius:12px;opacity:.85}

/* ---- scoreboard ---- */
table.score{border-collapse:collapse;font-size:36px;width:100%}
table.score th,table.score td{padding:20px 26px;text-align:center;
  border-bottom:2px solid var(--rule)}
table.score th{font-size:29px;color:var(--ink-soft);font-weight:650;letter-spacing:.02em}
table.score td.lbl{text-align:left;font-weight:650;white-space:nowrap}
table.score td.num{font-variant-numeric:tabular-nums;font-size:42px;font-weight:650}
table.score tr.ghost td{color:#ccc7b6}
table.score td.hide{color:transparent}
table.score td.pop{background:var(--hl);border-radius:10px}
table.score .win{color:var(--good)}

/* ---- trace table (live typing) ---- */
table.trace{border-collapse:collapse;font-size:27px;width:100%}
table.trace th,table.trace td{padding:13px 16px;text-align:left;
  border-bottom:2px solid var(--rule);font-family:"SF Mono",Menlo,monospace}
table.trace th{font-size:24px;color:var(--ink-soft);font-family:inherit;font-weight:650}
table.trace td.hide{color:transparent}
table.trace tr.dead td{color:var(--ink-soft)}

/* ---- media ---- */
.shot{max-width:100%;max-height:100%;object-fit:contain;display:block;margin:0 auto;
  border-radius:10px}
.shotwrap{flex:1;display:flex;align-items:center;justify-content:center;min-height:0}
/* hook: progressive reveal by clipping the source screenshot */
/* the image must be able to SHRINK, or it overflows onto the text below it */
.hook{display:flex;align-items:center;justify-content:center;flex:1 1 auto;min-height:0;
  overflow:hidden}
.hookimg{position:relative;display:flex;line-height:0;max-width:100%;max-height:100%;
  min-height:0;overflow:hidden;border-radius:10px}
.hookimg img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;
  display:block}
/* veils start opaque and peel away as fragments arrive (inverse of .fr) */
.veil{position:absolute;background:var(--paper);transition:opacity .2s}
.veil.fr{opacity:1}
.veil.fr.in{opacity:0}

/* ---- fragments ---- */
.fr{opacity:0;transition:opacity .16s}
.fr.in{opacity:1}
.revealable{opacity:0;transition:opacity .16s}
.revealable.in{opacity:1}
.fr.dimprev{opacity:.25}

/* ---- chrome ---- */
#bar{position:fixed;left:0;right:0;bottom:0;height:5px;background:rgba(255,255,255,.08);z-index:20}
#bar i{display:block;height:100%;background:var(--accent);width:0;transition:width .2s}
#hud{position:fixed;right:14px;bottom:16px;color:#8d8778;font-size:13px;z-index:20;
  font-variant-numeric:tabular-nums;font-family:"SF Mono",Menlo,monospace}
#hud .off{color:#c0392b}
#hud .ok{color:#6aa84f}

/* presenter notes */
#notes{position:fixed;inset:auto 0 0 0;max-height:46vh;overflow:auto;background:#14130f;
  color:#e4dfd0;padding:22px 30px 34px;font-size:15px;line-height:1.5;display:none;z-index:30;
  border-top:2px solid #35322a}
#notes.on{display:block}
#notes h4{font-size:13px;letter-spacing:.14em;text-transform:uppercase;color:#8d8778;margin-bottom:10px}
#notes p{font-size:15px;line-height:1.55;margin-bottom:10px}
#notes .cue{color:#e8c46a}
#notes .warn{color:#e08a7a}
/* markdown notes (deck.js md()): what to say, what to do, what to know */
#notes p.say{color:#f3efe4}
#notes p.dir{color:#9fb7d6;font-style:italic}
#notes p.bg{color:#b9b3a5}
#notes p.note.fact{color:#a9d3a0}
#notes p.note.key{color:#e8c46a}
#notes p.note.defend{color:#e08a7a}
#notes ul{margin:0 0 10px 20px}
#notes li{font-size:15px;line-height:1.55}
#notes code{font-family:var(--mono, monospace);font-size:.92em;color:#f0d9a8}
#notes .ref{color:#9fb7d6;font-weight:600}
.bad-ref{color:#e0573f;font-weight:700}

/* ?script — every slide's notes as one document, for rehearsal and print */
body.script-view{overflow:auto;background:#fbfaf6;color:#1d1c1a}
#script{max-width:860px;margin:0 auto;padding:48px 24px 96px;font:17px/1.6 system-ui,sans-serif}
#script h2{font-size:24px;letter-spacing:0;line-height:1.3;margin:44px 0 12px;padding-top:14px;border-top:1px solid #ddd8cc}
#script h2 .t{font-weight:400;color:#8d8778;font-size:17px;margin-left:6px}
#script h3{font-size:28px;line-height:1.25;margin:48px 0 14px}
#script > h3:first-child{margin-top:0;font-size:34px}
#script p,#script li{font-size:17px;line-height:1.6;margin:0 0 12px}
#script p.say{border-left:3px solid #c9c2b1;padding-left:14px}
#script p.dir{color:#4a6a92;font-style:italic}
#script p.bg{color:#555}
#script p.note{padding:6px 12px;border-radius:6px;background:#f2efe6;font-size:15px}
#script ul li{margin-bottom:6px}
#script p.note.warn{background:#fbe9e4}
#script p.note.fact{background:#e8f1e3}
#script ul{margin:0 0 12px 24px}
#script code{font-family:ui-monospace,monospace;font-size:.9em;background:#efece3;padding:0 3px;border-radius:3px}
#script a{color:#4a6a92;font-weight:600;text-decoration:none}
@media print{#script{max-width:none;padding:0;font-size:12pt} #script section{break-inside:avoid-page}}

#help{position:fixed;inset:0;background:rgba(10,9,6,.94);color:#e4dfd0;z-index:40;
  display:none;align-items:center;justify-content:center;font-size:17px;line-height:2}
#help.on{display:flex}
#help kbd{background:#2e2b24;border-radius:5px;padding:2px 9px;font-family:monospace;margin-right:10px}

/* ---- notebook / pandas motivation ---- */
/* Each cell is its own box, as in JupyterLab, not a divided panel: a cell that
   has not run yet is a fragment, and fading its contents out left its outline
   (and the panel's) on screen around empty space. Now the outline fades with it,
   and the space stays reserved so nothing moves when it arrives. */
.nb{display:flex;flex-direction:column;gap:10px;position:relative}   /* anchors svg.arrows */
.nb .cell{display:flex;gap:0;align-items:stretch;background:#fff;
  border:2px solid var(--rule);border-radius:12px;overflow:hidden}
.nb .gut{flex:0 0 132px;background:#f4f1e6;color:var(--ink-soft);font-size:24px;
  font-family:"SF Mono",Menlo,monospace;padding:22px 18px;text-align:right}
.nb .src{flex:1;padding:22px 26px;font-family:"SF Mono",Menlo,monospace;
  font-size:27px;line-height:1.5;white-space:pre-wrap}
.nb .out{flex:1;padding:22px 26px;font-family:"SF Mono",Menlo,monospace;
  font-size:23px;line-height:1.45;white-space:pre-wrap;color:var(--ink-soft)}
.nb .out .err{color:var(--mark);font-weight:700}
/* a notebook with tables in its outputs: tighter cells so three runs fit */
.nb.tight{gap:6px}
.nb.tight .gut{padding:8px 16px;font-size:21px;flex-basis:118px}
.nb.tight .src{padding:8px 22px;font-size:25px;line-height:1.5}
.nb.tight .out{padding:8px 22px}
.nb.tight .out table.gb{border-radius:6px}
.nb .out:has(> table){white-space:normal}   /* else the markup's indentation shows as blank lines */
.py-kw{color:var(--kw);font-weight:700}
.py-str{color:var(--str)}
.py-fn{color:var(--bind)}

/* ---- example / definition / motivation banding ---- */
.zone{margin-bottom:22px}
.zone-h{font-size:22px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-soft);font-weight:650;margin-bottom:10px}
.card .zone:last-child{margin-bottom:0}

/* ---- roadmap ---- */
ol.road{list-style:none;counter-reset:r}
ol.road li{counter-increment:r;font-size:36px;margin-bottom:20px;padding-left:76px;position:relative}
ol.road li::before{content:counter(r);position:absolute;left:0;top:-2px;width:50px;height:50px;
  border-radius:50%;background:var(--hl);color:var(--ink);font-size:28px;font-weight:700;
  display:flex;align-items:center;justify-content:center}
ol.road li.now::before{background:var(--accent);color:#fff}

/* ---- scoreboard: two-tier header ---- */
table.score th.grp{border-bottom:2px solid var(--rule);padding-bottom:10px}
table.score th.sub2{font-size:26px;font-weight:600;padding-top:8px}
table.score td.lbl{font-size:32px}

/* ---- Likert legend (BrBG-7, matches survey_plots/generate_plots.py) ---- */
.likert{display:flex;gap:0;align-items:stretch;flex-wrap:wrap;justify-content:center;
  margin-top:18px}
.likert .k{display:flex;align-items:center;gap:10px;margin:0 18px 8px 0;font-size:23px;
  color:var(--ink-soft)}
.likert .sw{width:30px;height:20px;border:1px solid #fff;border-radius:3px;display:inline-block}

/* ---- related work comparison ---- */
table.cmp{border-collapse:collapse;width:100%;font-size:28px}
table.cmp th,table.cmp td{padding:15px 14px;border-bottom:2px solid var(--rule);text-align:center}
table.cmp th{font-size:24px;color:var(--ink-soft);font-weight:650}
table.cmp td.lbl,table.cmp th.lbl{text-align:left;font-weight:600;white-space:nowrap}
table.cmp .yes{color:var(--good);font-size:30px}
table.cmp .part{color:var(--str);font-size:30px}
table.cmp .no{color:#c9c4b4;font-size:30px}
table.cmp col.ours,table.cmp td.ours{background:var(--hl)}

/* ---- prior-work list ---- */
ul.prior li{margin-bottom:16px;padding-left:0}
ul.prior li::before{display:none}
ul.prior li b{display:block;font-size:30px;margin-bottom:2px}
ul.prior li span{font-size:26px;color:var(--ink-soft);line-height:1.35}

/* ---- tab-completion popup on the notebook slide ---- */
/* completion popup: in normal flow, under the line it completes, so it can
   never land on top of the code (it used to be absolutely positioned) */
/* no box of its own: it is a popup hanging off the cell above */
/* Cell 2's completer, drawn the way JupyterLab draws one: a plain list hung
   directly under the caret, overlaying whatever is below the cell. The cell
   is `open` so the popup can escape its rounded, clipped box; the gutter then
   rounds its own outer corners. The popup's left edge is the gutter, the
   source padding and the six characters of `wide["` in the source font. */
.nb .cell.open{overflow:visible;position:relative}
.nb .cell.open .gut{border-radius:10px 0 0 10px}
.nb .src.swap .layer{padding:22px 26px}   /* the overlay sits where the in-flow text does */
.nb .popup{position:absolute;top:64px;left:calc(132px + 26px + 6ch);z-index:6;
  font-family:"SF Mono",Menlo,monospace;font-size:27px}   /* so 6ch is six source characters */
/* top: the source padding (22px) plus one line (1.5 x 27px), so the list hangs
   straight off the line, over the cell's bottom edge, as JupyterLab draws it */
.completer{background:#fff;border:1.5px solid #bdb9ae;border-radius:4px;flex:none;
  box-shadow:0 10px 26px rgba(0,0,0,.18);font-family:"SF Mono",Menlo,monospace;font-size:27px;
  overflow:hidden;min-width:230px}
.completer .it{padding:7px 40px 7px 20px;white-space:nowrap;color:var(--ink)}
.completer .it.sel{background:#1a73e8;color:#fff}
.caret{display:inline-block;width:3px;height:1.15em;margin-left:1px;vertical-align:-0.2em;
  background:var(--ink);animation:blink 1.1s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
.callout{border-left:6px solid var(--good);background:#eef6f0;border-radius:0 10px 10px 0;
  padding:16px 22px;font-size:27px;line-height:1.38}

/* ---- table footnote ---- */
.fnote{font-size:23px;color:var(--ink-soft);line-height:1.35;margin-top:14px}
.fnote sup{font-size:.85em}
/* on a slide whose content runs to the bottom of the content box: one line, anchored in
   the bottom padding band instead of pushed below it */
.fnote.foot{position:absolute;left:110px;right:110px;bottom:22px;margin:0;white-space:nowrap}
table.score td.num sup{font-size:.55em;color:var(--ink-soft);vertical-align:super}

/* ---- observed-value annotation, styled like a probe reading ---- */
.obs{color:var(--good);background:#e8f4ec;border-radius:6px;padding:1px 10px;
  font-family:"SF Mono",Menlo,monospace;font-size:.82em}
.obs-arrow{color:var(--ink-soft)}

/* ---- backup slides (after the summary; excluded from the timing) ---- */
.slide[data-backup]::after{content:"backup";position:absolute;top:30px;right:40px;
  font-size:20px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-soft);
  border:2px solid var(--rule);border-radius:20px;padding:4px 14px}

/* ---- annotated "why this is hard" example ---- */
.ann-row{display:flex;gap:18px;align-items:flex-start;margin-bottom:22px}
.ann-row .q{flex:0 0 auto;width:32px;height:32px;border-radius:50%;background:var(--mark);
  color:#fff;font-size:20px;font-weight:700;display:flex;align-items:center;
  justify-content:center;margin-top:5px}
.ann-row .t{font-size:30px;line-height:1.35}
.hot{background:#fde9e5;border-bottom:3px solid var(--mark);border-radius:3px;padding:0 3px}
.js-kw{color:var(--kw);font-weight:700}
.js-str{color:var(--str)}
.js-fn{color:var(--bind)}
.js-p{color:#9a978c}                    /* punctuation recedes, names carry the line */
.js-ty{color:var(--typ);font-weight:600}
pre.hz.js{line-height:1.62}

/* ---- precision lattice ---- */
.lattice{font-family:"SF Mono",Menlo,monospace;font-size:26px;text-align:center;line-height:2}
.lattice .n{display:inline-block;padding:3px 12px;border:2px solid var(--rule);
  border-radius:8px;background:#fff;margin:0 8px}
.lattice .n.hi{border-color:var(--good);background:#e8f4ec;color:var(--good);font-weight:700}
.lattice .lvl{margin:18px 0}
.lattice .arrows{color:var(--ink-soft);font-size:22px}

/* ---- design chain (the approach slide) ---- */
ol.chain{list-style:none;counter-reset:c}
ol.chain{display:flex;flex-direction:column;justify-content:space-between;height:100%}
ol.chain li{counter-increment:c;font-size:42px;line-height:1.2;margin-bottom:0;
  padding-left:78px;position:relative}
ol.chain li::before{content:counter(c);position:absolute;left:0;top:0;width:56px;height:56px;
  border-radius:50%;background:var(--hl);color:var(--ink);font-size:31px;font-weight:700;
  display:flex;align-items:center;justify-content:center}
ol.chain li .why{display:block;font-size:30px;color:var(--ink-soft);margin-top:6px}
ol.chain li.cost::before{background:#f7ded9;color:var(--mark)}
ol.chain li.pay::before{background:#e0efe6;color:var(--good)}
ol.chain li.cost .why{color:var(--mark)}
ol.chain li.pay .why{color:var(--good)}

/* ---- spotlights: highlight follows the current fragment ---- */
.spot{border-radius:4px;padding:0 3px;transition:background .15s,box-shadow .15s}
.spot.lit{background:#fde9e5;box-shadow:inset 0 -3px 0 var(--mark)}
th.spot.lit,td.spot.lit{box-shadow:inset 0 -3px 0 var(--mark)}
/* spotlights on a whole table, or a whole row of one.
   The table's is drawn on its cells, not as an outline, so that it can leave out
   a column the code on the slide adds: `gradebook` is the table without it.
   Each edge is a variable so a corner cell gets both of its sides. */
table.gb :is(th,td).spot{border-radius:0}   /* rounded cells notch the table box */
table.spot-t :is(th,td){--t:0 0 transparent;--b:0 0 transparent;--l:0 0 transparent;--r:0 0 transparent}
table.spot-t.lit tr:first-child > *{--t:inset 0 3px 0 var(--mark)}
table.spot-t.lit tr:last-child > *{--b:inset 0 -3px 0 var(--mark)}
table.spot-t.lit tr > :first-child{--l:inset 3px 0 0 var(--mark)}
table.spot-t.lit tr > :is(:last-child:not(.newcol), :has(+ .newcol)){--r:inset -3px 0 0 var(--mark)}
table.spot-t.lit tr > :not(.newcol){box-shadow:var(--t),var(--b),var(--l),var(--r)}
tr.spot-r td{transition:background .15s}
tr.spot-r.lit td:not(.newcol){background:#fde9e5}   /* `row` is a gradebook row: no ghost column */

/* ---- medium code size (between sm and the base) ---- */
pre.hz.md{font-size:32px;line-height:1.5}

/* ---- the gradebook table ---- */
table.gb{border-collapse:collapse;font-family:"SF Mono",Menlo,monospace;font-size:24px;
  background:#fff;border:2px solid var(--rule);border-radius:10px;overflow:hidden}
table.gb th{background:#f4f1e6;color:var(--ink-soft);font-weight:650;padding:9px 14px;
  text-align:left;white-space:nowrap;border-bottom:2px solid var(--rule)}
table.gb td{padding:8px 14px;color:var(--ink);white-space:nowrap;
  border-bottom:1px solid var(--rule)}
table.gb tr:last-child td{border-bottom:0}
table.gb td.s{color:var(--str)}

/* ---- values-become-columns diagram ---- */
.gb.tight{font-size:22px}
.gb .val{color:var(--accent);font-weight:700}
.gb th.val{color:var(--accent)}
/* a column the program on the slide adds: tinted so it reads as new */
/* A column the program would add: drawn as a ghost -- translucent, italic, in a
   blue nothing else on these tables uses -- so it reads as hypothetical output
   rather than as another highlight. */
table.gb .newcol{--ghost:#4a7bb0;background:rgba(74,123,176,.08);color:var(--ghost);
  font-style:italic;font-weight:500}
table.gb th.newcol{background:rgba(74,123,176,.14);color:var(--ghost)}

/* Absent, not invisible, until its fragment: an opacity-hidden cell still takes
   its width, leaving an empty column on the table. So it is display:none, and
   arrives with an animation, since a display change cannot be transitioned. */
table.gb .newcol.fr:not(.in){display:none}
table.gb .newcol.fr.in{animation:newcol .6s ease-out}
@keyframes newcol{from{opacity:0;transform:translateX(-8px)}to{opacity:1;transform:none}}
.flow{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:10px;padding:0 6px}
.flow .op{font-family:"SF Mono",Menlo,monospace;font-size:22px;color:var(--ink-soft);
  text-align:center;line-height:1.35}
.flow .arrow{font-size:46px;color:var(--accent);line-height:1}

/* ---- structural-operations reference table ---- */
table.ops{border-collapse:collapse;width:100%;table-layout:fixed;
  font-family:"SF Mono",Menlo,monospace;font-size:28px}
table.ops col.c-ex{width:46%}
table.ops col.c-ty{width:26%}
table.ops col.c-va{width:28%}
table.ops th{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-size:22px;
  color:var(--ink-soft);font-weight:650;text-align:left;padding:0 30px 10px 0;
  border-bottom:2px solid var(--rule)}
table.ops td{padding:13px 30px 13px 0;border-bottom:1px solid var(--rule);vertical-align:baseline;
  white-space:nowrap}
table.ops tr:last-child td{border-bottom:0}
table.ops td.name{font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-size:23px;
  color:var(--ink-soft);font-style:italic;padding-right:22px}
table.ops td.ty{color:var(--typ)}
table.ops td.ty .arr{color:var(--ink-soft);margin-right:10px}
table.ops td.val span{color:var(--good);background:#e4f1e9;border-radius:6px;padding:2px 12px}


/* ---- grouped citations: author + year, inline with the category ---- */
.cite{font-size:.8em;color:var(--ink-soft)}
ul.cats{list-style:none}
ul.cats li{margin-bottom:26px;padding-left:0}
ul.cats li::before{display:none}
ul.cats li b{display:block;font-size:33px;font-weight:700;line-height:1.2}
ul.cats li b .at{font-size:24px;font-weight:400;color:var(--ink-soft);margin-left:10px}
ul.cats li .cite{display:block;font-size:26px;color:var(--ink-soft);margin-top:4px}
ul.cats li .why{display:block;font-size:25px;color:var(--ink-soft);margin-top:4px;
  font-style:italic}
/* beside an embed, as the slide's only prose: read from the back, so bigger and upright */
ul.cats.big li{margin-bottom:40px}
ul.cats.big li b{font-size:42px}
ul.cats.big li .why{font-size:32px;font-style:normal;line-height:1.3}
/* two bodies of related work on one slide, split by a rule */
.cat-group+.cat-group{margin-top:30px;padding-top:26px;border-top:2px solid var(--rule)}
.cat-group .zone-h{margin-bottom:16px}
.cat-group ul.cats li:last-child{margin-bottom:0}


/* ---- the system, shown and named ---- */
/* no overflow:hidden here — it would clip the mascot, which deliberately
   overhangs the corner. The image rounds its own corners instead. */
.sysshot{position:relative;background:#fff;line-height:0}
.sysshot img.cap{width:100%;display:block;border:3px solid var(--rule);border-radius:14px}

/* ---- mascot, with eyes that wander ---- */
.nutwrap{position:absolute;right:-30px;bottom:-34px;width:146px;
  filter:drop-shadow(0 8px 18px rgba(0,0,0,.30))}
.nutwrap img{width:100%;display:block}
.nutwrap .eye{position:absolute;width:11%;aspect-ratio:1;border-radius:50%;
  background:#241f16;transform:translate(-50%,-50%);
  animation:look 9s ease-in-out infinite}
.nutwrap .eye.l{left:34%;top:60%}
.nutwrap .eye.r{left:66%;top:61%}
@keyframes look{
  0%,  100% { transform:translate(-50%,-50%) translate(0,0); }
  14%       { transform:translate(-50%,-50%) translate(5px,-2px); }
  30%       { transform:translate(-50%,-50%) translate(4px,3px); }
  46%       { transform:translate(-50%,-50%) translate(-4px,2px); }
  62%       { transform:translate(-50%,-50%) translate(-5px,-2px); }
  80%       { transform:translate(-50%,-50%) translate(1px,4px); }
}
@media (prefers-reduced-motion: reduce){ .nutwrap .eye{animation:none} }
.sysname{font-size:40px;font-weight:700;color:var(--accent);margin-top:14px;line-height:1.1}
.sysname span{display:block;font-size:25px;font-weight:400;color:var(--ink-soft);margin-top:4px}


/* ---- the TPS anatomy ---- */
.anat{display:flex;gap:18px;align-items:stretch}
.anat .part{flex:1;background:#fff;border:2px solid var(--rule);border-radius:14px;
  padding:22px 24px;text-align:center}
.anat .part h4{font-size:30px;font-weight:700;margin-bottom:10px}
.anat .part p{font-size:26px;color:var(--ink-soft);line-height:1.35}
.anat .plus{display:flex;align-items:center;font-size:40px;color:var(--ink-soft);font-weight:300}
.inst{display:flex;gap:18px;align-items:stretch;margin-top:12px}
.inst .part{flex:1;text-align:center;font-size:28px;font-weight:700;color:var(--accent);
  padding:10px 0}
.inst .plus{display:flex;align-items:center;font-size:30px;color:transparent;font-weight:300}


/* ---- representation slide: row top-left, table bottom-right ---- */
.repr{display:flex;flex-direction:column;justify-content:space-between;height:100%}
.repr .blk h3{font-size:34px;color:var(--accent);margin-bottom:14px}
.repr .right{align-self:flex-end;text-align:left}
.repr .foot{font-size:26px;color:var(--ink-soft)}


/* value chips sit inline in a code block, the way a probe reads in the editor */
pre.hz .obs{color:var(--good);background:#e4f1e9;border-radius:6px;padding:1px 10px}


/* ---- an operation spec: signature panel + requires/ensures grid ---- */
.spec{background:#fff;border:2px solid var(--rule);border-radius:14px;overflow:hidden}
.spec .sig{padding:16px 24px;background:#f7f4e9;border-bottom:2px solid var(--rule);
  font-family:"SF Mono",Menlo,monospace;font-size:27px;line-height:1.4;white-space:nowrap}
.spec .body{padding:18px 24px;display:grid;grid-template-columns:auto 1fr;
  column-gap:26px;row-gap:12px;align-items:baseline}
.spec .k{font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-size:23px;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;color:var(--accent);white-space:nowrap}
.spec .v{font-family:"SF Mono",Menlo,monospace;font-size:26px;line-height:1.5;color:var(--ink)}
.spec .v .dim{color:var(--ink-soft)}
/* The spec as a slide's centrepiece, revealed in two steps: signature, then
   constraints. The signature and the constraints are separate boxes, so the
   constraints keep their space while hidden (nothing moves when they arrive)
   and the signature is not sitting on top of an empty box in the meantime. */
.spec.big{background:none;border:0;border-radius:0;overflow:visible}
.spec.big .sig{font-size:32px;padding:20px 28px;border:2px solid var(--rule);border-radius:14px}
.spec.big:has(.body.in) .sig{border-bottom:0;border-radius:14px 14px 0 0}
.spec.big .body{padding:22px 28px;row-gap:14px;background:#fff;border:2px solid var(--rule);
  border-radius:0 0 14px 14px}
.spec.big:has(.body.in) .sig + .body{border-top:2px solid var(--rule)}
.spec.big .k{font-size:27px}
.spec.big .v{font-size:32px}


/* single-row scoreboard: show the denominator inline as a fraction */
table.score td.num .den{font-size:.58em;color:var(--ink-soft);font-weight:600}


/* ---- in-place content swap ---- */
.swap{position:relative}
.swap .layer{position:absolute;inset:0}
[data-until]{transition:opacity .18s}
[data-until].gone{opacity:0}


/* ---- example / type / value, stacked ---- */
.evt{font-family:"SF Mono",Menlo,monospace;font-size:26px;line-height:1.5}
.evt .ex{color:var(--ink)}
.evt .ty{color:var(--typ);display:block;margin-top:6px}
.evt .va{display:block;margin-top:6px}
.evt .va span{color:var(--good);background:#e4f1e9;border-radius:6px;padding:1px 10px}
.evt .lead-in{color:var(--ink-soft);margin-right:10px}


/* inline code that the tokenizer colours, e.g. inside a table cell */
code.hz-in{font-family:inherit;font-size:inherit}


/* ---- a live Hazel embed: its own origin, so its own IndexedDB ---- */
.embed{position:relative;border:3px solid var(--rule);border-radius:12px;overflow:hidden;
  background:#fff;flex:1;min-height:0}
.embed iframe{width:100%;height:100%;border:0;display:block}
.embed::after{content:"live";position:absolute;top:8px;right:10px;z-index:2;
  font-size:16px;letter-spacing:.14em;text-transform:uppercase;color:var(--good);
  background:#e4f1e9;border-radius:12px;padding:2px 10px;pointer-events:none}
/* the embed has the keyboard: typing edits it rather than driving the deck */
.embed.editing{border-color:var(--accent)}
.embed.editing::after{content:"editing";color:#fff;background:var(--accent)}


/* ---- controls that drive the embedded Hazel ---- */
.ectl{display:flex;gap:16px;align-items:center;margin-bottom:14px}
.ectl .grow{flex:1}
.bigtoggle{font:inherit;font-size:27px;font-weight:700;cursor:pointer;
  display:inline-flex;align-items:center;gap:14px;
  background:#fff;border:3px solid var(--rule);border-radius:999px;padding:9px 22px 9px 14px;
  color:var(--ink-soft);transition:color .15s,border-color .15s,background .15s}
/* Not `transition:all`: that includes visibility, which the toggle inherits from
   its .has-embed slide, so leaving the slide kept the toggle on screen for the
   length of the transition. */
.bigtoggle .knob{width:58px;height:32px;border-radius:999px;background:var(--rule);
  position:relative;transition:background .15s;flex:0 0 auto}
.bigtoggle .knob::after{content:"";position:absolute;top:3px;left:3px;width:26px;height:26px;
  border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.3);transition:left .15s}
.bigtoggle[aria-pressed="true"]{border-color:var(--good);color:var(--good);background:#eaf5ee}
.bigtoggle[aria-pressed="true"] .knob{background:var(--good)}
.bigtoggle[aria-pressed="true"] .knob::after{left:29px}
.ebtn{font:inherit;font-size:23px;font-weight:650;cursor:pointer;color:var(--ink-soft);
  background:#fff;border:3px solid var(--rule);border-radius:999px;padding:10px 22px}
.ebtn:hover{border-color:var(--ink-soft);color:var(--ink)}
.ectl .warn-msg{font-size:21px;color:var(--mark)}

/* ---- the three-way view selector on the live typing slide ----
   One control, three targets: clicking a segment selects it outright rather
   than cycling, so there is no miscount to recover from mid-sentence. */
.seg{display:inline-flex;background:#fff;border:3px solid var(--rule);
  border-radius:999px;padding:4px;gap:4px}
.seg button{font:inherit;font-size:23px;font-weight:650;cursor:pointer;
  color:var(--ink-soft);background:none;border:0;border-radius:999px;
  padding:7px 18px;transition:background .12s,color .12s}
.seg button:hover:not([aria-pressed="true"]){color:var(--ink);background:#f1efe9}
.seg button[aria-pressed="true"]{background:var(--ink);color:#fff}
.seg button:disabled{opacity:.45;cursor:default}

/* ---- an embed's slot before its Hazel is mounted ----
   Embeds boot a slide early and retire a slide late (see syncEmbeds), so this
   is what the box holds the rest of the time. It matters mainly in rehearsal,
   when you jump straight to a slide and want to know whether you are waiting
   for something or looking at a bug. */
.embed-ph{display:flex;align-items:center;justify-content:center;height:100%;
  border:3px dashed var(--rule);border-radius:14px;
  color:var(--ink-soft);font-size:24px;opacity:.55}


/* ---- Likert bars drawn on the slide (BrBG-7, matches survey_plots/generate_plots.py) ----
   The paper's survey figures shrink to ~14px text at slide size, unreadable from
   the back of a room; the study slides draw the rows they talk about instead.
   One segment per participant, seven per row, a 2px paper gap between them,
   the exact survey wording as the label and the mean at the end, as in the
   paper's figures. The legend is the .likert strip below. */
.lk{display:flex;flex-direction:column;gap:8px}
.lk .g{font-size:30px;font-weight:700;font-style:italic;margin:20px 0 4px}
.lk .g:first-child{margin-top:0}
.lk .r{display:grid;grid-template-columns:1fr 780px 130px;align-items:center;column-gap:28px}
.lk .l{font-size:29px;line-height:1.2;text-align:right}
.lk .b{display:flex;gap:2px;height:42px}
.lk .b i{flex:1;display:block}
.lk .b i:first-child{border-radius:4px 0 0 4px}
.lk .b i:last-child{border-radius:0 4px 4px 0}
.lk .m{font-size:26px;color:var(--ink-soft);font-variant-numeric:tabular-nums}
/* a chart with one group and few rows: the repeated stem is pulled into the
   group line, so the labels can be larger */
.lk.big{gap:14px}
.lk.big .g{font-size:36px;margin-bottom:8px}
.lk.big .l{font-size:36px}
.lk.big .l strong{white-space:nowrap}
/* ...and stacked: the label on its own line above the bar, the bar the full
   width of the slide with the mean beside it */
.lk.stack{gap:22px}
.lk.stack .r{grid-template-columns:1fr 130px;row-gap:6px}
.lk.stack .l{grid-column:1/-1;text-align:left}
.lk.stack .b{grid-column:1;height:44px}
.lk.stack .m{grid-column:2}
.lk.big .b{height:50px}
.lk.big .m{font-size:30px}
.lk .s1{background:#8c510a}.lk .s2{background:#d8b365}.lk .s3{background:#f6e8c3}
.lk .s4{background:#f5f5f5;box-shadow:inset 0 0 0 1px var(--rule)}
.lk .s5{background:#c7eae5}.lk .s6{background:#5ab4ac}.lk .s7{background:#01665e}

/* research questions, each with the task that answers it */
ol.rq{list-style:none;counter-reset:q}
ol.rq li{counter-increment:q;font-size:36px;line-height:1.25;margin-bottom:34px;padding-left:76px;position:relative}
ol.rq li::before{content:"RQ" counter(q);position:absolute;left:0;top:4px;font-size:22px;font-weight:700;
  letter-spacing:.06em;color:var(--accent);width:auto;height:auto;border-radius:0;background:none}
ol.rq li .task{display:block;font-size:27px;color:var(--ink-soft);margin-top:6px;line-height:1.35}

/* a denser related-work list: two columns on one slide */
ul.cats.tight li{margin-bottom:34px}
ul.cats.tight li b{font-size:40px}
ul.cats.tight li b .at{font-size:26px;white-space:nowrap}
ul.cats.tight li .why{font-size:31px;font-style:normal;line-height:1.3}

/* future work: shorter lists under the lead line */
ul.compact li{font-size:34px;line-height:1.35;margin-bottom:12px}

/* the comparison table's problem names, glossed for a room that has not read the paper */
table.cmp td.lbl .gloss{display:block;font-size:21px;font-weight:400;color:var(--ink-soft);
  white-space:normal;line-height:1.3;margin-top:2px}

/* the QR code beside the closing link */
img.qr{width:236px;height:236px;flex:0 0 auto;border:2px solid var(--rule);border-radius:10px;
  padding:10px;background:#fff}

/* ---------- ?presenter ----------
 * The stage is pinned top-left at --sw x --sh (deck.js fit()); OBS crops it
 * out and projects it. Everything else in the window is for the presenter
 * only: the panel to the right, and the notes either under the panel or,
 * when the window leaves a strip under the stage, in that strip. */
#panel{display:none}
body.presenter #stage{top:0;left:0;transform-origin:0 0}
body.presenter #hud{display:none}
body.presenter #bar{right:auto;width:var(--sw);top:calc(var(--sh) - 5px);bottom:auto}
body.presenter #help{left:var(--sw)}
body.presenter #panel{display:flex;flex-direction:column;position:fixed;top:0;right:0;bottom:0;
  left:var(--sw);background:#14130f;color:#e4dfd0;border-left:2px solid #35322a;z-index:30;
  font-variant-numeric:tabular-nums}
body.presenter #panel > .p-top{padding:18px 22px 0}
#pclock{display:flex;gap:6px;margin-right:auto;margin-left:14px;align-self:center}
#pclock button{width:30px;height:30px;border-radius:15px;border:1px solid #45413a;background:#2e2b24;
  color:#b9b3a5;font:13px/1 system-ui,sans-serif;cursor:pointer;padding:0}
#pclock button:hover{background:#3a362d;color:#f3efe4}
#pclock button:disabled{opacity:.35;cursor:default}
#pclock button.armed{background:#6b2a1f;border-color:#e0573f;color:#fff}
#pstat{padding:4px 22px 0;font-size:16px;line-height:1.45}
#ptime{padding:0 22px 14px;font-size:16px;line-height:1.45;border-bottom:2px solid #35322a}
body.presenter.stats-below #pstat{padding-top:14px;padding-bottom:6px;border-bottom:2px solid #35322a}
/* stats-below: the clock and timing in the strip under the stage (OBS crops it away) */
#pbottom{display:none}
body.presenter.stats-below #pbottom{display:flex;gap:28px;align-items:flex-start;position:fixed;
  top:var(--sh);left:0;width:var(--sw);bottom:0;padding:14px 22px 0;background:#14130f;
  color:#e4dfd0;border-top:2px solid #35322a;z-index:30;font-variant-numeric:tabular-nums;overflow:hidden}
body.presenter.stats-below #pbottom > .p-top{padding:0;flex:none}
body.presenter.stats-below #ptime{flex:1;padding:0;border:0;column-count:2;column-gap:32px;font-size:15px}
body.presenter.stats-below #ptime > *{break-inside:avoid}
body.presenter.stats-below #ptime .p-state{margin:0;min-height:0}
body.presenter.stats-below #ptime .p-state:empty{display:none}
body.presenter.stats-below #ptime .p-next{margin-top:0;padding-top:0;border-top:0}
body.presenter.stats-below #ptime .p-crop{margin-top:4px}
#panel .p-top, #pbottom .p-top{display:flex;align-items:baseline;justify-content:space-between}
#panel .p-clock, #pbottom .p-clock{font:600 56px/1 "SF Mono",Menlo,monospace;color:#f3efe4}
#panel .p-clock.over, #pbottom .p-clock.over{color:#e0573f}
#panel .p-wall, #pbottom .p-wall{font:15px "SF Mono",Menlo,monospace;color:#8d8778}
.pstat .p-state{min-height:22px;margin:4px 0 8px;color:#8d8778;font-size:14px}
.pstat kbd{background:#2e2b24;border-radius:4px;padding:0 6px;font-family:monospace}
.pstat .p-slide{font-size:13px;letter-spacing:.12em;text-transform:uppercase;color:#8d8778}
.pstat .p-title{font-size:21px;font-weight:600;color:#f3efe4;margin:2px 0 10px}
.pstat .p-meter{height:8px;border-radius:4px;background:#2e2b24;overflow:hidden;margin-bottom:8px}
.pstat .p-meter i{display:block;height:100%;background:#6aa84f;transition:width .4s}
.pstat .p-meter.warn i{background:#e8c46a}
.pstat .p-meter.late i{background:#e0573f}
.pstat .p-line{color:#b9b3a5}
.pstat .p-line b{color:#f3efe4;font-weight:600}
.pstat .late{color:#e0573f !important}
.pstat .early{color:#6aa84f !important}
.pstat .dim, .pstat .p-line.dim{color:#8d8778}
.pstat .p-next{margin-top:12px;padding-top:10px;border-top:1px solid #2e2b24;color:#b9b3a5}
.pstat .p-crop{margin-top:10px;font:11px "SF Mono",Menlo,monospace;color:#6b675c}

body.presenter #notes{display:block;position:relative;inset:auto;flex:1;max-height:none;
  border-top:0;padding:18px 22px 60vh;z-index:auto}
body.presenter.notes-below #notes{position:fixed;top:var(--sh);left:0;width:var(--sw);bottom:0;
  border-top:2px solid #35322a;padding:16px 28px 60vh}
body.presenter #notes h4{display:none}
body.presenter #notes > *{max-width:62ch}
body.presenter #notes p, body.presenter #notes li{font-size:18px;line-height:1.5}
body.presenter #notes p.say{font-size:20px}
body.presenter #notes > *{transition:opacity .2s}
body.presenter #notes .seg-past{opacity:.35}
body.presenter #notes .next-click{border-left:4px solid #e8c46a;padding-left:12px;color:#e8c46a}
body.presenter #notes .next-click::before{content:"NEXT CLICK  ";font:600 12px/1 system-ui;
  letter-spacing:.12em;font-style:normal}
body.presenter #notes [data-seg]:not(.seg-now):not(.seg-past):not(.next-click){opacity:.6}
/* Q&A facts sit last in each slide's notes; smaller and quieter than what you say */
body.presenter #notes p.note.fact{font-size:15px;color:#8fae88}

/* ?timing */
#timing{max-width:none;padding:40px 32px 80px;font:15px/1.5 system-ui,sans-serif}
#timing h1{font-size:30px;margin-bottom:6px}
#timing > p{margin-bottom:18px;font-size:15px;line-height:1.5}
#timing .scroll{overflow-x:auto}
#timing table{border-collapse:collapse;font-variant-numeric:tabular-nums}
#timing th, #timing td{padding:4px 12px;text-align:left;border-bottom:1px solid #e6e1d4;white-space:nowrap}
#timing th{font-size:13px;color:#555}
#timing tr.sub th{font-weight:400;color:#8d8778;font-size:12px}
#timing tr.total td{font-weight:700;border-top:2px solid #c9c2b1}
#timing .dim{color:#8d8778}
#timing .late{color:#c0392b}
#timing .early{color:#2f7d4f}
#timing .ok{color:#8d8778}
#timing td.clicks{color:#555;font-size:13px}
#timing button{border:1px solid #c9c2b1;background:#f2efe6;border-radius:4px;padding:0 6px;cursor:pointer;margin-left:6px}

/* the panel's back / next buttons and slide picker */
#pnav{display:flex;gap:8px;padding:10px 22px;border-bottom:2px solid #35322a}
#pnav button, #pnav select{background:#2e2b24;color:#e4dfd0;border:1px solid #45413a;border-radius:6px;
  font:15px system-ui,sans-serif;height:36px;cursor:pointer}
#pnav button{width:52px;flex:none;font-size:16px}
#pnav button:hover, #pnav select:hover{background:#3a362d}
#pnav button:active{background:#4a4538}
#pnav select{flex:1;min-width:0;padding:0 8px}

/* ---- University of Michigan mark ----
   The CSE logo-signature (Block M | Michigan Engineering / Computer Science and
   Engineering / University of Michigan), from Michigan Engineering's logo kit.
   Bottom-right on the title slide; top-right on the summary, whose content
   already fills the top and the bottom (the QR row). brand.umich.edu: never
   recolour or redraw the mark, keep clear space of twice the block serif around
   it (the slide padding gives more), and a unit logo is never under 36px tall
   on screen. */
.brandmark{position:absolute;right:110px;height:72px;width:auto}
.brandmark.top{top:64px}
.brandmark.bottom{bottom:64px}

/* ---- slide number ----
   One element on the stage, filled by paint(): the slide's number on the main
   run, B-n on a backup. Muted and small, in the bottom-right corner, clear of
   the CSE mark on the title slide (right:110px, bottom:64px) and of the
   footnote strip (.fnote.foot, left-aligned). Above the embed slides' z-index. */
#slidenum{position:absolute;right:44px;bottom:26px;z-index:3;pointer-events:none;
  font-size:20px;line-height:1;color:var(--ink-soft);opacity:.55;
  font-variant-numeric:tabular-nums;letter-spacing:.04em}

/* ---- arrows between elements of a slide ----
   An svg.arrows overlays its container; each path names the elements it joins
   (data-from, data-to) and deck.js computes the curve from their boxes. A white
   glow keeps the line legible where it crosses code. */
svg.arrows{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;z-index:4}
svg.arrows path[data-from]{fill:none;stroke:var(--mark);stroke-width:5;stroke-linecap:round;
  filter:drop-shadow(0 0 2px #fff) drop-shadow(0 0 2px #fff)}
svg.arrows marker path{fill:var(--mark)}
