Просмотр исходного кода

Restructure skill for progressive disclosure — reduce context bloat ~70%

SKILL.md trimmed from 1098 to 183 lines. Now serves as a concise map
with references to supporting files loaded on-demand per phase:
- viewport-base.css: single source of truth for mandatory CSS
- html-template.md: HTML architecture scaffold (Phase 3)
- animation-patterns.md: CSS/JS animation reference (Phase 3)
- scripts/extract-pptx.py: executable PPT extraction (Phase 4)
- STYLE_PRESETS.md: deduplicated, removed ~200 lines of repeated CSS

Follows Claude Code skill best practices (SKILL.md < 500 lines) and
OpenAI harness engineering progressive disclosure pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zara Zhang 3 месяцев назад
Родитель
Сommit
b206c50c5a
6 измененных файлов с 635 добавлено и 1229 удалено
  1. 84 1002
      SKILL.md
  2. 2 227
      STYLE_PRESETS.md
  3. 110 0
      animation-patterns.md
  4. 190 0
      html-template.md
  5. 96 0
      scripts/extract-pptx.py
  6. 153 0
      viewport-base.css

Разница между файлами не показана из-за своего большого размера
+ 84 - 1002
SKILL.md


+ 2 - 227
STYLE_PRESETS.md

@@ -1,163 +1,8 @@
 # Style Presets Reference
 
-Curated visual styles for Frontend Slides. Each preset is inspired by real design references—no generic "AI slop" aesthetics. **Abstract shapes only—no illustrations.**
+Curated visual styles for Frontend Slides. Each preset is inspired by real design references  no generic "AI slop" aesthetics. **Abstract shapes only  no illustrations.**
 
