/*
 * SPDX-FileCopyrightText: 2026 ALASCA e.V. <hello@alasca.cloud>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/* ==========================================================================
   ALASCA — reveal.js theme
   Brand colours per ALASCA guidelines:
     Primary   Green #51E064 · Dark Blue #243746 · Black · White
     Secondary Sand Yellow, Yellow, Orange, Red, Light Blue, Blue, Dark Blue Sec
   Rules honoured here:
     · Dark Blue dominates in proportion over Green.
     · Green is used for typographic elements — heading accents, kickers,
       numbers, rules and highlights — never as a large field.
     · Secondary colours appear only in charts / tags / diagrams.
   Fonts: Space Grotesk, Inter, JetBrains Mono — declared in theme/fonts.css,
   loaded from fonts/ so a deck never touches the network
   ========================================================================== */

:root {
  /* ---- Primary palette ------------------------------------------------- */
  --al-green:       #51E064;  /* Green      — typographic accent           */
  --al-green-dim:   #3FBF50;  /* Green, one step down for hairlines        */
  --al-green-deep:  #157A2B;  /* Green shade usable as text on white       */
  --al-navy:        #243746;  /* Dark Blue  — the dominant field           */
  --al-navy-2:      #08162A;  /* Dark Blue Sec — depth, code, panels       */
  --al-black:       #000000;
  --al-white:       #FFFFFF;

  /* ---- Secondary palette (infographics, tags, diagrams only) ----------- */
  --al-sand:        #E1E173;
  --al-yellow:      #F5E604;
  --al-orange:      #EC7629;
  --al-red:         #E1646E;
  --al-light-blue:  #7BCAEF;
  --al-blue:        #0E85A6;

  /* ---- Derived surfaces & text ---------------------------------------- */
  --al-surface:     #2C4354;  /* raised panel on the Dark Blue field       */
  --al-surface-2:   #35505F;  /* hover / stronger panel                    */
  --al-line:        rgba(81, 224, 100, 0.22);   /* green hairline rules    */
  --al-line-soft:   rgba(255, 255, 255, 0.10);  /* neutral hairline        */
  --al-paper:       #F5F7F8;  /* light slide background                    */
  --al-paper-line:  #DDE3E6;
  --al-muted:       #A7B7C2;  /* secondary text on Dark Blue               */
  --al-muted-2:     #56707F;  /* secondary text on paper                   */

  /* ---- Mermaid diagrams ------------------------------------------------ */
  /* Read at render time by templates/template/parts/deck-mermaid.html, so the
     colours a diagram is drawn with stay here with the rest of the brand.
     Green outlines the boxes, the Dark Blue field fills them, and only the
     series below reach for the secondary palette — the rule for infographics. */
  --mm-font:         "Inter", system-ui, sans-serif;
  --mm-font-size:    17px;
  --mm-height:       460px;                     /* the tallest a diagram draws */
  --mm-node-bg:      var(--al-surface);
  --mm-node-line:    var(--al-green);
  --mm-node-text:    #E8EEF2;
  --mm-alt-bg:       var(--al-navy-2);          /* the second kind of box      */
  --mm-alt-line:     var(--al-light-blue);
  --mm-line:         var(--al-muted);           /* edges and arrowheads        */
  --mm-text:         #E8EEF2;                   /* edge labels, titles         */
  --mm-cluster-bg:   rgba(255, 255, 255, 0.04); /* subgraph / grouping box     */
  --mm-cluster-line: rgba(255, 255, 255, 0.18);
  --mm-label-bg:     var(--al-navy);            /* behind a label on an edge   */
  --mm-note-bg:      var(--al-sand);
  --mm-note-text:    var(--al-navy-2);
  --mm-1: var(--al-green);      /* series colours — pies, journeys, quadrants */
  --mm-2: var(--al-light-blue);
  --mm-3: var(--al-sand);
  --mm-4: var(--al-orange);
  --mm-5: var(--al-blue);
  --mm-6: var(--al-red);

  /* ---- reveal.js core variables ---------------------------------------- */
  --r-background-color: var(--al-navy);
  --r-main-font: "Inter", system-ui, -apple-system, sans-serif;
  --r-main-font-size: 30px;
  --r-main-color: #E8EEF2;
  --r-block-margin: 22px;
  --r-heading-margin: 0 0 26px 0;
  --r-heading-font: "Space Grotesk", "Inter", sans-serif;
  --r-heading-color: var(--al-white);
  --r-heading-line-height: 1.08;
  --r-heading-letter-spacing: -0.01em;
  --r-heading-text-transform: none;
  --r-heading-font-weight: 600;
  --r-heading1-size: 3.1em;
  --r-heading2-size: 1.9em;
  --r-heading3-size: 1.3em;
  --r-heading4-size: 1.0em;
  --r-code-font: "JetBrains Mono", monospace;
  --r-link-color: var(--al-green);
  --r-link-color-hover: var(--al-white);
  --r-selection-background-color: var(--al-green);
  --r-selection-color: var(--al-navy-2);
}

/* -------------------------------------------------------------------------- */
/*  Base                                                                       */
/* -------------------------------------------------------------------------- */
.reveal-viewport {
  background:
    radial-gradient(1100px 680px at 82% -12%, rgba(81, 224, 100, 0.10), transparent 62%),
    linear-gradient(180deg, var(--al-navy) 0%, #1A2A38 62%, var(--al-navy-2) 100%);
}

/* reveal.js core carries no typography of its own — the --r-* tokens above are
   spent by a reveal *theme*, and this file is that theme. Everything on a slide
   is sized from --r-main-font-size, so the whole deck scales from one number. */
.reveal {
  font-family: var(--r-main-font);
  font-size: var(--r-main-font-size);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--r-main-color);
}

.reveal .slides { text-align: left; }
.reveal .slides section { height: 100%; }

.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
  margin: var(--r-heading-margin);
  font-family: var(--r-heading-font);
  font-weight: var(--r-heading-font-weight);
  line-height: var(--r-heading-line-height);
  letter-spacing: var(--r-heading-letter-spacing);
  text-transform: var(--r-heading-text-transform);
  color: var(--r-heading-color);
  text-wrap: balance;
  overflow-wrap: break-word;
}
.reveal h1 { font-size: var(--r-heading1-size); font-weight: 700; }
.reveal h2 { font-size: var(--r-heading2-size); }
.reveal h3 { font-size: var(--r-heading3-size); }
.reveal h4 { font-size: var(--r-heading4-size); }
.reveal h5, .reveal h6 { font-size: var(--r-heading4-size); }

/* --r-heading-margin is bottom-only, which is all a slide's opening heading
   needs. A heading further down has content above it and needs air there too —
   except right under another heading, where the pair belongs together. */
.reveal :is(h1, h2, h3, h4):not(:first-child) { margin-top: 0; }
.reveal :is(h1, h2, h3, h4, h5, h6) + :is(h1, h2, h3, h4) { margin-top: 0; }

.reveal strong { color: var(--al-white); font-weight: 600; }
.reveal em { color: var(--al-green); font-style: normal; }
.reveal .accent { color: var(--al-green); }

.reveal p { margin: 0 0 var(--r-block-margin) 0; line-height: 1.5; color: #DCE5EB; }
.reveal p:last-child { margin-bottom: 0; }
.reveal a {
  color: var(--al-green);
  text-decoration: none;
  border-bottom: 1px solid var(--al-green-dim);
  padding-bottom: 1px;
  transition: color .18s ease, border-color .18s ease;
}
.reveal a:hover { color: var(--al-white); border-color: var(--al-white); }

/* Grid ambient texture — the "infrastructure" signature */
.reveal .slides::before {
  content: "";
  position: absolute; inset: 0;
  background-image:
    linear-gradient(var(--al-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--al-line) 1px, transparent 1px);
  background-size: 64px 64px;
  opacity: 0.10;
  pointer-events: none;
  z-index: 0;
}
.reveal section > * { position: relative; z-index: 1; }

/* -------------------------------------------------------------------------- */
/*  Eyebrow (mono kicker) — encodes section context                           */
/* -------------------------------------------------------------------------- */
.reveal .eyebrow {
  font-family: var(--r-code-font);
  font-size: 0.52em;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--al-green);
  margin: 0 0 20px 0;
  display: inline-flex;
  align-items: center;
  gap: 12px;
}
.reveal .eyebrow::before {
  content: "";
  width: 26px; height: 2px;
  background: var(--al-green);
  display: inline-block;
}

