/* --- align base look with the project website (dark, monospace) ---
   Overrides dracula.css's theme custom properties rather than fighting
   its selectors. Headings use the generic `monospace` keyword (plain
   browser/OS default monospace reads cleaner here than a specific
   typewriter-style face like Courier). Body text is JetBrains Mono,
   monospace too (fits the theme better than sans-serif) but notably
   narrower than Courier so bullet-heavy slides don't feel as wide/silly
   - self-hosted as an npm package (@fontsource/*, linked in index.html
   <head>) rather than a CDN fetch, so the deck keeps working fully
   offline. Background moves to the site's near-black. Bullet markers
   get pulled to monochrome; dracula's other accent colors (links, bold,
   italic/quotes, inline code) are left alone - some color and
   highlight is still wanted, just not everywhere. The title slide
   (see .font-site below) is the one exception, using the website's
   literal Courier New stack instead of this generic monospace. */
:root {
	--r-background: #0a0a0a;
	--r-background-color: #0a0a0a;
	--r-main-font: 'JetBrains Mono', Consolas, Menlo, 'SF Mono', 'DejaVu Sans Mono', 'Liberation Mono', 'Ubuntu Mono', monospace;
	--r-main-font-size: 40px;
	--r-heading-font: monospace;
	--r-heading-color: #ffffff;
	--r-heading-font-weight: 400;
	--r-heading-letter-spacing: 0.03em;
	--r-list-bullet-color: rgba(255, 255, 255, 0.55);
}

.reveal ul, .reveal ol { display: block; }

/* Numbered list nested in a bullet list: "1." markers are wider than
   the square sub-bullets and hang left of the parent's text, so push
   them in to read as nested. */
.reveal ul ol { margin-left: calc(40px + 1em); }

/* Nav arrows + progress bar ride on --r-link-color by default (dracula
   pink), which is left alone elsewhere on purpose to keep some color in
   the content - but the chrome itself should stay monochrome. */
.reveal .controls { color: rgba(255, 255, 255, 0.7); }
.reveal .progress { color: rgba(255, 255, 255, 0.6); }

/* --- media / diagram placeholders ---
   Swap for a real <img>/<video> once assets are picked. Keep the label
   text short - it's a note to self, not slide content. */
.ph {
	border: 2px dashed rgba(255,255,255,.35);
	border-radius: 10px;
	padding: .8em 1em;
	margin: .5em auto;
	max-width: 90%;
	text-align: left;
	font-size: .48em;
	line-height: 1.35;
	background: rgba(255,255,255,.04);
}

.ph-label {
	display: block;
	font-weight: 700;
	letter-spacing: .04em;
	text-transform: uppercase;
	font-size: .85em;
	margin-bottom: .25em;
	opacity: .7;
}

/* --- caption for full-bleed background image/video slides ---
   Small pinned label, e.g. photo credit or a one-line callout. */
.cap {
	position: fixed;
	left: 50%;
	bottom: 6%;
	transform: translateX(-50%);
	background: rgba(0,0,0,.55);
	padding: .35em .9em;
	border-radius: 8px;
	font-size: .42em;
	letter-spacing: .02em;
	white-space: nowrap;
}

.cap a { color: inherit; }

/* --- inline image row, for photos shown alongside slide text ---
   Sized to safely fit up to 4 images in one row without wrapping
   (wrapping pushes content past the slide's vertical bounds, which
   reveal.js does not auto-shrink to fit). */
/* All sizes in slide px (deck is 1280x720), never vh/vw: reveal scales
   the slide, vh/vw follow the window, so they differ per screen size and
   aspect ratio. */
.imgrow {
	display: flex;
	gap: 1em;
	justify-content: center;
	align-items: flex-end;
	margin-top: .6em;
	flex-wrap: wrap;
}

.imgrow img {
	max-height: var(--row, 288px);
	max-width: 422px;
	border-radius: 8px;
	display: block;
}

.imgrow figure {
	margin: 0;
	text-align: center;
}

.imgrow figcaption {
	font-size: .4em;
	opacity: .75;
	margin-top: .3em;
}

/* Modifiers for a row that should read bigger than the shared sizing
   allows. Previous version used fixed vw widths per image count, which
   overflowed: reveal's `margin: 0.08` config shrinks the usable slide
   width to ~84%, not the 100vw the math assumed, so e.g. 3 * 30vw
   didn't actually fit. Flex-grow instead: each image's width is
   however much of the row's REAL available width it gets after
   splitting evenly, so it always fits no matter how many images -
   only `height` needs tuning per modifier, and object-fit: cover
   still crops to fill (native photos are ~3:4, so at these heights
   the crop stays modest, not drastic). */
.imgrow-lg,
.imgrow-lg3 {
	flex-wrap: nowrap;
}

.imgrow-lg img,
.imgrow-lg3 img {
	flex: 1 1 0;
	min-width: 0;
	width: auto;
	max-width: none;
	max-height: none;
	object-fit: cover;
}

.imgrow-lg img { height: 480px; }
.imgrow-lg3 img { height: var(--lg3, 360px); }

/* A bit bigger than the shared .imgrow sizing, for mixed portrait/
   landscape rows (slide px, see .imgrow-tall below for why not vh). */
.imgrow-mid img {
	max-height: 300px;
	max-width: none;
}

/* Portrait photos at full aspect (no crop), as tall as a slide with a
   title + one text line allows. In slide px (deck is 1280x720), not vh:
   reveal scales the slide, but vh follows the window, so on 16:10 a vh
   height grows past the slide's bottom edge. */
.imgrow-tall img {
	max-height: var(--tall, 450px);
	max-width: none;
}

/* --- heading pinned to top/bottom instead of vertical-centered ---
   For full-bleed background image/video slides where a centered
   heading would sit right on top of the subject. Reveal.js vertically
   centers slides by JS-computing an inline `top` on the <section> and
   leaving its height auto-sized to its content - so a plain
   `position: absolute` child (like an earlier version of this rule)
   only positions within that tiny content-sized box, not the actual
   slide area, and barely moves at all. `position: fixed` escapes that
   and resolves against reveal's own scaled slide viewport instead,
   same as `.cap` below already relies on. */
.heading-top,
.heading-bottom {
	position: fixed;
	left: 50%;
	transform: translateX(-50%);
	width: 92%;
	margin: 0;
}

.heading-top { top: 4%; }
.heading-bottom { bottom: 4%; }

/* --- website's title glow, for slides with a live WebGL background ---
   Same multi-layer text-shadow as #overlay h1/.subtitle on the project
   website: a black glow to separate text from the moving dots behind
   it, plus a faint white glow on top. */
.reveal .slides section.bg-glow h1,
.reveal .slides section.bg-glow h2,
.reveal .slides section.bg-glow h3,
.reveal .slides section.bg-glow p {
	text-shadow:
		0 0 40px rgba(0, 0, 0, 0.8),
		0 0 20px rgba(0, 0, 0, 0.9),
		0 0 10px rgba(0, 0, 0, 1.0),
		0 0 50px rgba(255, 255, 255, 0.15);
}

/* --- title & Fin slides only: match the website, not the deck's own
   look ---
   Every other slide uses generic `monospace` for headings and
   JetBrains Mono for body (see :root above). These two bookend slides
   instead should read as close to the real website as possible: the
   site's literal font stack (Courier New first), and monochrome links
   (the site has no colored links anywhere - dracula's pink is a
   deliberate accent everywhere else, but not here). Scoped via custom
   properties so heading, body and link color all resolve correctly
   within just these sections. */
.reveal .slides section.font-site {
	--r-heading-font: 'Courier New', Courier, Monaco, 'Lucida Console', monospace;
	--r-main-font: 'Courier New', Courier, Monaco, 'Lucida Console', monospace;
	--r-link-color: #ffffff;
	--r-link-color-dark: #cccccc;
	--r-link-color-hover: #aaaaaa;
}

/* --- interactive WebGL demo on the "Ordnung & Chaos" slide ---
   A real <iframe> as slide content rather than a data-background,
   since it needs to be a live, tunable demo, not decoration. Fixed
   positioning fills the slide area the same way .cap/.heading-* do
   (see their comments re: why `fixed` and not `absolute`). Starts
   near-invisible so the slide still reads as text-first; the opacity
   slider is what brings it forward to cover the text. The iframe
   itself ignores pointer events (nothing to click inside it), the
   controls re-enable them so the sliders stay usable. Both get
   explicit z-index: iframes are notorious for compositing above
   same-level siblings regardless of DOM order/paint order, so relying
   on source order alone (like .heading-* vs its background) isn't
   reliable here - without this the sliders were getting covered by
   the canvas. */