----
-
-## ⚠️ CRITICAL: Viewport Fitting (Non-Negotiable)
-
-**Every slide MUST fit exactly in the viewport. No scrolling within slides, ever.**
-
-### Content Density Limits Per Slide
-
-| Slide Type | Maximum Content |
-|------------|-----------------|
-| Title slide | 1 heading + 1 subtitle |
-| Content slide | 1 heading + 4-6 bullets (max 2 lines each) |
-| Feature grid | 1 heading + 6 cards (2x3 or 3x2) |
-| Code slide | 1 heading + 8-10 lines of code |
-| Quote slide | 1 quote (max 3 lines) + attribution |
-
-**Too much content? → Split into multiple slides. Never scroll.**
-
-### Required Base CSS (Include in ALL Presentations)
-
-```css
-/* ===========================================
-   VIEWPORT FITTING: MANDATORY
-   Copy this entire block into every presentation
-   =========================================== */
-
-/* 1. Lock document to viewport */
-html, body {
-    height: 100%;
-    overflow-x: hidden;
-}
-
-html {
-    scroll-snap-type: y mandatory;
-    scroll-behavior: smooth;
-}
-
-/* 2. Each slide = exact viewport height */
-.slide {
-    width: 100vw;
-    height: 100vh;
-    height: 100dvh; /* Dynamic viewport for mobile */
-    overflow: hidden; /* CRITICAL: No overflow ever */
-    scroll-snap-align: start;
-    display: flex;
-    flex-direction: column;
-    position: relative;
-}
-
-/* 3. Content wrapper */
-.slide-content {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-    max-height: 100%;
-    overflow: hidden;
-    padding: var(--slide-padding);
-}
-
-/* 4. ALL sizes use clamp() - scales with viewport */
-:root {
-    /* Typography */
-    --title-size: clamp(1.5rem, 5vw, 4rem);
-    --h2-size: clamp(1.25rem, 3.5vw, 2.5rem);
-    --body-size: clamp(0.75rem, 1.5vw, 1.125rem);
-    --small-size: clamp(0.65rem, 1vw, 0.875rem);
-
-    /* Spacing */
-    --slide-padding: clamp(1rem, 4vw, 4rem);
-    --content-gap: clamp(0.5rem, 2vw, 2rem);
-}
-
-/* 5. Cards/containers use viewport-relative max sizes */
-.card, .container {
-    max-width: min(90vw, 1000px);
-    max-height: min(80vh, 700px);
-}
-
-/* 6. Images constrained */
-img {
-    max-width: 100%;
-    max-height: min(50vh, 400px);
-    object-fit: contain;
-}
-
-/* 7. Grids adapt to space */
-.grid {
-    display: grid;
-    grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
-    gap: clamp(0.5rem, 1.5vw, 1rem);
-}
-
-/* ===========================================
-   RESPONSIVE BREAKPOINTS - Height-based
-   =========================================== */
-
-/* Short screens (< 700px height) */
-@media (max-height: 700px) {
-    :root {
-        --slide-padding: clamp(0.75rem, 3vw, 2rem);
-        --content-gap: clamp(0.4rem, 1.5vw, 1rem);
-        --title-size: clamp(1.25rem, 4.5vw, 2.5rem);
-    }
-}
-
-/* Very short (< 600px height) */
-@media (max-height: 600px) {
-    :root {
-        --slide-padding: clamp(0.5rem, 2.5vw, 1.5rem);
-        --title-size: clamp(1.1rem, 4vw, 2rem);
-        --body-size: clamp(0.7rem, 1.2vw, 0.95rem);
-    }
-
-    .nav-dots, .keyboard-hint, .decorative {
-        display: none;
-    }
-}
-
-/* Extremely short - landscape phones (< 500px) */
-@media (max-height: 500px) {
-    :root {
-        --slide-padding: clamp(0.4rem, 2vw, 1rem);
-        --title-size: clamp(1rem, 3.5vw, 1.5rem);
-        --body-size: clamp(0.65rem, 1vw, 0.85rem);
-    }
-}
-
-/* Narrow screens */
-@media (max-width: 600px) {
-    .grid {
-        grid-template-columns: 1fr;
-    }
-}
-
-/* Reduced motion */
-@media (prefers-reduced-motion: reduce) {
-    *, *::before, *::after {
-        animation-duration: 0.01ms !important;
-        transition-duration: 0.2s !important;
-    }
-}
-```
-
-### Viewport Fitting Checklist
-
-Before finalizing any presentation, verify:
-
-- [ ] Every `.slide` has `height: 100vh; height: 100dvh; overflow: hidden;`
-- [ ] All font sizes use `clamp(min, preferred, max)`
-- [ ] All spacing uses `clamp()` or viewport units
-- [ ] Breakpoints exist for heights: 700px, 600px, 500px
-- [ ] Content respects density limits (max 6 bullets, max 6 cards)
-- [ ] No fixed pixel heights on content elements
-- [ ] Images have `max-height` constraints
-- [ ] No negated CSS functions (use `calc(-1 * clamp(...))` not `-clamp(...)`)
+**Viewport CSS:** For mandatory base styles, see [viewport-base.css](viewport-base.css). Include in every presentation.
 
 ---
 
@@ -480,73 +325,3 @@ Before finalizing any presentation, verify:
 
 **Decorations:** Realistic illustrations, gratuitous glassmorphism, drop shadows without purpose
 
