/* ==========================================================================
   2110101 Computer Programming — reveal.js theme
   Cloned from the original PowerPoint deck (PDF: 2110101-Python-pptx-v62.pdf).

   The deck is authored on a 960 x 540 canvas, which is exactly the PDF's
   point size. That means 1 PDF point == 1 CSS pixel here, so every geometry
   value in this file can be lifted straight out of the source document.
   ========================================================================== */

/* Sarabun, self-hosted from @fontsource so the deck keeps working offline.
   The original deck was set in Tahoma; Sarabun is a deliberate modernisation,
   not a fidelity choice — see README, "The Sarabun switch". Latin and Thai
   ship as separate subsets, split by unicode-range. */
@font-face {
  font-family: "Sarabun";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/sarabun-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Sarabun";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/sarabun-thai-400-normal.woff2") format("woff2");
  unicode-range: U+02D7, U+0303, U+0331, U+0E01-0E5B, U+200C-200D, U+25CC;
}
@font-face {
  font-family: "Sarabun";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/sarabun-latin-700-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Sarabun";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/sarabun-thai-700-normal.woff2") format("woff2");
  unicode-range: U+02D7, U+0303, U+0331, U+0E01-0E5B, U+200C-200D, U+25CC;
}

/* The original deck sets its display type in FC Lamoon Light, a free Thai
   face with no web-font distribution. The copy embedded in the source PDF
   carries a full Thai + Latin cmap, so it is extracted once (see
   tools/extract_assets.py) and self-hosted here — that is what makes slide 6
   land on the same glyph advances as the original. */
@font-face {
  font-family: "FC Lamoon Light";
  src: url("../fonts/FCLamoonLight.woff") format("woff");
  font-weight: 300 400;
  font-style: normal;
  font-display: block;
}

:root {
  --cu-bg:        #ffffcc;  /* slide body */
  --cu-bar-mid:   #000099;  /* title bar gradient peak */
  --cu-title:     #ffff00;  /* title text */
  --cu-ink:       #000000;
  --cu-red:       #ff0000;
  --cu-bar-h:     61px;     /* measured title bar height */

  /* One family for everything. The source deck mixed Tahoma (Thai body),
     Cambria (Latin runs inside Thai sentences) and Calibri (cover titles);
     all three are now Sarabun, by decision of the course owner. --cu-latin
     and --cu-sans are kept as separate names so the distinction the source
     drew is still expressible if it is ever wanted back — changing one of
     these lines is all it would take. */
  --cu-thai:  "Sarabun", "Leelawadee UI", "Noto Sans Thai", Tahoma, sans-serif;
  --cu-latin: "Sarabun", "Leelawadee UI", "Noto Sans Thai", Tahoma, sans-serif;
  --cu-sans:  "Sarabun", "Leelawadee UI", "Noto Sans Thai", Tahoma, sans-serif;
  --cu-lamoon: "FC Lamoon Light", "Leelawadee UI", Tahoma, sans-serif;
  --cu-mono: "Cascadia Mono", "Consolas", "DejaVu Sans Mono", monospace;
  /* Code in this course is full of Thai comments, and no monospace face
     covers Thai — without an explicit fallback the browser picks one per
     platform and the line heights come out ragged. */
  --cu-code: "Cascadia Mono", "Consolas", "DejaVu Sans Mono",
             "Sarabun", "Leelawadee UI", "Noto Sans Thai", monospace;
}

/* --------------------------------------------------------------------------
   Canvas
   -------------------------------------------------------------------------- */

.reveal {
  font-family: var(--cu-thai);
  color: var(--cu-ink);
}

/* The 960x540 canvas is letterboxed whenever the window is not 16:9. reveal's
   default #333 reads as a rendering fault against the title bar's black
   gradient; plain black is what a projector shows either side of a slide, so
   the bands stop drawing attention. On a 16:9 display there are none. */
