ids theme: align list markers flush with the slide title

Native ::marker glyphs are anchored by the browser with engine- and
font-dependent offsets, so the bullet column never lined up exactly with
the heading text. Draw the markers as CSS boxes in absolutely positioned
li::before rules instead: dot/square/ring per nesting level, vertically
centered on the first line, with the first-level marker's left edge
deterministically flush with the title in every browser and at every
window size. Nested offsets scale with the 0.8em per-level font-size
cascade. Ordered lists use the CSS list-item counter (honours start/
value), right-aligned into the indent; pandoc task lists keep their
checkboxes.

Change-Id: Ie514c11fd9cc4911c4a1811c4c316cb217547a4a
diff --git a/NEWS.md b/NEWS.md
index c7a875b..76f8bd5 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -4,6 +4,8 @@
 
 - `ids` theme: second- and higher-level list items are now rendered progressively smaller (each nesting level at 80% of its parent), emphasising list hierarchy as in the IDS reference decks.
 
+- `ids` theme: list markers are now drawn as CSS boxes via `li::before` (dot/square/ring per level; ordered lists use the `list-item` counter honoring `start`/`value`), so the bullet column is deterministically flush with the slide title's left edge in every browser and at every window size, instead of relying on engine-dependent native `::marker` anchoring.
+
 ## REVEAL JS LIBRARY UPDATE
 
 This version of the package comes with reveal.js v4 which is a major update from previous reveal.js v3. Built-in plugins (`zoom`, `search` and `notes`) have been updated, as well as third party plugin included in this package (`menu` and `chalkboard`). A lot of bugfixes and improvements have been made in the upstream library and now the R package can benefit from it. However, this cause also some breaking changes for **revealjs** R package that we described below.
diff --git a/inst/reveal.js-5.1.0/css/theme/source/ids.scss b/inst/reveal.js-5.1.0/css/theme/source/ids.scss
index b069d58..20e9416 100644
--- a/inst/reveal.js-5.1.0/css/theme/source/ids.scss
+++ b/inst/reveal.js-5.1.0/css/theme/source/ids.scss
@@ -113,14 +113,88 @@
 }
 
 // reveal.js centers lists by making them inline-block; make them ordinary
