/* Shared, unscoped styles for the file-editor family (FileImageEditor / FileDocumentEditor /
   FileImageGalleryEditor / FileDocumentListEditor).

   Class prefix `fe-` (file-editor). Rules live here — not in scoped sidecars — when they:
     - must be applied to elements DX appends at runtime (e.g. zone-hover class), OR
     - are byte-identical across multiple editors (overlay-clear button, input-host, etc.).

   Colours follow docs/ai/conventions/theme-aware-ui.md: a --vl-* role read, never a chain
   restated here and never a literal.

   Colour comes from the --vl-* roles in visual-language.css; this file states no chain and
   no literal of its own. .fe-overlay-clear reads --vl-media-scrim / --vl-on-accent, the pair
   that stays dark-with-white-glyph in both modes so the glyph never goes unreadable. */

/* Round X clear/remove button placed over a preview tile. Hidden by default; revealed
   on per-component parent hover (rules in each editor's sidecar). */
.fe-overlay-clear {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 28px;
    height: 28px;
    border: none;
    border-radius: 50%;
    background-color: var(--vl-media-scrim);
    color: var(--vl-on-accent);
    display: none;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    /* Explicit: the glyph is sized in em, and the button is 28px of circle that must not
       track the inherited body size. */
    font-size: 14px;
    z-index: 2;
}

.fe-overlay-clear:hover {
    background-color: var(--vl-media-scrim-hover);
}

/* The shell's clear button renders its glyph in the button's own child content (DxButton's
   icon slot takes a class name, not markup), so the gap the old font icon's line box supplied
   has to be stated. */
.file-editor-shell-clear .app-icon {
    margin-right: 6px;
}

/* Click-through wrapper that hosts a FileDropZone tile so a parent @onclick can drive
   the chooser/upload route. */
.fe-zone-click-host {
    display: inline-block;
    cursor: pointer;
}

/* Hidden DxFileInput host — kept in the DOM so DX's ExternalSelectButtonCssSelector /
   ExternalDropZoneCssSelector can target the hidden trigger / drop zone selectors. */
.fe-input-host {
    display: none;
}

/* DX appends this class at runtime to the drop-zone element while a drag is over it.
   MUST stay unscoped — DX JS appends the class to an element that does not carry the
   Blazor scope attribute, so a scoped rule would never match. */
.fe-zone-hover {
    border-style: solid;
    border-color: var(--vl-accent);
    color: var(--vl-accent);
}

/* "EXT" badge rendered next to an external-link document title. */
.fe-ext-badge {
    font-size: 10px;
    padding: 1px 6px;
    margin-left: 6px;
    border-radius: 10px;
    background: var(--vl-surface-2);
    color: var(--vl-accent);
    vertical-align: middle;
}

/* ─────────────────────────────────────────────────────────────────────────────
   ExistingFilePicker — DxGrid height constraint + image row layout
   ─────────────────────────────────────────────────────────────────────────────
   Why unscoped: DxGrid renders across multiple component scope boundaries
   (<DXBL-GRID> → <DXBL-VIRTUAL-SCROLL-VIEWER> → <table.dxbl-grid-table>).
   Blazor's `::deep` only crosses one boundary, so scoped rules in
   ExistingFilePicker.razor.css don't reach the inner table.

   DOM verified via Playwright against DevExpress.Blazor v25.2.4.0 on
   2026-05-27. If DX upgrades materially change the emitted DOM, these
   selectors need a refresh.

   Tile-flow / multi-column-per-row was attempted but is structurally blocked
   by browser table-row semantics — <tbody> inside <table> refuses
   `display: flex` even with !important. Defer to a custom virtualized
   component if true tile flow ever becomes a hard requirement.
*/

/* Pin the picker grid height so the DxGrid scroller stays inside the popup
   instead of expanding the modal vertically. DxGrid sets its own inline
   height in virtual-scrolling mode; `!important` is needed to win against
   the inline style. */
.existing-file-picker-grid {
    height: 480px !important;
    max-height: 480px !important;
}