/* -------------------------------------------------------------------------- */
/*  Lists                                                                      */
/* -------------------------------------------------------------------------- */
.reveal ul, .reveal ol { display: block; margin: 0 0 var(--r-block-margin) 0; }
.reveal ul:last-child, .reveal ol:last-child { margin-bottom: 0; }
.reveal ul { list-style: none; padding-left: 0; }
.reveal ul li {
  position: relative;
  padding-left: 34px;
  margin: 0 0 16px 0;
  line-height: 1.45;
  color: #DCE5EB;
}
/* The default marker: a filled round disc. `content` and the absolute placement
   live here and only here — a ::before without `content` generates no box at
   all, so every variant below inherits this block rather than restating it. */
.reveal ul li::before {
  content: "";
  position: absolute; left: 2px; top: 0.62em;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--al-green);
  transform: translateY(-50%);
}

/* Marker variants. Put one on a single <ul> for one list, or on the <section>
   for the whole slide. Each redefines only what it changes, so the 34px text
   indent and the baseline alignment hold either way — but each must also undo
   the parts of the default disc it does not want. */

/* Square — the outlined rotated marker, so it clears the disc's fill and radius. */
.reveal .bullets-rectangle li::before {
  width: 9px; height: 9px;
  border: 2px solid var(--al-green);
  border-radius: 0;
  background: none;
  transform: translateY(-50%) rotate(45deg);
}

/* Ice cube — an isometric wireframe, carried as a mask rather than a coloured
   image so it still takes its colour from the palette (and so the light-slide
   rule below reaches it, which a background-image could not do). */
.reveal .bullets-ice li::before {
  left: 0; width: 14px; height: 14px;
  border: 0; border-radius: 0;
  background: var(--al-light-blue);
  transform: translateY(-50%);
  --cube: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M12 2 22 7v10l-10 5L2 17V7z'/%3E%3Cpath d='m2 7 10 5 10-5M12 12v10'/%3E%3C/svg%3E");
  -webkit-mask: var(--cube) center / contain no-repeat;
  mask: var(--cube) center / contain no-repeat;
}
/* Arrow — a marker for *one* item, not a whole list: the class goes on the <li>
   itself, so a single line can point onwards ("-> and so we did this") while its
   siblings keep the default disc.

     <li class="arrow">the one that leads somewhere</li>

   Masked like the cube above rather than drawn as a background image, so it
   still takes its colour from `background`. That means it inherits the default
   green here, and on a pale slide the generic light-slide rule repaints it deep
   green without this needing to know about it — that rule out-specifies this one
   on `background` alone, which is exactly the part it should win.

   Wider than it is tall, hence its own width; `left: 0` because an arrow reads
   from its own tail where a disc reads from its centre. border-radius has to go
   or the default 50% would clip the tail and the tip off the mask. */
.reveal ul li.arrow::before {
  /* Bigger than the 8px disc it replaces, because an arrow is read as a shape
     rather than registered as a dot — at disc size it reads as a smudge. 24px
     leaves a 10px gap to the text at the 34px indent, which is tighter than the
     disc's but right for an arrow: it should point *at* the line, not float away
     from it. The text still starts where every sibling's does. */
  left: 0; width: 24px; height: 21px;
  border-radius: 0;
  transform: translateY(-50%);
  /* stroke-width 3 in a 24-unit viewBox lands near 2px at this size, which is
     what it takes to carry the same weight as the filled disc from a projector
     distance — 2.5 read noticeably lighter than the default marker. */
  --arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12h15'/%3E%3Cpath d='m13 6 6 6-6 6'/%3E%3C/svg%3E");
  -webkit-mask: var(--arrow) center / contain no-repeat;
  mask: var(--arrow) center / contain no-repeat;
}

.reveal ol { counter-reset: al; list-style: none; padding-left: 0; }
.reveal ol li {
  counter-increment: al;
  position: relative;
  padding-left: 48px;
  margin: 0 0 16px 0;
  line-height: 1.45;
}
.reveal ol li::before {
  content: counter(al, decimal-leading-zero);
  position: absolute; left: 0; top: 0;
  font-family: var(--r-code-font);
  font-size: 0.7em;
  color: var(--al-green);
  letter-spacing: 0.05em;
}
.reveal li strong { color: var(--al-white); }
/* nested lists shouldn't inherit the top-level rhythm */
.reveal li > ul, .reveal li > ol { margin-top: 12px; }
.reveal li > ul li { font-size: 0.86em; margin-bottom: 10px; }

/* -------------------------------------------------------------------------- */
/*  Free placement                                                             */
/* -------------------------------------------------------------------------- */
/* The deck is a fixed 1280x720 canvas that reveal scales as one piece, so a
   coordinate written here means the same thing on every screen and in the PDF:
   0,0 is the slide's top-left, 640,360 its centre, 1280,720 its bottom-right.
   That is what makes absolute placement behave the way it does in Keynote
   rather than the way it does on a web page.

   `.place` has to out-specify `.reveal section > *`, which relatively positions
   every direct child so it clears the grid texture — hence the class rather
   than a bare `position: absolute` in a style attribute. */
.reveal .place {
  position: absolute;
  margin: 0;
  z-index: 1;
}
/* Treat left/top as the element's centre instead of its top-left corner —
   handy for putting something *at* a point rather than measuring its box. */
.reveal .place.pin { transform: translate(-50%, -50%); }
.reveal .place img, .reveal img.place { display: block; max-width: 100%; }

/* -------------------------------------------------------------------------- */
/*  Title slide                                                                */
/* -------------------------------------------------------------------------- */
.reveal section.title-slide { display: flex; flex-direction: column; justify-content: center; }
/* .wordmark works on any slide, not just the title. It carries the official
   logo lockup (partials/logo-sprite.html), sized by height — the SVG keeps its
   own proportions.  */
.reveal .wordmark {
  color: var(--al-white);
  margin: 0 0 26px 0;
  line-height: 0;
}
.reveal .wordmark .al-logo { height: 58px; }
.reveal .title-slide .wordmark { margin-bottom: 40px; }
.reveal .title-slide .wordmark .al-logo { height: 78px; }
.reveal section.light .wordmark { color: var(--al-navy); }

/* Any <use> of the logo symbols: the colour follows the text colour and the
   width follows the artwork, so only a height is ever set. The ratios are the
   measured bounds of the two view boxes in partials/logo-sprite.html — a bare
   <svg> has no intrinsic size of its own to go by.  */
.reveal .logo-defs { display: none; }
.reveal .al-logo, .reveal .al-mark {
  display: block;
  width: auto;
  fill: currentColor;
}
.reveal .al-logo { aspect-ratio: 780.8 / 642.4; }  /* mark over wordmark */
.reveal .al-mark { aspect-ratio: 350.3 / 390.6; }  /* hexagon alone      */
.reveal .title-slide h1 {
  font-size: 3.3em;
  line-height: 1.02;
  margin-bottom: 24px;
}
.reveal .title-slide .lead {
  font-size: 0.86em;
  color: var(--al-muted);
  max-width: 28ch;
  margin: 0;
}
.reveal .title-slide .meta {
  margin-top: 56px;
  font-family: var(--r-code-font);
  font-size: 0.5em;
  letter-spacing: 0.12em;
  color: var(--al-muted);
  display: flex; gap: 28px; flex-wrap: wrap;
}
.reveal .title-slide .meta span { color: var(--al-green); }

/* -------------------------------------------------------------------------- */
/*  Section divider                                                            */
/* -------------------------------------------------------------------------- */
.reveal section.section-slide {
  display: flex; flex-direction: column; justify-content: center;
  background: transparent;
}
.reveal .section-slide .index {
  font-family: var(--r-code-font);
  font-size: 0.9em;
  color: var(--al-green);
  letter-spacing: 0.1em;
  margin-bottom: 18px;
}
.reveal .section-slide h2 {
  font-size: 2.7em;
  max-width: 16ch;
  border-left: 3px solid var(--al-green);
  padding-left: 28px;
  margin: 0;
}
.reveal .section-slide p { margin-top: 24px; padding-left: 31px; color: var(--al-muted); max-width: 40ch; }