-// blocks so they line up with the headings, with the markers hanging in the
-// padding so the marker column itself aligns with the heading text.
+// blocks so they line up with the headings.
+//
+// The markers are NOT native ::marker glyphs: browsers anchor outside-position
+// markers at slightly different offsets per engine (and per scale), and even
+// ::before font glyphs carry font-specific side bearings, so a padding(text)-
+// based alignment of the bullet column with the heading text can never be
+// pixel-exact everywhere. Instead we DRAW the markers as CSS boxes in
+// absolutely positioned li::before rules. Geometry (all values in the item's
+// own font size, so the hierarchy scales with the 0.8em per-level sizing from
+// the NESTED LISTS section below):
+//
+//   - item text starts at (list left edge) + 0.65em
+//   - the marker box starts exactly at the list's left edge; on content
+//     slides that edge coincides with the heading text's left edge, i.e. the
+//     bullet column is flush with the slide title, deterministically in every
+//     browser and at every window size
+//
+// For nested lists the ::before offset has to be divided by the 0.8 font-size
+// factor, because the list's padding is computed in the PARENT item's font
+// size while the ::before offset is computed in the (smaller) child size.
+$idsListIndent: 0.65em;
+$idsListShrink: 0.8;
+$idsMarkerSize: 0.22em;
+$idsMarkerTop: calc((1.3em - #{$idsMarkerSize}) / 2);
+
 .reveal .slides section:not(.title-frame) ul,
 .reveal .slides section:not(.title-frame) ol {
   display: block !important;
   text-align: left !important;
   margin-left: 0 !important;
-  padding-left: 0.7em !important;
+  padding-left: $idsListIndent !important;
+  list-style: none !important;
+}
+
+.reveal .slides section:not(.title-frame) li {
+  position: relative !important;
+}
+
+.reveal .slides section:not(.title-frame) li::before {
+  position: absolute !important;
+  left: calc(-1 * #{$idsListIndent}) !important;
+  color: var(--r-list-bullet-color) !important;
+}
+
+.reveal .slides section:not(.title-frame) li li::before {
+  left: calc(-1 * #{$idsListIndent} / #{$idsListShrink}) !important;
+}
+
+// Unordered-list markers: drawn boxes (round dot, square, ring for deeper
+// levels), vertically centered on the item's first line.
+.reveal .slides section:not(.title-frame) ul > li::before {
+  content: "" !important;
+  top: $idsMarkerTop !important;
+  width: $idsMarkerSize !important;
+  height: $idsMarkerSize !important;
+  box-sizing: border-box !important;
+  border-radius: 50% !important;
+  background: var(--r-list-bullet-color) !important;
+}
+
+.reveal .slides section:not(.title-frame) ul ul > li::before {
+  border-radius: 0 !important;              // square
+}
+
+.reveal .slides section:not(.title-frame) ul ul ul > li::before {
+  background: transparent !important;       // ring
+  border: 0.055em solid var(--r-list-bullet-color) !important;
+  border-radius: 50% !important;
+}
+
+// Ordered lists: the CSS list-item counter honours the HTML start/value
+// attributes, so pandoc's `start`/`value` lists keep their numbers. Numbers
+// are right-aligned into the indent, ending just before the item text.
+.reveal .slides section:not(.title-frame) ol > li::before {
+  content: counter(list-item, decimal) "." !important;
+  width: calc(#{$idsListIndent} - 0.12em) !important;
+  text-align: right !important;
+}
+
+// Checkbox (pandoc task) lists keep their checkboxes, no markers on top.
+.reveal .slides section:not(.title-frame) ul.task-list li::before {
+  content: none !important;
 }
 
 // reveal.js centres code blocks (width: 90%; margin: auto); make them fill the
diff --git a/inst/reveal.js-5.1.0/dist/theme/ids.css b/inst/reveal.js-5.1.0/dist/theme/ids.css
index 3a5dcb0..b8c44f3 100644
--- a/inst/reveal.js-5.1.0/dist/theme/ids.css
+++ b/inst/reveal.js-5.1.0/dist/theme/ids.css
@@ -371,12 +371,80 @@
   text-align: left !important;
 }
 
+/* The markers are NOT native ::marker glyphs: browsers anchor outside-position
+   markers at slightly different offsets per engine (and per scale), and even
+   ::before font glyphs carry font-specific side bearings, so a padding(text)-
+   based alignment of the bullet column with the heading text can never be
+   pixel-exact everywhere. Instead we DRAW the markers as CSS boxes in
+   absolutely positioned li::before rules. Geometry (all values in the item's
+   own font size, so the hierarchy scales with the 0.8em per-level sizing):
+
+   - item text starts at (list left edge) + 0.65em
+   - the marker box starts exactly at the list's left edge; on content
+     slides that edge coincides with the heading text's left edge, i.e. the
+     bullet column is flush with the slide title, deterministically in every
+     browser and at every window size
+
+   For nested lists the ::before offset is divided by the 0.8 font-size factor,
+   because the list's padding is computed in the PARENT item's font size while
+   the ::before offset is computed in the (smaller) child size. */
 .reveal .slides section:not(.title-frame) ul,
 .reveal .slides section:not(.title-frame) ol {
   display: block !important;
   text-align: left !important;
   margin-left: 0 !important;
-  padding-left: 0.7em !important;
+  padding-left: 0.65em !important;
+  list-style: none !important;
+}
+
+.reveal .slides section:not(.title-frame) li {
+  position: relative !important;
+}
+
+.reveal .slides section:not(.title-frame) li::before {
+  position: absolute !important;
+  left: -0.65em !important;
+  color: var(--r-list-bullet-color) !important;
+}
+
+.reveal .slides section:not(.title-frame) li li::before {
+  left: -0.8125em !important;
+}
+
+/* Unordered-list markers: drawn boxes (round dot, square, ring for deeper
+   levels), vertically centered on the item's first line. */
+.reveal .slides section:not(.title-frame) ul > li::before {
+  content: "" !important;
+  top: calc((1.3em - 0.22em) / 2) !important;
+  width: 0.22em !important;
+  height: 0.22em !important;
+  box-sizing: border-box !important;
+  border-radius: 50% !important;
+  background: var(--r-list-bullet-color) !important;
+}
+
+.reveal .slides section:not(.title-frame) ul ul > li::before {
+  border-radius: 0 !important;              /* square */
+}
+
+.reveal .slides section:not(.title-frame) ul ul ul > li::before {
+  background: transparent !important;       /* ring */
+  border: 0.055em solid var(--r-list-bullet-color) !important;
+  border-radius: 50% !important;
+}
+
+/* Ordered lists: the CSS list-item counter honours the HTML start/value
+   attributes, so pandoc's start/value lists keep their numbers. Numbers are
+   right-aligned into the indent, ending just before the item text. */
+.reveal .slides section:not(.title-frame) ol > li::before {
+  content: counter(list-item, decimal) "." !important;
+  width: 0.53em !important;
+  text-align: right !important;
+}
+
+/* Checkbox (pandoc task) lists keep their checkboxes, no bullets on top. */
+.reveal .slides section:not(.title-frame) ul.task-list li::before {
+  content: none !important;
 }
 
 .reveal .slides section:not(.title-frame) pre {