.webgl-demo iframe {
	position: fixed;
	top: 0; left: 0; right: 0; bottom: 0;
	width: 100%;
	height: 100%;
	border: 0;
	opacity: 0.15;
	pointer-events: none;
	transition: opacity .1s linear;
	z-index: 1;
}

.webgl-controls {
	position: fixed;
	left: 50%;
	bottom: 5%;
	transform: translateX(-50%);
	display: flex;
	gap: 2em;
	background: rgba(0, 0, 0, .6);
	padding: .6em 1.4em;
	border-radius: 10px;
	pointer-events: auto;
	font-size: .38em;
	z-index: 2;
}

.webgl-controls label {
	display: flex;
	flex-direction: column;
	gap: .35em;
	align-items: center;
	white-space: nowrap;
}

.webgl-controls input[type="range"] {
	width: 14em;
	accent-color: #ffffff;
}

/* Styled on/off toggle (replaces a plain checkbox) for the demo's
   "bring to foreground" control - a slider felt like overkill for a
   binary switch. */
.toggle-track {
	position: relative;
	display: inline-block;
	width: 1.6em;
	height: .9em;
	background: rgba(255, 255, 255, .25);
	border-radius: 999px;
	transition: background .15s ease;
	flex: none;
}

.toggle-track input {
	position: absolute;
	inset: 0;
	margin: 0;
	opacity: 0;
	cursor: pointer;
}

.toggle-thumb {
	position: absolute;
	top: .15em;
	left: .15em;
	width: .6em;
	height: .6em;
	border-radius: 50%;
	background: #ffffff;
	transition: transform .15s ease, background .15s ease;
	pointer-events: none;
}

.toggle-track:has(input:checked) {
	background: rgba(255, 255, 255, .85);
}

.toggle-track:has(input:checked) .toggle-thumb {
	transform: translateX(.7em);
	background: #0a0a0a;
}

/* --- inline diagrams (SVG via <img>) ---
   Transparent background, drawn for the dark theme. */
.reveal img.diagram {
	display: block;
	width: 1200px;
	max-width: 100%;
	max-height: 580px;
	height: auto;
	object-fit: contain; /* never stretch when max-height kicks in */
	margin: .3em auto 0;
	border: none;
	box-shadow: none;
	background: none;
}

/* --- overlay text pinned to the bottom of a standalone horizontal
   slide ---
   .heading-bottom's `position: fixed` only works inside vertical
   stacks; on a standalone horizontal slide it resolves against the
   section's own content-sized box. Instead stretch the section to
   the full slide (overriding reveal's inline centering `top`) and pin
   the text absolutely inside it. */
.reveal .slides section.overlay-bottom {
	top: 0 !important;
	height: 100%;
}

.overlay-bottom .overlay-text {
	position: absolute;
	left: 50%;
	bottom: 4%;
	transform: translateX(-50%);
	width: 92%;
	margin: 0;
}

/* --- cheeky aside under a bullet list (e.g. "+ sicheres
   Congress-Ticket" on Motivation): smaller, italic, accent color --- */
.reveal .sidenote {
	font-size: 0.6em;
	font-style: italic;
	color: #f1fa8c;
	text-align: right;
	margin-top: 0.8em;
}

/* Struck-out text (~~old~~ new) fades back so the replacement reads
   first. */
.reveal del { color: rgba(255, 255, 255, 0.45); }

/* reveal caps code blocks at 400px with a scrollbar; show listings in
   full instead (a scrollbar can't be used during a talk anyway). */
.reveal pre code { max-height: none; }

/* --- readability of text on photo/video backgrounds ---
   Any slide with a background image or video gets a subtle, tight black
   glow on its text: enough to lift white text off a bright busy photo,
   without the heavier website-style glow of the title/Fin (.bg-glow). */
.reveal .slides section[data-background-image] :is(h1, h2, h3, p),
.reveal .slides section[data-background-video] :is(h1, h2, h3, p) {
	text-shadow:
		0 0 3px rgba(0, 0, 0, 0.9),
		0 0 10px rgba(0, 0, 0, 0.7);
}

/* Code listing a bit larger than reveal's default (.55em): must stay
   readable on a projector, and the framework slide has the room. */
.reveal pre { font-size: 0.68em; }

/* Closing punchline: a .sidenote that should land as the last word,
   so larger and centered instead of a small aside in the corner. */
.reveal .sidenote.punchline {
	font-size: 0.85em;
	text-align: center;
	margin-top: 1.4em;
}
