@ -1,12 +1,17 @@
< script lang = "ts" >
/**
* Depth demo — the Eidos depth channel, made visceral.
* Depth demo — the Eidos depth channel, made visceral, at reference depth .
*
* Not abstract tiles: real UI that RESPONDS. Three beats —
* 1. Reacciona — cards lift on hover (state moment: depth responds to interaction).
* 2. Asciende — a dialog RISES over a scrim on open (event moment: the `present-rise`
* firma — shadow grows + lift — fired by stamping `data-event-*`, as sema would).
* 3. Cada plano, un rol — the 5 planes shown as the real components they're for.
* Not abstract tiles: real UI that RESPONDS, plus the full catalog surface.
* 1. Reacciona — cards lift on hover (state moment).
* 2. Estados — depth responds to EVERY interaction state (dynamic elevation).
* 3. Asciende — a dialog RISES over a scrim on open (event moment: `present-rise`).
* 4. Escalera — the 5 planes as an ascending z-stack (the elevation scale).
* 5. Cada plano… — the planes shown as the real components they're for.
* 6. Catálogo — resting-plane table (the Material-style spec, our vocabulary).
* 7. Luz vs sombra — light/dark side-by-side: how the cue adapts to the mode.
* 8. Jaula abierta — retune the depth cue live (escape hatch).
* 9. Accesibilidad — depth is never the only signal; degrades by preference.
*
* Self-contained: eidos foundation (planes + firma) + `data-theme`.
*/
@ -14,6 +19,21 @@
let theme = $state< 'light' | 'dark'>('dark')
// Mirror the theme onto < html > . The `--depth-*` foundation tokens live at
// `:root` and reference `var(--color-surface-*)` / `var(--shadow-*)`; custom
// properties resolve var() at their declaration site, so the theme must be
// active at `:root` (not just on a descendant div) for the depth surface +
// shadow to stay mode-aware. This is the faithful real-app setup.
$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)
}
})
// ── Ascenso: a dialog that rises over a scrim (the event moment) ────────────
let open = $state(false)
let evt = $state< { event? : string ; phase? : string } >({} )
@ -29,6 +49,47 @@
open = false
evt = {}
}
// ── Jaula abierta: retune the depth cue live (escape hatch) ─────────────────
const SHADOW_VARIANTS = [
{ key : 'canon' , label : 'canónica' , shadow : 'var(--depth-overlay-shadow)' } ,
{ key : 'intense' , label : 'intensa' , shadow : '0 30px 70px rgb(0 0 0 / 0.5)' } ,
{
key: 'brand',
label: 'de marca',
shadow: '0 24px 56px color-mix(in oklab, var(--color-primary-solid) 55%, transparent)'
},
{ key : 'raw' , label : 'primitivo crudo' , shadow : 'var(--shadow-3)' }
] as const
let shadowKey = $state< string > ('canon')
const activeShadow = $derived(
SHADOW_VARIANTS.find((v) => v.key === shadowKey)?.shadow ?? SHADOW_VARIANTS[0].shadow
)
// ── Estados de interacción: la profundidad responde a CADA estado (dynamic elevation) ──
const STATE_LADDER = [
{ state : 'reposo' , moves : 'en su plano' , shadow : 'var(--depth-raised-shadow)' , lift : '0px' } ,
{ state : 'hover' , moves : 'sube un plano' , shadow : 'var(--depth-overlay-shadow)' , lift : '-5px' } ,
{ state : 'focus' , moves : 'sube + anillo' , shadow : 'var(--depth-overlay-shadow)' , lift : '-5px' } ,
{ state : 'pressed' , moves : 'se hunde' , shadow : 'var(--depth-raised-shadow)' , lift : '1px' } ,
{ state : 'disabled' , moves : 'sin relieve' , shadow : 'none' , lift : '0px' }
] as const
// ── Catálogo de planos en reposo (resting elevation — el equivalente a la tabla de Material) ──
const PLANE_CATALOG = [
{ plane : 'recessed' , z : 'base' , use : 'inputs activos · pozos · tracks · sliders' } ,
{ plane : 'flush' , z : 'base' , use : 'chips · badges · separadores · botones planos' } ,
{ plane : 'raised' , z : 'raised' , use : 'tarjetas · paneles · app bar · acordeones' } ,
{ plane : 'overlay' , z : 'popover' , use : 'menús · popovers · tooltips · selects · toasts' } ,
{ plane : 'modal' , z : 'modal' , use : 'diálogos · sheets · drawers · command palette' }
] as const
// The 5 planes bottom→top, for the z-stack ladder.
const PLANE_LADDER = ['recessed', 'flush', 'raised', 'overlay', 'modal'] as const
// The rim-light halo (canonical overlay value) — written inline in the light/dark panels so
// each resolves against its own nested `data-theme` (the `--depth-*` tokens can't, see $effect).
const RIM_HALO = 'inset 0 1px 0 0 color-mix(in oklab, white 7%, transparent)'
< / script >
< svelte:head > < title > Profundidad · Eidos< / title > < / svelte:head >
@ -65,7 +126,39 @@
< / div >
< / section >
<!-- ── 2 · Asciende (evento · la firma) ───────────────────────────────── -->
<!-- ── 2 · Estados (la profundidad responde a CADA estado) ──────────────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Responde a cada estado< / h2 >
< p class = "sec-note" >
No solo al hover. Reposo, foco, pulsado, deshabilitado — la profundidad < em > cambia con cada
estado< / em > . Donde Material publica una tabla de niveles por estado, aquí es un solo canal que
reacciona. El último control es real: pásale el cursor, enfócalo con < kbd > Tab< / kbd > , púlsalo.
< / p >
< / div >
< div class = "states" >
{ #each STATE_LADDER as s }
< figure class = "state" >
< div class = "state-stage" >
< span
class="state-chip"
data-state={ s . state }
style="box-shadow: { s . shadow } ; translate: 0 { s . lift } ;"
>Guardar< / span >
< / div >
< figcaption >< b > { s . state } </ b > · { s . moves } </ figcaption >
< / figure >
{ /each }
< figure class = "state" >
< div class = "state-stage" >
< button class = "state-chip state-btn" > Guardar< / button >
< / div >
< figcaption > < b > en vivo< / b > · hover · foco · pulsa< / figcaption >
< / figure >
< / div >
< / section >
<!-- ── 3 · Asciende (evento · la firma) ───────────────────────────────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Asciende< / h2 >
@ -112,7 +205,27 @@
< / div >
< / section >
<!-- ── 3 · Cada plano, un rol ─────────────────────────────────────────── -->
<!-- ── 4 · Escalera de planos (la jerarquía de un vistazo) ──────────────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > La escalera de planos< / h2 >
< p class = "sec-note" >
Cinco posiciones en la jerarquía de atención, de hundido a cima:
< code > recessed → flush → raised → overlay → modal< / code > . Cada peldaño compone superficie +
sombra + z — míralos trepar hacia ti.
< / p >
< / div >
< div class = "ladder" >
{ #each PLANE_LADDER as plane , i }
< div class = "ladder-step" data-depth = { plane } style="--i: { i } ;" >
< span class = "ladder-name" > { plane } </ span >
< span class = "ladder-z" > z · { PLANE_CATALOG . find (( p ) => p . plane === plane ) ? . z } </ span >
< / div >
{ /each }
< / div >
< / section >
<!-- ── 5 · Cada plano, un rol ─────────────────────────────────────────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Cada plano, un rol< / h2 >
@ -183,6 +296,119 @@
< / div >
< / section >
<!-- ── 6 · Catálogo de planos en reposo (la tabla, en nuestro vocabulario) ─ -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Catálogo de planos en reposo< / h2 >
< p class = "sec-note" >
La elevación en reposo de cada familia de componente — el equivalente a la tabla de
especificación de Material, pero en planos nombrados por su rol, no en milímetros.
< / p >
< / div >
< div class = "catalog" role = "table" aria-label = "planos en reposo" >
< div class = "cat-row cat-head" role = "row" >
< span role = "columnheader" > Plano< / span >
< span role = "columnheader" > Banda z< / span >
< span role = "columnheader" > Componentes< / span >
< span role = "columnheader" > Muestra< / span >
< / div >
{ #each PLANE_CATALOG as p }
< div class = "cat-row" role = "row" >
< span class = "cat-plane" role = "cell" >< b > { p . plane } </ b ></ span >
< span class = "cat-z" role = "cell" > { p . z } </ span >
< span class = "cat-use" role = "cell" > { p . use } </ span >
< span class = "cat-sample" role = "cell" >< span class = "cat-tile" data-depth = { p . plane } > </span ></ span >
< / div >
{ /each }
< / div >
< / section >
<!-- ── 7 · Luz vs sombra (cómo la señal se adapta al modo — Fase 2) ─────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Luz vs sombra< / h2 >
< p class = "sec-note" >
La misma tarjeta < code > overlay< / code > , los dos modos a la vez. En claro manda la
< strong > sombra< / strong > proyectada y el halo es invisible. En oscuro la sombra apenas se ve —
toma el relevo el < strong > halo< / strong > : un rim de luz computado en oklab. La señal se
< em > adapta< / em > al modo en vez de mentir.
< / p >
< / div >
< div class = "modes" >
< div class = "mode-panel" data-theme = "light" >
< div class = "mode-card" style = "box-shadow: var(--shadow-overlay), { RIM_HALO } ;" >
< span class = "mode-label" > claro< / span >
< span class = "mode-cue" > la sombra define el relieve< / span >
< / div >
< / div >
< div class = "mode-panel" data-theme = "dark" >
< div class = "mode-card" style = "box-shadow: var(--shadow-overlay), { RIM_HALO } ;" >
< span class = "mode-label" > oscuro< / span >
< span class = "mode-cue" > el halo toma el relevo< / span >
< / div >
< / div >
< / div >
< / section >
<!-- ── 8 · La jaula abierta (escape hatch en vivo) ────────────────────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > La jaula abierta< / h2 >
< p class = "sec-note" >
Opinión fuerte que < em > nunca< / em > atrapa. El plano < code > overlay< / code > trae una sombra
canónica — pero la mezcla es tuya: retúnea por config, anula por componente, o baja al
primitivo crudo. La misma tarjeta, cuatro mezclas — cámbiala y míralo en vivo.
< / p >
< / div >
< div class = "cage" >
< div class = "cage-stage" >
< div class = "cage-card" data-depth = "overlay" style = "box-shadow: { activeShadow } " >
< span class = "cage-card-tag" > data-depth="overlay"< / span >
< span class = "cage-card-title" > Sombra { SHADOW_VARIANTS . find (( v ) => v . key === shadowKey ) ? . label } </ span >
< span class = "cage-card-sub" > el plano no cambia — su mezcla, sí< / span >
< / div >
< / div >
< div class = "cage-panel" >
< div class = "cage-chips" role = "group" aria-label = "variante de sombra" >
{ #each SHADOW_VARIANTS as v }
< button
class="cage-chip"
data-active={ v . key === shadowKey }
onclick={() => ( shadowKey = v . key )}
>
{ v . label }
< / button >
{ /each }
< / div >
< code class = "cage-code" > box-shadow: { activeShadow } ;</ code >
< p class = "cage-foot" >
< b > canónica< / b > = el plano sin tocar · < b > de marca< / b > = mezcla OKLCH con el color
primario · < b > primitivo crudo< / b > = < code > var(--shadow-3)< / code > , siempre a un paso. En
una app real esto vive en < code > EidosConfig.depth< / code > o en un override de recipe — el
inline aquí es solo el atajo más corto a la misma puerta.
< / p >
< / div >
< / div >
< / section >
<!-- ── 9 · Accesibilidad (la profundidad nunca es la única señal) ───────── -->
< section class = "section" >
< div class = "sec-head" >
< h2 class = "sec-title" > Accesibilidad< / h2 >
< p class = "sec-note" > La profundidad acompaña — nunca carga sola con el significado.< / p >
< / div >
< div class = "a11y" >
{ #each [{ t : 'Nunca el único canal' , d : 'Cada plano va con borde, superficie y posición — no solo sombra. Quítale la sombra y la jerarquía sigue legible.' }, { t : 'Respeta reduced-motion' , d : 'El ascenso eventful (la firma) degrada a un cambio de estado instantáneo cuando se prefiere menos movimiento.' }, { t : 'Sobrevive a forced-colors' , d : 'En alto contraste el box-shadow desaparece; el plano se apoya en borde y superficie, que el modo sí respeta.' }, { t : 'Contraste, no relieve' , d : 'La profundidad no sustituye al contraste de texto: el contenido cumple APCA en cualquier plano.' }] as item }
< div class = "a11y-card" data-depth = "raised" >
< span class = "a11y-title" > { item . t } </ span >
< span class = "a11y-body" > { item . d } </ span >
< / div >
{ /each }
< / div >
< / section >
< footer class = "footnote" >
< p >
< strong > Default fuerte, jaula abierta.< / strong > Planos config-driven, primitivos
@ -267,16 +493,16 @@
.app-check { inline - size : 1.05rem ; block - size : 1.05rem ; border - radius : 5px ; border : 1.5px solid var ( -- rule ); flex : none ; }
.app-check[data-on='true'] { background : var ( -- color - primary - solid ); border - color : var ( -- color - primary - solid ); }
/* transparent click-catcher only — a dark scrim would swallow the rising dialog's
shadow (the whole point of a depth demo). The content recedes via `.dimmed` instead. */
.scrim {
position: absolute;
inset: 0;
border: 0;
padding: 0;
background: color-mix(in srgb, var(--color-neutral-contrast, #000) 55%, transparent) ;
background: transparent;
cursor: pointer;
animation: depth-scrim-in 240ms var(--ease-out, ease) both;
}
@keyframes depth-scrim-in { from { opacity : 0 ; } to { opacity : 1 ; } }
.risen {
position: absolute;
@ -341,6 +567,99 @@
.field-label { font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
.field-input { padding : 0.6rem 0.8 rem ; border - radius : var ( -- radius - md ); background : var ( -- color - surface - muted ); color : var ( -- color - content - muted ); font - size : var ( -- font - size - sm ); }
/* 4 · La jaula abierta */
.cage { display : grid ; grid - template - columns : 1fr ; gap : clamp ( 1.5 rem , 4 vw , 3 rem ); align - items : center ; }
@media (min-width: 48rem) { . cage { grid - template - columns : 1.05fr 1 fr ; } }
.cage-stage { min - block - size : 17rem ; display : grid ; place - items : center ; padding : clamp ( 1.5 rem , 4 vw , 3 rem ); border - radius : var ( -- radius - lg ); background : var ( -- color - surface - default ); border : 1px solid color - mix ( in srgb , var ( -- rule ) 60 % , transparent ); }
.cage-card {
inline-size: min(18rem, 100%);
padding: 1.75rem;
border-radius: var(--radius-xl);
background: var(--color-surface-overlay);
border: 1px solid var(--rule);
display: flex;
flex-direction: column;
gap: 0.4rem;
transition: box-shadow 320ms var(--ease-out, cubic-bezier(0.2, 0, 0, 1));
}
.cage-card-tag { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); letter - spacing : 0.02em ; color : var ( -- color - content - muted ); }
.cage-card-title { font - family : var ( -- font - family - display ); font - size : var ( -- font - size - lg ); font - weight : 600 ; color : var ( -- color - content - primary ); }
.cage-card-sub { font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
.cage-panel { display : flex ; flex - direction : column ; gap : 1rem ; }
.cage-chips { display : flex ; flex - wrap : wrap ; gap : 0.5rem ; }
.cage-chip { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); padding : 0.45rem 0.9 rem ; border - radius : 999px ; cursor : pointer ; background : var ( -- color - surface - raised ); color : var ( -- color - content - secondary ); border : 1px solid var ( -- rule ); transition : color 160 ms , background 160 ms , border - color 160 ms ; }
.cage-chip:hover { color : var ( -- color - content - primary ); }
.cage-chip[data-active='true'] { background : var ( -- color - primary - solid ); color : var ( -- color - primary - on - solid , # fff ); border - color : var ( -- color - primary - solid ); }
.cage-code { display : block ; font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); line - height : 1.5 ; padding : 0.85rem 1 rem ; border - radius : var ( -- radius - md ); background : var ( -- color - surface - muted ); color : var ( -- color - content - secondary ); word - break : break - all ; }
.cage-foot { margin : 0 ; font - size : var ( -- font - size - xs ); line - height : 1.6 ; color : var ( -- color - content - muted ); }
.cage-foot b { color : var ( -- color - content - secondary ); font - weight : 600 ; }
/* 2 · Estados (dynamic elevation) */
.states { display : grid ; grid - template - columns : repeat ( auto - fit , minmax ( min ( 100 % , 8.5 rem ), 1 fr )); gap : 1.25rem ; }
.state { margin : 0 ; display : flex ; flex - direction : column ; gap : 0.75rem ; }
.state-stage { min - block - size : 7rem ; display : grid ; place - items : center ; padding : 1rem ; border - radius : var ( -- radius - lg ); background : var ( -- color - surface - default ); border : 1px solid color - mix ( in srgb , var ( -- rule ) 60 % , transparent ); }
.state-chip { font - family : var ( -- font - family - primary ); font - size : var ( -- font - size - sm ); font - weight : 600 ; padding : 0.6rem 1.15 rem ; border - radius : var ( -- radius - md ); background : var ( -- color - surface - raised ); color : var ( -- color - content - primary ); border : 1px solid var ( -- rule ); }
.state-chip[data-state='focus'] { outline : 2px solid var ( -- color - primary - solid ); outline - offset : 2px ; }
.state-chip[data-state='pressed'] { background : var ( -- color - surface - muted ); }
.state-chip[data-state='disabled'] { opacity : 0.45 ; }
.state-btn { cursor : pointer ; box - shadow : var ( -- depth - raised - shadow ); transition : box - shadow 200 ms var ( -- ease - out , cubic - bezier ( 0.2 , 0 , 0 , 1 )), translate 200 ms var ( -- ease - out , cubic - bezier ( 0.2 , 0 , 0 , 1 )); }
.state-btn:hover { box - shadow : var ( -- depth - overlay - shadow ); translate : 0 - 5 px ; }
.state-btn:focus-visible { box - shadow : var ( -- depth - overlay - shadow ); translate : 0 - 5 px ; outline : 2px solid var ( -- color - primary - solid ); outline - offset : 2px ; }
.state-btn:active { box - shadow : var ( -- depth - raised - shadow ); translate : 0 1 px ; }
.state figcaption { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
.state figcaption b { color : var ( -- color - content - primary ); font - weight : 600 ; text - transform : capitalize ; }
@media (prefers-reduced-motion: reduce) { . state - btn { transition : box - shadow 200 ms ; } . state - btn :hover , . state - btn :focus - visible , . state - btn :active { translate : none ; } }
kbd { font - family : var ( -- font - family - mono ); font - size : 0.85em ; padding : 0.1em 0.4 em ; border - radius : 4px ; background : var ( -- color - surface - raised ); border : 1px solid var ( -- rule ); color : var ( -- color - content - secondary ); }
/* 4 · Escalera de planos (z-stack) */
.ladder { display : flex ; flex - direction : column ; align - items : center ; padding : clamp ( 2 rem , 7 vw , 5 rem ) 1 rem clamp ( 3 rem , 8 vw , 6 rem ); }
.ladder-step {
inline-size: min(20rem, 80%);
padding: 1rem 1.4rem;
border-radius: var(--radius-lg);
background: var(--color-surface-raised);
border: 1px solid var(--rule);
display: flex;
align-items: center;
justify-content: space-between;
translate: calc(var(--i) * 1.4rem - 2.8rem) calc(var(--i) * -0.35rem);
margin-block-start: -0.6rem;
}
.ladder-step:first-child { margin - block - start : 0 ; }
.ladder-name { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - sm ); color : var ( -- color - content - primary ); font - weight : 600 ; }
.ladder-z { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); color : var ( -- color - content - muted ); }
/* 6 · Catálogo (resting elevation table) */
.catalog { display : flex ; flex - direction : column ; border : 1px solid var ( -- rule ); border - radius : var ( -- radius - lg ); overflow : hidden ; background : var ( -- color - surface - raised ); }
.cat-row { display : grid ; grid - template - columns : 7rem 5 rem 1 fr 4 rem ; gap : 1rem ; align - items : center ; padding : 0.9rem 1.25 rem ; border - block - end : 1px solid color - mix ( in srgb , var ( -- rule ) 50 % , transparent ); }
.cat-row:last-child { border - block - end : 0 ; }
.cat-head { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); letter - spacing : 0.04em ; text - transform : uppercase ; color : var ( -- color - content - muted ); background : var ( -- color - surface - muted ); }
.cat-plane { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - sm ); }
.cat-plane b { color : var ( -- color - content - primary ); font - weight : 600 ; }
.cat-z { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); color : var ( -- color - content - secondary ); }
.cat-use { font - size : var ( -- font - size - sm ); color : var ( -- color - content - secondary ); }
.cat-sample { display : flex ; justify - content : flex - end ; }
.cat-tile { inline - size : 3rem ; block - size : 1.75rem ; border - radius : var ( -- radius - sm ); background : var ( -- color - surface - default ); border : 1px solid color - mix ( in srgb , var ( -- rule ) 50 % , transparent ); }
@media (max-width: 34rem) {
.cat-row { grid - template - columns : 6rem 1 fr ; }
.cat-z, .cat-sample, .cat-head span:nth-child(2), .cat-head span:nth-child(4) { display : none ; }
}
/* 7 · Luz vs sombra (mode-adaptive cue) */
.modes { display : grid ; grid - template - columns : 1fr ; gap : 1.5rem ; }
@media (min-width: 38rem) { . modes { grid - template - columns : 1fr 1 fr ; } }
.mode-panel { border - radius : var ( -- radius - lg ); padding : clamp ( 2 rem , 6 vw , 4 rem ); display : grid ; place - items : center ; min - block - size : 14rem ; background : var ( -- color - surface - default ); border : 1px solid var ( -- color - border - default ); }
.mode-card { inline - size : min ( 16 rem , 100 % ); padding : 1.5rem ; border - radius : var ( -- radius - lg ); background : var ( -- color - surface - overlay ); border : 1px solid var ( -- color - border - default ); display : flex ; flex - direction : column ; gap : 0.35rem ; }
.mode-label { font - family : var ( -- font - family - mono ); font - size : var ( -- font - size - xs ); text - transform : uppercase ; letter - spacing : 0.08em ; color : var ( -- color - content - muted ); }
.mode-cue { font - size : var ( -- font - size - sm ); color : var ( -- color - content - primary ); }
/* 9 · Accesibilidad */
.a11y { display : grid ; grid - template - columns : repeat ( auto - fit , minmax ( min ( 100 % , 15 rem ), 1 fr )); gap : 1.25rem ; }
.a11y-card { background : var ( -- color - surface - raised ); border : 1px solid var ( -- rule ); border - radius : var ( -- radius - lg ); padding : 1.4rem ; display : flex ; flex - direction : column ; gap : 0.5rem ; }
.a11y-title { font - family : var ( -- font - family - display ); font - size : var ( -- font - size - md ); font - weight : 600 ; color : var ( -- color - content - primary ); }
.a11y-body { 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 ; }
< / style >