diff --git a/SKILL.md b/SKILL.md
index 37d3073d..c117df8f 100644
--- a/SKILL.md
+++ b/SKILL.md
@@ -310,6 +310,7 @@ Before writing any HTML, analyze the content and plan each slide. For each slide
- Add detailed comments explaining each section
- Every section needs a clear `/* === SECTION NAME === */` comment block
- **Always generate speaker notes** — see Speaker Notes below
+- Portrait phones are handled by the reflow block in components.css, which collapses multi-column components to a single column so the deck adapts in place. The optional `.rotate-hint` overlay is only for decks you intentionally author landscape only. Add a `
` with generic prompt text when you want to ask the viewer to turn the device, and leave it out otherwise. It is CSS-only and clears itself on rotation to landscape.
### Speaker Notes (Mandatory)
diff --git a/assets/components.css b/assets/components.css
index d9ab5b90..b5c2f312 100644
--- a/assets/components.css
+++ b/assets/components.css
@@ -665,3 +665,43 @@ h2 { font-size: clamp(28px,3.5vw,48px); font-weight: 800; line-height: 1.15; let
color: var(--text-muted); max-width: 640px;
margin-top: clamp(6px, 1vh, 12px);
}
+
+/* ====== PORTRAIT REFLOW ====== */
+/* Landscape-composed decks degrade to one column on narrow phones.
+ Reflowing the columns keeps the .slide { overflow: hidden } no-scroll
+ invariant intact, where forcing scroll would break it. */
+@media (max-width: 700px) {
+ .flip-grid,
+ .use-case-grid {
+ grid-template-columns: 1fr;
+ }
+ .arch-flow,
+ .stats-row,
+ .vs-container,
+ .auth-compare,
+ .fullstack-bar {
+ flex-direction: column;
+ }
+ /* The arrow sits horizontal by default. Rotate it a quarter turn so it
+ points down between the now-stacked architecture boxes. */
+ .arch-arrow {
+ transform: rotate(90deg);
+ }
+}
+
+/* ====== ROTATE HINT (opt-in) ====== */
+/* CSS-only affordance for decks that are intentionally landscape only.
+ Hidden by default. The portrait, narrow-width query below reveals it,
+ so rotating the device back to landscape clears it with no JavaScript. */
+.rotate-hint {
+ display: none;
+ position: fixed; inset: 0; z-index: 9999;
+ flex-direction: column; align-items: center; justify-content: center;
+ gap: 16px; padding: 32px; text-align: center;
+ background: var(--bg, #000); color: var(--text, #fff);
+ font-size: clamp(16px, 4vw, 22px); font-weight: 600;
+}
+.rotate-hint-icon { font-size: clamp(40px, 12vw, 64px); }
+@media (orientation: portrait) and (max-width: 700px) {
+ .rotate-hint { display: flex; }
+}
diff --git a/assets/viewport-base.css b/assets/viewport-base.css
index 67290b5e..43fc9343 100644
--- a/assets/viewport-base.css
+++ b/assets/viewport-base.css
@@ -45,9 +45,11 @@ html {
--h2-size: clamp(1.25rem, 3.5vw, 2.5rem);
--h3-size: clamp(1rem, 2.5vw, 1.75rem);
- /* Body text */
- --body-size: clamp(0.75rem, 1.5vw, 1.125rem);
- --small-size: clamp(0.65rem, 1vw, 0.875rem);
+ /* Body text. The clamp minimums are floored at a legible size, because an
+ honest overflow the author then splits is better than text that shrinks
+ past readability. Preferred and max values are unchanged. */
+ --body-size: clamp(1rem, 1.5vw, 1.125rem);
+ --small-size: clamp(0.8125rem, 1vw, 0.875rem);
/* Spacing scales with viewport */
--slide-padding: clamp(1rem, 4vw, 4rem);
diff --git a/references/presentation-layer.md b/references/presentation-layer.md
index b74a593f..dae11e76 100644
--- a/references/presentation-layer.md
+++ b/references/presentation-layer.md
@@ -85,6 +85,8 @@ Each slide must fit exactly within 100vh. No scrolling, ever.
Content exceeds limits? Split into multiple slides. Never cram, never scroll.
+**Density tiering and the legible floor:** the clamp minimums on body and small text in `viewport-base.css` are floored at a readable size, so text holds a legible floor instead of shrinking out of sight on a small or projected viewport. When a slide would breach that floor it overflows, which is the signal to split it rather than pack it tighter. This is the same projection-readability constraint documented for diagram text in [libraries.md](libraries.md) (the 18px Mermaid `fontSize` floor). Pick a density tier per slide so every component clears the floor at the smallest viewport the deck will run on.
+
## Navigation Requirements
The presentation must support: