/*
 * filament-advanced-rich-editor
 *
 * Standalone stylesheet, registered through FilamentAsset::register(). It is served as
 * its own file and is NEVER piped through the consuming app's Tailwind build, so it must
 * stay plain CSS — no @apply, no @variant, no Tailwind utilities.
 *
 * One of two, and this is the one Filament puts on every page of the panel: everything
 * that can be on screen before anybody does anything. A stored document wherever it is
 * drawn - the editor, AdvancedRichEntry, AdvancedRichColumn, a plain Filament text entry -
 * and the field around the editor, its toolbar and the panels that toolbar renders closed.
 * What only an action opens lives in filament-advanced-rich-editor-overlays.css, which the
 * editor asks for as it starts. A rule for something PHP draws with the page, or something
 * JavaScript draws as the editor starts, belongs here; OverlayStylesheetTest refuses one in
 * the other sheet.
 *
 * Colours are expressed with the custom properties Filament registers at runtime
 * (--gray-*, --primary-*, --color-white, --prose-*), each with a literal fallback, so the
 * package follows the panel's own colour registration instead of hard-coding a palette.
 *
 * Dark mode uses `:where(.dark, .dark *)` — the exact selector Tailwind v4 compiles
 * Filament's `dark:` variant to (verified against the panel's published app.css). Because
 * :where() contributes zero specificity, each dark rule carries the same weight as the
 * light rule above it and wins purely on source order, matching upstream's behaviour.
 *
 * Custom properties a consumer may override:
 *   --fi-arte-sticky-offset  distance from the viewport top the pinned toolbar rests at
 *   --fi-arte-sticky-bg      opaque background painted behind the pinned toolbar
 *   --fi-arte-divider-color  colour of the vertical toolbar rule
 *   --fi-arte-toolbar-radius top corner radius of the pinned toolbar
 */

/* -------------------------------------------------------------------------- */
/* Toolbar divider                                                            */
/* -------------------------------------------------------------------------- */

/*
 * A rule spanning the toolbar's full height would run into the 0.5rem block padding
 * around the 2rem button row, so it is deliberately shorter than the buttons and centred
 * against them. `align-self` rather than a fixed margin keeps that alignment when the
 * toolbar wraps onto a second row.
 *
 * RichEditor::getToolbarButtons() groups consecutive non-array items, so a top-level
 * 'divider' token arrives wrapped in its own .fi-fo-rich-editor-toolbar-group. Styling
 * the divider element itself rather than that wrapper means the same rule also covers a
 * divider placed inside a group.
 */