----
-
-## CSS Gotchas (Common Mistakes)
-
-### Negating CSS Functions
-
-**WRONG — silently ignored by browsers:**
-```css
-right: -clamp(28px, 3.5vw, 44px);   /* ❌ Invalid! Browser ignores this */
-margin-left: -min(10vw, 100px);      /* ❌ Invalid! */
-top: -max(2rem, 4vh);                /* ❌ Invalid! */
-```
-
-**CORRECT — wrap in `calc()`:**
-```css
-right: calc(-1 * clamp(28px, 3.5vw, 44px));  /* ✅ */
-margin-left: calc(-1 * min(10vw, 100px));     /* ✅ */
-top: calc(-1 * max(2rem, 4vh));               /* ✅ */
-```
-
-CSS does not allow a leading `-` before function names like `clamp()`, `min()`, `max()`. The browser silently discards the entire declaration, causing the property to fall back to its initial/inherited value. This is especially dangerous because there is no console error — the element simply appears in the wrong position.
-
-**Rule: Always use `calc(-1 * ...)` to negate CSS function values.**
-
----
-
-## Troubleshooting Viewport Issues
-
-### Content Overflows the Slide
-
-**Symptoms:** Scrollbar appears, content cut off, elements outside viewport
-
-**Solutions:**
-1. Check slide has `overflow: hidden` (not `overflow: auto` or `visible`)
-2. Reduce content — split into multiple slides
-3. Ensure all fonts use `clamp()` not fixed `px` or `rem`
-4. Add/fix height breakpoints for smaller screens
-5. Check images have `max-height: min(50vh, 400px)`
-
-### Text Too Small on Mobile / Too Large on Desktop
-
-**Symptoms:** Unreadable text on phones, oversized text on big screens
-
-**Solutions:**
-```css
-/* Use clamp with viewport-relative middle value */
-font-size: clamp(1rem, 3vw, 2.5rem);
-/*              ↑       ↑      ↑
-            minimum  scales  maximum */
-```
-
-### Content Doesn't Fill Short Screens
-
-**Symptoms:** Excessive whitespace on landscape phones or short browser windows
-
-**Solutions:**
-1. Add `@media (max-height: 600px)` and `(max-height: 500px)` breakpoints
-2. Reduce padding at smaller heights
-3. Hide decorative elements (`display: none`)
-4. Consider hiding nav dots and hints on short screens
-
-### Testing Recommendations
-
-Test at these viewport sizes:
-- **Desktop:** 1920×1080, 1440×900, 1280×720
-- **Tablet:** 1024×768 (landscape), 768×1024 (portrait)
-- **Mobile:** 375×667 (iPhone SE), 414×896 (iPhone 11)
-- **Landscape phone:** 667×375, 896×414
-
-Use browser DevTools responsive mode to quickly test multiple sizes.

+ 110 - 0
animation-patterns.md

@@ -0,0 +1,110 @@
+# Animation Patterns Reference
+
+Use this reference when generating presentations. Match animations to the intended feeling.
+
+## Effect-to-Feeling Guide
+
+| Feeling | Animations | Visual Cues |
+|---------|-----------|-------------|
+| **Dramatic / Cinematic** | Slow fade-ins (1-1.5s), large scale transitions (0.9 to 1), parallax scrolling | Dark backgrounds, spotlight effects, full-bleed images |
+| **Techy / Futuristic** | Neon glow (box-shadow), glitch/scramble text, grid reveals | Particle systems (canvas), grid patterns, monospace accents, cyan/magenta/electric blue |
+| **Playful / Friendly** | Bouncy easing (spring physics), floating/bobbing | Rounded corners, pastel/bright colors, hand-drawn elements |
+| **Professional / Corporate** | Subtle fast animations (200-300ms), clean slides | Navy/slate/charcoal, precise spacing, data visualization focus |
+| **Calm / Minimal** | Very slow subtle motion, gentle fades | High whitespace, muted palette, serif typography, generous padding |
+| **Editorial / Magazine** | Staggered text reveals, image-text interplay | Strong type hierarchy, pull quotes, grid-breaking layouts, serif headlines + sans body |
+
+## Entrance Animations
+
+```css
+/* Fade + Slide Up (most versatile) */
+.reveal {
+    opacity: 0;
+    transform: translateY(30px);
+    transition: opacity 0.6s var(--ease-out-expo),
+                transform 0.6s var(--ease-out-expo);
+}
+.visible .reveal {
+    opacity: 1;
+    transform: translateY(0);
+}
+
+/* Scale In */
+.reveal-scale {
+    opacity: 0;
+    transform: scale(0.9);
+    transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
+}
+
+/* Slide from Left */
+.reveal-left {
+    opacity: 0;
+    transform: translateX(-50px);
+    transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
+}
+
+/* Blur In */
+.reveal-blur {
+    opacity: 0;
+    filter: blur(10px);
+    transition: opacity 0.8s, filter 0.8s var(--ease-out-expo);
+}
+```
+
+## Background Effects
+
+```css
+/* Gradient Mesh — layered radial gradients for depth */
+.gradient-bg {
+    background:
+        radial-gradient(ellipse at 20% 80%, rgba(120, 0, 255, 0.3) 0%, transparent 50%),
+        radial-gradient(ellipse at 80% 20%, rgba(0, 255, 200, 0.2) 0%, transparent 50%),
+        var(--bg-primary);
+}
+
+/* Noise Texture — inline SVG for grain */
+.noise-bg {
+    background-image: url("data:image/svg+xml,..."); /* Inline SVG noise */
+}
+
+/* Grid Pattern — subtle structural lines */
+.grid-bg {
+    background-image:
+        linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
+        linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
+    background-size: 50px 50px;
+}
+```
+
+## Interactive Effects
+
+```javascript
+/* 3D Tilt on Hover — adds depth to cards/panels */
+class TiltEffect {
+    constructor(element) {
+        this.element = element;
+        this.element.style.transformStyle = 'preserve-3d';
+        this.element.style.perspective = '1000px';
+
+        this.element.addEventListener('mousemove', (e) => {
+            const rect = this.element.getBoundingClientRect();
+            const x = (e.clientX - rect.left) / rect.width - 0.5;
+            const y = (e.clientY - rect.top) / rect.height - 0.5;
+            this.element.style.transform = `rotateY(${x * 10}deg) rotateX(${-y * 10}deg)`;
+        });
+
+        this.element.addEventListener('mouseleave', () => {
+            this.element.style.transform = 'rotateY(0) rotateX(0)';
+        });
+    }
+}
+```
+
+## Troubleshooting
+
+| Problem | Fix |
+|---------|-----|
+| Fonts not loading | Check Fontshare/Google Fonts URL; ensure font names match in CSS |
+| Animations not triggering | Verify Intersection Observer is running; check `.visible` class is being added |
+| Scroll snap not working | Ensure `scroll-snap-type: y mandatory` on html; each slide needs `scroll-snap-align: start` |
+| Mobile issues | Disable heavy effects at 768px breakpoint; test touch events; reduce particle count |
+| Performance issues | Use `will-change` sparingly; prefer `transform`/`opacity` animations; throttle scroll handlers |

+ 190 - 0
html-template.md

@@ -0,0 +1,190 @@
+# HTML Presentation Template
+
+Reference architecture for generating slide presentations. Every presentation follows this structure.
+
+## Base HTML Structure
+
+```html
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Presentation Title</title>
+
+    <!-- Fonts: use Fontshare or Google Fonts — never system fonts -->
+    <link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=...">
+
+    <style>
+        /* ===========================================
+           CSS CUSTOM PROPERTIES (THEME)
+           Change these to change the whole look
+           =========================================== */
+        :root {
+            /* Colors — from chosen style preset */
+            --bg-primary: #0a0f1c;
+            --bg-secondary: #111827;
+            --text-primary: #ffffff;
+            --text-secondary: #9ca3af;
+            --accent: #00ffcc;
+            --accent-glow: rgba(0, 255, 204, 0.3);
+
+            /* Typography — MUST use clamp() */
+            --font-display: 'Clash Display', sans-serif;
+            --font-body: 'Satoshi', sans-serif;
+            --title-size: clamp(2rem, 6vw, 5rem);
+            --subtitle-size: clamp(0.875rem, 2vw, 1.25rem);
+            --body-size: clamp(0.75rem, 1.2vw, 1rem);
+
+            /* Spacing — MUST use clamp() */
+            --slide-padding: clamp(1.5rem, 4vw, 4rem);
+            --content-gap: clamp(1rem, 2vw, 2rem);
+
+            /* Animation */
+            --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+            --duration-normal: 0.6s;
+        }
+
+        /* ===========================================
+           BASE STYLES
+           =========================================== */
+        * { margin: 0; padding: 0; box-sizing: border-box; }
+
+        /* --- PASTE viewport-base.css CONTENTS HERE --- */
+
+        /* ===========================================
+           ANIMATIONS
+           Trigger via .visible class (added by JS on scroll)
+           =========================================== */
+        .reveal {
+            opacity: 0;
+            transform: translateY(30px);
+            transition: opacity var(--duration-normal) var(--ease-out-expo),
+                        transform var(--duration-normal) var(--ease-out-expo);
+        }
+
+        .slide.visible .reveal {
+            opacity: 1;
+            transform: translateY(0);
+        }
+
+        /* Stagger children for sequential reveal */
+        .reveal:nth-child(1) { transition-delay: 0.1s; }
+        .reveal:nth-child(2) { transition-delay: 0.2s; }
+        .reveal:nth-child(3) { transition-delay: 0.3s; }
+        .reveal:nth-child(4) { transition-delay: 0.4s; }
+
+        /* ... preset-specific styles ... */
+    </style>
+</head>
+<body>
+    <!-- Optional: Progress bar -->
+    <div class="progress-bar"></div>
+
+    <!-- Optional: Navigation dots -->
+    <nav class="nav-dots"><!-- Generated by JS --></nav>
+
+    <!-- Slides -->
+    <section class="slide title-slide">
+        <h1 class="reveal">Presentation Title</h1>
+        <p class="reveal">Subtitle or author</p>
+    </section>
+
+    <section class="slide">
+        <div class="slide-content">
+            <h2 class="reveal">Slide Title</h2>
+            <p class="reveal">Content...</p>
+        </div>
+    </section>
+
+    <!-- More slides... -->
+
+    <script>
+        /* ===========================================
+           SLIDE PRESENTATION CONTROLLER
+           =========================================== */
+        class SlidePresentation {
+            constructor() {
+                this.slides = document.querySelectorAll('.slide');
+                this.currentSlide = 0;
+                this.setupIntersectionObserver();
+                this.setupKeyboardNav();
+                this.setupTouchNav();
+                this.setupProgressBar();
+                this.setupNavDots();
+            }
+
+            setupIntersectionObserver() {
+                // Add .visible class when slides enter viewport
+                // Triggers CSS animations efficiently
+            }
+
+            setupKeyboardNav() {
+                // Arrow keys, Space, Page Up/Down
+            }
+
+            setupTouchNav() {
+                // Touch/swipe support for mobile
+            }
+
+            setupProgressBar() {
+                // Update progress bar on scroll
+            }
+
+            setupNavDots() {
+                // Generate and manage navigation dots
+            }
+        }
+
+        new SlidePresentation();
+    </script>
+</body>
+</html>
+```
+
+## Required JavaScript Features
+
+Every presentation must include:
+
+1. **SlidePresentation Class** — Main controller with:
+   - Keyboard navigation (arrows, space, page up/down)
+   - Touch/swipe support
+   - Mouse wheel navigation
+   - Progress bar updates
+   - Navigation dots
+
+2. **Intersection Observer** — For scroll-triggered animations:
+   - Add `.visible` class when slides enter viewport
+   - Trigger CSS transitions efficiently
+
+3. **Optional Enhancements** (match to chosen style):
+   - Custom cursor with trail
+   - Particle system background (canvas)
+   - Parallax effects
+   - 3D tilt on hover
+   - Magnetic buttons
+   - Counter animations
+
+## Code Quality
+
+**Comments:** Every section needs clear comments explaining what it does and how to modify it.
+
+**Accessibility:**
+- Semantic HTML (`<section>`, `<nav>`, `<main>`)
+- Keyboard navigation works fully
+- ARIA labels where needed
+- `prefers-reduced-motion` support (included in viewport-base.css)
+
+## File Structure
+
+Single presentations:
+```
+presentation.html    # Self-contained, all CSS/JS inline
+assets/              # Images only, if any
+```
+
+Multiple presentations in one project:
+```
+[name].html
+[name]-assets/
+```