.reveal-viewport { background: #000; }

.reveal .slides { text-align: left; }

.reveal .slides > section,
.reveal .slides > section > section {
  width: 960px;
  height: 540px;
  padding: 0;
  box-sizing: border-box;
  background: var(--cu-bg);
  overflow: hidden;
}

/* Reveal's default typographic resets get in the way of a pixel clone. */
.reveal h1, .reveal h2, .reveal h3, .reveal p, .reveal ul, .reveal li {
  margin: 0;
  padding: 0;
  font-weight: normal;
  text-transform: none;
  letter-spacing: normal;
  line-height: 1;
}

/* --------------------------------------------------------------------------
   Slide chrome: title bar, footer, page number
   -------------------------------------------------------------------------- */

/* `.reveal h2` in the reset above is specificity (0,1,1) and would otherwise
   beat a plain `.cu-bar` (0,1,0) and force line-height:1 here — which pins the
   title to the top of the bar and clips its Thai tone marks. Qualifying with
   .reveal makes this (0,2,0) so the bar keeps its own metrics. */
.reveal .cu-bar {
  position: absolute;
  top: 0;
  left: 0;
  width: 960px;
  height: var(--cu-bar-h);
  line-height: var(--cu-bar-h);
  text-align: center;
  font-family: var(--cu-thai);
  font-size: 32px;
  color: var(--cu-title);
  background: linear-gradient(to bottom, #000000 0%, var(--cu-bar-mid) 50%, #000000 100%);
}

.cu-foot {
  position: absolute;
  left: 7.2px;
  top: 525px;
  font-family: var(--cu-thai);
  font-size: 12px;
  color: var(--cu-ink);
}

.cu-page {
  position: absolute;
  right: 7.2px;
  top: 525px;
  font-family: var(--cu-thai);
  font-size: 12px;
  color: var(--cu-ink);
}

/* --------------------------------------------------------------------------
   Positioned content primitives

   Every content element is absolutely placed with the coordinates measured
   from the source slide: `--x` / `--y` are the top-left of the text's ink box
   in PDF points, read straight off the source with PyMuPDF. The rules below
   are tuned so the rendered ink lands on exactly those coordinates — see
   tools/compare_to_pdf.py, which measures the residual drift.
   -------------------------------------------------------------------------- */

.cu-abs {
  position: absolute;
  left: var(--x, 0px);
  top: var(--y, 0px);
  white-space: nowrap;
}

/* A bulleted line. --x is the bullet's own left edge; the text sits 27px in,
   which is the gap used consistently throughout the original deck. */
.cu-bullet {
  position: absolute;
  left: var(--x, 0px);
  top: var(--y, 0px);
  font-size: 36px;
  line-height: 43px;
  white-space: nowrap;
}

.cu-bullet::before {
  content: "\2022";
  display: inline-block;
  width: 27px;
  font-family: var(--cu-latin);
}

/* Continuation line of a bullet: same metrics, no marker, text-aligned. */
.cu-cont {
  position: absolute;
  left: var(--x, 0px);
  top: var(--y, 0px);
  font-size: 36px;
  line-height: 43px;
  white-space: nowrap;
}

/* Latin runs inside Thai body text are set in Cambria in the original. */
.cu-latin { font-family: var(--cu-latin); }
.cu-sans  { font-family: var(--cu-sans); }
.cu-red   { color: var(--cu-red); }

.cu-img {
  position: absolute;
  left: var(--x, 0px);
  top: var(--y, 0px);
  width: var(--w, auto);
  height: var(--h, auto);
}

/* A static code listing — by far the most common element after session 1.
   Use this rather than inventing a per-session class. For code the student
   should be able to RUN, use `.pyx` instead; this is for fragments, syntax
   sketches and listings whose annotations sit on top of them. */
.cu-listing {
  position: absolute;
  left: var(--x, 0px);
  top: var(--y, 0px);
  margin: 0;
  font-family: var(--cu-code);
  font-size: var(--size, 20px);
  line-height: var(--leading, 1.35);
  white-space: pre;
  tab-size: 4;
  color: var(--ink, #000000);
}

/* The bordered box the source draws around many listings and examples.
   Put a .cu-listing inside it, or use it on its own as a callout frame. */
.cu-panel {
  position: absolute;
  left: var(--x, 0px);
  top: var(--y, 0px);
  width: var(--w, auto);
  height: var(--h, auto);
  box-sizing: border-box;
  background: var(--panel-bg, #ffffff);
  border: var(--panel-border, 1px solid #000000);
  border-radius: var(--panel-radius, 0);
}

/* Output blocks are set apart from the program that produced them. */
.cu-output {
  position: absolute;
  left: var(--x, 0px);
  top: var(--y, 0px);
  margin: 0;
  font-family: var(--cu-code);
  font-size: var(--size, 20px);
  line-height: var(--leading, 1.35);
  white-space: pre;
  color: var(--ink, #1a1a1a);
  background: var(--out-bg, transparent);
}

/* --------------------------------------------------------------------------
   Cover slide
   -------------------------------------------------------------------------- */

/* The defaults are session 1's English cover. Sessions with a Thai cover set
   it at a different size and position, so both are overridable per deck —
   e.g. style="--cover-size:44px; --cover-top:180px". */
.cu-cover .cu-cover-title {
  position: absolute;
  top: var(--cover-top, 193.7px);
  left: 0;
  width: 960px;
  text-align: center;
  font-family: var(--cu-sans);
  font-size: var(--cover-size, 54px);
  line-height: var(--cover-leading, 65px);
}

.cu-cover .cu-cover-org {
  position: absolute;
  top: var(--org-top, 397.3px);
  left: 0;
  width: 960px;
  text-align: center;
  font-family: var(--cu-thai);
  font-size: var(--org-size, 28px);
  line-height: var(--org-leading, 40.2px);
}

/* ==========================================================================
   Live Python exercise widget (Pyodide)

   Styled after the DataCamp-style exercise screenshots used on the original
   slides 7-9, but this one actually runs.
   ========================================================================== */

.pyx {
  /* Lets the widget restyle itself when it is squeezed onto a small slide,
     rather than every cramped cell needing hand-tuning. */
  container-type: inline-size;
  position: absolute;
  left: var(--x, 24px);
  top: var(--y, 72px);
  width: var(--w, 912px);
  height: var(--h, 444px);
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid #b9c6cf;
  border-radius: 2px;
  font-size: 16px;
  color: #333;
  overflow: hidden;
}

.pyx-prompt {
  flex: 0 0 auto;
  padding: 8px 14px;
  font-family: var(--cu-thai);
  font-size: 19px;
  color: #222;
  background: #fff;
}

.pyx-panes {
  flex: 1 1 auto;
  display: flex;
  min-height: 0;
  border-top: 1px solid #dde5ea;
}

.pyx-pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

/* 52% suits session 1's full-width cell. Narrow source listings need more or
   less; override --pyx-pane on the individual .pyx rather than editing this. */
.pyx-pane-code    { flex: 0 0 var(--pyx-pane, 52%); border-right: 1px solid #dde5ea; }
.pyx-pane-console { flex: 1 1 auto; }

.pyx-tab {
  flex: 0 0 auto;
  padding: 6px 16px;
  font-family: var(--cu-sans);
  font-size: 19px;
  color: #33454f;
  background: #dbe6ed;
  border-bottom: 1px solid #c4d3dc;
  align-self: flex-start;
  border-radius: 3px 3px 0 0;
}

.pyx-pane-code .pyx-tab,
.pyx-pane-console .pyx-tab { width: 100%; box-sizing: border-box; }

/* --------------------------------------------------------------------------
   The editor: a transparent <textarea> over a highlighted <pre>.

   Both layers MUST agree on font, size, line-height, padding, tab-size and
   white-space, or the colouring slides out of register with the characters
   the student is typing. Change one, change the other — the shared
   --pyx-code-* values below exist so that is hard to get wrong.
   -------------------------------------------------------------------------- */

.pyx {
  --pyx-code-size: 16px;
  --pyx-code-leading: 23px;
  --pyx-code-pad-y: 8px;
  --pyx-code-pad-x: 10px;
}

.pyx-editor {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: stretch;
  overflow: hidden;
  background: #fff;
}

.pyx-gutter {
  flex: 0 0 auto;
  overflow: hidden;
  padding: var(--pyx-code-pad-y) 8px var(--pyx-code-pad-y) 6px;
  border-right: 1px solid #eef1f3;
  font-family: var(--cu-code);
  font-size: var(--pyx-code-size);
  line-height: var(--pyx-code-leading);
  text-align: right;
  color: #97a6b0;
  background: #fff;
  user-select: none;
  white-space: pre;
  /* Wide enough for three digits so the numbers never reflow the code as a
     student adds lines, and never clip the way a measured width can. */
  min-width: 3ch;
}

/* Only the rows scroll; the gutter's own padding and border stay put. */
.pyx-gutter-rows { will-change: transform; }

.pyx-code {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.pyx-hl,
.pyx-ta {
  margin: 0;
  padding: var(--pyx-code-pad-y) var(--pyx-code-pad-x);
  border: 0;
  font-family: var(--cu-code);
  font-size: var(--pyx-code-size);
  line-height: var(--pyx-code-leading);
  tab-size: 4;
  white-space: pre;
  word-wrap: normal;
  overflow-wrap: normal;
}

.pyx-hl {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  color: #1a1a1a;
}

.pyx-ta {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  resize: none;
  outline: none;
  overflow: auto;
  background: transparent;
  /* The visible glyphs come from the layer underneath; only the caret and
     the selection highlight belong to the textarea. */
  color: transparent;
  caret-color: #111;
}

/* The textarea is painted OVER the highlighted <pre>, and its own glyphs are
   transparent. An opaque selection background would therefore hide the text
   it is selecting — so the selection has to be a translucent wash that the
   layer underneath shows through. */
.pyx-ta::selection { background: rgba(93, 160, 220, 0.35); }
.pyx-ta::-moz-selection { background: rgba(93, 160, 220, 0.35); }

/* Token colours, matched to the original exercise screenshots. */
.pyx-hl .t-c { color: #9a6a3a; }   /* comment */
.pyx-hl .t-s { color: #b34b2c; }   /* string  */
.pyx-hl .t-k { color: #0b6ea8; }   /* keyword */
.pyx-hl .t-b { color: #3a3ab0; }   /* builtin */
.pyx-hl .t-n { color: #1a7f5a; }   /* number  */

.pyx-console {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  padding: 8px 12px;
  overflow: auto;
  font-family: var(--cu-code);
  font-size: var(--pyx-code-size, 16px);
  line-height: 1.45;
  background: #fff;
}

/* One element per line of output, with a hanging indent.

   Console output is wrapped, not scrolled sideways, so without this a line
   that wraps is indistinguishable from two lines the program actually
   printed — which is exactly the wrong thing to be ambiguous about when the
   whole point of the slide is what print() emits. The indent means any line
   starting flush left is a real line, and anything indented is a continuation. */
.pyx-console .ln {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  padding-left: 2.5ch;
  text-indent: -2.5ch;
}

.pyx-console .out { color: #1a1a1a; }
.pyx-console .err { color: #b3392b; }
.pyx-console .sys { color: #7a8a94; font-style: italic; }
.pyx-console .echo { color: #3a6ea5; }

.pyx-status {
  flex: 0 0 auto;
  display: none;
  padding: 8px 14px;
  font-family: var(--cu-thai);
  font-size: 17px;
}

.pyx-status.ok   { display: block; background: #b4d2a8; color: #33452c; }
.pyx-status.bad  { display: block; background: #f4b0ac; color: #5c2320; }

.pyx-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: #eef3f6;
  border-top: 1px solid #dde5ea;
}

.pyx-btn {
  font-family: var(--cu-sans);
  font-size: 18px;
  padding: 7px 18px;
  border: 0;
  border-radius: 3px;
  cursor: pointer;
  color: #fff;
  background: #6d9eb8;
}

.pyx-btn:hover { filter: brightness(1.08); }
.pyx-btn:disabled { opacity: .5; cursor: default; filter: none; }
.pyx-btn-run { background: #f0a04b; }

/* Stop replaces Run in place while a program is running, so the button under
   the cursor is always the one that acts on the thing that is happening. */
.pyx-btn-stop { background: #c0392b; }
.pyx-btn[hidden] { display: none; }

.pyx-stdin-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  font-family: var(--cu-thai);
  font-size: 15px;
  color: #4a5b66;
}

.pyx-stdin-label { text-align: right; line-height: 1.3; }
.pyx-stdin-label small { font-size: 12px; color: #7a8a94; }

/* A textarea rather than a text field: input() consumes one line at a time,
   so the box shows real lines instead of an escaped "22\n29.4", which reads
   as noise to a first-week student. */
.pyx-stdin {
  font-family: var(--cu-code);
  font-size: 15px;
  line-height: 1.35;
  padding: 4px 8px;
  width: 130px;
  height: 46px;
  resize: none;
  overflow: auto;
  white-space: pre;
  border: 1px solid #b9c6cf;
  border-radius: 3px;
  background: #fff;
  color: #222;
}

.pyx-lamp {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #8bc34a;
  flex: 0 0 auto;
}

.pyx-lamp.busy { background: #f0a04b; animation: pyx-pulse .9s ease-in-out infinite; }
.pyx-lamp.cold { background: #b9c6cf; }

@keyframes pyx-pulse { 50% { opacity: .35; } }

/* A narrow cell sheds the parts that are nice-to-have before it sheds the
   parts that are not. The stdin hint wrapping onto three lines, as it did on
   the nested-calls slide, is worse than not showing the hint at all. */
@container (max-width: 620px) {
  .pyx-stdin-label small { display: none; }
  .pyx-stdin { width: 88px; }
  .pyx-btn { padding: 6px 12px; font-size: 16px; }
  .pyx-prompt { font-size: 17px; padding: 6px 10px; }
}

@container (max-width: 430px) {
  .pyx-stdin-label { display: none; }
  .pyx-tab { font-size: 15px; padding: 4px 10px; }
}

/* --------------------------------------------------------------------------
   Speaker-facing helper: a soft note that never shows in the deck itself
   -------------------------------------------------------------------------- */

.cu-note { display: none; }
