The book thesis, playable: ONE press fires the firma across channels, with a mixer to mute each and watch them layer. The 4 visible channels (color · motion · depth · shape) compose from one event with REAL eidos tokens — verified: press -> primary->affirm-solid + scale(0.93) + shadow overlay->raised + --shape-smoothing 2->3.4. Sound + haptic via Web Audio / Vibration (in-app the sema engine emits them from the same event). + the two-moment model section. CHANNELS_SYNTHESIS.md ties the five channel RFCs: two moments x N channels from one event, the layer ownership (sema runs sound/haptic + projects data-event-*; eidos materializes the visual channels), the cross-modal firma, the runtime-builder quintet. Verified: check 0 errors. Browser: all four visible channels compose on press. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>active-uix
parent
ac2ef0f19a
commit
21057c398f
@ -0,0 +1,81 @@
|
|||||||
|
# Los canales como un solo sistema — síntesis ("Diseñando lo que ocurre")
|
||||||
|
|
||||||
|
> **Capstone** de los RFCs por canal. La tesis del libro *Diseñando lo que ocurre*: una
|
||||||
|
> interacción **no es** color, ni movimiento, ni sonido — es **un solo suceso perceptual
|
||||||
|
> repartido en N canales**, bajo el modelo de **dos momentos**. Esto ata
|
||||||
|
> `COLOR_ENGINE_RFC` · `TYPOGRAPHY_ENGINE_RFC` · `DEPTH_ENGINE_RFC` · `SHAPE_ENGINE_RFC` ·
|
||||||
|
> `STRUCTURE_ENGINE_RFC`. Demo vivo: **`/temas/orquesta`** (el mezclador).
|
||||||
|
|
||||||
|
## 1. El modelo de dos momentos (transversal)
|
||||||
|
|
||||||
|
Todo canal —no solo motion— respeta el mismo eje doble:
|
||||||
|
|
||||||
|
| Momento | Atributo | Qué es | Quién |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **estado** | `data-state` | lo que el elemento **es** (persistente): color de reposo, plano, forma, posición | soma / morfo |
|
||||||
|
| **evento** | `data-event-*` | lo que le **ocurre** (transitorio, durante el `hold`): el tinte que pulsa, la sombra que recede, la esquina que se afirma, el tick, la vibración | sema (proyecta) → eidos (materializa visual) |
|
||||||
|
|
||||||
|
El orden entre ambos lo fija `sequence` (`pre` / `coincident` / `post`). Eidos lee **los dos**:
|
||||||
|
`presets` sobre `data-state`, `signatures` sobre `data-event-*`.
|
||||||
|
|
||||||
|
## 2. Los 8 canales del libro → sistemas Eidos
|
||||||
|
|
||||||
|
| Canal (libro) | Sistema | RFC / motor |
|
||||||
|
|---|---|---|
|
||||||
|
| tiempo + movimiento | **motion** | `arts/motion` + `eidos/lib/motion` (firma + presets) |
|
||||||
|
| presencia + profundidad | **depth** | `DEPTH_ENGINE_RFC` |
|
||||||
|
| **forma** | **shape** | `SHAPE_ENGINE_RFC` |
|
||||||
|
| color | **color** | `COLOR_ENGINE_RFC` |
|
||||||
|
| sonido | **sema · sound** | `sema/chans/sound` |
|
||||||
|
| háptica | **sema · haptic** | `sema/chans/haptic` |
|
||||||
|
|
||||||
|
Más el **espacio** (`STRUCTURE_ENGINE_RFC`) — estructural, **solo-estado** (el escenario, no el
|
||||||
|
suceso): no tiene momento-evento, y eso es deliberado.
|
||||||
|
|
||||||
|
## 3. Quién posee qué (la narrativa de capas)
|
||||||
|
|
||||||
|
- **Sema** ejecuta **2 canales runtime** (`sound` + `haptic`) y **proyecta el meta-canal visual**
|
||||||
|
(estampa `data-event-*` durante el hold). El motor no conoce DOM/CSS.
|
||||||
|
- **Eidos** materializa los **canales visuales** (motion · depth · shape · color) reaccionando en
|
||||||
|
CSS a `data-event-*` + `data-state`. Es el único dueño visual.
|
||||||
|
|
||||||
|
(Narrativa canónica completa en `CLAUDE.md` → "Sema: open channel registry".)
|
||||||
|
|
||||||
|
## 4. La composición — un evento, N canales
|
||||||
|
|
||||||
|
La **firma** (`BUILTIN_SIGNATURES` + `BUILTIN_KEYFRAMES`) es **cross-modal**: un solo keyframe
|
||||||
|
lleva varias modalidades. Ejemplo real (`press-squeeze`, familia `contact`):
|
||||||
|
|
||||||
|
```
|
||||||
|
contact · press → scale 0.96 (motion)
|
||||||
|
+ box-shadow → plano (depth recede)
|
||||||
|
+ --shape-smoothing 2→3 (shape: la esquina se afirma)
|
||||||
|
+ tick (sema sound)
|
||||||
|
+ vibración (sema haptic)
|
||||||
|
```
|
||||||
|
|
||||||
|
Un `engine.emit(...)` estampa `data-event-*` (los canales visuales reaccionan) **y** dispara
|
||||||
|
sound + haptic — desde el **mismo** evento. No hay 6 sistemas coordinándose a mano; hay un
|
||||||
|
suceso que se expresa en 6 dimensiones.
|
||||||
|
|
||||||
|
## 5. Builders runtime — el quinteto (jaula abierta)
|
||||||
|
|
||||||
|
Cada eje retunable en runtime, mismo patrón (semilla → bloque gestionado), opt-in sobre la
|
||||||
|
escala authored:
|
||||||
|
|
||||||
|
`applyColorScheme` · `applyTypeScale` · `applyDepth` · `applyShape` · `applySpacing`
|
||||||
|
|
||||||
|
(Futuro: `applyTheme(seed)` que componga varios desde una semilla compartida.)
|
||||||
|
|
||||||
|
## 6. Posición frente a referentes
|
||||||
|
|
||||||
|
Ningún referente reúne los canales bajo **un modelo único**. Material tiene morph de forma +
|
||||||
|
motion (ad-hoc, sin sonido/háptica integrados); Apple, continuidad + materiales (atado a
|
||||||
|
plataforma); todos, color. **Eidos los reúne** —los 6 perceptuales + el estructural— sobre el
|
||||||
|
modelo de dos momentos, disparados desde un evento, con builders runtime y registro abierto.
|
||||||
|
Eso es el framework, no la suma de sus partes.
|
||||||
|
|
||||||
|
## 7. Demo
|
||||||
|
|
||||||
|
`/temas/orquesta` — el mezclador: pulsa un control y silencia cada canal para oír su parte. Los
|
||||||
|
4 visibles usan tokens reales; sound + haptic los aproxima el demo (en la app, motor sema).
|
||||||
@ -0,0 +1,281 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
/**
|
||||||
|
* Capstone — "Diseñando lo que ocurre" made literal: ONE interaction firing many channels at
|
||||||
|
* once, with a mixer to mute each and watch them layer.
|
||||||
|
*
|
||||||
|
* The 4 visible channels (color · motion · depth · shape) use REAL eidos tokens, composed
|
||||||
|
* explicitly per-channel so each is independently mutable. Sound + haptic are fired with the
|
||||||
|
* Web Audio / Vibration APIs directly — in-app the sema engine emits them from the SAME event
|
||||||
|
* (the canonical sound + haptic channels); here they're approximated so the mixer can mute them.
|
||||||
|
*
|
||||||
|
* The point: an interaction is not color, nor motion, nor sound — it is one perceptual event
|
||||||
|
* across N channels, under the two-moment model. This is the book's thesis, playable.
|
||||||
|
*/
|
||||||
|
import '@/uix/eidos/index.css'
|
||||||
|
|
||||||
|
let theme = $state<'light' | 'dark'>('dark')
|
||||||
|
$effect(() => {
|
||||||
|
const el = document.documentElement
|
||||||
|
const prev = el.getAttribute('data-theme')
|
||||||
|
el.setAttribute('data-theme', theme)
|
||||||
|
return () => {
|
||||||
|
if (prev === null) el.removeAttribute('data-theme')
|
||||||
|
else el.setAttribute('data-theme', prev)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
type ChannelId = 'color' | 'motion' | 'depth' | 'shape' | 'sound' | 'haptic';
|
||||||
|
const CHANNELS: { id: ChannelId; label: string; note: string; eidos: boolean }[] = [
|
||||||
|
{ id: 'color', label: 'color', note: 'tinte de intención (→ affirm)', eidos: true },
|
||||||
|
{ id: 'motion', label: 'movimiento', note: 'escala bajo el dedo', eidos: true },
|
||||||
|
{ id: 'depth', label: 'profundidad', note: 'la sombra recede', eidos: true },
|
||||||
|
{ id: 'shape', label: 'forma', note: 'la esquina se afirma', eidos: true },
|
||||||
|
{ id: 'sound', label: 'sonido', note: 'un tick (motor sema)', eidos: false },
|
||||||
|
{ id: 'haptic', label: 'háptica', note: 'una vibración (motor sema)', eidos: false }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Visible channels on by default; sound + haptic OFF (audible/tactile side effects — sema canon).
|
||||||
|
let channels = $state<Record<ChannelId, boolean>>({
|
||||||
|
color: true,
|
||||||
|
motion: true,
|
||||||
|
depth: true,
|
||||||
|
shape: true,
|
||||||
|
sound: false,
|
||||||
|
haptic: false
|
||||||
|
});
|
||||||
|
|
||||||
|
let pressed = $state(false);
|
||||||
|
let pressCount = $state(0);
|
||||||
|
let lastFired = $state<string[]>([]);
|
||||||
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
let actx: AudioContext | undefined;
|
||||||
|
|
||||||
|
function tick(): void {
|
||||||
|
try {
|
||||||
|
actx ??= new (window.AudioContext ?? (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext)();
|
||||||
|
const o = actx.createOscillator();
|
||||||
|
const g = actx.createGain();
|
||||||
|
o.type = 'triangle';
|
||||||
|
o.frequency.value = 660;
|
||||||
|
o.connect(g);
|
||||||
|
g.connect(actx.destination);
|
||||||
|
const t = actx.currentTime;
|
||||||
|
g.gain.setValueAtTime(0.0001, t);
|
||||||
|
g.gain.exponentialRampToValueAtTime(0.16, t + 0.005);
|
||||||
|
g.gain.exponentialRampToValueAtTime(0.0001, t + 0.13);
|
||||||
|
o.start(t);
|
||||||
|
o.stop(t + 0.14);
|
||||||
|
} catch {
|
||||||
|
/* audio unavailable — no-op */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fire(): void {
|
||||||
|
pressCount += 1;
|
||||||
|
lastFired = CHANNELS.filter((c) => channels[c.id]).map((c) => c.label);
|
||||||
|
if (channels.sound) tick();
|
||||||
|
if (channels.haptic) navigator.vibrate?.(18);
|
||||||
|
pressed = true;
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => (pressed = false), 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeCount = $derived(Object.values(channels).filter(Boolean).length);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head><title>La orquesta · Eidos</title></svelte:head>
|
||||||
|
|
||||||
|
<div class="root" data-theme={theme}>
|
||||||
|
<header class="masthead">
|
||||||
|
<div class="mast-row">
|
||||||
|
<p class="kicker">Eidos · diseñando lo que ocurre</p>
|
||||||
|
<button class="theme-toggle" onclick={() => (theme = theme === 'light' ? 'dark' : 'light')}>
|
||||||
|
{theme === 'light' ? '◐ claro' : '◑ oscuro'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<h1 class="mast-title">Un evento, <em>seis canales</em></h1>
|
||||||
|
<p class="mast-lede">
|
||||||
|
Una interacción no es color, ni movimiento, ni sonido — es <strong>un solo suceso
|
||||||
|
perceptual</strong> repartido en varios canales, a la vez. Hemos construido los seis. Aquí
|
||||||
|
tocan juntos: pulsa, y <strong>silencia</strong> cada uno para oír su parte.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- ── La orquesta ──────────────────────────────────────────────────── -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="sec-head">
|
||||||
|
<h2 class="sec-title">El mezclador</h2>
|
||||||
|
<p class="sec-note">
|
||||||
|
Pulsa el botón: los canales activos disparan desde <em>un</em> evento. Apaga la forma y la
|
||||||
|
esquina deja de afirmarse; apaga la profundidad y la sombra no recede. Un instrumento
|
||||||
|
menos, el mismo gesto.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stage">
|
||||||
|
<div class="stage-target">
|
||||||
|
<button
|
||||||
|
class="target"
|
||||||
|
class:ch-color={channels.color}
|
||||||
|
class:ch-motion={channels.motion}
|
||||||
|
class:ch-depth={channels.depth}
|
||||||
|
class:ch-shape={channels.shape}
|
||||||
|
data-shape={channels.shape ? 'continuous' : undefined}
|
||||||
|
data-pressed={pressed}
|
||||||
|
onpointerdown={fire}
|
||||||
|
>Confirmar</button>
|
||||||
|
<span class="stage-hint">pulsa →</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mixer">
|
||||||
|
{#each CHANNELS as c}
|
||||||
|
<button class="track" data-on={channels[c.id]} onclick={() => (channels[c.id] = !channels[c.id])}>
|
||||||
|
<span class="track-led" data-on={channels[c.id]}></span>
|
||||||
|
<span class="track-meta">
|
||||||
|
<span class="track-label">{c.label}</span>
|
||||||
|
<span class="track-note">{c.note}</span>
|
||||||
|
</span>
|
||||||
|
<span class="track-tag" data-layer={c.eidos ? 'eidos' : 'sema'}>{c.eidos ? 'eidos' : 'sema'}</span>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="trace">
|
||||||
|
<span class="trace-k">último evento</span>
|
||||||
|
<span>1 press → {lastFired.length} canal(es){lastFired.length ? `: ${lastFired.join(' · ')}` : ' — pulsa'}</span>
|
||||||
|
<span class="trace-sep">·</span>
|
||||||
|
<span class="trace-k">activos</span><span>{activeCount}/6</span>
|
||||||
|
<span class="trace-sep">·</span>
|
||||||
|
<span class="trace-k">presses</span><span>{pressCount}</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ── Los dos momentos ─────────────────────────────────────────────── -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="sec-head">
|
||||||
|
<h2 class="sec-title">Dos momentos, un solo modelo</h2>
|
||||||
|
<p class="sec-note">
|
||||||
|
Todo canal respeta el mismo rigor: lo que el elemento <em>es</em> (estado, persistente) vs
|
||||||
|
lo que le <em>ocurre</em> (evento, durante el hold). El mezclador de arriba dispara el
|
||||||
|
momento-<em>evento</em>; el reposo es el momento-<em>estado</em>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="moments">
|
||||||
|
<div class="moment">
|
||||||
|
<span class="moment-h">estado · <code>data-state</code></span>
|
||||||
|
<p class="moment-p">color de reposo · plano de profundidad · forma en reposo · posición. Persistente — soma/morfo.</p>
|
||||||
|
</div>
|
||||||
|
<div class="moment moment-event">
|
||||||
|
<span class="moment-h">evento · <code>data-event-*</code></span>
|
||||||
|
<p class="moment-p">el tinte que pulsa · la sombra que recede · la esquina que se afirma · el tick · la vibración. Transitorio — sema, durante el hold.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="footnote">
|
||||||
|
<p>
|
||||||
|
<strong>Esto es el framework, no seis sistemas sueltos.</strong> Cada canal tiene su motor y
|
||||||
|
su demo (<code>/temas/{'{'}color · tipografia · profundidad · forma · estructura{'}'}</code>),
|
||||||
|
pero la tesis del libro es que <em>componen</em>: un evento sema reparte la firma por
|
||||||
|
motion + depth + shape + color (visual) y sound + haptic (sema), bajo el modelo de dos
|
||||||
|
momentos. Aquí los cuatro visibles usan tokens reales de eidos; sonido y háptica los
|
||||||
|
aproxima el demo (en la app los emite el motor sema desde el mismo evento). Ningún referente
|
||||||
|
reúne los seis canales sobre un modelo único.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.root {
|
||||||
|
min-block-size: 100vh;
|
||||||
|
background:
|
||||||
|
radial-gradient(130% 90% at 50% -15%, var(--color-surface-raised), transparent 55%),
|
||||||
|
var(--color-surface-default);
|
||||||
|
color: var(--color-content-primary);
|
||||||
|
font-family: var(--font-family-primary);
|
||||||
|
padding: clamp(1.5rem, 5vw, 5rem) clamp(1.25rem, 5vw, 6rem) 6rem;
|
||||||
|
--rule: var(--color-border-default);
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead { max-inline-size: 56rem; margin-block-end: clamp(3rem, 8vw, 6rem); }
|
||||||
|
.mast-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-block-end: 2.5rem; }
|
||||||
|
.kicker { margin: 0; font-family: var(--font-family-mono); font-size: var(--font-size-xs); letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-content-muted); }
|
||||||
|
.theme-toggle { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-content-secondary); background: var(--color-surface-raised); border: 1px solid var(--rule); border-radius: 999px; padding: 0.5rem 1rem; cursor: pointer; }
|
||||||
|
.theme-toggle:hover { color: var(--color-content-primary); }
|
||||||
|
.mast-title { font-family: var(--font-family-display); font-size: var(--font-size-xxxl); font-weight: 600; line-height: 1; letter-spacing: -0.03em; margin: 0 0 1.25rem; text-wrap: balance; }
|
||||||
|
.mast-title em { font-style: italic; color: var(--color-primary-text, var(--color-content-primary)); }
|
||||||
|
.mast-lede { max-inline-size: 52ch; font-size: var(--font-size-lg); line-height: 1.5; color: var(--color-content-secondary); margin: 0; text-wrap: pretty; }
|
||||||
|
.mast-lede strong { color: var(--color-content-primary); font-weight: 600; }
|
||||||
|
|
||||||
|
.section { margin-block-end: clamp(3.5rem, 9vw, 7rem); }
|
||||||
|
.sec-head { margin-block-end: 2.5rem; }
|
||||||
|
.sec-title { font-family: var(--font-family-display); font-size: var(--font-size-xl); font-weight: 600; letter-spacing: -0.01em; margin: 0 0 0.5rem; }
|
||||||
|
.sec-note { margin: 0; max-inline-size: 64ch; font-size: var(--font-size-sm); line-height: 1.55; color: var(--color-content-muted); }
|
||||||
|
.sec-note em { color: var(--color-content-primary); font-style: normal; font-weight: 600; }
|
||||||
|
code { font-family: var(--font-family-mono); font-size: 0.9em; color: var(--color-content-secondary); background: var(--color-surface-muted); padding: 0.1em 0.4em; border-radius: 4px; }
|
||||||
|
|
||||||
|
/* The orchestra */
|
||||||
|
.stage { display: grid; grid-template-columns: 1fr; gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
|
||||||
|
@media (min-width: 50rem) { .stage { grid-template-columns: 1fr 22rem; } }
|
||||||
|
.stage-target { display: flex; flex-direction: column; align-items: center; gap: 1.25rem; min-block-size: 16rem; justify-content: center; border-radius: var(--radius-lg); border: 1px solid color-mix(in srgb, var(--rule) 60%, transparent); background: var(--color-surface-default); padding: clamp(2rem, 5vw, 4rem); }
|
||||||
|
.stage-hint { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-content-muted); }
|
||||||
|
|
||||||
|
/* the target — each channel's effect gated by its class + [data-pressed], all transitioning
|
||||||
|
together from one event. --shape-smoothing transitions because it's a registered @property. */
|
||||||
|
.target {
|
||||||
|
font-family: var(--font-family-primary);
|
||||||
|
font-size: var(--font-size-lg);
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 1.1rem 2.5rem;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 22px;
|
||||||
|
background: var(--color-primary-solid);
|
||||||
|
color: var(--color-primary-on-solid, #fff);
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
transform 150ms var(--ease-out, cubic-bezier(0.2, 0, 0, 1)),
|
||||||
|
box-shadow 150ms var(--ease-out, ease),
|
||||||
|
background-color 150ms ease,
|
||||||
|
--shape-smoothing 150ms ease;
|
||||||
|
will-change: transform, box-shadow;
|
||||||
|
}
|
||||||
|
.target.ch-depth { box-shadow: var(--depth-overlay-shadow), var(--depth-overlay-halo); }
|
||||||
|
.target.ch-motion[data-pressed='true'] { transform: scale(0.93); }
|
||||||
|
.target.ch-depth[data-pressed='true'] { box-shadow: var(--depth-raised-shadow); }
|
||||||
|
.target.ch-shape[data-pressed='true'] { --shape-smoothing: 3.4; }
|
||||||
|
.target.ch-color[data-pressed='true'] { background: var(--color-affirm-solid, var(--color-primary-solid)); }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.target { transition: background-color 150ms ease; }
|
||||||
|
.target[data-pressed='true'] { transform: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.mixer { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||||
|
.track { display: flex; align-items: center; gap: 0.85rem; padding: 0.7rem 0.9rem; border-radius: var(--radius-md); border: 1px solid var(--rule); background: var(--color-surface-raised); cursor: pointer; text-align: start; transition: border-color 160ms, background 160ms; }
|
||||||
|
.track[data-on='false'] { opacity: 0.5; }
|
||||||
|
.track[data-on='true']:hover { border-color: color-mix(in srgb, var(--color-primary-solid) 40%, var(--rule)); }
|
||||||
|
.track-led { inline-size: 0.7rem; block-size: 0.7rem; border-radius: 50%; flex: none; background: var(--color-surface-muted); border: 1px solid var(--rule); transition: background 160ms, box-shadow 160ms; }
|
||||||
|
.track-led[data-on='true'] { background: var(--color-primary-solid); border-color: var(--color-primary-solid); box-shadow: 0 0 8px color-mix(in srgb, var(--color-primary-solid) 60%, transparent); }
|
||||||
|
.track-meta { display: flex; flex-direction: column; gap: 0.1rem; flex: 1; min-inline-size: 0; }
|
||||||
|
.track-label { font-family: var(--font-family-mono); font-size: var(--font-size-sm); color: var(--color-content-primary); }
|
||||||
|
.track-note { font-size: var(--font-size-xs); color: var(--color-content-muted); }
|
||||||
|
.track-tag { font-family: var(--font-family-mono); font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.06em; padding: 0.15rem 0.45rem; border-radius: 4px; }
|
||||||
|
.track-tag[data-layer='eidos'] { background: color-mix(in oklab, #12a594 22%, transparent); color: #2dd4bf; }
|
||||||
|
.track-tag[data-layer='sema'] { background: color-mix(in oklab, var(--color-primary-solid) 24%, transparent); color: var(--color-primary-text, #c4b5fd); }
|
||||||
|
|
||||||
|
.trace { margin-block-start: 2rem; display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-content-secondary); padding-block-start: 1.25rem; border-block-start: 1px solid color-mix(in srgb, var(--rule) 60%, transparent); }
|
||||||
|
.trace-k { color: var(--color-content-muted); text-transform: uppercase; letter-spacing: 0.06em; }
|
||||||
|
.trace-sep { color: var(--color-content-faint, var(--color-content-muted)); }
|
||||||
|
|
||||||
|
/* Two moments */
|
||||||
|
.moments { display: grid; grid-template-columns: 1fr; gap: 1.25rem; }
|
||||||
|
@media (min-width: 44rem) { .moments { grid-template-columns: 1fr 1fr; } }
|
||||||
|
.moment { padding: 1.5rem; border-radius: var(--radius-lg); border: 1px solid var(--rule); background: var(--color-surface-raised); display: flex; flex-direction: column; gap: 0.6rem; }
|
||||||
|
.moment-event { background: var(--color-surface-overlay); box-shadow: var(--depth-overlay-shadow), var(--depth-overlay-halo); }
|
||||||
|
.moment-h { font-family: var(--font-family-mono); font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-content-secondary); }
|
||||||
|
.moment-p { margin: 0; font-size: var(--font-size-sm); line-height: 1.55; color: var(--color-content-muted); }
|
||||||
|
|
||||||
|
.footnote { max-inline-size: 60rem; padding-block-start: 2rem; border-block-start: 1px solid var(--rule); }
|
||||||
|
.footnote p { margin: 0; font-size: var(--font-size-sm); line-height: 1.6; color: var(--color-content-muted); text-wrap: pretty; }
|
||||||
|
.footnote strong { color: var(--color-content-primary); font-weight: 600; }
|
||||||
|
</style>
|
||||||
Loading…
Reference in new issue