diff --git a/.gitignore b/.gitignore
index 590c496..573af53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ htmlcov/
.coverage
.pytest_cache/
*.egg-info/
+.DS_Store
diff --git a/docs/superpowers/specs/2026-03-19-figment-mode-design.md b/docs/superpowers/specs/2026-03-19-figment-mode-design.md
new file mode 100644
index 0000000..d75c346
--- /dev/null
+++ b/docs/superpowers/specs/2026-03-19-figment-mode-design.md
@@ -0,0 +1,225 @@
+# Figment Mode Design Spec
+
+> Periodic full-screen SVG glyph overlay with flickery animation, theme-aware coloring, and extensible physical device control.
+
+## Overview
+
+Figment mode displays a randomly selected SVG from the `figments/` directory as a flickery, glitchy half-block terminal overlay on top of the running ticker. It appears once per minute (configurable), holds for ~4.5 seconds with a three-phase animation (progressive reveal, strobing hold, dissolve), then fades back to the ticker. Colors are randomly chosen from the existing theme gradients.
+
+The feature is designed for extensibility: a generic input protocol allows MQTT, ntfy, serial, or any other control surface to trigger figments and adjust parameters in real time.
+
+## Architecture: Hybrid Plugin + Overlay
+
+The figment is an **EffectPlugin** for lifecycle, discovery, and configuration, but delegates rendering to a **layers-style overlay helper**. This avoids stretching the `EffectPlugin.process()` contract (which transforms line buffers) while still benefiting from the plugin system for C&C, auto-discovery, and config management.
+
+### Component Diagram
+
+```
+ +-------------------+
+ | FigmentTrigger | (Protocol)
+ | - NtfyTrigger | (v1)
+ | - MqttTrigger | (future)
+ | - SerialTrigger | (future)
+ +--------+----------+
+ |
+ | FigmentCommand
+ v
++------------------+ +-----------------+ +----------------------+
+| figment_render |<---| FigmentPlugin |--->| render_figment_ |
+| .py | | (EffectPlugin) | | overlay() in |
+| | | | | layers.py |
+| SVG -> PIL -> | | Timer, state | | |
+| half-block cache | | machine, SVG | | ANSI cursor-position |
+| | | selection | | commands for overlay |
++------------------+ +-----------------+ +----------------------+
+ |
+ | get_figment_state()
+ v
+ +-------------------+
+ | scroll.py |
+ | (5-line block) |
+ +-------------------+
+```
+
+## Section 1: SVG Rasterization
+
+**File: `engine/figment_render.py`**
+
+Reuses the same PIL-based half-block encoding that `engine/render.py` uses for OTF fonts.
+
+### Pipeline
+
+1. **Load**: `cairosvg.svg2png()` converts SVG to PNG bytes in memory (no temp files)
+2. **Resize**: PIL scales to fit terminal — width = `tw()`, height = `th() * 2` pixels (each terminal row encodes 2 pixel rows via half-blocks)
+3. **Threshold**: Convert to greyscale ("L" mode), apply binary threshold to get visible/not-visible
+4. **Half-block encode**: Walk pixel pairs top-to-bottom. For each 2-row pair, emit `█` (both lit), `▀` (top only), `▄` (bottom only), or space (neither)
+5. **Cache**: Results cached per `(svg_path, terminal_width, terminal_height)` — invalidated on terminal resize
+
+### Dependency
+
+`cairosvg` added as an optional dependency in `pyproject.toml` (like `sounddevice`). Figment mode gracefully degrades if unavailable.
+
+### Key Function
+
+```python
+def rasterize_svg(svg_path: str, width: int, height: int) -> list[str]:
+ """Convert SVG file to list of half-block terminal rows (uncolored)."""
+```
+
+## Section 2: Figment Overlay Rendering
+
+**Integration point: `engine/layers.py`**
+
+New function following the `render_message_overlay()` pattern.
+
+### Function Signature
+
+```python
+def render_figment_overlay(figment_state: FigmentState, w: int, h: int) -> list[str]:
+ """Return ANSI cursor-positioning commands for the current figment frame."""
+```
+
+### Animation Phases (~4.5 seconds total)
+
+| Phase | Duration | Behavior |
+|-------|----------|----------|
+| **Reveal** | ~1.5s | Progressive scanline fill. Each frame, a percentage of the figment's non-empty cells become visible in random block order. |
+| **Hold** | ~1.5s | Full figment visible. Strobes between full brightness and dimmed/partial visibility every few frames. |
+| **Dissolve** | ~1.5s | Inverse of reveal. Cells randomly drop out, replaced by spaces. |
+
+### Color
+
+A random theme gradient is selected from `THEME_REGISTRY` at trigger time. Applied via `lr_gradient()` — the same function that colors headlines and messages.
+
+### Positioning
+
+Figment is centered in the viewport. Each visible row is an ANSI `\033[row;colH` command appended to the buffer, identical to how the message overlay works.
+
+## Section 3: FigmentPlugin (Effect Plugin)
+
+**File: `effects_plugins/figment.py`**
+
+An `EffectPlugin(ABC)` subclass that owns the figment lifecycle.
+
+### Responsibilities
+
+- **Timer**: Tracks elapsed frames. At the configured interval (default 60s), triggers a new figment.
+- **SVG selection**: Randomly picks from `figments/*.svg`. Avoids repeating the last shown.
+- **State machine**: `idle -> reveal -> hold -> dissolve -> idle`. Tracks phase progress (0.0 to 1.0).
+- **Color selection**: Picks a random theme key (`"green"`, `"orange"`, `"purple"`) at trigger time.
+- **Rasterization**: Calls `rasterize_svg()` on trigger, caches result for the display duration.
+
+### State Machine
+
+```
+idle ──(timer fires or trigger received)──> reveal
+reveal ──(progress >= 1.0)──> hold
+hold ──(progress >= 1.0)──> dissolve
+dissolve ──(progress >= 1.0)──> idle
+```
+
+### Interface
+
+The `process()` method is a no-op (returns buffer unchanged) since rendering is handled by the overlay. The plugin exposes state via:
+
+```python
+def get_figment_state(self, frame_number: int) -> FigmentState | None:
+ """Tick the state machine and return current state, or None if idle."""
+```
+
+This mirrors the `ntfy_poller.get_active_message()` pattern.
+
+### EffectConfig
+
+- `enabled`: bool (default `False` — opt-in)
+- `intensity`: float — scales strobe frequency and reveal/dissolve speed
+- `params`:
+ - `interval_secs`: 60 (time between figments)
+ - `display_secs`: 4.5 (total animation duration)
+ - `figment_dir`: "figments" (SVG source directory)
+
+Controllable via C&C: `/effects figment on`, `/effects figment intensity 0.7`.
+
+## Section 4: Input Abstraction (FigmentTrigger)
+
+**File: `engine/figment_trigger.py`**
+
+### Protocol
+
+```python
+class FigmentTrigger(Protocol):
+ def poll(self) -> FigmentCommand | None: ...
+```
+
+### FigmentCommand
+
+```python
+@dataclass
+class FigmentCommand:
+ action: str # "trigger" | "set_intensity" | "set_interval" | "set_color" | "stop"
+ value: float | str | None = None
+```
+
+### Adapters
+
+| Adapter | Transport | Dependency | Status |
+|---------|-----------|------------|--------|
+| `NtfyTrigger` | Existing C&C ntfy topic | None (reuses ntfy) | v1 |
+| `MqttTrigger` | MQTT broker | `paho-mqtt` (optional) | Future |
+| `SerialTrigger` | USB serial | `pyserial` (optional) | Future |
+
+### Integration
+
+The `FigmentPlugin` accepts a list of triggers. Each frame, it polls all triggers and acts on commands. Triggers are optional — if none are configured, the plugin runs on its internal timer alone.
+
+### EventBus Bridge
+
+Any trigger can publish a `FIGMENT_TRIGGER` event to the EventBus, allowing other components to react (logging, multi-display sync).
+
+## Section 5: Scroll Loop Integration
+
+Minimal change to `engine/scroll.py` — 5 lines following the ntfy overlay pattern:
+
+```python
+# In stream() setup:
+figment_plugin = registry.get("figment")
+
+# In frame loop, after effects processing, before display.show():
+if figment_plugin and figment_plugin.config.enabled:
+ figment_state = figment_plugin.get_figment_state(frame_number)
+ if figment_state is not None:
+ figment_overlay = render_figment_overlay(figment_state, w, h)
+ buf.extend(figment_overlay)
+```
+
+### Overlay Priority
+
+Figment overlay appends **after** effects processing but **before** the ntfy message overlay. This means:
+- Ntfy messages always appear on top of figments (higher priority)
+- Existing glitch/noise effects run over the ticker underneath the figment
+
+## Section 6: File Summary
+
+### New Files
+
+| File | Purpose |
+|------|---------|
+| `effects_plugins/figment.py` | FigmentPlugin — lifecycle, timer, state machine, SVG selection |
+| `engine/figment_render.py` | SVG to half-block rasterization pipeline |
+| `engine/figment_trigger.py` | FigmentTrigger protocol, FigmentCommand, NtfyTrigger adapter |
+| `tests/test_figment.py` | Plugin lifecycle, state machine, timer, overlay rendering |
+| `tests/fixtures/test.svg` | Minimal SVG for testing rasterization |
+
+### Modified Files
+
+| File | Change |
+|------|--------|
+| `engine/scroll.py` | 5-line figment overlay integration |
+| `engine/layers.py` | Add `render_figment_overlay()` function |
+| `pyproject.toml` | Add `cairosvg` as optional dependency |
+
+## Testing Strategy
+
+- **Unit**: State machine transitions, timer accuracy, SVG rasterization output, FigmentCommand parsing
+- **Integration**: Plugin discovery, overlay rendering with mock terminal dimensions, C&C command handling
+- **Fixture**: Minimal `test.svg` (simple shape) for deterministic rasterization tests
diff --git a/figments/animal-head-symbol-of-mexico-antique-cultures-svgrepo-com.svg b/figments/animal-head-symbol-of-mexico-antique-cultures-svgrepo-com.svg
new file mode 100644
index 0000000..264eb08
--- /dev/null
+++ b/figments/animal-head-symbol-of-mexico-antique-cultures-svgrepo-com.svg
@@ -0,0 +1,32 @@
+
+
+
+
\ No newline at end of file
diff --git a/figments/mayan-mask-of-mexico-svgrepo-com.svg b/figments/mayan-mask-of-mexico-svgrepo-com.svg
new file mode 100644
index 0000000..75fca60
--- /dev/null
+++ b/figments/mayan-mask-of-mexico-svgrepo-com.svg
@@ -0,0 +1,60 @@
+
+
+
+
\ No newline at end of file
diff --git a/figments/mayan-symbol-of-mexico-svgrepo-com.svg b/figments/mayan-symbol-of-mexico-svgrepo-com.svg
new file mode 100644
index 0000000..a396536
--- /dev/null
+++ b/figments/mayan-symbol-of-mexico-svgrepo-com.svg
@@ -0,0 +1,110 @@
+
+
+
+
\ No newline at end of file