+ 96 - 0
scripts/extract-pptx.py

@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+"""
+Extract all content from a PowerPoint file (.pptx).
+Returns a JSON structure with slides, text, and images.
+
+Usage:
+    python extract-pptx.py <input.pptx> [output_dir]
+
+Requires: pip install python-pptx
+"""
+
+import json
+import os
+import sys
+from pptx import Presentation
+
+
+def extract_pptx(file_path, output_dir="."):
+    """
+    Extract all content from a PowerPoint file.
+    Returns a list of slide data dicts with text, images, and notes.
+    """
+    prs = Presentation(file_path)
+    slides_data = []
+
+    # Create assets directory for extracted images
+    assets_dir = os.path.join(output_dir, "assets")
+    os.makedirs(assets_dir, exist_ok=True)
+
+    for slide_num, slide in enumerate(prs.slides):
+        slide_data = {
+            "number": slide_num + 1,
+            "title": "",
+            "content": [],
+            "images": [],
+            "notes": "",
+        }
+
+        for shape in slide.shapes:
+            # Extract text content
+            if shape.has_text_frame:
+                if shape == slide.shapes.title:
+                    slide_data["title"] = shape.text
+                else:
+                    slide_data["content"].append(
+                        {"type": "text", "content": shape.text}
+                    )
+
+            # Extract images
+            if shape.shape_type == 13:  # Picture type
+                image = shape.image
+                image_bytes = image.blob
+                image_ext = image.ext
+                image_name = f"slide{slide_num + 1}_img{len(slide_data['images']) + 1}.{image_ext}"
+                image_path = os.path.join(assets_dir, image_name)
+
+                with open(image_path, "wb") as f:
+                    f.write(image_bytes)
+
+                slide_data["images"].append(
+                    {
+                        "path": f"assets/{image_name}",
+                        "width": shape.width,
+                        "height": shape.height,
+                    }
+                )
+
+        # Extract speaker notes
+        if slide.has_notes_slide:
+            notes_frame = slide.notes_slide.notes_text_frame
+            slide_data["notes"] = notes_frame.text
+
+        slides_data.append(slide_data)
+
+    return slides_data
+
+
+if __name__ == "__main__":
+    if len(sys.argv) < 2:
+        print("Usage: python extract-pptx.py <input.pptx> [output_dir]")
+        sys.exit(1)
+
+    input_file = sys.argv[1]
+    output_dir = sys.argv[2] if len(sys.argv) > 2 else "."
+
+    slides = extract_pptx(input_file, output_dir)
+
+    # Write extracted data as JSON
+    output_path = os.path.join(output_dir, "extracted-slides.json")
+    with open(output_path, "w") as f:
+        json.dump(slides, f, indent=2)
+
+    print(f"Extracted {len(slides)} slides to {output_path}")
+    for s in slides:
+        img_count = len(s["images"])
+        print(f"  Slide {s['number']}: {s['title'] or '(no title)'} — {img_count} image(s)")

+ 153 - 0
viewport-base.css