/* The .ipt-wrapper (ImagePreviewTile root) is the immediate child of our
   row layout in ExistingImagePicker. Override the default 240×240 default
   if a width/height is not set — done via component params, but a safety
   net here keeps the row from ballooning if the params get dropped. */
.existing-image-picker-row {
    display: flex;
    flex-flow: row nowrap;
    align-items: center;
    gap: 12px;
    min-height: 96px;
}

.existing-image-picker-name {
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1 1 auto;
    min-width: 0;
}

/* ─────────────────────────────────────────────────────────────────────────────
   SoftConfirmUnknownMimePopup — global rules
   ─────────────────────────────────────────────────────────────────────────────
   Lives here, not in the scoped .razor.css sidecar, because DxPopup BodyTemplate
   renders into <dxbl-modal-root> outside Core.Blazor's scoped-CSS bundle (which
   the host's _Host.cshtml does not link as a project-level <link>). Same as
   ExternalLinkInputPopup's documented portal caveat.

   The popup itself also inherits oversized header / body typography from a
   parent DxPopup style cascade — pin font sizes here so the dialog reads as
   regular body text, not a heading. */

.soft-confirm-popup {
    /* Horizontal padding (24px) matches DxPopup's default header inset so body / meta / buttons
       align with the title. DxPopup's BodyTemplate slot has no built-in horizontal padding —
       the component is intentionally a thin container, layout is the consumer's job. */
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 8px 24px 16px 24px;
    min-width: 440px;
    max-width: 560px;
    font-size: 14px;
    line-height: 1.5;
    font-weight: 400;
}

.soft-confirm-popup-body {
    font-size: 14px;
    line-height: 1.5;
    font-weight: 400;
    color: var(--dxds-color-content-neutral-default-rest, inherit);
}

.soft-confirm-popup-meta {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px;
    border-radius: 4px;
    background: var(--vl-surface-2);
    font-size: 13px;
    line-height: 1.5;
}

.soft-confirm-popup-meta-label {
    font-weight: 600;
    color: var(--dxds-color-content-neutral-secondary-rest, inherit);
    margin-right: 6px;
}

.soft-confirm-popup-buttons {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 8px;
}

/* ─────────────────────────────────────────────────────────────────────────────
   Indeterminate "uploading…" tile + spinner
   ─────────────────────────────────────────────────────────────────────────────
   Rendered by FileEditorShell's UploadingView slot (scalar editors) and inline
   by the list / gallery editors while UploadOneAsync is in flight. Mirrors the
   dashed-border + muted-text shape of .file-drop-zone so the empty-state →
   uploading → bound transition reads as continuous, not as a layout shift.

   Theme-aware: reuses the same roles FileDropZone uses for its border, fill and
   text colour, so the two tiles stay identical without either restating a value. */
.fe-uploading-tile {
    display: inline-flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: 2px dashed var(--vl-line);
    border-radius: var(--vl-control-radius);
    background-color: var(--vl-surface-2);
    color: var(--vl-text-muted);
    padding: 12px;
    font-size: 13px;
    line-height: 1.3;
    box-sizing: border-box;
    text-align: center;
}

/* CSS-only rotate keyframes for the busy spinner glyph. 1s linear loop reads as a
   familiar busy indicator. */
.fe-spin {
    display: inline-block;
    animation: fe-spin-rotate 1s linear infinite;
    font-size: 18px;
    line-height: 1;
}

/* Shell overlay during upload. The source slot (EmptyEditableView / BoundView) stays mounted
   underneath — disposing it would tear down the DxFileInput whose SignalR stream the upload
   is reading from, surfacing as OperationCanceledException. `visibility: hidden` on the source
   keeps the layout box (and JS subscriptions) live while suppressing visual output; the
   overlay is absolutely positioned on top. */
.fe-shell-uploading-host {
    position: relative;
    display: inline-block;
}

.fe-shell-uploading-source {
    visibility: hidden;
}

.fe-shell-uploading-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

@keyframes fe-spin-rotate {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}