/* -------------------------------------------------------------------------- */
/*  Outline / agenda                                                           */
/* -------------------------------------------------------------------------- */
.reveal .outline {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 64px;
  row-gap: 24px;
  margin-top: 14px;
  counter-reset: outline;
  list-style: none;
  padding: 0;
}
.reveal .outline.one-col { grid-template-columns: 1fr; }
.reveal .outline > li {
  counter-increment: outline;
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 20px;
  align-items: baseline;
  padding: 0 0 22px 0;
  margin: 0;
  border-bottom: 1px solid var(--al-line-soft);
}
.reveal .outline > li::before {
  content: counter(outline, decimal-leading-zero);
  position: static;
  font-family: var(--r-code-font);
  font-size: 0.62em;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--al-green);
  border: 0; width: auto; height: auto; transform: none;
}
.reveal .outline .t {
  display: block;
  font-family: var(--r-heading-font);
  font-weight: 600;
  font-size: 0.86em;
  line-height: 1.2;
  color: var(--al-white);
}
.reveal .outline .d {
  display: block;
  margin-top: 8px;
  font-size: 0.55em;
  line-height: 1.5;
  color: var(--al-muted);
}
/* Re-use the same outline between sections and mark where you are */
.reveal .outline > li.is-current .t { color: var(--al-green); }
.reveal .outline > li.is-current { border-bottom-color: var(--al-green-dim); }
.reveal .outline > li.is-done .t,
.reveal .outline > li.is-done .d,
.reveal .outline > li.is-done::before { opacity: 0.42; }

.reveal section.light .outline > li { border-bottom-color: var(--al-paper-line); }
.reveal section.light .outline > li::before { color: var(--al-green-deep); }
.reveal section.light .outline .t { color: var(--al-navy); }
.reveal section.light .outline .d { color: var(--al-muted-2); }

/* -------------------------------------------------------------------------- */
/*  Light slide variant                                                        */
/* -------------------------------------------------------------------------- */
/* The paper field bleeds past the slide box in every direction, so the fixed
   chrome — corner mark, logo, footer — sits on paper too rather than being cut
   in half by the edge of the slide. Overview mode (Esc) tiles the slides, where
   a bleeding background would paint over its neighbours, so it is dropped
   there.  */
.reveal section.light {
  background: var(--al-paper);
  color: var(--al-navy);
  box-shadow: 0 0 0 100vmax var(--al-paper);
  clip-path: inset(-100vmax);
}
.reveal.overview section.light { box-shadow: none; clip-path: none; }

/* Ink-dark type for a slide whose backdrop is a pale background image. It is a
   separate class from `light` on purpose: `light` fills the section itself, and
   a section with a background of its own hides reveal's image layer behind it.
   This one only ever sets colour. */
.reveal section.on-light,
.reveal section.on-light h1,
.reveal section.on-light h2,
.reveal section.on-light h3,
.reveal section.on-light h4 { color: var(--al-navy); }
.reveal section.on-light p,
.reveal section.on-light li { color: #26404f; }
.reveal section.on-light strong { color: var(--al-navy); }
.reveal section.on-light em,
.reveal section.on-light .accent,
.reveal section.on-light .eyebrow { color: var(--al-green-deep); }
.reveal section.on-light .eyebrow::before { background: var(--al-green-deep); }
.reveal section.on-light ol li::before { color: var(--al-green-deep); }

/* The same class on the element instead of the section, for the one-off: a
   slide that is dark overall but has a single block sitting on a pale patch of
   its background image. The rules above all read `section.on-light X`, so they
   only reach a descendant — this reaches the element wearing the class.

       <p class="eyebrow on-light">…</p>

   Two classes out-specify the one in `.reveal .eyebrow`, so it wins wherever it
   is put, and it costs nothing on a slide that never uses it. */
.reveal .eyebrow.on-light { color: var(--al-green-deep); }
.reveal .eyebrow.on-light::before { background: var(--al-green-deep); }

/* Links, same treatment `light` already gets below. `light` paints its own
   background, so it was covered; `on-light` only sets colour and left links on
   the accent green, which is a colour for glowing on navy — over a pale
   backdrop it is about 1.7:1 and the hover to white is the backdrop itself.
   Deep green rests at 5.1:1, and hover runs to the ink colour: on dark slides
   it runs to white because white is that field's high-contrast end, and on a
   pale one that end is navy. Same rule, opposite direction. */
.reveal section.on-light a {
  color: var(--al-green-deep);
  border-bottom-color: var(--al-green-deep);
}
.reveal section.on-light a:hover {
  color: var(--al-red);
  border-bottom-color: var(--al-red);
}
/* Overview is for navigating, not presenting — the fixed chrome only gets
   in the way of the tiles.  */
.reveal.overview .slide-frame,
.reveal.overview .deck-logo,
.reveal.overview .deck-footer { opacity: 0; }
.reveal section.light h1,
.reveal section.light h2,
.reveal section.light h3,
.reveal section.light h4 { color: var(--al-navy); }
.reveal section.light p { color: #3B4E5B; }
.reveal section.light ul li { color: #3B4E5B; }
.reveal section.light ol li::before { color: var(--al-green-deep); }

/* Markers on a pale slide. The default disc is filled, so it takes the deep
   green as a background; the square is outlined and takes it as a border. The
   variants carry one class more than the generic rule and come after it, which
   is what keeps the generic rule from repainting them. */
.reveal section:is(.light, .on-light) ul li::before { background: var(--al-green-deep); }
.reveal section:is(.light, .on-light) .bullets-rectangle li::before {
  background: none;
  border-color: var(--al-green-deep);
}
.reveal section:is(.light, .on-light) .bullets-ice li::before { background: var(--al-green-deep); }
.reveal section.light strong { color: var(--al-navy); }
.reveal section.light em { color: var(--al-green-deep); }
.reveal section.light a { color: var(--al-green-deep); border-bottom-color: var(--al-green-deep); }
.reveal section.light a:hover { color: var(--al-navy); border-bottom-color: var(--al-navy); }
.reveal section.light .accent { color: var(--al-green-deep); }
.reveal section.light .eyebrow { color: var(--al-green-deep); }
.reveal section.light .eyebrow::before { background: var(--al-green-deep); }
/* A quote is the one block that sets all three of its colours itself — white
   text, green quote mark, yellow cite — so none of them inherit the pale-slide
   ink and all three have to be restated. `on-light` is in here with `light`
   because a quote over a pale *image* is in the same position as one on paper:
   white on it is invisible, and the yellow cite is worse. */
.reveal section:is(.light, .on-light) blockquote { color: var(--al-navy); }
.reveal section:is(.light, .on-light) blockquote::before,
.reveal section:is(.light, .on-light) blockquote cite { color: var(--al-green-deep); }
.reveal section.light .section-slide h2,
.reveal section.light.section-slide h2 { border-left-color: var(--al-green-deep); }
/* Custom properties inherit, so a diagram inside a light slide is drawn with
   these without knowing anything about where it sits. */
.reveal section.light {
  --mm-node-bg:      var(--al-white);
  --mm-node-line:    var(--al-green-deep);
  --mm-node-text:    var(--al-navy);
  --mm-alt-bg:       #E7EDF0;
  --mm-alt-line:     var(--al-blue);
  --mm-line:         var(--al-muted-2);
  --mm-text:         var(--al-navy);
  --mm-cluster-bg:   rgba(36, 55, 70, 0.04);
  --mm-cluster-line: var(--al-paper-line);
  --mm-label-bg:     var(--al-paper);
  --mm-1: var(--al-green-deep);
}

/* -------------------------------------------------------------------------- */
/*  Columns                                                                     */
/* -------------------------------------------------------------------------- */
.reveal .cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: start;
  margin-top: 10px;
}
.reveal .cols.c3 { grid-template-columns: repeat(3, 1fr); gap: 34px; }
.reveal .cols.c4 { grid-template-columns: repeat(4, 1fr); gap: 26px; }
.reveal .cols .col { min-width: 0; }
.reveal .cols .col > *:first-child { margin-top: 0; }
.reveal .cols .col > *:last-child  { margin-bottom: 0; }
/* smaller measure inside 3- and 4-up grids keeps the text readable */
.reveal .cols.c3 .col p, .reveal .cols.c3 .col li { font-size: 0.78em; line-height: 1.55; }
.reveal .cols.c4 .col p, .reveal .cols.c4 .col li { font-size: 0.68em; line-height: 1.55; }

/* Cards */
.reveal .card {
  background: var(--al-surface);
  border: 1px solid var(--al-line-soft);
  border-top: 2px solid var(--al-green);
  border-radius: 4px;
  padding: 30px 30px 32px;
  height: 100%;
  display: flex;
  flex-direction: column;
}
.reveal .card h3 {
  font-size: 1.02em;
  margin: 0 0 14px 0;
  color: var(--al-white);
  line-height: 1.15;
}
.reveal .card p { font-size: 0.72em; line-height: 1.6; color: var(--al-muted); margin: 0; }
.reveal .cols.c3 .card p { font-size: 0.68em; }
.reveal .card .tag {
  font-family: var(--r-code-font);
  font-size: 0.46em; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--al-green); display: block; margin-bottom: 18px;
}
.reveal section.light .card {
  background: var(--al-white);
  border-color: var(--al-paper-line);
  border-top-color: var(--al-green-deep);
}
.reveal section.light .card h3 { color: var(--al-navy); }
.reveal section.light .card p { color: var(--al-muted-2); }
.reveal section.light .card .tag { color: var(--al-green-deep); }

/* Secondary-colour card accents — for infographic-style groupings only */
.reveal .card.c-blue        { border-top-color: var(--al-blue); }
.reveal .card.c-blue .tag   { color: var(--al-light-blue); }
.reveal .card.c-orange      { border-top-color: var(--al-orange); }
.reveal .card.c-orange .tag { color: var(--al-orange); }
.reveal .card.c-sand        { border-top-color: var(--al-sand); }
.reveal .card.c-sand .tag   { color: var(--al-sand); }
.reveal .card.c-red         { border-top-color: var(--al-red); }
.reveal .card.c-red .tag    { color: var(--al-red); }

/* -------------------------------------------------------------------------- */
/*  Project card                                                              */
/* -------------------------------------------------------------------------- */
/* One slide, one project — the slide a deck repeats once per project, so the
   room learns where to look once and then stops having to. Name, one sentence,
   who is answerable, what it is written in, code and docs, and a QR to point a
   phone at:

     <section class="project">
       <p class="eyebrow">Project</p>
       <h2>Yaook</h2>
       <p class="lede">One sentence about what it does.</p>
       <div class="project-body">
         <div class="project-facts">
           <div class="fact">      ...a .fact-k label and its content   </div>
           <div class="fact wide"> ...one that spans both columns       </div>
         </div>
         <figure class="qr-card">
           <div class="qr" data-qr="https://…"></div>
           <figcaption><a href="https://…">the URL, spelled out</a></figcaption>
         </figure>
       </div>
     </section>

   The facts are a two-column grid so "maintained by" and "written in" sit side
   by side and the link list, which is the widest thing on the slide, takes
   `.wide` and spans both. Every block is optional: drop one and the rest
   closes up, which is what lets one project have five maintainers and the next
   have none without a second layout.

   The QR column is `auto`, so the code keeps its size and the prose takes
   whatever is left rather than the two negotiating. */
/* The logo leads. A project is recognised by its artwork before its name, so
   the slot is sized to be read from the back of the room rather than to sit
   politely beside the heading — and the name block takes what is left.

   The <img> is capped on both axes and set to `contain`, which is what lets a
   wide lockup and a square mark share one slot: whichever dimension binds
   first wins, and neither is ever stretched. */
.reveal .project-head {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 34px;
  align-items: center;
  margin-bottom: 30px;
}
.reveal .project-logo {
  display: flex; align-items: center; justify-content: center;
  width: 260px; height: 132px;
}
.reveal .project-logo img,
.reveal .project-logo svg {
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
}
/* No logo? The block goes and the name takes the full width, without the
   slide needing a second class. */
.reveal .project-head:not(:has(.project-logo)) { grid-template-columns: 1fr; }

.reveal .project-name h2 { margin-bottom: 12px; }
.reveal .project .lede {
  font-size: 0.62em;
  line-height: 1.45;
  color: var(--al-muted);
  max-width: 30em;
  margin: 0;
}
/* The QR stands against the whole slide, not against the facts alone: it is
   the one thing on here aimed at the room's phones rather than at the room,
   so it gets its own column for the full height and everything else shares
   the other. */
.reveal .project-body {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 48px;
  align-items: center;
}
.reveal .project-facts {
  display: grid;
  grid-template-columns: 1.3fr 1fr 0.7fr;
  gap: 22px 32px;
  align-items: start;
}
.reveal .project-facts .fact.wide { grid-column: 1 / -1; }

/* The label above a block — the deck's mono kicker, one size down, so a fact
   is read as a caption and not as another heading. */
.reveal .project .fact-k {
  font-family: var(--r-code-font);
  font-size: 0.36em;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--al-green);
  margin: 0 0 10px 0;
}