@@ -0,0 +1,153 @@
+/* ===========================================
+   VIEWPORT FITTING: MANDATORY BASE STYLES
+   Include this ENTIRE file in every presentation.
+   These styles ensure slides fit exactly in the viewport.
+   =========================================== */
+
+/* 1. Lock html/body to viewport */
+html, body {
+    height: 100%;
+    overflow-x: hidden;
+}
+
+html {
+    scroll-snap-type: y mandatory;
+    scroll-behavior: smooth;
+}
+
+/* 2. Each slide = exact viewport height */
+.slide {
+    width: 100vw;
+    height: 100vh;
+    height: 100dvh; /* Dynamic viewport height for mobile browsers */
+    overflow: hidden; /* CRITICAL: Prevent ANY overflow */
+    scroll-snap-align: start;
+    display: flex;
+    flex-direction: column;
+    position: relative;
+}
+
+/* 3. Content container with flex for centering */
+.slide-content {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    max-height: 100%;
+    overflow: hidden; /* Double-protection against overflow */
+    padding: var(--slide-padding);
+}
+
+/* 4. ALL typography uses clamp() for responsive scaling */
+:root {
+    /* Titles scale from mobile to desktop */
+    --title-size: clamp(1.5rem, 5vw, 4rem);
+    --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);
+
+    /* Spacing scales with viewport */
+    --slide-padding: clamp(1rem, 4vw, 4rem);
+    --content-gap: clamp(0.5rem, 2vw, 2rem);
+    --element-gap: clamp(0.25rem, 1vw, 1rem);
+}
+
+/* 5. Cards/containers use viewport-relative max sizes */
+.card, .container, .content-box {
+    max-width: min(90vw, 1000px);
+    max-height: min(80vh, 700px);
+}
+
+/* 6. Lists auto-scale with viewport */
+.feature-list, .bullet-list {
+    gap: clamp(0.4rem, 1vh, 1rem);
+}
+
+.feature-list li, .bullet-list li {
+    font-size: var(--body-size);
+    line-height: 1.4;
+}
+
+/* 7. Grids adapt to available space */
+.grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
+    gap: clamp(0.5rem, 1.5vw, 1rem);
+}
+
+/* 8. Images constrained to viewport */
+img, .image-container {
+    max-width: 100%;
+    max-height: min(50vh, 400px);
+    object-fit: contain;
+}
+
+/* ===========================================
+   RESPONSIVE BREAKPOINTS
+   Aggressive scaling for smaller viewports
+   =========================================== */
+
+/* Short viewports (< 700px height) */
+@media (max-height: 700px) {
+    :root {
+        --slide-padding: clamp(0.75rem, 3vw, 2rem);
+        --content-gap: clamp(0.4rem, 1.5vw, 1rem);
+        --title-size: clamp(1.25rem, 4.5vw, 2.5rem);
+        --h2-size: clamp(1rem, 3vw, 1.75rem);
+    }
+}
+
+/* Very short viewports (< 600px height) */
+@media (max-height: 600px) {
+    :root {
+        --slide-padding: clamp(0.5rem, 2.5vw, 1.5rem);
+        --content-gap: clamp(0.3rem, 1vw, 0.75rem);
+        --title-size: clamp(1.1rem, 4vw, 2rem);
+        --body-size: clamp(0.7rem, 1.2vw, 0.95rem);
+    }
+
+    /* Hide non-essential elements */
+    .nav-dots, .keyboard-hint, .decorative {
+        display: none;
+    }
+}
+
+/* Extremely short (landscape phones, < 500px height) */
+@media (max-height: 500px) {
+    :root {
+        --slide-padding: clamp(0.4rem, 2vw, 1rem);
+        --title-size: clamp(1rem, 3.5vw, 1.5rem);
+        --h2-size: clamp(0.9rem, 2.5vw, 1.25rem);
+        --body-size: clamp(0.65rem, 1vw, 0.85rem);
+    }
+}
+
+/* Narrow viewports (< 600px width) */
+@media (max-width: 600px) {
+    :root {
+        --title-size: clamp(1.25rem, 7vw, 2.5rem);
+    }
+
+    /* Stack grids vertically */
+    .grid {
+        grid-template-columns: 1fr;
+    }
+}
+
+/* ===========================================
+   REDUCED MOTION
+   Respect user preferences
+   =========================================== */
+@media (prefers-reduced-motion: reduce) {
+    *, *::before, *::after {
+        animation-duration: 0.01ms !important;
+        transition-duration: 0.2s !important;
+    }
+
+    html {
+        scroll-behavior: auto;
+    }
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов