You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/palabras/palabras.css

676 lines
27 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* `<palabras>` — editor-specific styles.
*
* Layout shell is composed from our components (Flex). This file styles what
* those don't: the editing surface, the active-block selection marker, and the
* FLOATING inspector panel (its own overlay surface — deliberately distinct from
* the docked Words inspector). Content reading styles stay minimal — `scheme-style`
* will own the per-element look next.
*/
[data-palabras] {
display: block;
}
[data-palabras-shell] {
inline-size: 100%;
}
/* The breadcrumb wrapper only delegates clicks — transparent to layout. */
[data-palabras-breadcrumb-wrap] {
display: contents;
}
/* ── Stage: document full-width, panel floats over the frame's top-right ──── */
[data-palabras-stage] {
/* not positioned: the seam line (painted by Words' block-drag controller) is
calculated frame-relative; the inspector panel becomes frame-anchored too
via the rules below. */
}
[data-palabras-doc-card] {
min-block-size: 20rem;
}
/* Make the Words frame the positioned ancestor for the drop-line seam (and the
inspector panel below). The Words.Provider stamps `data-words` on the SAME
div palabras stamps `data-palabras`, so the compound attribute selector
targets this exact element only; standalone <Words> is untouched. */
[data-palabras][data-words] {
position: relative;
}
[data-palabras-doc] {
/* Two real columns: the grip RAIL (its own div with width) + the canvas. */
display: flex;
align-items: stretch;
}
/* Left RAIL — a real column with its OWN width (not padding). Holds the block
grip; `position: relative` so the grip slot positions against it. Same width
on mobile + desktop (option A). */
[data-palabras-rail] {
flex: 0 0 var(--palabras-rail, 32px);
position: relative;
/* Bullet-proof containing block for the absolutely-positioned grip slot. If
`position: relative` alone ever fails to register the rail as the slot's
offsetParent, the slot jumps past DOC + CARD (both static — they can't be CBs
without breaking the Words drop-line seam's frame anchor) straight to the outer
frame, landing the grip ~25px LEFT at the card edge. A transform establishes
the containing block via a second, independent mechanism so it can't fall through. */
transform: translateZ(0);
}
/* Canvas — the editing column. `min-inline-size: 0` lets it shrink within the
flex row instead of overflowing. NOT positioned: the drop-line seam painted by
Words' block-drag controller is calculated frame-relative ([data-words]); any
positioned ancestor between the seam and the frame breaks the anchor. */
[data-palabras-canvas] {
flex: 1 1 auto;
min-inline-size: 0;
}
/* Block-grip SLOT — owns the gutter placement. The grip sits at the visited
block's mid-height (PalabrasHandle sets inline `top` = block centre) and
GLIDES between blocks via the `top` transition. The vertical centring
(translateY(-50%)) lives on the slot, NOT the Button, so the Button's
`:active { translateY(1px) }` press transform never fights it. Scoped under
[data-palabras-doc] (0,2,0) to beat the Button recipe's `position`. */
[data-palabras-doc] [data-palabras-handle-slot] {
position: absolute;
/* `left` is set INLINE per-block by PalabrasHandle: 0 for a top-level block
(flush in the rail), or the block's own left edge for a NESTED block (the
handle floating out of the rail into the canvas — the rail has no overflow
clip). Only `top` glides; `left` snaps so a rail↔inline jump isn't a fly. */
transform: translateY(-50%);
z-index: 5;
transition: top var(--duration-fast, 0.12s) var(--ease-default, ease);
}
/* Action-menu entrance — pin ONE animation. The menu gets TWO competing entrance
animations on open: the sema/motion `present-rise` (applied WHILE the
`data-event` hold is up, ~240ms, because the `open` event is family `emerge`)
and the dropdown recipe's `dropdown-menu-enter` (on `data-state=open`). When
the hold ends the `animation-name` switches → the browser RESTARTS the
animation, snapping opacity back to 0 for a frame. This menu is rendered
in-place (position:fixed but DOM-nested in the rail, NOT portaled), so that
transparent frame reveals the editor blocks behind it — the "block background
flashing over the menu" flicker. Pin the recipe animation with `!important`
so the name never switches and opacity never resets to 0. */
[data-palabras-handle-menu][data-state='open'] {
animation: dropdown-menu-enter var(--duration-fast, 0.12s) var(--ease-out, ease) !important;
}
/* Every menu item (main actions + insert tiles + the submenu trigger) is an
icon + label row. The label grows so any trailing chevron (on the "Insertar
bloque" sub-trigger) is pushed to the right edge. */
[data-palabras-handle-menu] [role='menuitem'] {
display: flex;
align-items: center;
gap: var(--space-2);
}
[data-palabras-handle-menu] [role='menuitem'] > span {
flex: 1 1 auto;
}
[data-palabras-handle-menu] [role='menuitem'] svg {
flex: 0 0 auto;
inline-size: 1rem;
block-size: 1rem;
opacity: 0.85;
}
/* "Insertar bloque" submenu — a compact 2-column grid of type tiles. Keyboard
arrow-nav stays vertical over DOM order (mouse-first for now). */
[data-palabras-insert-grid] {
display: grid;
grid-template-columns: repeat(2, minmax(7.5rem, 1fr));
gap: 2px;
}
/* Unified round tool button — the block-action grip (⋮) AND the column "+"
inserter share ONE elevated, bordered, fully-round chrome (overriding the ghost
Button's transparent look). Scoped under [data-palabras-doc] (0,2,0) so it beats
the Button recipe. */
[data-palabras-doc] [data-palabras-handle],
[data-palabras-canvas] [data-palabras-column-inserter] [data-palabras-column-inserter-button] {
background: var(--color-surface-default);
border: 1px solid var(--color-neutral-border);
border-radius: 999px;
box-shadow:
0 4px 12px -2px rgb(0 0 0 / 0.22),
0 2px 4px -1px rgb(0 0 0 / 0.16);
color: var(--color-content-muted);
}
/* Hover — the arm carries `[data-button]` + `:not([data-disabled])` so it (0,5,0)
beats the eidos ghost-Button hover `[data-button]:hover:not()…` (0,4,0), which
for ghost forces bg + border to transparent. `background` is re-asserted because
the ghost hover would otherwise blank the round chip's surface. */
[data-palabras-doc] [data-button][data-palabras-handle]:hover:not([data-disabled]),
[data-palabras-canvas]
[data-palabras-column-inserter]
[data-button][data-palabras-column-inserter-button]:hover:not([data-disabled]) {
background: var(--color-surface-default);
border-color: var(--color-content-primary);
color: var(--color-content-primary);
}
/* The grip is also a drag source. */
[data-palabras-doc] [data-palabras-handle] {
cursor: grab;
}
[data-palabras-doc] [data-palabras-handle]:active {
cursor: grabbing;
}
/* A block NESTED inside a column gets a slightly smaller grip than the
rail/top-level one (`scale` is independent of the recipe's `:active` transform,
so the press nudge still composes). */
[data-palabras-doc] [data-palabras-handle][data-nested] {
scale: 0.8;
}
[data-palabras-doc] [data-words-content] {
display: block;
outline: none;
}
/* Drop indicator painted by Words' block-drag controller while a block is being
dragged by its grip. The controller positions it frame-relative ([data-words])
at the nearest inter-block seam; the bar spans the target block's width. We
restyle here (Words' own recipe is not loaded under palabras). */
[data-palabras-canvas] [data-words-block-drop-line] {
position: absolute;
z-index: 5;
block-size: 2px;
margin-block-start: -1px;
border-radius: 1px;
background: var(--color-primary-solid);
pointer-events: none;
}
/* Kill the archetype focus ring (box-shadow) on the editing surface — the caret
is the focus indicator; otherwise the whole document gets boxed in violet. */
[data-palabras-doc] [data-words-content][data-archetype]:focus-visible {
box-shadow: none;
}
[data-palabras-doc] [data-words-content] :where(h1, h2, h3, p) {
margin-block: 0 0.6em;
}
/* Selection marker for the active/selected block — dotted (NOT the solid focus
ring, which is suppressed above). Stamped by WordsActivate on the active
block element. */
[data-palabras-doc] [data-words-active] {
outline: 1.5px dashed var(--color-primary-border);
outline-offset: 6px;
border-radius: var(--radius-sm);
}
/* A block nested inside a column (or a table cell) must keep its selection ring
WITHIN the container's own dashed border. The top-level 6px offset would spill
past it; a tighter 3px offset fits inside the container's inner padding
(`--space-2`), so the ring reads as "contained by" the structure, not bleeding
over it. */
[data-palabras-doc] :is([data-words-column], [data-words-node='table-cell']) [data-words-active] {
outline-offset: 3px;
}
/* Columns block — flex layout, MIRRORED FROM Words' `words.css` (palabras does
NOT load Words' recipe, so without this the block gets no layout or height).
An empty columns block otherwise collapses to 0px: invisible AND it breaks the
grip — a zero-height block makes the rail's vertical block-targeting bands
(`blockAtY`, Voronoi-on-Y) DEGENERATE, so the grip jumps and the block can't
be selected or targeted, and inserts land off-by-one. `min-block-size` + the
dashed per-column outline give it real, discoverable, clickable structure. */
[data-palabras-doc] [data-words-block='columns'] {
display: flex;
flex-direction: row;
/* Wide enough for a block's inline grip handle to sit in the inter-column
gutter WITHOUT overlapping the previous column (an overlap re-anchors the
grip on hover). Tied to HANDLE_REACH in palabras-handle.svelte. */
gap: 2.5rem;
margin-block: var(--space-3) 0;
align-items: stretch;
}
[data-palabras-doc] [data-words-block='columns'] [data-words-column] {
display: flex;
flex-direction: column;
min-inline-size: 0;
min-block-size: 3em;
/* Inner padding so a nested block is NOT flush with the column border. This
gives the active-block selection ring (an `outline` drawn OUTSIDE the block
box) room to sit INSIDE the column instead of spilling past its dashed
border. Must be >= the ring's reach (offset + width) — see the nested
`[data-words-active]` rule below. */
padding: var(--space-2);
border: 1px dashed var(--color-neutral-border);
border-radius: var(--radius-sm);
transition: border-color var(--duration-fast, 0.12s) var(--ease-default, ease);
}
[data-palabras-doc] [data-words-block='columns'] [data-words-column]:hover,
[data-palabras-doc] [data-words-block='columns'] [data-words-column]:focus-within {
border-color: var(--color-content-primary);
}
/* Reset the universal block-rhythm margin on each column's first nested block. */
[data-palabras-doc] [data-words-block='columns'] [data-words-column] > *:first-child {
margin-block-start: 0;
}
/* Per-column "+" inserter — an overlay centred over each EMPTY column. Absolute
so it anchors to the FRAME (not the canvas — a positioned canvas breaks the
drop-line seam's frame-relative anchor); the inline left/top are
frame-relative. The menu is portaled, so the centring transform here doesn't
become the menu's containing block. */
[data-palabras-canvas] [data-palabras-column-inserter] {
position: absolute;
transform: translate(-50%, -50%);
z-index: 4;
/* Hidden until the cursor is over its column (set via `data-visible` by the
inserter's hover effect). */
opacity: 0;
pointer-events: none;
transition: opacity var(--duration-fast, 0.12s) var(--ease-default, ease);
}
[data-palabras-canvas] [data-palabras-column-inserter][data-visible] {
opacity: 1;
pointer-events: auto;
}
/* ── Image ─────────────────────────────────────────────────────────────────
palabras does NOT load Words' recipe, so the <figure> gets none of its layout
(and keeps the UA default `margin-inline: 40px`, and the engine's
`data-words-image-full-width` / `data-words-image-align` attrs have nothing to
react to). Mirror the essential figure rules here, scoped to the doc. */
[data-palabras-doc] figure[data-words-block='image'] {
display: flex;
flex-direction: column;
align-items: center;
margin-inline: 0;
margin-block: var(--space-4) 0;
/* The image is an ATOMIC block (the figure is contenteditable=false). Without
this, clicking it inside the contenteditable SELECTS the image and the browser
paints its native SOLID selection outline over our dashed active marker.
`user-select` inherits, so this covers the img / tile / placeholder / caption. */
user-select: none;
}
[data-palabras-doc] figure[data-words-block='image'] img {
display: block;
max-inline-size: 100%;
block-size: auto;
border-radius: var(--radius-sm);
}
/* Float intent — only emitted by the engine when NOT full width. */
[data-palabras-doc] figure[data-words-block='image'][data-words-image-align='left'] {
align-items: flex-start;
}
[data-palabras-doc] figure[data-words-block='image'][data-words-image-align='right'] {
align-items: flex-end;
}
/* Full width — span the whole content column (the default for new images). */
[data-palabras-doc] figure[data-words-block='image'][data-words-image-full-width] {
display: block;
inline-size: 100%;
}
[data-palabras-doc] figure[data-words-block='image'][data-words-image-full-width] img {
inline-size: 100%;
}
[data-palabras-doc] figure[data-words-block='image'] figcaption {
margin-block-start: var(--space-2);
font-size: var(--font-size-sm);
color: var(--color-content-muted);
text-align: center;
}
/* ── Image fit modes (Fill / Fit / Crop / Tile) ───────────────────────────────
The FRAME (fixed 16:9 + overflow clip) lives on the IMG / tile, NOT the figure,
so the figcaption stays OUTSIDE the clip and remains visible below it. */
[data-palabras-doc] figure[data-words-block='image'][data-words-image-fit] {
display: flex;
flex-direction: column;
align-items: stretch;
inline-size: 100%;
}
[data-palabras-doc]
figure[data-words-block='image'][data-words-image-fit]
:where(img, [data-words-image-tile]) {
inline-size: 100%;
aspect-ratio: 16 / 9;
block-size: auto;
overflow: hidden;
border-radius: var(--radius-sm);
background: var(--color-surface-muted);
/* fill / crop default to cover; fit / tile override below. */
object-fit: cover;
}
[data-palabras-doc] figure[data-words-image-fit='fit'] img {
object-fit: contain;
}
/* Tile — the image is a repeated background on a div that fills the frame. */
[data-palabras-doc] figure[data-words-image-fit='tile'] [data-words-image-tile] {
background-repeat: repeat;
background-size: auto;
background-position: center;
}
/* ── Image placeholder ────────────────────────────────────────────────────
Empty-src state (`{ type: 'image', src: '' }`): the engine renders
`[data-words-image-placeholder]` (a div + two label spans). palabras shows ONLY
a large default image SVG — a clean full-width drop-zone card; the URL is filled
from the inspector, so the engine's label/hint text spans are hidden. */
[data-palabras-doc] [data-words-image-placeholder] {
display: flex;
align-items: center;
justify-content: center;
inline-size: 100%;
box-sizing: border-box;
min-block-size: 11rem;
padding: var(--space-6) var(--space-4);
border: 1.5px dashed var(--color-neutral-border);
border-radius: var(--radius-md);
background: var(--color-surface-muted);
color: var(--color-content-muted);
user-select: none;
}
/* The icon ONLY — larger (lucide `image`, muted grey). */
[data-palabras-doc] [data-words-image-placeholder]::before {
content: '';
inline-size: 4.5rem;
block-size: 4.5rem;
background: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%239aa0aa'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Crect%20width='18'%20height='18'%20x='3'%20y='3'%20rx='2'%20ry='2'/%3E%3Ccircle%20cx='9'%20cy='9'%20r='2'/%3E%3Cpath%20d='m21%2015-3.086-3.086a2%202%200%200%200-2.828%200L6%2021'/%3E%3C/svg%3E")
center / contain no-repeat;
}
/* The engine's placeholder text is hidden — palabras keeps just the icon. */
[data-palabras-doc] [data-words-image-placeholder-label],
[data-palabras-doc] [data-words-image-placeholder-hint] {
display: none;
}
/* ── Floating inspector panel ─────────────────────────────────────────────── */
/* The floating surface IS a framework `<Popover.Content>` — positioning (side /
align / flip / shift / fit-to-viewport), the portal, dismissal AND the surface
(border / bg / shadow / max-height) all come from the popover recipe. Here we
only RE-SHAPE the content's inner layout into a flex column with a pinned
header, a scrollable body and a pinned footer — overriding the popover recipe's
`display: grid` / padding / gap / `overflow: auto`. Doubled attribute (0,2,0)
beats `[data-popover-content]` (0,1,0) regardless of stylesheet load order. */
[data-palabras-panel-card][data-palabras-panel-card] {
display: flex;
flex-direction: column;
gap: 0;
padding: 0;
overflow: hidden;
font-family: var(--font-ui);
}
/* Force the UI SANS across the whole panel + every descendant. The framework
Accordion's content archetype maps to the READING serif (Lora), which would
leak into the field labels. A DOUBLED scope attribute = (0,2,0) beats any
single-attribute component font rule regardless of code-split load order —
same technique as Words' chrome typeface guard. */
[data-palabras-panel-card][data-palabras-panel-card],
[data-palabras-panel-card][data-palabras-panel-card] * {
font-family: var(--font-ui);
}
/* Header: block-defining icon · title (grows) · trash */
[data-palabras-panel-head] {
flex: 0 0 auto;
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-4) var(--space-4) var(--space-3);
/* The header is the drag handle (PalabrasPanel). */
cursor: move;
user-select: none;
touch-action: none;
}
/* Body — header + footer stay pinned, this holds the scrollable region. It CLIPS
(overflow hidden) rather than scrolls: the panel is compact (single open
accordion) and clipping means NO scrollbar ever flaps in/out during a collapse
animation (which momentarily measures the full content height) — that flap was
the "trigger shifts right" glitch — and NO gutter is reserved (no wasted width). */
[data-palabras-panel-body] {
flex: 1 1 auto;
min-block-size: 0;
/* Scrolls within the floating-ui height cap (`--_palabras-float-max-h`) when
the panel can't fit fully on screen — header + footer stay pinned. */
overflow-y: auto;
padding: 0 var(--space-4) var(--space-4);
}
/* The block's defining icon, tinted by the schema's dotToken (via currentColor). */
[data-palabras-panel-icon] {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
}
[data-palabras-panel-icon] svg {
inline-size: 1.05rem;
block-size: 1.05rem;
}
[data-palabras-panel-title] {
flex: 1 1 auto;
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--color-content-primary);
}
/* Section: collapsible title + fields */
[data-palabras-panel-section] {
margin-block-start: var(--space-3);
}
[data-palabras-panel-section-title] {
display: flex;
align-items: center;
justify-content: space-between;
inline-size: 100%;
padding: 0;
border: none;
background: none;
cursor: pointer;
font: inherit;
font-size: var(--font-size-xs);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-content-muted);
}
[data-palabras-panel-section-title] svg {
inline-size: 0.85rem;
block-size: 0.85rem;
opacity: 0.7;
}
[data-palabras-panel-fields] {
margin-block-start: var(--space-3);
}
/* Pinned header section — always visible, no toggle; sits flush at the top. */
[data-palabras-panel-section][data-pinned] {
margin-block-start: 0;
}
[data-palabras-panel-section][data-pinned] [data-palabras-panel-fields] {
margin-block-start: 0;
}
/* URL + upload-button row (the image `src` field). */
[data-palabras-src-group] {
display: flex;
align-items: center;
gap: var(--space-1);
inline-size: 100%;
}
[data-palabras-src-group] [data-palabras-text] {
flex: 1 1 auto;
}
/* Slider field — label + value ABOVE, the slider full-width BELOW (photo-app).
Compact (Apariencia stacks 7 in a row): tight label line-height + reduced gaps. */
[data-palabras-field][data-slider] {
gap: var(--space-0-5);
}
[data-palabras-field][data-slider] + [data-palabras-field][data-slider] {
margin-block-start: var(--space-2);
}
[data-palabras-field][data-slider] [data-palabras-slider-head] {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-2);
}
[data-palabras-field][data-slider] [data-palabras-field-label],
[data-palabras-field][data-slider] [data-palabras-slider-value] {
line-height: 1.1;
}
[data-palabras-field][data-slider] [data-palabras-slider-value] {
font-size: var(--font-size-xs);
color: var(--color-content-muted);
font-variant-numeric: tabular-nums;
}
[data-palabras-panel-accordion] {
margin-block-start: var(--space-2);
}
/* The OPEN (active) accordion group gets a distinct surface so it stands out. */
[data-palabras-panel-accordion] [data-accordion-item][data-state='open'] {
background: var(--color-surface-muted);
border-radius: var(--radius-sm);
}
/* Fields */
[data-palabras-field] {
display: flex;
flex-direction: column;
gap: var(--space-1-5);
}
[data-palabras-field] + [data-palabras-field] {
margin-block-start: var(--space-3);
}
/* Inline fields (toggle · anchor · colour · number): a 2-column grid so the
label is one column and the control the other — every control's right edge
lines up across rows. */
[data-palabras-field][data-inline] {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: var(--space-3);
}
/* The control column right-aligns its content (colour swatch / number field). */
[data-palabras-field][data-inline] > :not([data-palabras-field-label]) {
justify-self: end;
}
[data-palabras-field-label] {
font-size: var(--font-size-xs);
color: var(--color-content-primary);
}
[data-palabras-field-todo] {
font-size: var(--font-size-xs);
font-style: italic;
color: var(--color-content-muted);
}
/* Anchor "chip" — monospace slug, compact, right-aligned. */
[data-palabras-text] {
inline-size: 7rem;
padding: var(--space-1) var(--space-2);
border: 1px solid var(--color-neutral-border);
border-radius: var(--radius-sm);
background: var(--color-surface-default);
color: var(--color-content-primary);
font: inherit;
font-size: var(--font-size-xs);
font-family: var(--font-mono);
text-align: end;
}
[data-palabras-text]:focus-visible {
outline: none;
border-color: var(--color-focus-ring);
}
/* Wide variant (URL / path) — full-width, left-aligned, normal font. */
[data-palabras-text][data-wide] {
inline-size: 100%;
font-family: inherit;
text-align: start;
}
/* Rejected value (bad URL / bad CSS length) — red ring, value not committed. */
[data-palabras-text][data-invalid] {
border-color: var(--color-threat-border, var(--color-danger-border, crimson));
}
/* CSS-length field (Ancho / Alto) — the framework <CssField> (its own border /
bg / invalid ring / xs size) with −/+ steppers, kept compact in the inline row. */
[data-palabras-field][data-inline] [data-css-field] {
inline-size: 9rem;
}
/* Number field (width / height) — compact input + unit suffix, right-aligned. */
[data-palabras-number] {
display: inline-flex;
align-items: center;
gap: var(--space-1);
}
[data-palabras-number-input] {
inline-size: 4rem;
min-inline-size: 0;
padding: var(--space-1) var(--space-2);
border: 1px solid var(--color-neutral-border);
border-radius: var(--radius-sm);
background: var(--color-surface-default);
color: var(--color-content-primary);
font: inherit;
font-size: var(--font-size-xs);
text-align: end;
}
[data-palabras-number-input]:focus-visible {
outline: none;
border-color: var(--color-focus-ring);
}
[data-palabras-number-unit] {
font-size: var(--font-size-xs);
color: var(--color-content-muted);
}
/* A gated field (width / height / align under "Ancho completo") dims + locks. */
[data-palabras-field][data-disabled] {
opacity: 0.45;
pointer-events: none;
}
/* Footer: themed-appearance line */
/* Footer — pinned band at the card bottom (full width, muted surface, rounded
bottom corners). A flex child of the card, so it always spans edge-to-edge
regardless of the body's scrollbar. */
[data-palabras-panel-foot] {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
padding: var(--space-2-5) var(--space-4);
border-block-start: 1px solid var(--color-neutral-border);
border-end-start-radius: var(--radius-lg);
border-end-end-radius: var(--radius-lg);
background: var(--color-surface-muted);
font-size: var(--font-size-xs);
color: var(--color-content-muted);
}
[data-palabras-panel-edit] {
border: none;
background: none;
color: var(--color-primary-text);
font: inherit;
font-size: var(--font-size-sm);
cursor: pointer;
}
[data-palabras-panel-edit][disabled] {
opacity: 0.6;
cursor: default;
}
[data-palabras-panel-empty] {
margin: 0;
padding: var(--space-4);
font-size: var(--font-size-sm);
color: var(--color-content-muted);
}
/* ── Appearance popover (2nd window, opened by "Editar") ───────────────────── */
/* Also a framework `<Popover.Content>` (positioning + surface from the recipe);
we only force the UI sans across it + its descendants (the popover's content
archetype would otherwise leak the reading serif into the field labels). */
[data-palabras-appearance][data-palabras-appearance],
[data-palabras-appearance][data-palabras-appearance] * {
font-family: var(--font-ui);
}
/* NumberField in the appearance popover — compact, fixed width so the grid's
control column stays tidy. */
[data-palabras-appearance] [data-number-field] {
inline-size: 6rem;
}
/* ── Help line ────────────────────────────────────────────────────────────── */
[data-palabras-help] {
margin: 0;
font-size: var(--font-size-sm);
color: var(--color-content-muted);
}
[data-palabras-help] kbd {
font: inherit;
padding-inline: var(--space-1);
border: 1px solid var(--color-neutral-border);
border-radius: var(--radius-sm);
background: var(--color-surface-muted);
}
/* Mobile: the panels stay framework `<Popover>`s — the popover recipe already
keeps them within `calc(100vw - …)` and floating-ui shifts them on screen, so
no bespoke bottom-drawer layer is needed. */

Powered by TurnKey Linux.