/* Who is answerable. A name is the thing being looked up, so it carries the
   weight; the role sits under it in the secondary grey and wraps freely.
   No bullets: these are a set of names, not a sequence of points, and the
   deck's green dot would read as one. */
.reveal .project .people { list-style: none; margin: 0; padding: 0; }
.reveal .project .people li {
  display: flex; flex-direction: column;
  margin: 0 0 9px 0;
  padding-left: 0;
  line-height: 1.25;
}
.reveal .project .people li::before { content: none; }
.reveal .project .people li:last-child { margin-bottom: 0; }
.reveal .project .people .who {
  font-family: var(--r-heading-font); font-weight: 600;
  font-size: 0.52em; color: var(--al-white);
}
.reveal .project .people .role {
  font-size: 0.4em; color: var(--al-muted);
}

/* Languages as badges rather than a comma list: the count is the information
   as much as the names are, and badges make it countable at a glance. */
.reveal .project .langs {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 0; font-size: 1em;
}
/* The licence is metadata like the languages beside it, so it takes the same
   pill — but in white rather than Green, or the eye reads it as one more
   language in the list next door. */
.reveal .project .lang.lic {
  color: var(--al-white);
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.22);
}
.reveal section.light .project .lang.lic {
  color: var(--al-navy);
  background: rgba(36, 55, 70, 0.06);
  border-color: var(--al-paper-line);
}
.reveal .project .lang {
  font-family: var(--r-code-font);
  font-size: 0.4em;
  line-height: 1;
  padding: 8px 12px;
  border-radius: 999px;
  color: var(--al-green);
  background: rgba(81, 224, 100, 0.08);
  border: 1px solid var(--al-line);
  white-space: nowrap;
}

/* The QR patch. Its own light field, never inverted — parts/deck-qr.html says
   why. --qr-bg and --qr-fg are what the renderer reads; setting them here
   rather than in the script is what lets a light slide restyle a code. */
.reveal .project .qr-card {
  --qr-bg: var(--al-white);
  --qr-fg: var(--al-navy);
  margin: 0;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
}
.reveal .qr {
  width: 190px; height: 190px;
  background: var(--qr-bg);
  border-radius: 8px;
  padding: 10px;
  box-sizing: border-box;
}
.reveal .qr svg { display: block; width: 100%; height: 100%; }
.reveal .project .qr-card figcaption {
  font-family: var(--r-code-font);
  font-size: 0.38em;
  letter-spacing: 0.04em;
  color: var(--al-green);
  text-align: center;
}

/* What the project is *about*, under the code — the same block, and the same
   controlled vocabulary, that theme/alasca-master.css documents at length.
   Here it is a second row in the QR's column rather than an absolute box:
   `grid-column: 2` is what keeps it under the patch instead of letting it wrap
   back under the facts, which is where a third child would otherwise land.

     <div class="project-tags">
       <p class="fact-k">Context</p>
       <ul class="ptags"><li>Kubernetes</li>…</ul>
     </div>

   Filled Green, not the outlined pill the languages take: these are read at a
   glance and from the back of the room, and the fill is what stops the eye
   filing them as one more language list. */
/* A second row in the QR's column. The facts then span both rows and the
   patch stops centring against them — without that, row 1 is as tall as the
   facts, the code floats in the middle of it and the tags land at the foot of
   the slide, on top of the page number. Scoped with :has() so a project slide
   with no tags keeps the centred patch it has always had. */
.reveal .project-body:has(> .project-tags) {
  align-items: start;
  /* `auto 1fr`, not two autos: a spanning item inflates auto rows to share its
     height, which pushed the tags halfway down the slide. An fr row absorbs
     that slack instead, so row 1 stays exactly as tall as the QR patch. */
  grid-template-rows: auto 1fr;
  row-gap: 0;
}
/* :first-child, not .project-main: whatever the prose column is called, it is
   the one that has to take both rows. */
.reveal .project-body:has(> .project-tags) > *:first-child {
  grid-row: 1 / span 2;
}
.reveal .project-body > .project-tags {
  grid-column: 2; grid-row: 2;
  margin-top: 20px;
  max-width: 210px;                 /* the QR patch plus its padding */
  justify-self: center;
}
.reveal .project .ptags {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 8px;
}
.reveal .project .ptags li::before { content: none; }
.reveal .project .ptags li {
  margin: 0; padding: 7px 12px;
  font-size: 0.4em; line-height: 1;
  border-radius: 999px;
  color: var(--al-navy);
  background: var(--tag-accent, var(--al-green));
  white-space: nowrap;
}
/* Four families — platform, estate, operating model, discipline — so a colour
   means the same thing on every project slide. theme/alasca-master.css names
   the terms that belong to each. */
.reveal .project .ptags .c-blue   { --tag-accent: var(--al-light-blue); }
.reveal .project .ptags .c-sand   { --tag-accent: var(--al-sand); }
.reveal .project .ptags .c-orange { --tag-accent: var(--al-orange); }
/* On paper the Green has to go deep to carry white text; the other three
   accents are light enough to keep both the fill and the Navy they wear on the
   dark field, so `:not([class])` leaves them alone. */
.reveal section.light .project .ptags li:not([class]) {
  color: var(--al-white);
  background: var(--al-green-deep);
}

/* On paper the patch would be a white square on an almost-white field, so it
   takes a hairline to keep its edge, and the modules go to the deep Green that
   is the one Green legible on white. */
.reveal section.light .project .qr-card,
.reveal section.project.light .qr-card {
  --qr-fg: var(--al-navy);
}
.reveal section.light .qr { border: 1px solid var(--al-paper-line); }
.reveal section.light .project .fact-k,
.reveal section.light .project .qr-card figcaption { color: var(--al-green-deep); }
.reveal section.light .project .people .who { color: var(--al-navy); }
.reveal section.light .project .people .role,
.reveal section.light .project .lede { color: var(--al-muted-2); }
.reveal section.light .project .lang {
  color: var(--al-green-deep);
  background: rgba(21, 122, 43, 0.07);
  border-color: var(--al-paper-line);
}

/* -------------------------------------------------------------------------- */
/*  Icon tiles                                                                  */
/* -------------------------------------------------------------------------- */
/* A grid of small boxes, each an icon over a name and a line about it — the
   slide for "here is the whole set at once": the parts of a stack, the working
   groups, what the project ships. Four, six or eight of them.

     <div class="tiles t6">
       <div class="tile">
         <i class="fa-solid fa-cube ic" aria-hidden="true"></i>
         <h3>Compute</h3>
         <p>One line about it.</p>
       </div>
       ...five more
     </div>

   The count is a class because the grid has to know it: `.t4` is four across,
   `.t6` three across in two rows, `.t8` four across in two rows. Six as three
   across is the point of `.t6` — six in a four-column grid leaves two orphans
   on the second row, which is the one arrangement that always looks like a
   mistake. Anything other than 4, 6 or 8 wants `.cols` and `.card` instead.

   `.ic` is the green tile the closing slide puts behind its icons; here it
   takes its size from the density, so a slide never carries a px value. The
   <p> is optional — leave it out and the grid reads as a badge wall. The four
   card accents work here too (`.c-blue`, `.c-orange`, `.c-sand`, `.c-red`);
   they recolour the icon, which is the only place a tile is allowed colour. */
.reveal .tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 22px;
  align-items: stretch;
  margin-top: 10px;
}
.reveal .tiles.t4 { grid-template-columns: repeat(4, 1fr); }
.reveal .tiles.t6 { grid-template-columns: repeat(3, 1fr); }
.reveal .tiles.t8 { grid-template-columns: repeat(4, 1fr); gap: 18px; }

.reveal .tile {
  --tile-accent: var(--al-green);
  background: var(--al-surface);
  border: 1px solid var(--al-line-soft);
  border-radius: 4px;
  padding: 24px 22px 26px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.reveal .tile .ic {
  width: 46px; height: 46px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 10px;
  font-size: 21px;
  color: var(--tile-accent);
  background: color-mix(in srgb, var(--tile-accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--tile-accent) 26%, transparent);
  margin-bottom: 18px;
}
.reveal .tile h3 {
  font-size: 0.84em;
  line-height: 1.15;
  color: var(--al-white);
  margin: 0 0 9px 0;
}
.reveal .tile p {
  font-size: 0.56em;
  line-height: 1.5;
  color: var(--al-muted);
  margin: 0;
}
.reveal .tile > *:last-child { margin-bottom: 0; }

/* Denser grids: the box loses width, so the icon and the type come down with
   it rather than the text wrapping to five lines. */
.reveal .tiles.t8 .tile { padding: 18px 16px 20px; }
.reveal .tiles.t8 .ic {
  width: 38px; height: 38px; border-radius: 8px; font-size: 17px; margin-bottom: 13px;
}
.reveal .tiles.t8 .tile h3 { font-size: 0.7em; margin-bottom: 7px; }
.reveal .tiles.t8 .tile p  { font-size: 0.48em; }

/* An optional heading per column. They are direct children of the grid, one
   per column, ahead of the tiles — grid auto-flow then fills the first row
   with them and the tiles start on the second:

     <div class="tiles t6">
       <p class="col-head">Today</p>
       <p class="col-head">With Yaook</p>
       <p class="col-head">What it costs</p>
       ...six .tile blocks
     </div>

   So the count has to match the column count: three for .t6, four for .t4 and
   .t8. Fewer and the tiles shuffle up into the gap, which is the one way to
   get this wrong and is visible immediately.

   `align-self: end` sits the label on the tiles below it rather than floating
   in the middle of a row stretched to the tallest heading. */
.reveal .tiles .col-head {
  align-self: end;
  margin: 0 0 2px 0;
  font-family: var(--r-code-font);
  font-size: 0.36em;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--al-green);
}
/* The heading row should sit close to what it labels, closer than the tiles
   sit to each other. */
.reveal .tiles:has(.col-head) { row-gap: 10px; }
.reveal section.light .tiles .col-head { color: var(--al-green-deep); }

/* Filling down instead of across. By default the grid is row-major — the
   tiles run left to right and wrap onto the second row — which is right when
   the set is just a set. When the columns *mean* something, and especially
   when they carry a .col-head, the two tiles under a heading should be the
   two that belong to it. `.flow-down` turns the grid column-major:

     <div class="tiles t6 flow-down">
       <p class="col-head">Runs it</p>
       <div class="tile">…</div>          first two go under "Runs it"
       <div class="tile">…</div>
       <p class="col-head">Keeps it running</p>
       <div class="tile">…</div>
       <div class="tile">…</div>
       …
     </div>

   Note the source order changes with the class, and this is the one thing to
   get right: row-major wants every .col-head first, then all the tiles;
   column-major wants each column's heading followed by that column's tiles.
   Writing one order with the other class puts a heading in the middle of the
   grid, which is at least obvious.

   Fragments follow source order, so a `.flow-down` grid whose tiles carry
   `class="fragment"` also *reveals* down each column and then across, without
   a single data-fragment-index.

   Explicit rows are what make column flow work at all — grid needs to know
   when to start the next column. Two, because that is what .t6 and .t8 are;
   .t4 is a single row and the class does nothing there. */
.reveal .tiles.flow-down {
  grid-auto-flow: column;
  grid-template-rows: repeat(2, auto);
}
.reveal .tiles.flow-down:has(.col-head) {
  grid-template-rows: auto repeat(2, auto);
}

/* The card accents, on the icon only */
.reveal .tile.c-blue   { --tile-accent: var(--al-light-blue); }
.reveal .tile.c-orange { --tile-accent: var(--al-orange); }
.reveal .tile.c-sand   { --tile-accent: var(--al-sand); }
.reveal .tile.c-red    { --tile-accent: var(--al-red); }

.reveal section.light .tile {
  --tile-accent: var(--al-green-deep);
  background: var(--al-white);
  border-color: var(--al-paper-line);
}
.reveal section.light .tile h3 { color: var(--al-navy); }
.reveal section.light .tile p  { color: var(--al-muted-2); }

/* -------------------------------------------------------------------------- */
/*  Big stats                                                                   */
/* -------------------------------------------------------------------------- */
.reveal .stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 48px; margin-top: 16px; }
.reveal .stats.s2 { grid-template-columns: repeat(2, 1fr); }
.reveal .stats.s4 { grid-template-columns: repeat(4, 1fr); gap: 32px; }
.reveal .stat .num {
  font-family: var(--r-heading-font);
  font-weight: 700;
  font-size: 2.6em;
  line-height: 1;
  color: var(--al-green);
  letter-spacing: -0.02em;
}
.reveal .stat .lbl {
  font-family: var(--r-code-font);
  font-size: 0.5em; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--al-muted); margin-top: 14px; display: block; line-height: 1.4;
}
.reveal section.light .stat .num { color: var(--al-green-deep); }
.reveal section.light .stat .lbl { color: var(--al-muted-2); }

/* -------------------------------------------------------------------------- */
/*  Quote                                                                       */
/* -------------------------------------------------------------------------- */
.reveal blockquote {
  border: none;
  background: none;
  box-shadow: none;
  font-family: var(--r-heading-font);
  font-weight: 500;
  font-size: 1.5em;
  line-height: 1.32;
  color: var(--al-white);
  width: 100%;
  padding: 0;
  max-width: 48ch;
}
.reveal blockquote::before {
  content: "\201C";
  color: var(--al-yellow);
  font-size: 1.4em; line-height: 0; vertical-align: -0.35em; margin-right: 6px;
}
.reveal blockquote cite {
  display: block;
  font-family: var(--r-code-font);
  font-size: 0.42em;
  font-style: normal;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--al-yellow);
  margin-top: 30px;
}