.fi-arte-toolbar-divider {
    flex: 0 0 auto;
    align-self: center;
    width: 1px;
    height: 1.5rem;
    background-color: var(--fi-arte-divider-color, var(--gray-200, #e4e4e7));
}

.fi-arte-toolbar-divider:where(.dark, .dark *) {
    background-color: var(
        --fi-arte-divider-color,
        color-mix(in oklab, var(--color-white, #fff) 10%, transparent)
    );
}

/* -------------------------------------------------------------------------- */
/* Sticky toolbar                                                             */
/* -------------------------------------------------------------------------- */

/*
 * Upstream sets `position: relative` on the toolbar; replacing it with `sticky` preserves
 * the containing block the dropdown menus are anchored to.
 *
 * The background has to be opaque, otherwise editor content scrolls visibly through the
 * pinned bar. Filament's input wrapper is plain white in light mode; in dark mode it is
 * two stacked white/5 overlays (section content container + input wrapper) on top of
 * gray-900, and the color-mix below is the flattened equivalent of those two overlays.
 *
 * The bottom border is restated because a pinned bar has nothing but that border
 * separating it from the content sliding underneath.
 *
 * z-index 10 is the value Filament already uses for its own sticky custom-block headers,
 * so the two never fight when both are pinned.
 */
.fi-fo-rich-editor-toolbar.fi-arte-sticky {
    position: sticky;
    top: var(--fi-arte-sticky-offset, 4rem);
    z-index: 10;
    background-color: var(--fi-arte-sticky-bg, var(--color-white, #fff));
    border-bottom-style: solid;
    border-bottom-width: 1px;
    border-bottom-color: var(--gray-200, #e4e4e7);
    /*
     * The input wrapper rounds its corners but does not clip its children, so an opaque
     * bar would paint square corners over the rounded ones. `inherit` is no help here -
     * the toolbar's parent is the editor's plain wire:ignore container - so the radius is
     * restated. It matches Filament's `rounded-lg` and can be retuned by a theme through
     * the custom property.
     */
    border-start-start-radius: var(--fi-arte-toolbar-radius, 0.5rem);
    border-start-end-radius: var(--fi-arte-toolbar-radius, 0.5rem);
}

.fi-fo-rich-editor-toolbar.fi-arte-sticky:where(.dark, .dark *) {
    background-color: var(
        --fi-arte-sticky-bg,
        color-mix(in oklab, var(--color-white, #fff) 10%, var(--gray-900, #18181b))
    );
    border-bottom-color: color-mix(in oklab, var(--color-white, #fff) 10%, transparent);
}

/*
 * Stuck under the page header, the bar is square. The round corners above belong to the
 * top of the field, and once that has scrolled away they cut two notches out of a bar that
 * spans the field, with the text sliding past behind them. `sticky-toolbar.js` sets the
 * class while the bar is held at its offset; Filament's own sticky bar, from 5.9, has the
 * same corners and gets the same treatment.
 *
 * Both spellings of the corners: Filament writes the physical ones and this sheet the
 * logical ones, and a stuck bar is square at the top whichever way the text runs.
 */
.fi-arte .fi-fo-rich-editor-toolbar.fi-arte-sticky.fi-arte-stuck,
.fi-arte .fi-fo-rich-editor-toolbar.fi-fo-rich-editor-sticky-toolbar.fi-arte-stuck {
    border-start-start-radius: 0;
    border-start-end-radius: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

/* -------------------------------------------------------------------------- */
/* Task lists                                                                 */
/* -------------------------------------------------------------------------- */

/*
 * These two classes live in the stored rich content, not only in the editor, so they are
 * deliberately NOT scoped under .fi-arte — the same markup has to render correctly in
 * text entries, tables and front-end output produced by RichContentRenderer.
 *
 * .fi-prose reaches lists through `ul:where(:not(.fi-not-prose, …))` selectors, which
 * carry element-level specificity only. A single class here therefore outranks the disc
 * marker and the list indentation without needing !important.
 */
.fi-arte-task-list {
    margin-inline-start: 0;
    padding-inline-start: 0;
    list-style: none;
}

.fi-arte-task-item {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.5rem;
    padding-inline-start: 0;
    list-style: none;
}

/* WebKit still paints a marker on a `display: flex` list item unless it is emptied. */
.fi-arte-task-item::marker {
    content: '';
}

/* Tighter than prose's 1rem sibling rhythm: a checklist should read as a single block. */
.fi-arte-task-item + .fi-arte-task-item {
    margin-top: 0.25rem;
}

/*
 * The checkbox is centred inside a box exactly one prose line tall so that it lands on
 * the item's FIRST line. Centring it against the whole item would drift downwards on
 * multi-line entries and break the checklist's left column.
 */
.fi-arte-task-item > label {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    /*
     * As tall as the item's first line, so the control ends up on its optical centre.
     * The editor's node view measures that line and writes --fi-arte-task-line, which is
     * the only way to follow an inline font size applied to the text. Outside the editor
     * the fallbacks apply: the real line box where `lh` is supported, otherwise a size
     * relative to the font.
     */
    height: var(--fi-arte-task-line, 1.5em);
    /* Half the line's leading, so the control centres on the text rather than the box. */
    margin: var(--fi-arte-task-line-offset, 0px) 0 0 0;
    cursor: pointer;
    user-select: none;
}

@supports (height: 1lh) {
    .fi-arte-task-item > label {
        height: var(--fi-arte-task-line, 1lh);
    }
}

.fi-arte-task-item > label > input[type='checkbox'] {
    width: 1rem;
    height: 1rem;
    margin: 0;
    border-radius: 0.25rem;
    cursor: pointer;
    accent-color: var(--primary-600, #2563eb);
}

.fi-arte-task-item > label > input[type='checkbox']:where(.dark, .dark *) {
    accent-color: var(--primary-500, #3b82f6);
}

/*
 * TipTap emits a sibling span next to the input as a hook for a custom check mark. It is
 * unused here, and an empty inline box would still consume the flex gap — but only hide
 * it while it really is empty, so a future custom mark keeps working.
 */
.fi-arte-task-item > label > span:empty:not(.fi-arte-task-item-box) {
    display: none;
}

/*
 * `min-width: 0` lets long unbroken words wrap instead of widening the flex row.
 *
 * The margin reset matters as much: Filament's prose styles give block children a top
 * margin, which would push the text a full paragraph gap below the checkbox that is
 * supposed to sit on its first line.
 */
.fi-arte-task-item > div {
    flex: 1 1 0;
    min-width: 0;
    margin-block: 0;
}

.fi-arte-task-item > div > :first-child {
    margin-top: 0;
}

.fi-arte-task-item > div > :last-child {
    margin-bottom: 0;
}

/*
 * A sub-list nests inside the item's content div, which the checkbox column has already
 * offset, so it needs no further indent. When TipTap instead emits the sub-list as a
 * direct child of the item, `flex-basis: 100%` drops it onto its own row and the padding
 * restores that same one-column step by hand.
 */
.fi-arte-task-list .fi-arte-task-list {
    margin-block: 0.25rem;
    padding-inline-start: 0;
}

.fi-arte-task-item > .fi-arte-task-list {
    flex: 1 0 100%;
    padding-inline-start: 1.5rem;
}

/* Completed items read as muted; the checkbox itself stays at full contrast. */
.fi-arte-task-item[data-checked='true'] > div {
    color: var(--gray-400, #a1a1aa);
}

.fi-arte-task-item[data-checked='true'] > div:where(.dark, .dark *) {
    color: var(--gray-500, #71717a);
}

/*
 * Struck through on the item's own paragraphs only. Text decoration propagates into
 * in-flow block descendants and cannot be cancelled further down the tree, so setting it
 * on the content wrapper would permanently strike out every nested sub-item too.
 */
.fi-arte-task-item[data-checked='true'] > div > p {
    text-decoration-line: line-through;
}

/* Colour is inherited, so a checked parent would otherwise mute its unchecked children. */
.fi-arte-task-item[data-checked='true'] .fi-arte-task-list {
    color: var(--prose-color, var(--gray-700, #3f3f46));
}

.fi-arte-task-item[data-checked='true'] .fi-arte-task-list:where(.dark, .dark *) {
    color: var(--prose-color, var(--gray-300, #d4d4d8));
}

/*
 * The box outside the editor.
 *
 * Filament sanitises rich content before it reaches a page, and its allow list drops
 * `<input>` and `data-checked` - so saved content carries neither the checkbox the editor
 * renders nor the attribute the rules above key on. `TaskItem::renderHTML()` therefore
 * emits an empty span and puts the state on a class, and this is the half that draws it.
 */
.fi-arte-task-item-box {
    display: inline-block;
    inline-size: 1rem;
    block-size: 1rem;
    position: relative;
    flex: 0 0 auto;
    border: 1px solid var(--gray-400, #a1a1aa);
    border-radius: 0.25rem;
    background-color: transparent;
}

.fi-arte-task-item-box:where(.dark, .dark *) {
    border-color: var(--gray-500, #71717a);
}

.fi-arte-task-item-checked > .fi-arte-task-item-control > .fi-arte-task-item-box {
    border-color: var(--primary-600, #2563eb);
    background-color: var(--primary-600, #2563eb);
}

.fi-arte-task-item-checked > .fi-arte-task-item-control > .fi-arte-task-item-box:where(.dark, .dark *) {
    border-color: var(--primary-500, #3b82f6);
    background-color: var(--primary-500, #3b82f6);
}

/* The tick, drawn as two borders rather than shipped as an asset. */
.fi-arte-task-item-checked > .fi-arte-task-item-control > .fi-arte-task-item-box::after {
    content: '';
    position: absolute;
    inset-block-start: 0.0625rem;
    inset-inline-start: 0.3125rem;
    inline-size: 0.25rem;
    block-size: 0.5rem;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

/*
 * The same muting the editor gives a ticked item, keyed on the class that survives the
 * sanitiser instead of on the attribute that does not.
 */
.fi-arte-task-item-checked > .fi-arte-task-item-content {
    color: var(--gray-400, #a1a1aa);
}

.fi-arte-task-item-checked > .fi-arte-task-item-content:where(.dark, .dark *) {
    color: var(--gray-500, #71717a);
}

.fi-arte-task-item-checked > .fi-arte-task-item-content > p {
    text-decoration-line: line-through;
}

.fi-arte-task-item-checked .fi-arte-task-list {
    color: var(--prose-color, var(--gray-700, #3f3f46));
}

.fi-arte-task-item-checked .fi-arte-task-list:where(.dark, .dark *) {
    color: var(--prose-color, var(--gray-300, #d4d4d8));
}

/*
 * The control on a view page where somebody may tick a box.
 *
 * A button rather than the label the renderer draws, because on that page it does
 * something and the thing that does it has to be reachable with a keyboard. The rules are
 * a reset plus the two states a button owes anyone using it: a pointer that says it can be
 * pressed, and a focus ring.
 */
.fi-arte-task-item-tickable {
    appearance: none;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    border-radius: 0.25rem;
}

.fi-arte-task-item-tickable:focus-visible {
    outline: 2px solid var(--primary-600, #2563eb);
    outline-offset: 2px;
}

.fi-arte-task-item-tickable:disabled {
    cursor: progress;
}

.fi-arte-task-item-tickable:hover > .fi-arte-task-item-box {
    border-color: var(--primary-600, #2563eb);
}

/* -------------------------------------------------------------------------- */
/* Definition lists                                                           */
/* -------------------------------------------------------------------------- */

/*
 * In the stored content like the task list, so not scoped under .fi-arte either: a glossary
 * has to read as one in a text entry and on a front end too. Filament's prose has no rules
 * for `dl` at all, so without these a term and its description are two lines of the same
 * weight and nothing says which is which.
 *
 * The term carries the weight and the description the indent - the way a dictionary sets
 * them, and the way every browser already indents a `<dd>`, only made to match the prose.
 */
.fi-arte-description-list > dt {
    font-weight: 600;
}

.fi-arte-description-list > dt + dt {
    margin-block-start: 0;
}

.fi-arte-description-list > dd + dt {
    margin-block-start: 0.75em;
}

.fi-arte-description-list > dd {
    margin-block: 0.125em 0;
    margin-inline-start: 1.5em;
}

/* -------------------------------------------------------------------------- */
/* Callouts                                                                   */
/* -------------------------------------------------------------------------- */

/*
 * Like the task list, these classes live in the stored rich content rather than only in
 * the editor, so they are deliberately NOT scoped under .fi-arte — the same markup has to
 * render in text entries, in tables and on a front end that has never heard of this
 * package.
 *
 * The colour is set once, on the variant class, as three custom properties: the accent
 * that draws the rule and the icon, the wash behind the text, and the icon itself. The
 * box below then reads those, so a project adds a kind of its own with one small rule
 * rather than a copy of everything here.
 *
 * A variant nobody defined properties for falls through to the neutral values on
 * .fi-arte-callout and is drawn as a grey box with no icon, which reads as "set apart"
 * even when nothing says which kind it is.
 */
.fi-arte-callout {
    --fi-arte-callout-accent: var(--gray-400, #a1a1aa);
    --fi-arte-callout-wash: rgba(9, 9, 11, 0.04);
    /*
     * A mask that hides everything, rather than `none`. `mask-image: none` means no mask
     * at all, so the accent behind it fills the whole box and a kind with no icon is drawn
     * as a solid square. A transparent gradient is a mask that paints nothing.
     */
    --fi-arte-callout-icon: linear-gradient(transparent, transparent);

    position: relative;
    /* Room for the icon column, which is drawn rather than sitting in the markup. */
    padding: 0.75rem 1rem 0.75rem 2.75rem;
    border-inline-start: 3px solid var(--fi-arte-callout-accent);
    border-radius: 0.375rem;
    background-color: var(--fi-arte-callout-wash);
}

.fi-arte-callout:where(.dark, .dark *) {
    --fi-arte-callout-wash: rgba(255, 255, 255, 0.05);
}

/*
 * The icon is painted as a mask over the accent colour rather than shipped as an element.
 * Nothing then has to be `contenteditable="false"` inside the editor, and nothing has to
 * survive the sanitiser on the way to a page — the class carries the whole drawing.
 */
.fi-arte-callout::before {
    content: '';
    position: absolute;
    inset-inline-start: 0.9375rem;
    /* Centred on the first line of the box's own text, not on the box. */
    top: calc(0.75rem + 0.75em);
    margin-top: -0.625rem;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    background-color: var(--fi-arte-callout-accent);
    -webkit-mask-image: var(--fi-arte-callout-icon);
    mask-image: var(--fi-arte-callout-icon);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
}

/*
 * Filament's prose styles give block children a top margin, which would push the first
 * paragraph away from the icon that is meant to sit beside it.
 */
.fi-arte-callout > :first-child {
    margin-top: 0;
}

.fi-arte-callout > :last-child {
    margin-bottom: 0;
}

/*
 * The four kinds the package ships. Each names its colour through Filament's own
 * registered properties, so a panel with a different palette gets its own note blue and
 * its own warning amber rather than a hard-coded one.
 */
.fi-arte-callout-note {
    --fi-arte-callout-accent: var(--info-600, #2563eb);
    --fi-arte-callout-wash: color-mix(in oklab, var(--info-600, #2563eb) 7%, transparent);
    --fi-arte-callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='black' stroke-width='1.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z'/%3E%3C/svg%3E");
}

.fi-arte-callout-tip {
    --fi-arte-callout-accent: var(--success-600, #16a34a);
    --fi-arte-callout-wash: color-mix(in oklab, var(--success-600, #16a34a) 7%, transparent);
    --fi-arte-callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='black' stroke-width='1.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18'/%3E%3C/svg%3E");
}

.fi-arte-callout-warning {
    --fi-arte-callout-accent: var(--warning-600, #ca8a04);
    --fi-arte-callout-wash: color-mix(in oklab, var(--warning-600, #ca8a04) 9%, transparent);
    --fi-arte-callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='black' stroke-width='1.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z'/%3E%3C/svg%3E");
}

.fi-arte-callout-danger {
    --fi-arte-callout-accent: var(--danger-600, #dc2626);
    --fi-arte-callout-wash: color-mix(in oklab, var(--danger-600, #dc2626) 7%, transparent);
    --fi-arte-callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='black' stroke-width='1.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M12 9v3.75m0-10.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.75c0 5.592 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.57-.598-3.75h-.152c-3.196 0-6.1-1.25-8.25-3.286Zm0 13.036h.008v.008H12v-.008Z'/%3E%3C/svg%3E");
}

/*
 * The lighter end of each ramp in dark mode: the 600s are drawn for white behind them and
 * go muddy against the panel's near-black.
 */
.fi-arte-callout-note:where(.dark, .dark *) {
    --fi-arte-callout-accent: var(--info-400, #60a5fa);
    --fi-arte-callout-wash: color-mix(in oklab, var(--info-400, #60a5fa) 10%, transparent);
}

.fi-arte-callout-tip:where(.dark, .dark *) {
    --fi-arte-callout-accent: var(--success-400, #4ade80);
    --fi-arte-callout-wash: color-mix(in oklab, var(--success-400, #4ade80) 10%, transparent);
}

.fi-arte-callout-warning:where(.dark, .dark *) {
    --fi-arte-callout-accent: var(--warning-400, #facc15);
    --fi-arte-callout-wash: color-mix(in oklab, var(--warning-400, #facc15) 10%, transparent);
}

.fi-arte-callout-danger:where(.dark, .dark *) {
    --fi-arte-callout-accent: var(--danger-400, #f87171);
    --fi-arte-callout-wash: color-mix(in oklab, var(--danger-400, #f87171) 10%, transparent);
}

/* -------------------------------------------------------------------------- */
/* Textual toolbar dropdowns                                                  */
/* -------------------------------------------------------------------------- */

/*
 * Scoped to .fi-arte so a plain Filament RichEditor on the same page stays untouched.
 * Filament already lays the textual menu out (column direction, min-width, small type);
 * the one gap is icon sizing, because generate_icon_html() emits 1.25rem icons that tower
 * over the 0.8125rem option labels beside them.
 */
.fi-arte
    .fi-fo-rich-editor-dropdown-tool-textual
    .fi-fo-rich-editor-dropdown-tool-option
    svg {
    flex: 0 0 auto;
    width: 1rem;
    height: 1rem;
}

/*
 * An option keeps its height. The menu is a column flex box and Filament leaves its options
 * at the default `flex-shrink: 1`, so a list that runs past the cap below - this package's
 * cap; Filament sets no height on this menu at all - does not scroll, it squashes. Every row loses a few pixels and the menu quietly ends up with shorter rows
 * than the menu beside it, which is the one failure mode here nobody would think to look
 * for. Overflowing is the honest answer, and the rule under this one is what it overflows
 * into.
 */
.fi-arte
    .fi-fo-rich-editor-dropdown-tool-textual
    .fi-fo-rich-editor-dropdown-tool-option {
    flex-shrink: 0;
}

/*
 * Capping the menu keeps it inside the viewport when the toolbar is pinned near the bottom
 * of a tall form. The number is arithmetic rather than taste, and worth writing down: a row
 * is 2rem tall with a 0.25rem gap BETWEEN rows, and the menu adds 0.5rem of padding and 2px
 * of border, so n rows want 36n + 6 pixels. The longest list this package ships is the
 * overflow menu at ten, which measures 366; 26rem is 416, so that one fits and a project
 * has somewhere to put an eleventh tool - 402 - before anything scrolls.
 *
 * The headroom matters beyond the scrolling itself. A scrollbar takes its width out of the
 * content box, so the options in a scrolling menu are some fifteen pixels narrower than the
 * ones in a menu beside it - the highlight under the pointer stops short of the edge, and
 * two menus built from the same component stop looking like it.
 */
.fi-arte
    .fi-fo-rich-editor-dropdown-tool-textual
    .fi-fo-rich-editor-dropdown-tool-menu {
    max-height: 26rem;
    overflow-y: auto;
    /*
     * Vertically only, and the other axis has to be said out loud rather than left alone:
     * once one axis is anything but `visible`, the specification computes the other from
     * `visible` to `auto`. So the `overflow-y` above was quietly asking for a sideways
     * scrollbar too, and a menu that can scroll sideways is one that will eventually be
     * caught doing it - a label a fraction too wide, a font that loaded late.
     *
     * Filament sets neither. Its own rule for this menu is border, padding, shadow and
     * `position: absolute`; the height cap and the scrolling are this package's, which
     * makes the sideways scrollbar this package's too.
     *
     * Deliberately NOT `scrollbar-gutter: stable`: the browser already accounts for the
     * scrollbar when it works out how wide a menu wants to be, so the gutter fixes nothing
     * and charges every menu that does not scroll fifteen pixels of dead space on the
     * right, with the highlight stopping short of it.
     */
    overflow-x: hidden;
}

/* -------------------------------------------------------------------------- */
/* Toolbar alignment                                                          */
/* -------------------------------------------------------------------------- */

/*
 * Filament's toolbar is a wrapping flex row, so the groups are positioned with
 * `justify-content`. Only the horizontal axis is touched; spacing, wrapping and the
 * bottom border stay upstream's.
 *
 * Centred and end-aligned bars say `safe`. Plain `center` splits an overflow between both
 * edges and plain `flex-end` puts all of it past the start, and what hangs over the start
 * of a field is out of reach - a narrow column cut the undo button and the bold button in
 * half. `safe` falls back to the start edge when the row does not fit. The plain value is
 * stated first for a browser that does not know the keyword.
 */
.fi-fo-rich-editor-toolbar.fi-arte-toolbar-align-start,
.fi-arte-toolbar-align-start > .fi-arte-toolbar-flow {
    justify-content: flex-start;
}

.fi-fo-rich-editor-toolbar.fi-arte-toolbar-align-center,
.fi-arte-toolbar-align-center > .fi-arte-toolbar-flow {
    justify-content: center;
    justify-content: safe center;
}

.fi-fo-rich-editor-toolbar.fi-arte-toolbar-align-end,
.fi-arte-toolbar-align-end > .fi-arte-toolbar-flow {
    justify-content: flex-end;
    justify-content: safe flex-end;
}

.fi-fo-rich-editor-toolbar.fi-arte-toolbar-align-between,
.fi-arte-toolbar-align-between > .fi-arte-toolbar-flow {
    justify-content: space-between;
}

/*
 * A group may wrap. Filament never lets one, so the narrowest a bar can get is its widest
 * group - the marks, six buttons and two pickers wide - and a field in a narrow column
 * is narrower than that. A group that fits its row is drawn exactly as before; only one
 * that does not breaks onto a second line, which is still better than a button nobody can
 * reach. The pinned half keeps its groups on one line, for the reason written below.
 */
.fi-arte .fi-fo-rich-editor-toolbar .fi-fo-rich-editor-toolbar-group {
    flex-wrap: wrap;
}

.fi-arte .fi-arte-toolbar-pinned .fi-fo-rich-editor-toolbar-group {
    flex-wrap: nowrap;
}

/* -------------------------------------------------------------------------- */
/* Pinned toolbar buttons                                                     */
/* -------------------------------------------------------------------------- */

/*
 * A toolbar carrying a 'pin' marker is rendered as two containers rather than a row of
 * groups: the ones that travel with the alignment, and the ones that keep an edge. Grid
 * rather than flexbox, because an auto margin would eat the free space the alignment
 * needs - the flow would stop being centred the moment anything was pinned.
 *
 * The columns are declared for the pinned side, and the halves are placed into them by
 * number, so the DOM order stays the reading order whichever edge is asked for.
 */
.fi-fo-rich-editor-toolbar.fi-arte-toolbar-split {
    display: grid;
    align-items: center;
    grid-template-columns: minmax(0, 1fr) auto;
    /*
     * The bar measures itself for the query at the end of this section. A media query
     * would read the window, and a narrow column on a wide screen is the case it is for.
     * The bar's width comes from the field around it, never from its buttons, so taking
     * its own content out of that sum changes nothing about how wide it is.
     */
    container-name: fi-arte-toolbar;
    container-type: inline-size;
}

.fi-arte-toolbar-split.fi-arte-toolbar-pin-start {
    grid-template-columns: auto minmax(0, 1fr);
}

/*
 * The pinned half is a corner of the bar, not a row that reflows: a handful of buttons
 * that belong together and are looked for in one place. Keeping it on one line is also
 * what gives its column a minimum to hold on to - a wrapping half would report the width
 * of its widest button as its minimum and let the flow overrun it.
 */
.fi-arte-toolbar-pinned {
    flex-wrap: nowrap;
    /*
     * A corner, so it stays in the corner: when the flow wraps onto a second and third
     * row the pinned half keeps the first one instead of floating in the middle of the
     * bar. On a toolbar that fits in one row this changes nothing.
     */
    align-self: start;
}

.fi-arte-toolbar-split.fi-arte-toolbar-pin-end > .fi-arte-toolbar-flow,
.fi-arte-toolbar-split.fi-arte-toolbar-pin-start > .fi-arte-toolbar-pinned {
    grid-column: 1;
}

.fi-arte-toolbar-split.fi-arte-toolbar-pin-end > .fi-arte-toolbar-pinned,
.fi-arte-toolbar-split.fi-arte-toolbar-pin-start > .fi-arte-toolbar-flow {
    grid-column: 2;
}

/*
 * A centred bar gets a third column, empty and the same width as the one the pinned half
 * sits in. Without it the flow would be centred on what is left of the bar rather than on
 * the bar, and the whole toolbar would look nudged away from the pinned corner.
 *
 * The empty column can collapse to nothing and the flow column can wrap its own contents,
 * so a narrow bar loses the symmetry before it loses any button.
 */
.fi-arte-toolbar-split.fi-arte-toolbar-align-center {
    grid-template-columns: minmax(0, 1fr) auto 1fr;
}

.fi-arte-toolbar-split.fi-arte-toolbar-align-center.fi-arte-toolbar-pin-start {
    grid-template-columns: 1fr auto minmax(0, 1fr);
}

.fi-arte-toolbar-split.fi-arte-toolbar-align-center > .fi-arte-toolbar-flow {
    grid-column: 2;
}

.fi-arte-toolbar-split.fi-arte-toolbar-align-center.fi-arte-toolbar-pin-end
    > .fi-arte-toolbar-pinned {
    grid-column: 3;
}

.fi-arte-toolbar-split.fi-arte-toolbar-align-center.fi-arte-toolbar-pin-start
    > .fi-arte-toolbar-pinned {
    grid-column: 1;
}

.fi-arte-toolbar-split > .fi-arte-toolbar-pinned {
    justify-self: end;
}

.fi-arte-toolbar-split.fi-arte-toolbar-pin-start > .fi-arte-toolbar-pinned {
    justify-self: start;
}

/*
 * Too narrow for both halves side by side, the pinned half takes the first row on its own,
 * still in its corner, and the flow gets the full width of the rows below. Side by side,
 * the pinned half would keep its column all the way down a bar that has wrapped to ten
 * rows, and squeeze the flow into what is left - a few buttons per row next to empty space.
 *
 * 24rem is the widest group, the pinned half and the gap between them, with room to spare.
 */
@container fi-arte-toolbar (inline-size < 24rem) {
    .fi-arte .fi-fo-rich-editor-toolbar.fi-arte-toolbar-split > .fi-arte-toolbar-pinned {
        grid-row: 1;
        grid-column: 1 / -1;
    }

    .fi-arte .fi-fo-rich-editor-toolbar.fi-arte-toolbar-split > .fi-arte-toolbar-flow {
        grid-row: 2;
        grid-column: 1 / -1;
    }
}

/*
 * Both halves repeat the bar's own row: Filament styles `.fi-fo-rich-editor-toolbar` as
 * the flex container the groups sit in, and that container is now one level further out.
 */
.fi-arte-toolbar-flow,
.fi-arte-toolbar-pinned {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.75rem;
    row-gap: 0.25rem;
}

/* -------------------------------------------------------------------------- */
/* Font size                                                                  */
/* -------------------------------------------------------------------------- */

/* One control, two parts: a number to type in and a chevron to open the list with. */
.fi-arte-font-size-control {
    display: flex;
    align-items: center;
    gap: 0.125rem;
    padding-inline: 0.5rem;
    cursor: text;
}

.fi-arte-font-size-value {
    inline-size: 2.5rem;
    border: 0;
    background-color: transparent;
    font-size: 0.8125rem;
    font-weight: 400;
    line-height: 1.25rem;
    font-variant-numeric: tabular-nums;
    text-align: center;
    color: inherit;
}

.fi-arte-font-size-value:focus {
    outline: none;
}

/* Padded to a size a finger can find, without growing the control around it. */
.fi-arte-font-size-toggle {
    display: flex;
    align-items: center;
    align-self: stretch;
    padding-inline-start: 0.25rem;
    cursor: pointer;
}

.fi-arte-font-size-menu {
    flex-direction: column;
    min-inline-size: 6rem;
    max-block-size: 18rem;
    overflow-y: auto;
}

.fi-arte-font-size-menu .fi-fo-rich-editor-dropdown-tool-option {
    font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------------------- */
/* Font picker                                                                */
/* -------------------------------------------------------------------------- */

.fi-arte-font-picker-trigger {
    gap: 0.25rem;
    padding-inline: 0.5rem;
}

/* The name is set in the typeface it names, so the trigger is its own preview. */
.fi-arte-font-picker-label {
    max-inline-size: 7rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.8125rem;
    font-weight: 400;
    line-height: 1.25rem;
}

.fi-arte-font-menu {
    flex-direction: column;
    min-inline-size: 11rem;
    max-block-size: 18rem;
    overflow-y: auto;
}

.fi-arte-font-search {
    inline-size: 100%;
    margin-block-end: 0.375rem;
    padding: 0.25rem 0.5rem;
    border-radius: 0.375rem;
    border: 1px solid var(--gray-300, #d4d4d8);
    background-color: transparent;
    font-family: inherit;
    font-size: 0.8125rem;
    line-height: 1.25rem;
    color: inherit;
}

.fi-arte-font-search:where(.dark, .dark *) {
    border-color: var(--gray-600, #52525b);
}

.fi-arte-font-search:focus {
    outline: 2px solid var(--primary-600, #2563eb);
    outline-offset: -1px;
}

.fi-arte-font-option {
    inline-size: 100%;
    font-size: 0.9375rem;
}

/* The typeface the theme sets, named beside the option that hands the text back to it. */
.fi-arte-font-default {
    font-family: inherit;
    font-size: 0.75rem;
    color: var(--gray-400, #a1a1aa);
}

/* The way back to the theme's own size, named the same way and set apart the same way. */
.fi-arte-font-size-default {
    font-size: 0.75rem;
    color: var(--gray-400, #a1a1aa);
}

/*
 * The menu of a dropdown this package builds itself - the colour pickers, the font size, the
 * styles, the image and list panels - hanging under its trigger.
 *
 * Filament drew this until 5.10: its rule for `.fi-fo-rich-editor-dropdown-tool-menu` was the
 * box - border, background, shadow - and `position: absolute` under the trigger. In 5.10 its
 * own dropdowns became a component that floats a panel, the box moved to the panel, and the
 * menu inside it is only a row of buttons. These menus have no panel, so without this they
 * stood in the toolbar's flow and pushed the bar apart. The values are Filament's 5.9 ones.
 *
 * A direct child only: Filament's own menu now sits inside its panel and is left alone. Before
 * 5.10 Filament's rule says the same thing, so the two agree. Placed before the upwards rule
 * below, and no more specific than it, so a menu turned over still turns over.
 */
.fi-fo-rich-editor-dropdown-tool > .fi-fo-rich-editor-dropdown-tool-menu {
    position: absolute;
    inset-inline-start: 0;
    top: calc(100% + 0.25rem);
    z-index: 30;
    border: 1px solid var(--gray-300);
    border-radius: var(--radius-lg);
    background-color: var(--color-white);
    padding: calc(var(--spacing) * 1);
    box-shadow:
        0 10px 15px -3px rgb(0 0 0 / 0.1),
        0 4px 6px -4px rgb(0 0 0 / 0.1);
}

.fi-fo-rich-editor-dropdown-tool > .fi-fo-rich-editor-dropdown-tool-menu:where(.dark, .dark *) {
    border-color: var(--gray-600);
    background-color: var(--gray-800);
}

/*
 * A menu turned upwards, because there was no room for it below.
 *
 * Every dropdown in this package hangs off its trigger with `position: absolute`, which is
 * right in the middle of a page and wrong at the foot of one. Two edges cut a menu off and
 * only one of them is the window: `.fi-fo-rich-editor-content` scrolls its own overflow, so
 * a menu opening low in the editor is clipped by the editor. The bar over a selection makes
 * that the normal case, since it hangs below the text it belongs to.
 *
 * Raising `z-index` would not help with either of them. A menu reaching past the bottom of a
 * scrolling ancestor is cut off by geometry, and paint order has no say in it.
 *
 * Both classes on the selector, so this wins wherever the two stylesheets land in the
 * cascade. See `OpensAwayFromTheEdge` for the measuring.
 */
.fi-fo-rich-editor-dropdown-tool-menu.fi-arte-menu-up {
    inset-block-start: auto;
    inset-block-end: 100%;
    margin-block-start: 0;
    margin-block-end: 0.25rem;
}

/*
 * The named styles a project's own design system offers. Drawn like the font picker
 * next to it, because it is the same kind of thing: a list of names, one of which the
 * text currently is.
 */
.fi-arte-style-picker-trigger {
    gap: 0.25rem;
    padding-inline: 0.5rem;
}

.fi-arte-style-picker-label {
    max-inline-size: 7rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.8125rem;
    font-weight: 400;
    line-height: 1.25rem;
}

.fi-arte-style-menu {
    flex-direction: column;
    min-inline-size: 11rem;
    max-block-size: 18rem;
    overflow-y: auto;
}

/*
 * The optional marking on text a style sits on, drawn only where a field asked for it with
 * `->stylePreview()`.
 *
 * Off by default on purpose. A style is a set of the project's own classes, and none of
 * them resolve in an admin panel that has never loaded the front end's stylesheet - so the
 * package cannot show what a style looks like, only that one is set. This says that much
 * and no more: a rule down the side of a block, a dotted line under a run of text.
 *
 * Everything here is a plain single class, so a project's own `[data-style="…"]` rules -
 * which is the way to get the real appearance - override it without a fight.
 */
.fi-arte-style-preview .fi-fo-rich-editor-content [data-style] {
    text-decoration: underline dotted var(--gray-400, #a1a1aa);
    text-underline-offset: 0.25em;
}

/*
 * A block carries its marking down the side instead: an underline under a whole paragraph
 * says "link" to everyone who has ever used a browser.
 */
.fi-arte-style-preview .fi-fo-rich-editor-content p[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content h1[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content h2[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content h3[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content h4[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content h5[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content h6[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content blockquote[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content li[data-style],
.fi-arte-style-preview .fi-fo-rich-editor-content pre[data-style] {
    text-decoration: none;
    border-inline-start: 2px solid var(--gray-300, #d4d4d8);
    padding-inline-start: 0.625rem;
}

.fi-arte-style-preview .fi-fo-rich-editor-content [data-style]:where(.dark, .dark *) {
    border-color: var(--gray-600, #52525b);
}

.fi-arte-style-option {
    inline-size: 100%;
    font-size: 0.9375rem;
}

/*
 * A style that cannot apply where the caret is - one meant for headings while the caret
 * sits in a paragraph. Dimmed rather than removed: a list that changes length as the
 * caret moves is a list nobody can aim at.
 */
.fi-arte-style-option.fi-disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/*
 * Which of the two kinds the rows under it are. Only drawn when both are in the list,
 * because with one kind there is nothing to tell apart.
 */
.fi-arte-style-group {
    padding: 0.375rem 0.5rem 0.125rem;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--gray-400, #a1a1aa);
}

/*
 * The picker itself, which carries no look of its own - the class is what the trigger and
 * the menu are found by, and what a project overrides when it wants a different one.
 */
.fi-arte-style-picker {
    position: relative;
}

/*
 * The bars that float over a picture, a table or a selection, out of the layout until one
 * is opened.
 *
 * Filament renders every bar into the field up front and hides it with `visibility:
 * hidden`, which hides a box without taking it out of the layout: the browser still works
 * out the style and the size of everything inside, on every recalculation, for a bar nobody
 * can see. Filament has one; a field of this package has up to eight, and on a page of
 * editors starting up they were nearly all of its long tasks.
 *
 * TipTap takes a bar out of the document when it hides it again, and puts it back with an
 * inline `visibility: visible` when it shows it - before measuring it. So a bar without an
 * inline visibility has never been shown, and nothing measures it until it is. One that
 * TipTap hid in place, with an inline `hidden`, keeps its box: it is measured again before
 * it comes back.
 */
.fi-arte .fi-fo-rich-editor-floating-toolbar:not([style*='visibility']) {
    display: none;
}

/*
 * The one button in the image toolbar that takes the image away. It is the only
 * destructive thing in there, so it is the only thing that is not grey.
 */
.fi-arte-image-delete:hover,
.fi-arte-image-delete:focus-visible {
    color: var(--danger-600, #dc2626);
}

.fi-arte-image-delete:hover:where(.dark, .dark *),
.fi-arte-image-delete:focus-visible:where(.dark, .dark *) {
    color: var(--danger-400, #f87171);
}

/* -------------------------------------------------------------------------- */
/* Fullscreen                                                                 */
/* -------------------------------------------------------------------------- */

/*
 * The overlay the fullscreen button switches on. It is a fixed box rather than the
 * browser's own Fullscreen API - that promotes one element into the top layer, where
 * Filament's modals, rendered at the end of the body, could no longer be seen.
 *
 * z-index 35 puts it over the panel's topbar and sidebar (30) and under Filament's
 * modals and their overlay (40), which is the whole point of not using the real thing.
 *
 * The background has to be restated: Filament's input wrapper is plain white in light
 * mode but a translucent white/5 in dark mode, and the page would show through a
 * translucent overlay. The colour is the same flattened value the sticky toolbar uses.
 */
.fi-fo-rich-editor.fi-arte-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 35;
    display: flex;
    flex-direction: column;
    margin: 0;
    border-radius: 0;
    background-color: var(--fi-arte-fullscreen-bg, var(--color-white, #fff));
}

.fi-fo-rich-editor.fi-arte-fullscreen:where(.dark, .dark *) {
    background-color: var(
        --fi-arte-fullscreen-bg,
        color-mix(in oklab, var(--color-white, #fff) 10%, var(--gray-900, #18181b))
    );
}

/*
 * The two containers between the wrapper and the editor: Filament's own content column
 * and the plain `wire:ignore` div the Alpine component lives on. Both have to carry the
 * height down, or the writing area would still be as tall as its text.
 *
 * `min-height: 0` on each of them is what makes the content scroll instead of pushing
 * the bottom of the editor past the bottom of the window: a flex child's automatic
 * minimum size is its content, and that overrides the height the parent hands it.
 */
.fi-arte-fullscreen > .fi-input-wrp-content-ctn,
.fi-arte-fullscreen > .fi-input-wrp-content-ctn > * {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

/* -------------------------------------------------------------------------- */
/* Capped height                                                              */
/* -------------------------------------------------------------------------- */

/*
 * A field with `maxHeight()` scrolls inside itself rather than pushing the rest of the
 * form down the page. Only the content box is capped, so the toolbar stays above the text
 * without being pinned to anything - which is why the field turns the sticky toolbar off
 * for itself when a height is set.
 *
 * The cap sits on the document rather than on the box around it, and that is not a detail.
 * Filament puts the floating toolbars - the one over a selection, the one over a picture -
 * inside `.fi-fo-rich-editor-content`, beside the document rather than inside it. Capping
 * that box makes it a scrolling ancestor of those toolbars, and a scrolling ancestor clips
 * what its children position outside it: the bar over a picture near the foot of the field
 * gets cut off at the border, or wedged against it.
 *
 * `z-index` has nothing to say about that. Clipping by a scrolling ancestor is geometry, and
 * paint order does not enter into it - the same thing the note on the upward-opening menus
 * further up says. Moving the cap one element in is what actually lets a toolbar leave the
 * field: the document scrolls, the box around it does not, and nothing clips.
 */
.fi-fo-rich-editor.fi-arte-max-height .fi-fo-rich-editor-content > .tiptap {
    max-height: var(--fi-arte-max-height);
    overflow-y: auto;
}

/*
 * Fullscreen is a height of its own and wins: the overlay already fills the window, and a
 * 400px box inside it would leave most of the screen empty.
 */
.fi-arte-fullscreen.fi-arte-max-height .fi-fo-rich-editor-content > .tiptap {
    max-height: none;
}

/*
 * The direction is deliberately left alone: Filament reverses this column so the panels
 * sit above the text, and turns it into a row from `2xl` up. Only the height is ours.
 */
.fi-arte-fullscreen .fi-fo-rich-editor-main {
    flex: 1 1 auto;
    min-height: 0;
}

.fi-arte-fullscreen .fi-fo-rich-editor-content {
    min-height: 0;
    overflow-y: auto;
}

/*
 * Nothing left to be sticky against, and nothing left to round: the toolbar is the top
 * edge of the overlay, and the topbar its offset was measured from is behind it.
 */
.fi-arte-fullscreen .fi-fo-rich-editor-toolbar.fi-arte-sticky {
    top: 0;
    border-start-start-radius: 0;
    border-start-end-radius: 0;
}

/* The page behind the overlay must not scroll away under it. */
body.fi-arte-fullscreen-lock {
    overflow: hidden;
}

/* -------------------------------------------------------------------------- */
/* Image resizing                                                             */
/* -------------------------------------------------------------------------- */

/*
 * The size readout that follows a drag. Appended to the resize wrapper by the assist
 * extension and removed again when the drag ends, so it only exists while it is useful.
 */
.fi-arte-image-size {
    position: absolute;
    inset-block-end: 0.5rem;
    inset-inline-end: 0.5rem;
    z-index: 10;
    padding: 0.125rem 0.375rem;
    border-radius: 0.375rem;
    background-color: rgba(9, 9, 11, 0.75);
    color: #fff;
    font-size: 0.6875rem;
    font-variant-numeric: tabular-nums;
    line-height: 1.25rem;
    white-space: nowrap;
    /* Never in the way of the handle being dragged. */
    pointer-events: none;
}

/*
 * A quarter turned image is given compensating margins so its layout box matches what is
 * drawn, which makes that box NARROWER than the image itself. `max-width: 100%` then reads
 * the shrunken box as the limit and squashes the picture - a landscape image turned on its
 * side would lose everything past its own height. The margins already account for the real
 * footprint, so the limit is dropped for exactly these images.
 */
.fi-arte [data-resize-wrapper] > img[style*='rotate('] {
    max-width: none;
}

/*
 * A floated picture, inside the editor.
 *
 * The float is written on the `<img>`, because that is where it has to end up in the
 * stored markup - but inside the editor the picture is not what is laid out. Filament
 * draws an image through a resize node view: the picture sits in a resize wrapper, and
 * that wrapper sits in an `inline-flex` box which is the thing the paragraph actually
 * lays out. Floating either of the inner two moves nothing, so the outer one is floated.
 * The node view marks that box `data-resize-container`, and `:has(> [data-resize-wrapper]
 * …)` picks out the ones whose picture was floated.
 *
 * The attribute in front of the `:has()` is what keeps these rules cheap, not what makes
 * them match. Without it a rule is tried against every element on the page on every style
 * recalculation, and a page of editors recalculates constantly while it starts - see
 * `SelectorCostTest`.
 *
 * Matched on the style the extension wrote rather than on a class: a class would have to
 * survive the sanitiser to be worth writing, and this one never leaves the editor.
 *
 * The gap is drawn here too, from the property the field writes the configured value onto.
 * The stored markup carries its own copy, written in PHP - which is the half a save
 * renders from, and the only half a page ever sees.
 */
.fi-arte [data-resize-container]:has(> [data-resize-wrapper] img[style*='float: left']) {
    float: left;
    margin-inline-end: var(--fi-arte-image-float-gap, 1rem);
    margin-block-end: var(--fi-arte-image-float-gap, 1rem);
}

.fi-arte [data-resize-container]:has(> [data-resize-wrapper] img[style*='float: right']) {
    float: right;
    margin-inline-start: var(--fi-arte-image-float-gap, 1rem);
    margin-block-end: var(--fi-arte-image-float-gap, 1rem);
}

/*
 * Centred, which is not a float and cannot be one: CSS has no way to run text down both
 * sides of a block. A block with automatic margins is what every editor means by it, and
 * `fit-content` is what keeps that block the width of the picture rather than the width of
 * the line - without it the automatic margins have nothing to share out.
 */
.fi-arte [data-resize-container]:has(> [data-resize-wrapper] img[style*='margin-inline: auto']) {
    /*
     * `!important` on this one line, and it is load-bearing: the node view writes
     * `display: inline-flex` as an inline style on this element, which no ordinary rule
     * outranks. The float rules above need nothing of the sort - `float` is not in that
     * inline style.
     */
    display: block !important;
    inline-size: fit-content;
    margin-inline: auto;
}

/*
 * The paragraph a floated picture sits in collapses to nothing, so without this the
 * following block starts at the top of the picture rather than beside it - which is right
 * on a page and wrong in an editor, where a caret has to be reachable. The float is
 * cleared at the end of the document instead.
 */
.fi-arte .fi-fo-rich-editor-content::after {
    content: '';
    display: block;
    clear: both;
}

/* The unlocked state is the notable one, so it is the one that gets the accent. */
.fi-arte-image-lock.fi-active {
    color: var(--primary-600, #2563eb);
}

.fi-arte-image-lock.fi-active:where(.dark, .dark *) {
    color: var(--primary-400, #60a5fa);
}

/*
 * An image whose file did not load. Without this the node keeps its size and renders as
 * an empty hole, which reads like a bug in the editor rather than a missing file.
 */
.fi-arte [data-resize-wrapper][data-arte-image='error'] {
    border: 1px dashed rgba(9, 9, 11, 0.35);
    border-radius: 0.5rem;
    background-color: rgba(9, 9, 11, 0.06);
}

.fi-arte [data-resize-wrapper][data-arte-image='error']:where(.dark, .dark *) {
    border-color: rgba(255, 255, 255, 0.35);
    background-color: rgba(255, 255, 255, 0.06);
}

.fi-arte [data-resize-wrapper][data-arte-image='error']::after {
    content: '';
    position: absolute;
    inset: 0;
    margin: auto;
    /* A crossed-out picture frame, drawn rather than shipped as an asset. */
    background-color: currentColor;
    opacity: 0.55;
    inline-size: 2rem;
    block-size: 2rem;
    -webkit-mask-image: var(--fi-arte-image-error-icon);
    mask-image: var(--fi-arte-image-error-icon);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

.fi-arte {
    --fi-arte-image-error-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='black' stroke-width='1.5'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z'/%3E%3Cpath stroke-linecap='round' d='M3 21L21 3'/%3E%3C/svg%3E");
}

/* -------------------------------------------------------------------------- */
/* Colour pickers                                                             */
/* -------------------------------------------------------------------------- */

/* The trigger carries the icon with a bar of the current colour under it. */
.fi-arte-color-preview {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 0.125rem;
}

.fi-arte-color-preview-bar {
    inline-size: 1rem;
    block-size: 0.25rem;
    border-radius: 0.125rem;
    background-color: currentColor;
}

/* Nothing set yet: a muted rail rather than an accidental black bar. */
.fi-arte-color-preview-bar-empty {
    background-color: var(--gray-300, #d4d4d8);
}

.fi-arte-color-preview-bar-empty:where(.dark, .dark *) {
    background-color: var(--gray-600, #52525b);
}

.fi-arte-color-menu {
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem;
}

.fi-arte-color-grid {
    display: grid;
    grid-template-columns: repeat(6, 1.5rem);
    gap: 0.25rem;
}

.fi-arte-color-swatch {
    inline-size: 1.5rem;
    block-size: 1.5rem;
    border-radius: 0.375rem;
    border: 1px solid rgba(9, 9, 11, 0.1);
    background-color: var(--fi-arte-color, transparent);
    cursor: pointer;
}

.fi-arte-color-swatch:where(.dark, .dark *) {
    border-color: rgba(255, 255, 255, 0.15);
    background-color: var(--fi-arte-dark-color, var(--fi-arte-color, transparent));
}

.fi-arte-color-swatch:hover {
    transform: scale(1.08);
}

/* The active swatch is ringed rather than recoloured, so its colour stays readable. */
.fi-arte-color-swatch.fi-active {
    outline: 2px solid var(--primary-600, #2563eb);
    outline-offset: 1px;
}

.fi-arte-color-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--fi-arte-divider-color, rgba(9, 9, 11, 0.1));
}

.fi-arte-color-actions:where(.dark, .dark *) {
    border-top-color: rgba(255, 255, 255, 0.1);
}

.fi-arte-color-clear {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.25rem 0.375rem;
    border-radius: 0.375rem;
    font-size: 0.75rem;
    line-height: 1rem;
    white-space: nowrap;
    cursor: pointer;
}

.fi-arte-color-clear:hover {
    background-color: rgba(9, 9, 11, 0.05);
}

.fi-arte-color-clear:hover:where(.dark, .dark *) {
    background-color: rgba(255, 255, 255, 0.05);
}

/* The "no colour" swatch: a slash, the way a colour picker usually spells empty. */
.fi-arte-color-clear-swatch {
    position: relative;
    inline-size: 1rem;
    block-size: 1rem;
    border-radius: 0.25rem;
    border: 1px solid rgba(9, 9, 11, 0.2);
    overflow: hidden;
}

.fi-arte-color-clear-swatch:where(.dark, .dark *) {
    border-color: rgba(255, 255, 255, 0.25);
}

.fi-arte-color-clear-swatch::after {
    content: '';
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: -20%;
    inline-size: 140%;
    block-size: 1px;
    background-color: var(--danger-500, #ef4444);
    transform: rotate(-45deg);
}

/* The native picker is the input itself; only its trigger is styled. */
.fi-arte-color-custom {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    border-radius: 0.375rem;
    cursor: pointer;
}

.fi-arte-color-custom:hover {
    background-color: rgba(9, 9, 11, 0.05);
}

.fi-arte-color-custom:hover:where(.dark, .dark *) {
    background-color: rgba(255, 255, 255, 0.05);
}

.fi-arte-color-custom-input {
    position: absolute;
    inset: 0;
    opacity: 0;
    inline-size: 100%;
    block-size: 100%;
    cursor: pointer;
}

/* -------------------------------------------------------------------------- */
/* List properties panel                                                      */
/* -------------------------------------------------------------------------- */

/*
 * The panel in the bubble that appears while the caret is in a list. Its own family of
 * classes rather than the image panel's: the two look alike on purpose but they are
 * different controls, and a project restyling one of them should not silently be
 * restyling the other.
 */
.fi-arte-list-panel-menu {
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem;
    min-inline-size: 12rem;
}

.fi-arte-list-panel-label {
    margin: 0;
    font-size: 0.6875rem;
    line-height: 1rem;
    color: var(--gray-500, #71717a);
}

/*
 * The markers wrap rather than scroll: five of them at text width is one row on a wide
 * bubble and two on a narrow one, and either is easier to aim at than a row that slides.
 */
.fi-arte-list-panel-markers {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
}

.fi-arte-list-panel-marker {
    padding: 0.1875rem 0.4375rem;
    border: 1px solid var(--fi-arte-divider-color, rgba(9, 9, 11, 0.15));
    border-radius: 0.375rem;
    font-size: 0.75rem;
    line-height: 1rem;
    /* The ordered markers are drawn as examples - "a, b, c" - so they are read as text. */
    font-variant-numeric: tabular-nums;
    cursor: pointer;
}

.fi-arte-list-panel-marker:where(.dark, .dark *) {
    border-color: rgba(255, 255, 255, 0.15);
}

.fi-arte-list-panel-marker:hover {
    background-color: rgba(9, 9, 11, 0.05);
}

.fi-arte-list-panel-marker:hover:where(.dark, .dark *) {
    background-color: rgba(255, 255, 255, 0.05);
}

.fi-arte-list-panel-marker.fi-active {
    border-color: var(--primary-600, #2563eb);
    color: var(--primary-600, #2563eb);
}

.fi-arte-list-panel-marker.fi-active:where(.dark, .dark *) {
    border-color: var(--primary-400, #60a5fa);
    color: var(--primary-400, #60a5fa);
}

.fi-arte-list-panel-field {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.fi-arte-list-panel-input {
    inline-size: 100%;
    padding: 0.25rem 0.375rem;
    border: 1px solid var(--fi-arte-divider-color, rgba(9, 9, 11, 0.15));
    border-radius: 0.375rem;
    background-color: transparent;
    font-size: 0.75rem;
    line-height: 1rem;
    color: inherit;
}

.fi-arte-list-panel-input:where(.dark, .dark *) {
    border-color: rgba(255, 255, 255, 0.15);
}

.fi-arte-list-panel-input:focus {
    outline: 2px solid var(--primary-600, #2563eb);
    outline-offset: -1px;
}

.fi-arte-list-panel-check {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.75rem;
    line-height: 1rem;
    cursor: pointer;
    user-select: none;
}

.fi-arte-list-panel-check > input[type='checkbox'] {
    inline-size: 0.875rem;
    block-size: 0.875rem;
    margin: 0;
    border-radius: 0.25rem;
    cursor: pointer;
    accent-color: var(--primary-600, #2563eb);
}

.fi-arte-list-panel-check > input[type='checkbox']:where(.dark, .dark *) {
    accent-color: var(--primary-500, #3b82f6);
}

/* -------------------------------------------------------------------------- */
/* Image panels                                                               */
/* -------------------------------------------------------------------------- */

.fi-arte-image-panel-menu {
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem;
    min-inline-size: 13rem;
}

.fi-arte-image-panel-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.fi-arte-image-panel-field {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    flex: 1 1 0;
    min-inline-size: 0;
}

.fi-arte-image-panel-label {
    font-size: 0.6875rem;
    line-height: 1rem;
    color: var(--gray-500, #71717a);
}

.fi-arte-image-panel-input {
    inline-size: 100%;
    padding: 0.25rem 0.375rem;
    border: 1px solid var(--fi-arte-divider-color, rgba(9, 9, 11, 0.15));
    border-radius: 0.375rem;
    background-color: transparent;
    font-size: 0.75rem;
    line-height: 1rem;
    color: inherit;
}

.fi-arte-image-panel-input:where(.dark, .dark *) {
    border-color: rgba(255, 255, 255, 0.15);
}

.fi-arte-image-panel-input:focus {
    outline: 2px solid var(--primary-600, #2563eb);
    outline-offset: -1px;
}

.fi-arte-image-panel-hint {
    margin: 0;
    font-size: 0.6875rem;
    line-height: 1rem;
    color: var(--gray-400, #a1a1aa);
}

.fi-arte-image-panel-reset {
    align-self: flex-start;
    padding: 0.25rem 0.375rem;
    border-radius: 0.375rem;
    font-size: 0.75rem;
    line-height: 1rem;
    cursor: pointer;
}

.fi-arte-image-panel-reset:hover {
    background-color: rgba(9, 9, 11, 0.05);
}

.fi-arte-image-panel-reset:hover:where(.dark, .dark *) {
    background-color: rgba(255, 255, 255, 0.05);
}

.fi-arte-image-panel-actions {
    display: flex;
    align-items: center;
    gap: 0.375rem;
}

.fi-arte-image-panel-apply {
    padding: 0.25rem 0.625rem;
    border-radius: 0.375rem;
    background-color: var(--primary-600, #2563eb);
    color: #fff;
    font-size: 0.75rem;
    line-height: 1rem;
    font-weight: 500;
    cursor: pointer;
}

.fi-arte-image-panel-apply[disabled] {
    opacity: 0.5;
    cursor: default;
}

/* Sits between the two fields, where it says what it links. */
.fi-arte-image-panel-lock {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    align-self: flex-end;
    inline-size: 1.75rem;
    block-size: 1.75rem;
    margin-block-end: 0.0625rem;
    border-radius: 0.375rem;
    color: var(--gray-500, #71717a);
    cursor: pointer;
}

.fi-arte-image-panel-lock:hover {
    background-color: rgba(9, 9, 11, 0.05);
}

.fi-arte-image-panel-lock:hover:where(.dark, .dark *) {
    background-color: rgba(255, 255, 255, 0.05);
}

/* Unlocked is the state worth pointing out, so that is the one with the accent. */
.fi-arte-image-panel-lock.fi-active {
    color: var(--primary-600, #2563eb);
}

.fi-arte-image-panel-lock.fi-active:where(.dark, .dark *) {
    color: var(--primary-400, #60a5fa);
}

/* -------------------------------------------------------------------------- */
/* Character count                                                            */
/* -------------------------------------------------------------------------- */

/*
 * Sits under the editor, in Filament's own slot for things below a field. Right aligned and
 * quiet: it is a number to glance at, not to read, until it has something to say.
 */
.fi-arte-character-count {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.375rem;
    font-size: 0.75rem;
    line-height: 1rem;
    font-variant-numeric: tabular-nums;
    color: var(--gray-500, #71717a);
}

.fi-arte-character-count-warning {
    color: var(--warning-600, #ca8a04);
}

.fi-arte-character-count-warning:where(.dark, .dark *) {
    color: var(--warning-400, #facc15);
}

.fi-arte-character-count-danger {
    color: var(--danger-600, #dc2626);
    font-weight: 500;
}

.fi-arte-character-count-danger:where(.dark, .dark *) {
    color: var(--danger-400, #f87171);
}

/* -------------------------------------------------------------------------- */
/* Video embeds                                                               */
/* -------------------------------------------------------------------------- */

/*
 * The frame on a rendered page. The wrapper carries the aspect ratio - written into its
 * own `style`, which is what survives the sanitiser - and the frame fills it, so the video
 * keeps its shape at any width without anyone computing a padding hack.
 *
 * These two rules are the only ones here that matter outside the panel. This stylesheet is
 * loaded into the admin, not into the page the content ends up on, so a project styling
 * its own front end restates them there.
 */
.fi-arte-embed {
    width: 100%;
    overflow: hidden;
    border-radius: 0.5rem;
}

.fi-arte-embed > iframe {
    width: 100%;
    height: 100%;
    border: 0;
}

/*
 * The questions before a delete or a replacement are Filament's own confirmation modal, drawn
 * inside the browser. Filament fades the overlay of a modal that another one is stacked on, so
 * the page is dimmed once rather than once per layer - but it looks for the later modal as a
 * sibling, and this one is a descendant. The same rule, for the other relation.
 */
.fi-modal.fi-modal-open:has(.fi-arte-media-confirm.fi-modal-open) > .fi-modal-close-overlay {
    opacity: 0;
}

/*
 * A video or a sound that lives on this server.
 *
 * The first two rules are the ones that matter outside the panel, and they are also
 * written inline onto the element itself - this stylesheet is loaded into the admin and
 * the page the content ends up on is somebody else's, where a `<video>` with only a class
 * on it is drawn at its own pixel size and overflows the column.
 */
.fi-arte-media {
    max-width: 100%;
    border-radius: 0.5rem;
}

.fi-arte-media-video {
    display: block;
    width: 100%;
    height: auto;
    background-color: rgb(0 0 0);
}

.fi-arte-media-audio {
    display: block;
    width: 100%;
}

/*
 * The block the editor wraps it in, which is what makes it behave like the rest of the
 * document. A media element answers every click with its own controls, so without
 * something around it there would be nowhere left to click to select the block, drag it or
 * delete it. The padding is that somewhere.
 */
.fi-arte-media-block {
    margin: 0.75rem 0;
    padding: 0.25rem;
    border-radius: 0.625rem;
    cursor: default;
}

.fi-arte-media-block.ProseMirror-selectednode {
    outline: 2px solid var(--fi-arte-media-selected, rgb(59 130 246));
    outline-offset: 1px;
}

/*
 * A block pointing at nothing. It happens to a document written by hand or pasted from a
 * page whose player had no address, and a block with no height at all cannot be selected
 * to be deleted - so it is drawn as an empty strip rather than as nothing.
 */
.fi-arte-media-block-empty {
    min-height: 2.5rem;
    border: 1px dashed var(--fi-arte-media-border, rgb(228 228 231));
}

.fi-arte-media-block-empty:where(.dark, .dark *) {
    --fi-arte-media-border: rgb(63 63 70);
}

/*
 * What the editor draws in place of the video: a card naming what will be there.
 *
 * Deliberately not the player. Ten embeds in one document would be ten players loading
 * from YouTube, each with its own requests and its own cookie, in a screen where nobody is
 * watching anything - and an iframe swallows every click, so the block could not be
 * selected, dragged or deleted like the rest.
 */
.fi-arte-embed-card {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    margin: 0.75rem 0;
    padding: 0.75rem 0.875rem;
    border-radius: 0.5rem;
    border: 1px solid var(--fi-arte-embed-border, rgb(228 228 231));
    background-color: var(--fi-arte-embed-bg, rgb(250 250 250));
    font-size: 0.875rem;
    cursor: default;
}

.fi-arte-embed-card:where(.dark, .dark *) {
    --fi-arte-embed-border: rgb(63 63 70);
    --fi-arte-embed-bg: rgb(39 39 42);
}

/* The selected block reads as selected, the way an image does. */
.fi-arte-embed-card.ProseMirror-selectednode {
    outline: 2px solid var(--fi-arte-embed-selected, rgb(59 130 246));
    outline-offset: 1px;
}

.fi-arte-embed-card-provider {
    flex-shrink: 0;
    padding: 0.125rem 0.5rem;
    border-radius: 9999px;
    background-color: var(--fi-arte-embed-badge, rgb(228 228 231));
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.fi-arte-embed-card-provider:where(.dark, .dark *) {
    --fi-arte-embed-badge: rgb(63 63 70);
}

.fi-arte-embed-card-title {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.fi-arte-embed-card-start {
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
    color: var(--fi-arte-embed-muted, rgb(113 113 122));
}

.fi-arte-embed-card-start:where(.dark, .dark *) {
    --fi-arte-embed-muted: rgb(161 161 170);
}

/* -------------------------------------------------------------------------- */
/* Document cards                                                             */
/* -------------------------------------------------------------------------- */

/*
 * An uploaded document, on both sides of the save.
 *
 * Almost nothing here is load-bearing, and that is the point. The card carries its own
 * shape in an inline `style` written by `Nodes/FileCard` and by `file-card.js` - the page a
 * reader opens belongs to the project, and this stylesheet is only ever loaded into the
 * admin panel, so a card that needed it would arrive there as three bare spans.
 *
 * What is left for CSS is what only the editor has: a card that reads as selected, a
 * pointer that says "not a link right now", and a hover that says it is a thing you can
 * grab. A project overriding the look overrides these classes, which both halves emit.
 *
 * No margin here, deliberately. The card is inline and carries its own spacing in the
 * `style` both halves write, so two cards sit apart on a page that never loaded this file
 * as well as in the panel - and a rule here would be overridden by that inline value
 * anyway, which is the kind of dead line that reads as a working one.
 */
.fi-arte-file {
    cursor: default;
    background-color: var(--fi-arte-file-bg, rgb(250 250 250));
    transition: background-color 75ms ease;
}

.fi-arte-file:where(.dark, .dark *) {
    --fi-arte-file-bg: rgb(39 39 42);
}

.fi-arte-file:hover {
    --fi-arte-file-bg: rgb(244 244 245);
}

.fi-arte-file:hover:where(.dark, .dark *) {
    --fi-arte-file-bg: rgb(52 52 56);
}

/* The selected block reads as selected, the way the embed card and an image do. */
.fi-arte-file.ProseMirror-selectednode {
    outline: 2px solid var(--fi-arte-file-selected, rgb(59 130 246));
    outline-offset: 1px;
}

/*
 * A card with no address behind it. Only reachable from a node whose `src` was refused -
 * a `javascript:` link, or a document written by hand with the address left out - and it
 * exists so the block can still be selected and deleted rather than being an invisible
 * thing the cursor jumps over.
 */
.fi-arte-file-empty {
    display: inline-block;
    min-width: 3rem;
    min-height: 2.5rem;
    border: 1px dashed rgb(161 161 170);
    border-radius: 0.5rem;
}

/* -------------------------------------------------------------------------- */
/* Code block language                                                        */
/* -------------------------------------------------------------------------- */

/*
 * The picker sits on the block rather than in the toolbar: which language a block is
 * written in is a property of that block, and a document may hold several in different
 * ones. It rides in the corner of the box, where it is out of the way of the code.
 */
.fi-arte-code-block {
    position: relative;
}

.fi-arte-code-block-language {
    position: absolute;
    inset-block-start: 0.375rem;
    inset-inline-end: 0.375rem;
    z-index: 1;
    padding: 0.125rem 1.5rem 0.125rem 0.5rem;
    border-radius: 0.375rem;
    border: 1px solid var(--fi-arte-code-border, rgb(228 228 231));
    background-color: var(--fi-arte-code-bg, rgb(255 255 255 / 0.85));
    font-size: 0.6875rem;
    line-height: 1.5;
    color: var(--fi-arte-code-text, rgb(82 82 91));
    cursor: pointer;
    /* Quiet until the block is worked on: chrome, not content. */
    opacity: 0.55;
    transition: opacity 150ms;
}

.fi-arte-code-block:hover .fi-arte-code-block-language,
.fi-arte-code-block-language:focus {
    opacity: 1;
}

.fi-arte-code-block-language:where(.dark, .dark *) {
    --fi-arte-code-border: rgb(63 63 70);
    --fi-arte-code-bg: rgb(24 24 27 / 0.85);
    --fi-arte-code-text: rgb(212 212 216);
}

/* -------------------------------------------------------------------------- */
/* Highlighted code, in a dark panel                                          */
/* -------------------------------------------------------------------------- */

/*
 * `highlightCode(themes: [...])` writes both themes into the same markup — the light one
 * as ordinary colours, the dark one as custom properties — and leaves the swap to the
 * project, because which switch counts is the project's idea of dark mode.
 *
 * Inside a Filament panel that question has one answer, and it is `.dark`. This stylesheet
 * is registered with Filament and loads nowhere else, so the swap can be shipped here
 * rather than asked for. A front end that renders the same content still writes its own
 * three lines — the documentation says so, and this rule cannot reach it.
 *
 * Only a block that carries both themes is touched: `phiki-themes` is the class Phiki adds
 * when it was given a pair, and a block with a single theme has no dark colour to swap to.
 */
.phiki-themes:where(.dark, .dark *),
.phiki-themes:where(.dark, .dark *) code,
.phiki-themes:where(.dark, .dark *) span {
    /*
     * `!important`, and it is load-bearing rather than lazy: every token carries its light
     * colour as an inline style, which no ordinary rule outranks. The same line is in the
     * documentation for the front-end half, for the same reason.
     */
    color: var(--phiki-dark-color) !important;
    background-color: var(--phiki-dark-background-color) !important;
}

/* -------------------------------------------------------------------------- */
/* Image captions                                                             */
/* -------------------------------------------------------------------------- */

/*
 * The caption under a picture in the editor.
 *
 * Drawn from an attribute rather than from an element: what is stored is `data-caption` on
 * the image, and the `<figure>` it becomes is built when the page is rendered. An `<img>`
 * is void and cannot carry a `::after`, so the script mirrors the text onto the box
 * Filament draws around a resizable image and the rule below reads it from there.
 */
[data-arte-caption]::after {
    content: attr(data-arte-caption);
    display: block;
    padding-block-start: 0.375rem;
    font-size: 0.8125rem;
    line-height: 1.4;
    text-align: center;
    color: var(--fi-arte-caption-color, rgb(113 113 122));
}

[data-arte-caption]:where(.dark, .dark *)::after {
    --fi-arte-caption-color: rgb(161 161 170);
}

/*
 * The figure on a rendered page. Like the embed, this is restated in the project's own
 * stylesheet - this one is loaded into the admin panel and not into the page the content
 * ends up on - but shipping it keeps the two looking alike where both are loaded.
 */
.fi-arte-figure {
    margin-inline: 0;
    /*
     * Boxed to the picture rather than to the column. A figure is a block, so without this
     * it is as wide as the text around it whatever the picture measures - and then a
     * centred caption is centred on the column instead of under the picture, and the
     * `margin-inline: auto` that centres a picture has nothing to push against. Written
     * on the element as well, because the page a rendered figure lands on does not load
     * this stylesheet.
     */
    inline-size: fit-content;
    max-inline-size: 100%;
}

/*
 * A picture wider than the column it sits in overflows it. Most projects have this in a
 * reset already; the rule is scoped to the figure so it never argues with one that does.
 */
.fi-arte-figure img {
    max-width: 100%;
    height: auto;
}

.fi-arte-figure > figcaption {
    /*
     * And the caption must not widen the figure back out. Its longest word is all it
     * contributes to the figure's preferred width; the minimum brings it back to whatever
     * the picture settled on.
     */
    inline-size: min-content;
    min-inline-size: 100%;
    padding-block-start: 0.375rem;
    font-size: 0.8125rem;
    line-height: 1.4;
    text-align: center;
    color: var(--fi-arte-caption-color, rgb(113 113 122));
}

/*
 * The media browser inside the image dialog.
 *
 * Two columns, because choosing a picture is two questions: which one, and is this the right
 * one. The left side answers the first by showing many at once; the right answers the second
 * about the one that is selected, with the numbers that tell two similar photographs apart.
 *
 * The controls themselves are Filament's own components, so almost nothing here styles them -
 * what follows is layout, the two ways of listing a picture, and the panel.
 *
 * Only the root is in this sheet; the browser itself is in the overlay sheet, under "Media
 * browser". The root stays because its class is also the one an audio or video element in
 * a stored document carries, and that one is on the page before anybody opens anything.
 */
.fi-arte-media {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    --fi-arte-media-border: rgb(228 228 231);
    --fi-arte-media-bg: #fff;
    --fi-arte-media-tile: rgb(244 244 245);
    --fi-arte-media-hover: rgb(161 161 170);
    --fi-arte-media-muted: rgb(113 113 122);
    --fi-arte-media-picked: rgb(59 130 246);
    --fi-arte-media-pending: rgb(161 98 7);
}

.fi-arte-media:where(.dark, .dark *) {
    --fi-arte-media-border: rgb(63 63 70);
    --fi-arte-media-bg: rgb(24 24 27);
    --fi-arte-media-tile: rgb(39 39 42);
    --fi-arte-media-hover: rgb(82 82 91);
    --fi-arte-media-muted: rgb(161 161 170);
    --fi-arte-media-pending: rgb(250 204 21);
}

/* -------------------------------------------------------------------------- */
/* Drag handle                                                                */
/* -------------------------------------------------------------------------- */

/*
 * The grip in the margin of the block under the mouse, and the plus beside it.
 *
 * Fixed rather than absolute, and that is the whole of why it needs no room in the
 * layout: the gutter it sits in is the editor's own padding, so nothing about the field
 * changes shape for a control that is only there while something is being hovered. A
 * field with `maxHeight()` is its own scrolling window, which an absolutely positioned
 * child of the box around it would not follow.
 *
 * It is drawn out of the flow and out of the way: no pointer events until it is shown,
 * so a click that lands in the margin of a field nobody is hovering goes to the text.
 */
/*
 * Room for it. Filament leaves twenty pixels to the left of the text and two controls need
 * fifty, so a field that has a grip is given a wider gutter to keep it in - the attribute
 * is written by the view only where the feature is on, which is what keeps every other
 * editor on the page exactly as Filament drew it.
 */
.fi-fo-rich-editor-content[data-arte-drag-handle] {
    /* Logical rather than left: the gutter belongs beside the start of the text, and in a
       field written right to left that is the other side of the box. */
    padding-inline-start: 3.25rem;
}

.fi-arte-drag-handle {
    position: fixed;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: 0.125rem;
    /* Its own size, stated: it is a sibling of the document inside `.fi-prose`, which has
       an opinion about the height of everything that is not told otherwise. The margin is
       that opinion too, and the element carries `fi-not-prose` for it - see the script.
       Both are kept because a size the layout depends on should be read here rather than
       inferred from a class somewhere else. */
    height: 1.5rem;
    margin: 0;
    line-height: 1;
    opacity: 0;
    pointer-events: none;
    transition: opacity 75ms ease-out;
}

.fi-arte-drag-handle-visible {
    opacity: 1;
    pointer-events: auto;
}

/*
 * Both controls are the same shape and neither is a button anybody should notice until
 * they are looking for it - the block is the thing on the page, and this is a handle on
 * it.
 */
.fi-arte-drag-handle-grip,
.fi-arte-drag-handle-insert {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.5rem;
    border-radius: 0.25rem;
    color: var(--gray-400, #a1a1aa);
    background-color: transparent;
    transition: background-color 75ms ease-out, color 75ms ease-out;
}

.fi-arte-drag-handle-grip:hover,
.fi-arte-drag-handle-insert:hover {
    color: var(--gray-700, #3f3f46);
    background-color: var(--gray-100, #f4f4f5);
}

.fi-arte-drag-handle-grip:where(.dark, .dark *):hover,
.fi-arte-drag-handle-insert:where(.dark, .dark *):hover {
    color: var(--color-white, #fff);
    background-color: color-mix(in oklab, var(--color-white, #fff) 10%, transparent);
}

.fi-arte-drag-handle-grip svg,
.fi-arte-drag-handle-insert svg {
    width: 1rem;
    height: 1rem;
}

/* Grab, then grabbing: the one cursor pair every list of this kind has taught. */
.fi-arte-drag-handle-grip {
    cursor: grab;
    touch-action: none;
    user-select: none;
}

.fi-arte-drag-handle-grip:active {
    cursor: grabbing;
}

/*
 * While a block is in the air. The text cursor over the document would say the drop is
 * about to happen where the mouse is, and it is not - ProseMirror draws the line that
 * says where.
 */
.fi-arte-dragging {
    cursor: grabbing;
}

.fi-arte-dragging * {
    cursor: grabbing !important;
}

/* -------------------------------------------------------------------------- */
/* Draft bar                                                                  */
/* -------------------------------------------------------------------------- */

/*
 * Offered above the document when the browser is holding a draft that says something the
 * page does not. It is one row, it is not a notification, and it does not go away on its
 * own: the two answers it wants are both destructive in one direction or the other, so it
 * waits for one of them.
 *
 * Amber rather than red or green, because neither is true - nothing has gone wrong and
 * nothing has succeeded. Something is being held that somebody has to decide about.
 */
.fi-arte-draft-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--fi-arte-divider-color, rgba(9, 9, 11, 0.15));
    background-color: var(--amber-50, #fffbeb);
    font-size: 0.8125rem;
    line-height: 1.25rem;
}

.fi-arte-draft-bar:where(.dark, .dark *) {
    border-color: color-mix(in oklab, var(--color-white, #fff) 15%, transparent);
    background-color: color-mix(in oklab, var(--amber-400, #fbbf24) 12%, transparent);
}

.fi-arte-draft-message {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    color: var(--amber-900, #78350f);
}

.fi-arte-draft-message:where(.dark, .dark *) {
    color: var(--amber-200, #fde68a);
}

/* Both answers are the same weight on purpose: neither of them is the safe one. */
.fi-arte-draft-restore,
.fi-arte-draft-discard {
    flex: 0 0 auto;
    padding: 0.125rem 0.5rem;
    border: 1px solid var(--fi-arte-divider-color, rgba(9, 9, 11, 0.15));
    border-radius: 0.375rem;
    background-color: var(--color-white, #fff);
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.25rem;
    color: inherit;
}

.fi-arte-draft-restore:hover,
.fi-arte-draft-discard:hover {
    background-color: var(--gray-50, #fafafa);
}

.fi-arte-draft-restore:where(.dark, .dark *),
.fi-arte-draft-discard:where(.dark, .dark *) {
    border-color: color-mix(in oklab, var(--color-white, #fff) 15%, transparent);
    background-color: color-mix(in oklab, var(--color-white, #fff) 10%, transparent);
}

.fi-arte-draft-restore:where(.dark, .dark *):hover,
.fi-arte-draft-discard:where(.dark, .dark *):hover {
    background-color: color-mix(in oklab, var(--color-white, #fff) 18%, transparent);
}

/* -------------------------------------------------------------------------- */
/* Format brush                                                               */
/* -------------------------------------------------------------------------- */

/*
 * The format brush, while it is armed.
 *
 * A cursor rather than a colour on the text, because nothing has been decided yet: the
 * document is untouched until a selection is finished, and a preview drawn on the passage
 * under the pointer would be a promise the brush has not made. `copy` is the one cursor
 * every platform draws and every user already reads as "this will be put somewhere".
 *
 * On the element and not on its descendants, which is a correction rather than a shortcut:
 * `cursor` inherits, so one declaration already reaches every word - and the `*` that used
 * to be here also reached the image handles, the drag grip and the table grips, painting
 * "this will be copied" over the three cursors that say what dragging them does.
 */
.fi-arte-brush-armed {
    cursor: copy;
}

/*
 * Armed for good, as opposed to armed for one stroke. Both states light the button - that
 * is Filament's own `fi-active` - and this is the difference between them, drawn as a ring
 * rather than a second colour so it reads on top of whatever the panel's theme does.
 *
 * Unscoped, and that is the fix rather than the shortcut: this rule shipped as
 * `.fi-rich-editor [data-arte-brush='sticky']` and matched nothing at all. Measured in a
 * running panel - the wrapper is `fi-fo-rich-editor`, nine levels above the button, and the
 * name written here was one this package uses exactly once. An ancestor bought nothing to
 * begin with: `data-arte-brush` is written by one tool in one package, so there is nothing
 * on any page for it to collide with, and naming a parent only adds a second thing that has
 * to stay true - including if Filament ever renders the overflow menu somewhere else.
 */
[data-arte-brush='sticky'] {
    outline: 2px solid currentColor;
    outline-offset: -2px;
}