/* -------------------------------------------------------------------------- */
/*  Code                                                                        */
/* -------------------------------------------------------------------------- */
.reveal pre { width: 100%; box-shadow: none; font-size: 0.5em; margin: 0; }
.reveal pre code {
  background: var(--al-navy-2);
  border: 1px solid var(--al-line-soft);
  border-radius: 4px;
  padding: 22px 24px;
  max-height: 460px;
  line-height: 1.5;
}
.reveal code { font-family: var(--r-code-font); }
.reveal :not(pre) > code {
  background: var(--al-navy-2);
  color: var(--al-green);
  padding: 2px 8px; border-radius: 3px; font-size: 0.85em;
}
.reveal section.light :not(pre) > code { background: #E7EDEA; color: var(--al-green-deep); }

/* -------------------------------------------------------------------------- */
/*  Mermaid diagrams                                                           */
/* -------------------------------------------------------------------------- */
/* The colours are the --mm-* tokens above; this is only the box the SVG lands
   in. A diagram that needs more room says so on its own slide:
   <section style="--mm-height: 560px">  */
.reveal .mermaid {
  display: block;
  margin: 0 auto var(--r-block-margin);
  text-align: center;
  line-height: 1;
  white-space: normal;      /* also correct once a <pre class="mermaid"> renders */
}
.reveal .mermaid svg {
  max-width: 100%;
  height: auto;
  max-height: var(--mm-height, 460px);
}
/* Until it is drawn, a diagram is still its own source text. The class is set
   by the renderer and only when there is something to draw, so a deck with no
   diagram in it never hides anything. */
.mermaid-pending .reveal .mermaid { visibility: hidden; }
/*  Images                                                                     */
/* -------------------------------------------------------------------------- */
/* reveal's core stylesheet gives every <img> a white field and a grey border —
   the scanned-photo look of its own themes, which has nothing to do with this
   one. Undo it once, here, so a picture on a slide is just the picture.

   --img-height is the tallest an image draws: a portrait photograph would
   otherwise push the slide off its own canvas. Raise it per slide when a
   picture is the point of one: <section style="--img-height: 560px">  */
.reveal section img {
  display: block;
  margin: 0 auto var(--r-block-margin);
  max-width: 100%;
  max-height: var(--img-height, 420px);
  height: auto;
  background: none;
  border: 0;
  box-shadow: none;
  border-radius: 4px;
}

/* A picture with something to say about it. The caption is set in the mono
   kicker face, like .eyebrow, so it reads as a label rather than as body copy. */
.reveal figure { margin: 0 0 var(--r-block-margin); text-align: center; }
.reveal figure img { margin-bottom: 0; }
.reveal figcaption {
  font-family: var(--r-code-font);
  font-size: 0.5em;
  letter-spacing: 0.08em;
  color: var(--al-muted);
  margin-top: 14px;
}
.reveal section.light figcaption { color: var(--al-muted-2); }

/* A screenshot needs an edge, or it bleeds into the field it sits on. */
.reveal img.framed,
.reveal figure.framed img {
  border: 1px solid var(--al-line-soft);
  background: var(--al-navy-2);
}
.reveal section.light img.framed,
.reveal section.light figure.framed img {
  border-color: var(--al-paper-line);
  background: var(--al-white);
}

/* -------------------------------------------------------------------------- */
/*  Tables                                                                      */
/* -------------------------------------------------------------------------- */
.reveal table { border-collapse: collapse; width: 100%; font-size: 0.7em; }
.reveal table th {
  font-family: var(--r-code-font);
  font-size: 0.78em; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--al-green); text-align: left; padding: 12px 16px;
  border-bottom: 1px solid var(--al-line);
}
.reveal table td { padding: 14px 16px; border-bottom: 1px solid var(--al-line-soft); color: #DCE5EB; }
.reveal section.light table td { color: #3B4E5B; border-color: var(--al-paper-line); }
.reveal section.light table th { border-color: var(--al-paper-line); color: var(--al-green-deep); }

/* -------------------------------------------------------------------------- */
/*  Swatches (brand palette reference)                                        */
/* -------------------------------------------------------------------------- */
.reveal .swatches { display: grid; grid-template-columns: repeat(4, 1fr); gap: 22px; margin-top: 12px; }
.reveal .swatches.s7 { grid-template-columns: repeat(7, 1fr); gap: 16px; }
.reveal .swatch .chip { height: 74px; border-radius: 4px; border: 1px solid var(--al-line-soft); }
.reveal .swatch .n {
  display: block; margin-top: 12px;
  font-family: var(--r-heading-font); font-weight: 600; font-size: 0.5em; color: var(--al-white);
}
.reveal .swatch .h {
  display: block; margin-top: 4px;
  font-family: var(--r-code-font); font-size: 0.42em; letter-spacing: 0.06em; color: var(--al-muted);
}
.reveal section.light .swatch .n { color: var(--al-navy); }
.reveal section.light .swatch .h { color: var(--al-muted-2); }

/* Small footnote paragraph under a block */
.reveal .note {
  margin-top: 34px;
  font-size: 0.6em;
  line-height: 1.6;
  color: var(--al-muted);
  max-width: 66ch;
}
.reveal section.light .note { color: var(--al-muted-2); }

/* -------------------------------------------------------------------------- */
/*  Tinting a flat logo                                                       */
/* -------------------------------------------------------------------------- */
/* A logo drawn in one flat colour — the ALASCA project marks in img/projects/
   are black paths with no `fill` attribute at all — is the wrong colour on
   half the slides in a deck. Rather than keep a second file per colour:

     <img class="tint-white" src="../../img/projects/ALASCA-Krake-black.svg"
          alt="Krake" />

   `brightness(0)` crushes every colour in the artwork to black whatever it
   started as, and `invert(1)` then lifts the whole thing to white. Going
   through black first is what makes it safe on artwork you have not looked at:
   plain `invert(1)` would turn a coloured logo into its opposite instead of
   into white. It only suits flat, single-colour marks — run it over a
   photograph or a two-tone lockup and you get a silhouette, which is a fine
   thing to want and a bad thing to be surprised by.

   These work on <img>, so nothing about the file or the markup changes. A
   filter cannot reach *into* an external SVG, which is also why the fills in
   those files stay unreachable from here — see .tint below for arbitrary
   colours. */
.reveal img.tint-white { filter: brightness(0) invert(1); }
.reveal img.tint-black { filter: brightness(0); }

/* Any colour, not just the two ends. The artwork becomes a mask and the
   element paints its own `color` through it, so the mark takes a brand token
   like anything else:

     <span class="tint" role="img" aria-label="Krake"
           style="mask-image: url(../../img/projects/ALASCA-Krake-black.svg);
                  -webkit-mask-image: url(../../img/projects/ALASCA-Krake-black.svg)">
     </span>

   The image goes on the element rather than into a custom property here, and
   that is not a style choice: a relative url() inside a custom property is
   resolved against the stylesheet that used it, not the page — Chrome turns
   ../../img/… into theme/img/… and the mark silently disappears. On the
   element it resolves against the deck, the same ../../ everything else on a
   slide is written with. Both spellings, because Safari wants the prefix.

   The shape comes from the file's transparency, so this wants artwork on a
   transparent ground — which the project marks are. It has no intrinsic size,
   having no content: give it a box, or drop it in one that is already sized,
   like the project slide's .project-logo. */
.reveal .tint {
  display: block;
  width: 100%; height: 100%;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center;  mask-position: center;
  -webkit-mask-size: contain;     mask-size: contain;
}

/* -------------------------------------------------------------------------- */
/*  Link grid (closing slide)                                                  */
/* -------------------------------------------------------------------------- */
.reveal .link-groups { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; margin-top: 8px; }
.reveal .link-group h3 {
  font-family: var(--r-code-font);
  font-size: 0.46em; font-weight: 500; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--al-muted); margin: 0 0 18px 0;
}
.reveal .links { list-style: none; padding: 0; margin: 0; }
.reveal .links li {
  padding: 0 0 10px 0;
  margin: 0 0 10px 0;
  border-bottom: 1px solid var(--al-line-soft);
  display: grid;
  grid-template-columns: auto auto 1fr;   /* icon · label · address */
  align-items: center;
  column-gap: 14px;
}
.reveal .links li::before { content: none; }

/* Icon tiles. The glyph is a Font Awesome <i>, so the tile is a flex box that
   centres it rather than a box sized to artwork: inline-flex overrides the
   inline-block Font Awesome sets, and the fixed width beats its 1.25em. */
.reveal .links .ic {
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px;
  font-size: 15px;
  color: var(--al-green);
  background: rgba(81, 224, 100, 0.08);
  border: 1px solid var(--al-line);
}
.reveal .links li:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.reveal .links .k {
  font-family: var(--r-heading-font); font-weight: 600;
  font-size: 0.58em; color: var(--al-white); white-space: nowrap;
}
.reveal .links a {
  font-family: var(--r-code-font);
  font-size: 0.44em;
  letter-spacing: 0.01em;
  color: var(--al-green);
  border-bottom: 0;
  word-break: break-all;
  text-align: right;
}
.reveal .links a:hover { color: var(--al-white); }
.reveal section.light .links li { border-bottom-color: var(--al-paper-line); }
.reveal section.light .links .k { color: var(--al-navy); }
.reveal section.light .links a { color: var(--al-green-deep); }
.reveal section.light .links .ic {
  color: var(--al-green-deep);
  background: rgba(21, 122, 43, 0.07);
  border-color: rgba(21, 122, 43, 0.22);
}

/* -------------------------------------------------------------------------- */
/*  Störer — the ad-world eye-catcher, for one call to action per deck         */
/* -------------------------------------------------------------------------- */
/* A Störer earns attention by breaking the grid: tilted where the deck is
   square, bright where it is navy, the only round thing on a page of straight
   rules. All three are deliberate — straightening it out is what kills it.

   The outline is a twelve-spike crystal. A starburst is the classic Störer
   shape, and alternating the spike lengths turns the same figure into frost,
   which is the one ornament this deck can wear without explaining itself.

   Markup — the two elements are both load-bearing, so keep them:

     <div class="stoerer-slot place pin fragment" style="left: …; top: …">
       <a class="stoerer" href="mailto:…">
         <p class="stoerer-kicker">…</p>
         <p class="stoerer-shout">…</p>
         <p class="stoerer-address">…</p>
       </a>
     </div>

   It is absolutely placed, so it lies *over* the slide's text and no line
   reflows to accommodate it. Never narrow a column to make room: rebreaking
   every line on the slide is the one thing a Störer must not do. Put it last in
   the section so `.fragment` lands it after the content it answers to.

   `--stoerer-size` is the only dial. Everything inside — type, rules, margins,
   the glow — is expressed in em against a font-size derived from it, so setting
   the size on the slot resizes the badge whole rather than leaving small type
   marooned in a big crystal:

     <div class="stoerer-slot place pin fragment"
          style="--stoerer-size: 560px; left: …; top: …">

   Remember the box is square and `.pin` measures from its centre, so half the
   size has to fit on every side of the point you name. */
.reveal .stoerer-slot {
  --stoerer-size: 280px;
  /* The internal type scale. /10 makes the base 28px at the default size, which
     is the number every em below was chosen against. */
  font-size: calc(var(--stoerer-size) / 10);
  /* `.place` already gives 1, which beats the bullets' auto; stated so nothing
     later has to guess that the badge belongs on top. */
  z-index: 5;
  /* clip-path is applied *after* filter on the same element, so a drop-shadow
     on the <a> below would be cut away with the corners it exists to escape —
     hence the shadow lives up here and traces the clipped child. The dark halo
     is structural, not decoration: text runs under the badge, and this is what
     keeps the two from touching. */
  filter: drop-shadow(0 0.36em 0.79em rgba(8, 22, 42, 0.7))
          drop-shadow(0 0 0.64em rgba(123, 202, 239, 0.35));
}
.reveal a.stoerer {
  display: grid; place-content: center;
  width: var(--stoerer-size); height: var(--stoerer-size);
  border-bottom: 0; padding: 0;   /* a badge is a shape, not an underlined word */
  text-align: center;
  /* Frost, lit from the top-left so the crystal has a direction. The badge is a
     large field, and the brand keeps green to typographic accents only, so this
     reaches for Light Blue rather than the primary. */
  background: radial-gradient(circle at 34% 28%,
    var(--al-white) 0%, #D6F0FD 38%, var(--al-light-blue) 78%, var(--al-blue) 100%);
  /* Tips alternate 50%/44% of the box, valleys sit at 38% — deep enough to be
     spiky, shallow enough that the text block's corners (~101px from centre)
     clear them (~106px). Deepen the valleys and the type starts getting bitten. */
  clip-path: polygon(
    50% 0%,     59.8% 13.3%, 72% 11.9%,   76.9% 23.1%,
    93.3% 25%,  86.7% 40.2%, 94% 50%,     86.7% 59.8%,
    93.3% 75%,  76.9% 76.9%, 72% 88.1%,   59.8% 86.7%,
    50% 100%,   40.2% 86.7%, 28% 88.1%,   23.1% 76.9%,
    6.7% 75%,   13.3% 59.8%, 6% 50%,      13.3% 40.2%,
    6.7% 25%,   23.1% 23.1%, 28% 11.9%,   40.2% 13.3%
  );
  /* Tilt via `rotate`, not `transform`: the slot centres itself with
     `transform: translate(-50%,-50%)` from `.pin`, and `rotate` is applied after
     `transform`, so tilting the *slot* would swing it around the un-centred
     origin on a ~190px arc — badge off target, animation orbiting. Nested, each
     transform is about its own element's centre. */
  rotate: -9deg;
  animation: stoerer-rock 4.5s ease-in-out infinite;
  transition: scale .18s ease;
}
/* Enough movement to catch peripheral vision, not enough to watch. */
@keyframes stoerer-rock {
  0%, 100% { rotate: -9deg; }
  50%      { rotate: -4deg; }
}
/* The animation owns `rotate`, so hover has to reach for another property or a
   running animation would win. */
.reveal a.stoerer:hover { scale: 1.06; }

/* Inside the crystal: kicker, the shout, the address you act on. Everything is
   proportional to --stoerer-size so the badge resizes whole, but the two kinds
   of unit are not interchangeable here:

     · font-size in em — em resolves against the *parent's* font-size, which is
       the base the slot derived, so 0.464em is 13px at the default. Correct.
     · every other length via calc() on --stoerer-size — because em on these
       elements resolves against their *own* font-size, and the three of them
       have three different ones. `max-width: 6.43em` gave the kicker a 84px
       column and the shout a 186px one instead of the 180px both wanted.

   The ratios are the px values this was drawn at over the 280px default: a
   180px column, 5/7px shout margins, 6px above the address rule. */
.reveal a.stoerer > * {
  margin: 0;
  color: var(--al-navy-2);
  /* the spikes eat the corners, so the text keeps to the middle */
  max-width: calc(var(--stoerer-size) * 0.643);
}
.reveal a.stoerer .stoerer-kicker {
  font-family: var(--r-code-font);
  font-size: 0.464em; letter-spacing: 0.16em; text-transform: uppercase;
  opacity: 0.75;
}
.reveal a.stoerer .stoerer-shout {
  font-family: var(--r-heading-font); font-weight: 700;
  /* At this ratio a long single word still fits the column („mitmachen!" is
     165px of 180 at the default size, „RUNNER!" less), so the shout breaks at
     its spaces. Do not add a &shy; to help it: line breaking is greedy, so an
     in-word opportunity beats the space before it — „Jetzt mit- / machen!"
     instead of „Jetzt / mitmachen!". Leave words whole; the column breaks them.
     Because both the type and the column are in em, this holds at any size. */
  font-size: 1.036em; line-height: 1.05; letter-spacing: -0.015em;
  margin: calc(var(--stoerer-size) * 0.018) 0 calc(var(--stoerer-size) * 0.025);
}
.reveal a.stoerer .stoerer-address {
  font-family: var(--r-code-font);
  font-size: 0.446em; line-height: 1.25;
  color: var(--al-navy);
  /* max() so the rule never lands under a pixel when the badge is small. */
  border-top: max(1px, calc(var(--stoerer-size) * 0.0036)) solid rgba(8, 22, 42, 0.28);
  padding-top: calc(var(--stoerer-size) * 0.0214);
}

/* -------------------------------------------------------------------------- */
/*  Persistent chrome: corner mark + logo + footer                            */
/*  The corner mark sits top-left and the logo top-right, so neither collides */
/*  with reveal's bottom-right controls & slide number, or the footer.        */
/* -------------------------------------------------------------------------- */
.reveal .slide-frame {
  position: fixed; inset: 26px;
  pointer-events: none; z-index: 40;
  border: 0;
}
.reveal .slide-frame::before {
  content: ""; position: absolute; width: 22px; height: 22px;
  border-color: var(--al-green); border-style: solid;
  top: 0; left: 0; border-width: 2px 0 0 2px;
}

/* The logo sits inside the top-right corner mark — the one part of the canvas
   no slide layout uses. Slides that show the lockup themselves (title, closing)
   step it aside rather than repeat it.  */
.reveal .deck-logo {
  position: fixed; top: 26px; right: 26px;
  color: var(--al-white);
  z-index: 41;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.reveal .deck-logo .al-mark { height: 38px; }
.reveal:has(section.present .wordmark) .deck-logo { opacity: 0; }
.reveal:has(section:is(.light, .on-light).present) .deck-logo { color: var(--al-navy); }

/* Home. Site navigation rather than deck chrome, so it stays muted until the
   pointer finds it. It stacks directly above the footer: the top-left corner
   belongs to the eyebrow, and at margin 0.09 the canvas reaches x=58 / y=33,
   so the bottom-left strip the footer already occupies is the only edge no
   slide layout writes into. */
.reveal .deck-home,
.reveal .deck-lang {
  position: fixed; left: 46px; bottom: 56px;
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--r-code-font);
  font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--al-muted); opacity: 0.5;
  border: 0; padding: 0;
  z-index: 41; pointer-events: auto;
  transition: opacity 0.18s ease, color 0.18s ease;
}
.reveal .deck-home svg {
  width: 15px; height: 15px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
.reveal .deck-home:hover,
.reveal .deck-lang:hover { opacity: 1; color: var(--al-green); }

/* The same talk in another language: one chip per twin deck, written by
   ci/build.py only for the languages that actually exist. The row sits beside
   the home link and grows to the right, so a third language costs no layout. */
.reveal .deck-langs {
  position: fixed; left: 190px; bottom: 56px;
  display: flex; align-items: center; gap: 22px;
  z-index: 41; pointer-events: auto;
  transition: opacity 0.18s ease;
}
/* Muted by colour rather than opacity — opacity would take the flag down with
   the text, and a washed-out flag stops being a flag. */
.reveal .deck-lang {
  position: static;
  opacity: 1;
  color: var(--al-muted);
}
.reveal .deck-lang:hover { color: var(--al-green); }
/* The flag keeps its own colours, unlike the stroked icon the home link uses;
   the hairline stops a white stripe from bleeding into the field behind it. */
.reveal .deck-lang .flag {
  width: 22px; height: 15px; flex: none;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.28);
}

.reveal .deck-footer {
  position: fixed; left: 46px; bottom: 30px;
  font-family: var(--r-code-font);
  font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--al-muted); z-index: 41;
  pointer-events: none;
}
/* The band ignores the pointer so it never swallows a click meant for the
   slide — the link inside it is the one thing that takes one back. */
.reveal .deck-footer a { color: inherit; pointer-events: auto; }
.reveal .deck-footer a:hover { color: var(--al-green); text-decoration: underline; }
.reveal .deck-footer b { color: var(--al-green); font-weight: 500; }

/* Keep the chrome legible when a light slide is on screen */
.reveal:has(section:is(.light, .on-light).present) .deck-footer { color: var(--al-muted-2); }
.reveal:has(section:is(.light, .on-light).present) .deck-footer b { color: var(--al-green-deep); }
.reveal:has(section:is(.light, .on-light).present) .slide-frame::before { border-color: var(--al-green-deep); }
/* 0.5 reads as "subtle" on navy but as "washed out" on paper, so light slides
   get the same grey at more of it. */
.reveal:has(section:is(.light, .on-light).present) .deck-home { color: var(--al-muted-2); opacity: 0.8; }
.reveal:has(section:is(.light, .on-light).present) .deck-lang { color: var(--al-muted-2); }
.reveal:has(section:is(.light, .on-light).present) .deck-home:hover,
.reveal:has(section:is(.light, .on-light).present) .deck-lang:hover { color: var(--al-green-deep); }

/* Navigation chrome in brand green, kept clear of the footer */
.reveal .progress { color: var(--al-green); height: 3px; }
.reveal .controls { color: var(--al-green); right: 18px; bottom: 14px; }
.reveal .slide-number {
  background: transparent;
  font-family: var(--r-code-font);
  color: var(--al-muted);
  font-size: 13px;
  /* the arrow cluster is ~108px tall and sits at bottom:14px, so clear 122px */
  right: 26px; bottom: 134px;
}
.reveal .slide-number a { border: none; color: inherit; }

/* -------------------------------------------------------------------------- */
/*  Crisp type on a scaled stage                                              */
/* -------------------------------------------------------------------------- */
/* The deck is laid out at 1280 x 720 and then fitted to the window with
   `transform: translate(-50%, -50%) scale(S)` — S is 1.25 on a 1080p screen and
   1.71 on a 1440p one, so almost every slide is drawn small and blown up.
   A browser rasterises a composited layer once, at the scale the layer had when
   it was made, and only re-rasterises after things settle; until then it
   stretches the tiles it already has. That stretch is the soft type that snaps
   sharp a moment later — the glyphs are not blurry, the cached bitmap of them
   is.
   Fewer layers, fewer stale rasters. Two of reveal's own are pure cost here:
   the `perspective` that only the 3D transitions (convex, concave, cube) use —
   this deck runs `fade`, which is opacity and nothing else — and the
   `will-change: opacity` that promotes every fragment at load time, i.e. before
   reveal has applied the scale at all, which is exactly the layer that comes
   out stretched. A deck that opts a slide into a 3D transition keeps its
   perspective: the rule below is scoped to `.reveal.fade`. */
.reveal.fade .slides,
.reveal.fade .slides > section { perspective: none; }
.reveal .fragment:not(.custom) { will-change: auto; }

/* -------------------------------------------------------------------------- */
/*  Fragments, focus, reduced motion                                          */
/* -------------------------------------------------------------------------- */

.reveal .highlight-green { color: var(--al-green); }

.reveal .fragment.highlight-green.visible,
.reveal .fragment.highlight-teal.visible { color: var(--al-green); }
.reveal :focus-visible { outline: 2px solid var(--al-green); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .reveal .slides section { transition: none !important; }
  /* A Störer that moves is an accessibility problem for some viewers, and the
     badge does its job on tilt and colour alone. */
  .reveal a.stoerer { animation: none; }
}

/* Speaker-view + print niceties */
@media print {
  .reveal .slide-frame, .reveal .deck-footer, .reveal .deck-home { display: none; }
  /* Otherwise the PDF freezes the badge at whatever angle the rock happened to
     be at, which differs page to page. Print gets the deliberate -9deg. */
  .reveal a.stoerer { animation: none; }
}
