/* Scroll container around .canvas-stage: panning a zoomed-in canvas scrolls
   HERE, never the page — the left panel and the sticky toolbar stay put.
   Floating chrome clamps within this box because anything positioned outside
   it would be clipped by the overflow. */
.canvas-viewport {
    overflow: auto;
}

/* Editor app shell (lg+, where the panel/stage columns sit side by side):
   the PAGE itself does not scroll — its height is pinned to the window by
   canvas_zoom_controller, and the left panel + canvas viewport each scroll
   on their own. Sizing it in JS rather than calc(100vh - Npx) is deliberate:
   the title/breadcrumb block above wraps to arbitrary heights and the theme
   adds its own padding below, so the offset is not a constant. Without this
   the window keeps a second scrollbar next to the canvas one. */
@media (min-width: 992px) {
    /* Class set by canvas_zoom_controller while the shell fits the window: the
       page can then never scroll, so the canvas viewport owns the only
       scrollbar. body's overflow propagates to the viewport (html is visible),
       the same mechanism Bootstrap's modal-open uses. */
    body.editor-shell-page {
        overflow: hidden;
    }

    .editor-shell {
        display: flex;
        flex-direction: column;
    }

    /* The single-variant editor wraps its toolbar + columns in a <form>; the
       group editor has no form. Both reach .editor-shell-row either way. */
    .editor-shell > form {
        display: flex;
        flex: 1 1 auto;
        flex-direction: column;
        min-height: 0;
    }

    /* Toolbars / the group variant rail keep their natural height (flex items
       don't shrink below their content); the columns row takes the rest. */
    .editor-shell-row {
        flex: 1 1 auto;
        min-height: 0;
    }

    .editor-shell-row > [class*="col-"] {
        min-height: 0;
        max-height: 100%;
    }

    .editor-left-panel {
        overflow-y: auto;
    }

    /* Stage column: viewport takes the leftover height, the pointer-modifier
       hint stays pinned under it instead of scrolling away with the canvas. */
    .editor-stage-col {
        display: flex;
        flex-direction: column;
    }

    .editor-stage-col > .canvas-viewport {
        flex: 1 1 auto;
        min-height: 0;
    }
}

/* Below lg the columns stack and the page scrolls normally — the panel card
   sticks to the top of the page instead of scrolling in its own pane, clearing
   the sticky `.mobile-topbar` (56px, the only chrome above the content since
   the theme topbar was removed). */
@media (max-width: 991.98px) {
    .editor-left-panel-card {
        position: sticky;
        top: 64px;
        z-index: 999;
    }
}

.canvas-wrapper {
    display: inline-block;
    /* Top-align the inline-block (the .fill-stage rule): baseline-aligned, the
       stage's line box grows to the wrapper's INTERNAL baseline — Fabric's
       lower-canvas is an in-flow inline element, so that baseline sits at the
       UNSCALED canvas bottom (A4 = 3508px down) and the zoom controller's
       negative-margin compensation only trims space BELOW the baseline. */
    vertical-align: top;
    transform-origin: top left;
}

/* Floating element-anchored editing chrome (canvas_floating_toolbar_controller).
   .canvas-stage is the UNSCALED positioning context; .canvas-wrapper inside it
   is the only element the zoom transform scales. The toolbar/popovers/outlines
   are absolutely positioned within the stage so they never scale with the canvas. */
.canvas-stage {
    position: relative;
}

.canvas-mini-toolbar {
    position: absolute;
    z-index: 1005;
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 4px;
    background: #fff;
    border: 1px solid var(--ct-border-color, #e3e6ef);
    border-radius: 10px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);
    white-space: nowrap;
}

.canvas-mini-toolbar .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: none;
    border-radius: 7px;
    line-height: 1;
    background: transparent;
    color: var(--ct-body-color, #4a4a5a);
    transition: background-color .12s ease, color .12s ease;
}

.canvas-mini-toolbar .btn i {
    font-size: 1.05rem;
}

.canvas-mini-toolbar .btn:hover {
    background: rgba(0, 0, 0, 0.07);
}

.canvas-mini-toolbar .btn.active {
    background: rgba(114, 124, 245, 0.16);
    color: #727cf5;
}

.canvas-mini-toolbar .btn.text-danger:hover {
    background: rgba(250, 92, 124, 0.14);
}

.canvas-mini-toolbar-sep {
    align-self: stretch;
    width: 1px;
    margin: 4px 3px;
    background: var(--ct-border-color, #e3e6ef);
}

/* "Přidat tvar" picker (left panel dropdown): an icon grid rather than a list
   of menu items — a shape is recognised by its silhouette long before its
   name is read, and three per row keeps the menu narrower than the panel. */
.shape-picker {
    min-width: 15rem;
}

.shape-picker__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 4px;
}

.shape-picker__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 8px 4px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: transparent;
    color: var(--ct-body-color, #4a4a5a);
    font-size: .7rem;
    line-height: 1.1;
    text-align: center;
    transition: background-color .12s ease, border-color .12s ease;
}

.shape-picker__item i {
    font-size: 1.35rem;
    line-height: 1;
}

.shape-picker__item:hover,
.shape-picker__item:focus-visible {
    background: rgba(114, 124, 245, 0.12);
    border-color: rgba(114, 124, 245, 0.35);
    color: #727cf5;
}

/* Scoped under .canvas-stage so `position: absolute` outranks the Hyper theme's
   `.card { position: relative }` (the popover is also a .card, and the theme CSS
   loads AFTER app.css — equal specificity would let the theme win and drop the
   popover to its static position at the bottom of the page). */
.canvas-stage .canvas-floating-popover {
    position: absolute;
    z-index: 1006;
    width: 360px;
    max-width: 92vw;
    max-height: 80vh;
    overflow-y: auto;
    /* Never scroll horizontally — a long <option> label (e.g. a verbose font
       family name) sizes the <select> to its widest option and would otherwise
       force a horizontal scrollbar (overflow-y:auto makes overflow-x compute to
       auto too). The control rule below caps every field at the popover width. */
    overflow-x: hidden;
    margin: 0;
    border: 1px solid var(--ct-border-color, #e3e6ef);
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.22);
}

/* Keep every form control inside the popover width so it truncates instead of
   overflowing (the <select> renders its own ellipsis + arrow when capped). */
.canvas-stage .canvas-floating-popover .form-select,
.canvas-stage .canvas-floating-popover .form-control {
    max-width: 100%;
    min-width: 0;
}

/* Drag grip in the popover title: lets the user nudge the popover aside when it
   covers the element it edits. Shared look with the fill-page grip below. */
.canvas-popover-drag,
.fill-popover__drag {
    flex: 0 0 auto;
    cursor: move;
    opacity: 0.45;
    touch-action: none;
    transition: opacity .12s ease;
}

.canvas-popover-drag:hover,
.fill-popover__drag:hover,
.canvas-popover-drag.is-dragging,
.fill-popover__drag.is-dragging {
    opacity: 0.85;
}

.editable-outline {
    position: absolute;
    z-index: 900;
    pointer-events: none;
    border: 1.5px dashed #727cf5;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.45);
}

/* Captions on, frames off: the element still carries the name tag and still
   rides the after:render positioning loop — only the frame stops painting. */
.editable-outline--frameless {
    border-color: transparent;
    box-shadow: none;
}

/* Field-name tag inside a highlight outline (same look as the fill page's
   .fill-box__name), so the designer sees which input each box is. Its own
   top-bar switch (#caption-visible-control) — the tag sits over the top of
   every element, which is exactly what a composition review does not want. */
.editable-outline__name {
    position: absolute;
    top: 0;
    left: 0;
    max-width: calc(100% - 4px);
    padding: 1px 5px;
    font-size: 11px;
    line-height: 1.4;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: #fff;
    background: #727cf5;
    border-radius: 0 0 3px 0;
    pointer-events: none;
}

/* Smart-guide chrome (canvas_snapping_controller): magenta alignment lines +
   gap badges + `=` equal-spacing marks. Plain DOM in the unscaled stage layer,
   like .editable-outline — shown only mid-drag, never on the canvas bitmap, so
   they can't leak into the saved preview or the server PNG export. Above the
   outlines, below the floating toolbar (z 1005). Never intercept the drag. */
.snap-guide {
    position: absolute;
    z-index: 1000;
    pointer-events: none;
    background: #f24bb0;
}

.snap-guide--v {
    width: 1px;
    transform: translateX(-0.5px);
}

.snap-guide--h {
    height: 1px;
    transform: translateY(-0.5px);
}

.snap-badge {
    position: absolute;
    z-index: 1001;
    pointer-events: none;
    transform: translate(-50%, -50%);
    padding: 1px 5px;
    font-size: 11px;
    line-height: 1.4;
    font-weight: 600;
    white-space: nowrap;
    color: #fff;
    background: #f24bb0;
    border-radius: 4px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

/* Equal-spacing mark: a bolder `=` chip so an even rhythm reads at a glance. */
.snap-badge--eq {
    min-width: 16px;
    text-align: center;
    font-size: 12px;
}

/* Photoshop-style rulers + guides (canvas_rulers_controller). The bars are 2D
   <canvas> elements in the unscaled stage layer; .has-rulers reserves a gutter
   for them so they never cover the artwork. Guides are DOM lines with a wider
   transparent hit strip (grabbable), all export-invisible by construction. */
.canvas-stage.has-rulers {
    padding-top: 23px;
    padding-left: 23px;
}

.canvas-ruler {
    position: absolute;
    z-index: 996;
    background: #fafbfe;
    border: 1px solid #e3e6ef;
    user-select: none;
}

.canvas-ruler--h {
    border-left: 0;
    cursor: row-resize;
}

.canvas-ruler--v {
    border-top: 0;
    cursor: col-resize;
}

.canvas-ruler-corner {
    position: absolute;
    z-index: 997;
    background: #fafbfe;
    border: 1px solid #e3e6ef;
}

/* A guide: 7px invisible hit strip centered on a 1px cyan line (the span
   child). Sits under the snap chrome (z 1000) so magenta feedback wins. */
.canvas-guide {
    position: absolute;
    z-index: 940;
}

.canvas-guide--v {
    width: 7px;
    transform: translateX(-3.5px);
    cursor: col-resize;
}

.canvas-guide--h {
    height: 7px;
    transform: translateY(-3.5px);
    cursor: row-resize;
}

.canvas-guide > span {
    position: absolute;
    pointer-events: none;
    background: rgba(0, 184, 216, 0.85);
}

.canvas-guide--v > span {
    left: 3px;
    top: 0;
    width: 1px;
    height: 100%;
}

.canvas-guide--h > span {
    top: 3px;
    left: 0;
    height: 1px;
    width: 100%;
}

.canvas-guide:hover > span,
.canvas-guide.is-dragging > span {
    background: #00b8d8;
    box-shadow: 0 0 0 0.5px rgba(0, 184, 216, 0.35);
}

/* Dragged outside the canvas = will be deleted on release. */
.canvas-guide.is-deleting > span {
    background: rgba(250, 92, 124, 0.9);
}

/* Live position chip next to the cursor while dragging a guide. */
.canvas-guide-badge {
    position: absolute;
    z-index: 1010;
    pointer-events: none;
    padding: 2px 7px;
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: #fff;
    background: #37404a;
    border-radius: 4px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

/* Axis-lock cursor everywhere while a guide drag is in flight. */
body.is-dragging-guide-v,
body.is-dragging-guide-v * { cursor: col-resize !important; }
body.is-dragging-guide-h,
body.is-dragging-guide-h * { cursor: row-resize !important; }

/* Hovering a row in the layers panel (canvas_layers_controller) highlights the
   object on the stage — a stronger, filled sibling of .editable-outline. */
.layer-hover-outline {
    position: absolute;
    z-index: 901;
    pointer-events: none;
    border: 2px solid #727cf5;
    border-radius: 2px;
    background: rgba(114, 124, 245, 0.12);
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.45);
}

/* Layers panel (canvas_layers_controller): compact Photoshop-style rows in the
   left card — topmost object first. Rows are drag-sortable (SortableJS,
   fallback mode reusing the shared .gu-mirror / .gu-transit drag classes). */
.canvas-layers-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 42vh;
    overflow-y: auto;
    margin: 0 -0.4rem;
    padding: 0 0.4rem;
}

.canvas-layer-row {
    display: flex;
    align-items: stretch;
    gap: 2px;
    border-radius: 6px;
    color: var(--ct-body-color, #4a4a5a);
    transition: background-color .12s ease, color .12s ease;
}

.canvas-layer-row:hover {
    background: rgba(0, 0, 0, 0.05);
}

.canvas-layer-row.is-active {
    background: rgba(114, 124, 245, 0.14);
    color: #727cf5;
}

/* The background layer of a layer-mode variant: visually distinct (tint +
   colored icon) so it always reads as "the background", yet an ordinary,
   freely reorderable row like any other layer. */
.canvas-layer-row--background {
    background: rgba(57, 175, 209, 0.10);
    border: 1px dashed rgba(57, 175, 209, 0.45);
}

.canvas-layer-row--background:hover {
    background: rgba(57, 175, 209, 0.18);
}

.canvas-layer-row--background .canvas-layer-row__icon {
    color: #39afd1;
}

/* Layer-mode variants may have no background at all — a checkerboard behind
   the (transparent) canvas element makes that state visible in the editor,
   like every raster editor does. Any background layer paints over it. */
.canvas-wrapper--transparent canvas.lower-canvas,
.canvas-wrapper--transparent > canvas,
.fill-preview-img--transparent {
    background-image:
        linear-gradient(45deg, #e4e7ec 25%, transparent 25%, transparent 75%, #e4e7ec 75%),
        linear-gradient(45deg, #e4e7ec 25%, transparent 25%, transparent 75%, #e4e7ec 75%);
    background-color: #f8f9fb;
    background-size: 24px 24px;
    background-position: 0 0, 12px 12px;
}

.canvas-layer-row__main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 2px 4px 6px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: inherit;
    font-size: 0.8rem;
    text-align: left;
}

.canvas-layer-row__main:focus-visible {
    outline: 2px solid #727cf5;
    outline-offset: -2px;
}

.canvas-layer-row__icon {
    flex: 0 0 auto;
    font-size: 0.95rem;
    opacity: 0.75;
    line-height: 1;
}

.canvas-layer-row__label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.canvas-layer-row__flag {
    flex: 0 0 auto;
    font-size: 0.8rem;
    opacity: 0.6;
    line-height: 1;
}

/* Padlock, same restraint as the eye: barely there until hovered, full
   strength once the layer is actually locked (that is the state worth
   noticing at a glance). */
.canvas-layer-row__lock {
    flex: 0 0 auto;
    align-self: center;
    padding: 2px 4px;
    border: 0;
    border-radius: 4px;
    background: transparent;
    color: inherit;
    font-size: 0.9rem;
    line-height: 1;
    opacity: 0.45;
}

.canvas-layer-row__lock:hover {
    opacity: 1;
    background: rgba(0, 0, 0, 0.07);
}

.canvas-layer-row__lock:focus-visible {
    outline: 2px solid #727cf5;
    outline-offset: -2px;
    opacity: 1;
}

.canvas-layer-row--locked .canvas-layer-row__lock {
    opacity: 0.9;
}

/* Photoshop-style visibility eye. Subtle until hovered/hidden so the panel
   stays calm; a hidden row dims as a whole (the eye keeps full opacity as
   the affordance to un-hide). */
.canvas-layer-row__eye {
    flex: 0 0 auto;
    align-self: center;
    padding: 2px 4px;
    border: 0;
    border-radius: 4px;
    background: transparent;
    color: inherit;
    font-size: 0.9rem;
    line-height: 1;
    opacity: 0.45;
}

.canvas-layer-row__eye:hover {
    opacity: 1;
    background: rgba(0, 0, 0, 0.07);
}

.canvas-layer-row__eye:focus-visible {
    outline: 2px solid #727cf5;
    outline-offset: -2px;
    opacity: 1;
}

.canvas-layer-row--hidden .canvas-layer-row__main,
.canvas-layer-row--hidden .canvas-layer-row__grip {
    opacity: 0.4;
}

.canvas-layer-row--hidden .canvas-layer-row__eye {
    opacity: 0.8;
}

/* Drag affordance at the row's right edge (the whole row is draggable). */
.canvas-layer-row__grip {
    flex: 0 0 auto;
    align-self: center;
    margin-right: 4px;
    font-size: 0.9rem;
    line-height: 1;
    opacity: 0.3;
    cursor: grab;
}

.canvas-layer-row:hover .canvas-layer-row__grip {
    opacity: 0.7;
}

/* The cursor-following drag mirror (SortableJS fallback clone, .gu-mirror
   adds the fixed positioning): give the detached row a card so it reads as
   "picked up" over any backdrop. */
.canvas-layer-row.gu-mirror {
    background: #fff;
    border: 1px solid var(--ct-border-color, #e3e6ef);
    border-radius: 6px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);
}

/* Container ("smart text area") zone overlay (canvas_container_controller).
   Plain DOM in the unscaled stage layer — like .editable-outline it never
   touches the canvas bitmap, so it can't leak into thumbnails or exports.
   The zone itself is click-through; only the label + resize handle are
   interactive. */
/* Zones are DEPTH-COLOR-CODED: the container controller stamps --zone-color /
   --zone-tint per zone (root cyan, child violet, grandchild green, cycled) so
   parent and child containers never read as the same box. The legacy cyan
   stays as the var fallback. */
.container-zone {
    position: absolute;
    z-index: 899;
    pointer-events: none;
    border: 1.5px dashed var(--zone-color, #39afd1);
    border-radius: 3px;
    background: var(--zone-tint, rgba(57, 175, 209, 0.04));
}

/* Top-bar switch (#container-zones-control): hide the zone chrome entirely.
   A class rather than inline styles because _positionZones() rewrites
   `zone.style.display` on every render frame; clearing that inline value is
   what lets this rule through. Definitions are untouched — the design still
   reflows and the Kontejnery panel still lists them. */
.container-zones-off .container-zone {
    display: none;
}

/* Hovering a zone's label lifts the whole zone above colliding siblings so
   its icon row is reachable even when two containers share a top edge. */
.container-zone--raise {
    z-index: 930;
}

/* Panel-row hover: the hovered container's zone pops, everything else fades —
   the overlapping-chrome escape hatch driven from the Kontejnery panel. */
.container-zones-dim .container-zone {
    opacity: 0.15;
}

.container-zones-dim .container-zone--focus {
    opacity: 1;
    z-index: 931;
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 0 12px rgba(0, 0, 0, 0.25);
}

.container-zone--overflow {
    border-color: #fa5c7c;
    background: rgba(250, 92, 124, 0.06);
}

/* The label doubles as the MOVE handle for the whole container (members are
   dragged individually on the canvas itself). */
.container-zone__label {
    position: absolute;
    top: -20px;
    left: -1.5px;
    padding: 1px 6px;
    font-size: 11px;
    line-height: 1.4;
    white-space: nowrap;
    color: #fff;
    background: var(--zone-color, #39afd1);
    border-radius: 3px 3px 0 0;
    pointer-events: auto;
    cursor: move;
    user-select: none;
}

.container-zone--overflow .container-zone__label {
    background: #fa5c7c;
}

/* Removes the container definition (texts stay). Sits next to the label. */
.container-zone__delete {
    position: absolute;
    top: -20px;
    right: -1.5px;
    padding: 1px 7px;
    border: none;
    font-size: 12px;
    line-height: 1.3;
    color: #fff;
    background: var(--zone-color, #39afd1);
    border-radius: 3px 3px 0 0;
    pointer-events: auto;
    cursor: pointer;
}

.container-zone__delete:hover {
    background: #fa5c7c;
}

.container-zone--overflow .container-zone__delete {
    background: #fa5c7c;
}

.container-zone__handle {
    position: absolute;
    bottom: -6px;
    left: 50%;
    width: 44px;
    height: 10px;
    margin-left: -22px;
    border-radius: 5px;
    background: var(--zone-color, #39afd1);
    box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.8);
    cursor: ns-resize;
    pointer-events: auto;
}

.container-zone--overflow .container-zone__handle {
    background: #fa5c7c;
}

/* Width handles: resizing the container rescales the member textboxes'
   wrap widths, so the text re-wraps and the flow re-runs. */
.container-zone__side {
    position: absolute;
    top: 50%;
    width: 10px;
    height: 44px;
    margin-top: -22px;
    border-radius: 5px;
    background: var(--zone-color, #39afd1);
    box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.8);
    cursor: ew-resize;
    pointer-events: auto;
}

.container-zone__side--left {
    left: -6px;
}

.container-zone__side--right {
    right: -6px;
}

.container-zone--overflow .container-zone__side {
    background: #fa5c7c;
}

/* Nested container zone: hugs its content (no maxHeight box of its own — the
   outer container's bound is the contract), visually quieter than its parent
   so the hierarchy reads at a glance. */
.container-zone--nested {
    z-index: 900;
    border-style: dotted;
    border-color: var(--zone-color, #39afd1);
    background: var(--zone-tint, rgba(57, 175, 209, 0.02));
}

/* Nested chrome sits INSIDE the dotted box (root chrome sits above its top
   edge), so parent and child rows occupy different bands and never overlap
   even when the two boxes share a top edge. */
.container-zone--nested .container-zone__label {
    top: 2px;
    left: 2px;
    border-radius: 3px;
}

.container-zone--nested .container-zone__delete {
    top: 2px;
    right: 2px;
    border-radius: 3px;
}

/* Per-zone settings (⚙ → gap / max height popover). Sits left of the ×. */
.container-zone__settings,
.container-zone__duplicate {
    position: absolute;
    top: -20px;
    padding: 1px 6px;
    border: none;
    font-size: 11px;
    line-height: 1.5;
    color: #fff;
    background: var(--zone-color, #39afd1);
    border-radius: 3px 3px 0 0;
    pointer-events: auto;
    cursor: pointer;
}

.container-zone__settings {
    right: 22px;
}

/* Duplicates the container INCLUDING its objects. Sits left of the ⚙. */
.container-zone__duplicate {
    right: 46px;
}

.container-zone__settings:hover,
.container-zone__duplicate:hover {
    background: #2c8caa;
}

.container-zone--overflow .container-zone__settings,
.container-zone--overflow .container-zone__duplicate {
    background: #fa5c7c;
}

.container-zone--nested .container-zone__settings {
    top: 2px;
    right: 26px;
    border-radius: 3px;
}

.container-zone--nested .container-zone__duplicate {
    top: 2px;
    right: 50px;
    border-radius: 3px;
}

/* Kontejnery left-panel list (the Vrstvy pattern): depth-colored rows, hover
   drives the zone highlight + dim on the stage, click selects. */
.container-panel-list {
    max-height: 220px;
    overflow-y: auto;
}

.container-panel-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 6px;
    font-size: 12px;
    cursor: pointer;
    border-left: 3px solid var(--zone-color, #39afd1);
    border-radius: 2px;
    user-select: none;
    min-width: 0;
}

.container-panel-row:hover {
    background: rgba(114, 124, 245, 0.08);
}

.container-panel-row .mdi {
    color: var(--zone-color, #39afd1);
}

.container-zone-popover {
    position: absolute;
    z-index: 1001;
    width: 230px;
    padding: 10px 12px;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
    pointer-events: auto;
}

.container-zone-popover__title {
    margin-bottom: 8px;
    padding-right: 20px;
    font-size: 12px;
    font-weight: 600;
}

.container-zone-popover__close {
    position: absolute;
    top: 4px;
    right: 8px;
    padding: 0;
    border: none;
    background: none;
    font-size: 16px;
    line-height: 1;
    color: #98a6ad;
    cursor: pointer;
}

.container-zone-popover__close:hover {
    color: #343a40;
}

/* ---- User-fill placeholder overlay (variant_fill_overlay_controller) ----
   Click-into-preview editing: each placeholder shows an always-visible icon
   cluster (pencil = edit, eye = hide) over the preview. The highlight toggle
   controls ONLY the dashed border. Text edits in a floating popover; image
   picking in a gallery modal.

   Progressive enhancement: the BASE rules (no `.fill-js`) render the popovers as
   a plain stacked, editable list and hide the boxes, so a JS failure never
   strands the user. The controller adds `.fill-js` on connect. */
/* Viewport around the stage. The controller caps its max-height to the visible
   area and picks a fit zoom where the WHOLE canvas fits inside, so by default
   nothing scrolls anywhere; only a manual zoom-in past fit pans in here.
   overscroll-behavior keeps that panning from chaining into a page scroll. */
.fill-viewport {
    overflow: auto;
    overscroll-behavior: contain;
}

.fill-stage {
    position: relative;
    display: inline-block;
    /* Top-align the inline-block: sitting on the text baseline would add the
       line box's descender space below it — a few phantom pixels that turn
       into a needless scrollbar in the exactly-fitted viewport. */
    vertical-align: top;
    max-width: 100%;
}

/* JS mode: the fit/zoom transform owns ALL fitting, so the responsive clamps
   must not fight it — a shrink-to-fit-clamped stage makes zooming in shrink
   the layout box instead of growing the artwork (self-cancelling zoom). The
   base max-widths above/below stay for the no-JS fallback. */
.fill-form.fill-js .fill-stage {
    max-width: none;
}

.fill-preview-img {
    max-width: 100%;
    height: auto;
    display: block;
}

.fill-form.fill-js .fill-preview-img {
    max-width: none;
}

/* Client-side text echo: while the user types, the settle preview yields to
   the text-transparent base + the echo canvas stacked over it (both sized to
   the same displayed width, so the CSS zoom transform scales all three
   together). Hidden by default — the settle render is the truth at rest. */
.fill-echo-base,
.fill-echo-canvas {
    position: absolute;
    top: 0;
    left: 0;
    display: block;
    visibility: hidden;
    pointer-events: none;
}

.fill-echo-base {
    width: 100%;
    height: auto;
}

.fill-echo-active .fill-echo-base,
.fill-echo-active .fill-echo-canvas {
    visibility: visible;
}

/* Text branch only: the settle img hides under the echo composite — the base
   may legitimately be transparent (layer-mode variants), so the settle text
   must not show through. The image branch keeps its fill canvas visible (it
   IS the composite there; only its sources swap). */
.fill-echo-active .fill-preview-img {
    visibility: hidden;
}

/* While the echo covers the typing, the "rendering" veil is noise — the user
   already sees their text; the settle lands silently underneath. */
.fill-echo-active .fill-spinner {
    display: none;
}

/* Base (no-JS) — the popovers render as a plain stacked, editable list under
   the preview. (The .fill-overlay itself only holds the boxes, hidden here.) */
.fill-popovers {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

.fill-box {
    display: none;
    position: absolute;
    pointer-events: none;
    border: 1.5px dashed transparent;
    border-radius: 2px;
}

/* The icon cluster follows the "Zobrazit oblasti k vyplnění" toggle: shown with
   the highlight, hidden when it's off so the preview is clean (see the
   :not(.fill-highlight-on) rule below). */
.fill-box__tools {
    position: absolute;
    top: -14px;
    right: -14px;
    display: flex;
    gap: 4px;
    pointer-events: auto;
    /* The box scales with the artwork, but the tappable icon cluster must keep
       its default size at any zoom — counter-scale by the inverse of the stage
       zoom, anchored at the corner the cluster hangs from. */
    transform: scale(calc(1 / var(--fill-zoom, 1)));
    transform-origin: top right;
}

/* Edge-aware placement (toggled per box in the controller's reposition) so the
   cluster never hangs off the top of the canvas or past the right edge. */
.fill-box--tools-inside .fill-box__tools {
    top: 4px;
    right: 4px;
}

.fill-box--tools-left .fill-box__tools {
    right: auto;
    left: -14px;
    transform-origin: top left;
}

.fill-box--tools-left.fill-box--tools-inside .fill-box__tools {
    left: 4px;
}

.fill-box__tool {
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: #fff;
    color: #4a4a5a;
    line-height: 1;
    cursor: pointer;
    /* Dual ring (white + dark) so the badge reads on any artwork. */
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3), 0 0 0 1.5px rgba(0, 0, 0, 0.12);
    transition: filter .12s ease, background-color .12s ease, color .12s ease, transform .12s ease;
}

.fill-box__tool i {
    font-size: 1.05rem;
}

.fill-box__tool--edit {
    background: #727cf5;
    color: #fff;
}

.fill-box--image .fill-box__tool--edit {
    background: #0acf97;
}

.fill-box__tool:hover {
    filter: brightness(0.96);
    transform: scale(1.06);
}

.fill-box__tool:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
    box-shadow: 0 0 0 2px #727cf5, 0 2px 8px rgba(0, 0, 0, 0.35);
}

.fill-box__tool--hide.is-active {
    background: #fa5c7c;
    color: #fff;
}

/* JS-enhanced floating overlay.
   `overflow: clip` (NOT hidden, NOT visible): a placeholder whose designed
   frame runs off the canvas — the editor's "Prvky mimo plátno" case — gets a
   box that wide too, and an unclipped one inflates .fill-viewport's scrollable
   width, so the user could pan hundreds of px into empty space past the edge
   of the artwork. `hidden` would fix that but also eats the tool clusters,
   which hang 14 px outside the canvas by design; `clip` + a clip margin keeps
   them visible while contributing nothing to any ancestor's scroll extent
   (clip, unlike hidden, is not a scroll container). Old browsers fall back to
   `visible`, i.e. today's behaviour. */
.fill-form.fill-js .fill-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: clip;
    overflow-clip-margin: 20px;
}

.fill-form.fill-js .fill-popovers {
    display: contents;
}

.fill-form.fill-js .fill-box {
    display: block;
}

/* The highlight toggle controls ONLY the dashed border. */
.fill-form.fill-js.fill-highlight-on .fill-box--text {
    border-color: #727cf5;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.45);
}

/* Container-overflow state: the affected boxes go red regardless of the
   highlight toggle — this is a validation signal, not a hint. */
.fill-form.fill-js .fill-box--overflow {
    border-color: #fa5c7c !important;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.45);
}

.fill-form.fill-js.fill-highlight-on .fill-box--image {
    border-color: #0acf97;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.45);
}

/* Toggle off: hide the pencil/eye clusters too (all boxes, overflow included)
   so the preview is completely undisturbed. */
.fill-form.fill-js:not(.fill-highlight-on) .fill-box__tools {
    display: none;
}

/* Hovering a row in the layers panel: a solid, filled highlight regardless of
   the highlight toggle (it's a pointing gesture, not a hint). */
.fill-form.fill-js .fill-box--layer-hover {
    border-color: #727cf5 !important;
    border-style: solid;
    background: rgba(114, 124, 245, 0.10);
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.45);
}

.fill-form.fill-js .fill-box--image.fill-box--layer-hover {
    border-color: #0acf97 !important;
    background: rgba(10, 207, 151, 0.10);
}

/* Field-name tag INSIDE the box (never hanging outside like the container
   label, so neighbouring boxes' tags can't collide). Anchored to the top edge
   opposite the tool cluster — left by default, since the tools sit top-right —
   with the tool corner reserved so the name is NEVER hidden under the pencil.
   Shown/hidden by its OWN toggle, not the dashed border's. */
.fill-box__name {
    position: absolute;
    top: 0;
    left: 0;
    max-width: calc(100% - 68px);
    padding: 1px 5px;
    font-size: 11px;
    line-height: 1.4;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: #fff;
    background: #727cf5;
    border-radius: 0 0 3px 0;
    pointer-events: none;
    opacity: 0;
    transition: opacity .12s ease;
}

.fill-box--image .fill-box__name {
    background: #0acf97;
}

/* Near the right edge the tool cluster flips to the LEFT (fill-box--tools-left);
   put the name on the right so the two never overlap. */
.fill-box--tools-left .fill-box__name {
    left: auto;
    right: 0;
    border-radius: 0 0 0 3px;
}

/* Own switch (#fill-captions-toggle), independent of the border toggle — and
   always visible for an overflowing member, which is a validation signal. */
.fill-form.fill-js.fill-captions-on .fill-box__name {
    opacity: 1;
}

.fill-form.fill-js .fill-box--overflow .fill-box__name {
    background: #fa5c7c;
    opacity: 1;
}

/* ---- Layers panel (Vrstvy) on the fill page ------------------------------
   Sits beside the preview viewport; JS-only chrome (rows drive the overlay
   controller), hidden in the no-JS fallback. Stacks under the preview on
   narrow screens. */
.fill-body {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
}

.fill-body .fill-viewport {
    flex: 1 1 auto;
    min-width: 0;
}

.fill-layers {
    display: none;
    flex: 0 0 230px;
    position: sticky;
    top: 1rem;
    padding: 0.6rem 0.65rem;
    background: #fff;
    border: 1px solid var(--ct-border-color, #e3e6ef);
    border-radius: 10px;
}

.fill-form.fill-js .fill-layers {
    display: block;
}

@media (max-width: 767.98px) {
    /* Stacked layout. `align-items: flex-start` above is what keeps the layers
       panel from stretching to the preview's height in the ROW layout — but
       once the direction flips it governs WIDTH instead, and shrink-to-fit
       made .fill-viewport as wide as its UNSCALED content (1080 px inside a
       382 px phone). The fit zoom measures that width, so it computed "already
       fits" (~100 %) and never scaled the artwork down; the max-height cap
       then sliced the bottom off the design. Stretch both children to the
       screen width so the fit has something real to fit into. */
    .fill-body {
        flex-direction: column;
        align-items: stretch;
    }

    .fill-layers {
        position: static;
        width: 100%;
        flex-basis: auto;
    }
}

.fill-layers__title {
    margin: 0 0 0.35rem;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ct-secondary-color, #8a969c);
}

.fill-layers__list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 60vh;
    overflow-y: auto;
}

.fill-layer-row {
    display: flex;
    align-items: center;
    gap: 2px;
    border-radius: 6px;
    color: var(--ct-body-color, #4a4a5a);
    transition: background-color .12s ease;
}

.fill-layer-row:hover {
    background: rgba(0, 0, 0, 0.05);
}

.fill-layer-row__main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 4px 5px 6px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: inherit;
    font-size: 0.8rem;
    text-align: left;
}

button.fill-layer-row__main:focus-visible {
    outline: 2px solid #727cf5;
    outline-offset: -2px;
}

.fill-layer-row__icon {
    flex: 0 0 auto;
    font-size: 0.95rem;
    opacity: 0.75;
    line-height: 1;
}

.fill-layer-row__label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.fill-layer-row__flag {
    flex: 0 0 auto;
    font-size: 0.8rem;
    opacity: 0.6;
    line-height: 1;
}

.fill-layer-row__eye {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin-right: 2px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: inherit;
    opacity: 0.6;
    line-height: 1;
}

.fill-layer-row__eye:hover {
    opacity: 1;
    background: rgba(0, 0, 0, 0.08);
}

.fill-layer-row__eye:focus-visible {
    outline: 2px solid #727cf5;
    outline-offset: -1px;
    opacity: 1;
}

.fill-layer-row__eye.is-active {
    color: #fa5c7c;
    opacity: 1;
}

/* A hidden layer reads as switched off in the list too. */
.fill-layer-row:has(.fill-layer-row__eye.is-active) .fill-layer-row__label {
    opacity: 0.55;
    text-decoration: line-through;
}

/* Fixed in viewport coordinates (the controller positions + clamps it fully
   on-screen). It lives OUTSIDE the zoom-scaled stage and the scrolling
   .fill-viewport, so no ancestor overflow or transform can ever clip it — and
   it needs no zoom counter-scale. */
.fill-form.fill-js .fill-popover {
    position: fixed;
    z-index: 1200;
    width: 280px;
    max-width: 92vw;
    margin: 0;
    display: none;
    pointer-events: auto;
    border: 1px solid var(--ct-border-color, #e3e6ef);
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.22);
}

.fill-form.fill-js .fill-popover.is-open {
    display: block;
}

/* Popover text field: an auto-growing textarea. The controller sets its height
   to scrollHeight on open + every input, so the whole entered value is always
   visible — no horizontal scroll, no manual drag handle. */
.fill-popover__input {
    resize: none;
    overflow: hidden;
    min-height: calc(1.5em + 0.5rem + 2px);
    line-height: 1.4;
    white-space: pre-wrap;
    word-break: break-word;
}

/* --- Rich text (WYSIWYG) popover ------------------------------------------ */

/* The toolbar needs more room than the plain text popover. */
.fill-form.fill-js .fill-popover--rich {
    width: 360px;
}

/* --- "Všechna pole" panel --------------------------------------------------
   The popovers container (.fill-popovers) has THREE presentations: the no-JS
   stacked list (base rules above), the JS floating mode (display: contents —
   each popover positions itself, image cards hidden) and the DOCKED panel:
   the very same container becomes a centred modal listing every text popover
   + image card as one stacked form. Same DOM, same editor instances — a
   WYSIWYG keeps its state across the switch — only the layout changes.
   z-order: above the page and the floating popovers (never open at the same
   time), BELOW the gallery pickers (.fill-modal 1300, Bootstrap's modal 1055)
   so picking from a docked image card opens the picker over the panel. */
.fill-panel-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1040;
    background: rgba(0, 0, 0, 0.5);
}

.fill-form.fill-js.fill-panel-open .fill-panel-backdrop {
    display: block;
}

.fill-popovers__header {
    display: none;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--ct-border-color, #e3e6ef);
}

/* Panel-only chrome: one card per image slot (thumb + picker button + eye,
   the group page adds its per-dimension placement rows). Floating mode has
   the box's pencil for that. */
.fill-form.fill-js .fill-popover--image {
    display: none;
}

.fill-form.fill-js.fill-panel-open .fill-popovers {
    display: flex;
    flex-direction: column;
    /* Roomy enough that the cards' shadows never touch. */
    gap: 0.75rem;
    position: fixed;
    top: 4vh;
    left: 50%;
    transform: translateX(-50%);
    width: 560px;
    max-width: 94vw;
    max-height: 92vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 1041;
    margin: 0;
    padding: 1rem 1.25rem;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3);
}

.fill-form.fill-js.fill-panel-open .fill-popovers__header {
    display: flex;
}

.fill-form.fill-js.fill-panel-open .fill-popover,
.fill-form.fill-js.fill-panel-open .fill-popover--rich,
.fill-form.fill-js.fill-panel-open .fill-popover--image {
    position: static;
    display: block;
    width: auto;
    max-width: none;
    /* Each field is its own card: a soft lift (not the floating popover's
       deep drop shadow) so the stack reads as separate rows on the white
       panel instead of hairline-bordered boxes melting into it. */
    border: 1px solid var(--ct-border-color, #e3e6ef);
    border-radius: 10px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.05);
}


/* Floating-only bits of a popover (drag grip, Uložit, close): pointless in
   a stacked form. !important: the Uložit button carries Bootstrap's
   `d-inline-flex` utility, which is !important itself. */
.fill-form.fill-js.fill-panel-open .fill-popover__chrome {
    display: none !important;
}

/* The popover's own hide eye — the panel row's hide control (a box has its
   own eye next to the pencil, so the floating popover hides it). */
.fill-popover__eye {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: inherit;
    opacity: 0.6;
    line-height: 1;
}

.fill-popover__eye:hover {
    opacity: 1;
    background: rgba(0, 0, 0, 0.08);
}

.fill-popover__eye.is-active {
    color: #fa5c7c;
    opacity: 1;
}

.fill-form.fill-js .fill-popover__eye {
    display: none;
}

.fill-form.fill-js.fill-panel-open .fill-popover__eye {
    display: inline-flex;
}

.fill-image-card__thumb {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    border: 1px solid rgba(0, 0, 0, 0.12);
    border-radius: 0.25rem;
    background-color: #f6f7fb;
    background-size: cover;
    background-position: center;
}

/* A fill SURFACE (one preview of one dimension) — the overlay's boxes are
   absolutely positioned inside it. The single page's .fill-stage is one;
   the group page wraps each dimension's preview frame in one. */
.fill-surface {
    position: relative;
}

/* The contenteditable editing surface. Grows with content (no fixed height);
   single visual line by default, wraps like the exported textbox. */
.fill-richtext-editor {
    min-height: calc(1.5em + 0.5rem + 2px);
    height: auto;
    line-height: 1.4;
    white-space: pre-wrap;
    word-break: break-word;
    overflow-wrap: break-word;
    cursor: text;
}

.fill-richtext-editor:focus {
    outline: 0;
    border-color: #86b7fe;
    box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}

/* Placeholder hint while empty (contenteditable has no placeholder attr). */
.fill-richtext-editor:empty::before {
    content: "Zadejte text\2026";
    color: var(--ct-secondary-color, #98a6ad);
    pointer-events: none;
}

.fill-richtext-toolbar__font {
    width: auto;
    max-width: 11rem;
    flex: 1 1 auto;
}

/* Color chips: brand swatches + the "default" and free-picker chips. */
.fill-richtext-swatch {
    width: 24px;
    height: 24px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, 0.2);
    border-radius: 50%;
    cursor: pointer;
    flex: 0 0 auto;
}

.fill-richtext-swatch:hover {
    box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.35);
}

/* Selected chip (admin editor: the textbox's current fill). */
.fill-richtext-swatch.is-active {
    box-shadow: 0 0 0 2px #727cf5;
}

/* "Default color" chip: white with a diagonal strike = inherit designed color. */
.fill-richtext-swatch--auto {
    background:
        linear-gradient(to top right, transparent 44%, #dc3545 46%, #dc3545 54%, transparent 56%),
        #fff;
}

/* Free-picker chip: rainbow ring wrapping a visually-hidden <input type=color>
   so the native picker opens on click. */
.fill-richtext-swatch--custom {
    background: conic-gradient(#f44, #fb0, #4c4, #19d, #b3f, #f44);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 0;
}

.fill-richtext-swatch__input {
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    border: 0;
    padding: 0;
}

/* Live-preview spinner overlay. No z-index: DOM order (preview < spinner <
   overlay) keeps it above the preview but below the icons + open popover, so it
   never covers the field the user is typing in. */
.fill-spinner {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.4);
    pointer-events: none;
}

.fill-spinner.is-active {
    display: flex;
}

/* Self-contained spinner (no dependency on the theme's .spinner-border): a white
   badge with a CSS-keyframe ring, centered over the preview. */
.fill-spinner__badge {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    background: #fff;
    border-radius: 14px;
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.22);
}

.fill-spinner__icon {
    display: block;
    width: 32px;
    height: 32px;
    border: 4px solid rgba(114, 124, 245, 0.25);
    border-top-color: #727cf5;
    border-radius: 50%;
    animation: fill-spinner-rotate 0.7s linear infinite;
}

@keyframes fill-spinner-rotate {
    to {
        transform: rotate(360deg);
    }
}

/* Image gallery modal (controller-managed; no Bootstrap JS dependency). */
.fill-modal {
    position: fixed;
    inset: 0;
    z-index: 1300;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(0, 0, 0, 0.5);
}

.fill-modal.is-open {
    display: flex;
}

.fill-modal__dialog {
    width: 640px;
    max-width: 100%;
    max-height: 86vh;
    overflow-y: auto;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3);
    padding: 1rem 1.25rem;
}

.fill-modal__thumb {
    width: 96px;
    height: 96px;
    padding: 0;
    overflow: hidden;
}

.fill-modal__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.manual-preview-color {
    text-align: center;
    color: #fff;
    min-height: 600px;
    padding: 40px 0;
}

.manual-font {
    word-wrap: anywhere;
}

.manual-image-wrapper {
    text-align: center;
    position: relative;
}

.manual-image-wrapper img {
    max-height: 200px;
    max-width: 100%;
    width: 100%;
}

.manual-image-wrapper.manual-image-square img {
    max-height: 200px;
}

.manual-image-wrapper.manual-image-horizontal img {
    max-height: 120px;
}

/* Equal-height logo cards.
   Horizontal / square cards are laid out in 2-column grids, but each card used
   to size to its own logo, so columns drifted out of alignment (logos have
   different aspect ratios). Give every card of a type one fixed height and
   center the logo inside it, so neighbouring columns always line up.
   Height = image max-height cap + wrapper padding (72px/side) + border (1px/side),
   so the tallest possible logo still fits (never clipped) and shorter logos are
   centered with even whitespace. Applies to every manual (backward compatible
   + future proof); protected-zone / vertical-big variants keep their own sizing. */
.manual-image-wrapper.manual-image-horizontal,
.manual-image-wrapper.manual-image-square {
    display: flex;
    align-items: center;
    justify-content: center;
}

.manual-image-wrapper.manual-image-horizontal {
    min-height: 266px;
}

.manual-image-wrapper.manual-image-square {
    min-height: 346px;
}

.manual-image-wrapper.manual-image-square-smaller img {
    max-height: 200px;
}

.manual-image-wrapper.manual-image-square-symbol {
    height: 416px;
}

.manual-image-wrapper.manual-image-square-symbol img {
    max-height: 200px;
}

.manual-image-wrapper.manual-image-vertical-big img {
    max-height: 120px;
}

.manual-kozelsky-koupelny-logomanual .manual-image-vertical-big.protected-zone-wrapper img {
    width: 400px !important;
}

.manual-kozelsky-koupelny-logomanual .manual-image-symbol.protected-zone-wrapper img {
    width: 110px !important;
}

.manual-kozelsky-koupelny-logomanual .manual-image-vertical.protected-zone-wrapper img {
    width: 400px !important;
}

.manual-page {
    margin-bottom: 50px;
}

.protected-zone-wrapper img {
    width: 100% !important;
    max-height: unset !important;
}

.protected-zone-top,
.protected-zone-bottom,
.protected-zone-left,
.protected-zone-right {
    position: absolute;
    display: block;
    color: #ffffff;
    font-size: 12px;
}

.protected-zone-top {
    border-left: 1px solid #ffffff;
    left: 30%;
    top: 0;
    height: 72px;
    line-height: 72px;
    padding-left: 10px;
}

.protected-zone-bottom {
    border-left: 1px solid #ffffff;
    left: 30%;
    bottom: 0;
    height: 72px;
    line-height: 72px;
    padding-left: 10px;
}

.protected-zone-left {
    border-bottom: 1px solid #ffffff;
    left: 0;
    top: 40%;
    width: 72px;
    text-align: center;
}

.protected-zone-right {
    border-bottom: 1px solid #ffffff;
    right: 0;
    top: 40%;
    width: 72px;
    text-align: center;
}

.text-lighter {
    color: #bbb !important;
}

.dragula-handle-absolute-left {
    text-align: left !important;
    left: -6px !important;
    width: 30px !important;
    color: #bbb !important;
}

.with-width-info {
    padding-top: 35px;
}

.logo-width-info {
    position: absolute;
    top: -38px;
    left: 0;
    text-align: center;
    border-bottom: 2px solid #ccc;
    width: 100%;
    font-weight: bold;
}

.logo-width-info::before {
    content: "";
    width: 2px;
    height: 10px;
    position: absolute;
    background: #ccc;
    left: -2px;
    bottom: -6px;
}

.logo-width-info::after {
    content: "";
    width: 2px;
    height: 10px;
    position: absolute;
    background: #ccc;
    right: -2px;
    bottom: -6px;
}


.with-height-info {
    padding-left: 100px;
}

.logo-height-info {
    position: absolute;
    left: -100px;
    text-align: center;
    border-right: 2px solid #ccc;
    width: 85px;
    height: 100%;
    font-weight: bold;
    display: flex;
    justify-content: center;
    align-items: center;
}


.logo-height-info::before {
    content: "";
    height: 2px;
    width: 10px;
    position: absolute;
    background: #ccc;
    top: -2px;
    right: -6px;
}

.logo-height-info::after {
    content: "";
    height: 2px;
    width: 10px;
    position: absolute;
    background: #ccc;
    bottom: -2px;
    right: -6px;
}

/* Weekly Menu Planner Styles */
.btn-delete-day {
    background-color: rgba(255, 255, 255, 0.15);
    border: 1px solid rgba(255, 255, 255, 0.5);
    color: #fff;
    transition: all 0.15s ease;
}

.btn-delete-day:hover {
    background-color: rgba(255, 255, 255, 0.25);
    border-color: rgba(255, 255, 255, 0.7);
    color: #fff;
}

.meal-card {
    cursor: pointer;
    transition: all 0.15s ease;
    background: #fff;
    border: 1px solid #dee2e6;
}

.meal-card:hover {
    background: #f8f9fa;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    border-color: #6c757d;
    transform: translateY(-1px);
}

.meal-chip {
    transition: all 0.15s ease;
}

.meal-chip:hover {
    filter: brightness(0.95);
}

.meal-chip .btn-close {
    opacity: 0.5;
    transition: opacity 0.15s ease;
}

.meal-chip:hover .btn-close {
    opacity: 0.8;
}

.meal-type-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    display: inline-block;
    flex-shrink: 0;
}

/* Dragula drag and drop styles */
.gu-mirror {
    position: fixed !important;
    margin: 0 !important;
    z-index: 9999 !important;
    opacity: 0.8;
    pointer-events: none;
}

.gu-hide {
    display: none !important;
}

.gu-unselectable {
    -webkit-user-select: none !important;
    -moz-user-select: none !important;
    -ms-user-select: none !important;
    user-select: none !important;
}

.gu-transit {
    opacity: 0.4;
}

/* Compact Meal Selector Modal */
.meal-selector-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-content: flex-start;
}

.meal-selector-item {
    display: inline-flex;
    flex-direction: column;
    padding: 6px 8px;
    background: #fff;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.12s ease;
    min-width: 120px;
    max-width: 200px;
    flex: 0 1 auto;
}

.meal-selector-item:hover {
    background: #f0f4ff;
    border-color: #727cf5;
    box-shadow: 0 1px 4px rgba(114, 124, 245, 0.25);
}

.meal-selector-item.d-none {
    display: none !important;
}

.meal-selector-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 4px;
}

.meal-selector-name {
    font-weight: 600;
    font-size: 13px;
    line-height: 1.2;
    color: #333;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
}

.meal-selector-type {
    font-size: 11px;
    font-weight: 700;
    color: #fff;
    padding: 1px 3px;
    border-radius: 2px;
    flex-shrink: 0;
    line-height: 1;
}

.meal-selector-meta {
    font-size: 12px;
    color: #666;
    line-height: 1.2;
    margin-top: 1px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.meal-selector-diet {
    font-weight: 500;
    color: #555;
}

.meal-selector-variants {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    margin-top: 5px;
    padding-top: 6px;
    border-top: 1px solid #eee;
}

.meal-selector-variant {
    font-size: 11px;
    background: #f5f5f5;
    border: 1px solid #ddd;
    padding: 0 3px;
    border-radius: 2px;
    color: #555;
    line-height: 1.3;
}

/* Reference Meal Selector (in form) */
.reference-meal-grid-container {
    max-height: 300px;
    overflow-y: auto;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    padding: 8px;
    background: #fafafa;
}

.reference-meal-preview {
    min-height: 50px;
}

.reference-meal-selected .meal-selector-item {
    cursor: default;
    background: #e8f4fd;
    border-color: #727cf5;
}

.reference-meal-selected .meal-selector-item:hover {
    background: #e8f4fd;
    box-shadow: none;
}

.reference-meal-empty {
    font-size: 0.875rem;
    text-align: center;
}

/* Selected state for meal cards */
.meal-selector-item.selected {
    background: #e8f4fd;
    border-color: #727cf5;
    box-shadow: 0 0 0 2px rgba(114, 124, 245, 0.3);
}

.meal-selector-item.selected::before {
    content: '\2713';
    position: absolute;
    top: 2px;
    right: 4px;
    font-size: 0.65rem;
    color: #727cf5;
    font-weight: bold;
}

.meal-selector-item {
    position: relative;
}

/* Planner meal cards (in day planner, reusing modal styles) */
.planner-meal-card {
    cursor: default;
}

.planner-meal-card.draggable {
    cursor: grab;
}

.planner-meal-card.draggable:active {
    cursor: grabbing;
}

.planner-meal-card .meal-selector-header {
    gap: 6px;
}

.planner-meal-card .btn-close {
    opacity: 0.4;
    transition: opacity 0.15s ease;
    margin-left: auto;
}

.planner-meal-card:hover .btn-close {
    opacity: 0.8;
}

/* Nutritional values display styles */
/* Nutrition summary base styles */
.nutrition-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.nutrition-label {
    font-size: 0.75rem;
    color: #666;
    font-weight: 500;
}

.nutrition-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 3px;
    align-items: center;
}

.nutrition-badge-item {
    font-size: 10px;
    font-weight: 500;
    padding: 2px 5px;
    border-radius: 3px;
    line-height: 1.2;
    white-space: nowrap;
    background-color: #fff !important;
    border: 1px solid currentColor;
}

.nutrition-badge-warning {
    background-color: #ffc107 !important;
    color: #212529 !important;
}

/* Weekly summary - stats grid design */
.nutrition-stats-weekly {
    background: linear-gradient(135deg, rgba(var(--bs-primary-rgb), 0.06) 0%, rgba(var(--bs-primary-rgb), 0.02) 100%);
    border: 1px solid rgba(var(--bs-primary-rgb), 0.15);
    border-radius: 8px;
    padding: 12px 16px;
}

.nutrition-stats-header {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--bs-primary);
    margin-bottom: 10px;
}

.nutrition-stats-header i {
    font-size: 1rem;
}

.nutrition-incomplete-badge {
    background: #ffc107;
    color: #212529;
    font-size: 0.65rem;
    padding: 2px 6px;
    border-radius: 10px;
    margin-left: auto;
}

.nutrition-stats-grid {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.nutrition-stat-item {
    background: #fff;
    border: 1px solid rgba(var(--bs-primary-rgb), 0.12);
    border-radius: 6px;
    padding: 8px 12px;
    min-width: 80px;
    text-align: center;
}

.nutrition-stat-value {
    display: block;
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--bs-primary);
    line-height: 1.2;
}

.nutrition-stat-label {
    display: block;
    font-size: 0.65rem;
    color: #888;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    margin-top: 2px;
}


/* Variant summary */
.nutrition-summary-variant {
    margin-top: 8px;
    padding: 6px 8px;
    background: #f8f9fa;
    border-radius: 4px;
    border: 1px dashed #ddd;
}

.nutrition-summary-variant .nutrition-label {
    font-size: 0.7rem;
}

.nutrition-summary-variant .nutrition-badge-item {
    font-size: 9px;
    padding: 1px 4px;
}

/* Legacy support - remove if not used elsewhere */
.nutrition-badge {
    font-size: 0.7rem;
    background: rgba(0, 0, 0, 0.08);
    padding: 2px 6px;
    border-radius: 4px;
    font-weight: 500;
    color: #555;
}

.meal-selector-nutrition {
    display: flex;
    flex-wrap: wrap;
    gap: 3px;
    margin-top: 5px;
    padding-top: 6px;
    border-top: 1px dashed #eee;
}

/* Menu display nutrition styles */
.menu-nutrition-summary {
    font-size: 0.9rem;
    color: #666;
    margin-top: 8px;
}

.meal-nutrition {
    font-size: 0.8em;
    color: #888;
    font-weight: normal;
    margin-left: 4px;
}

/* Collapsible sections using native <details> element */
.collapsible-section {
    /* Remove default marker */
}

.collapsible-section > summary {
    list-style: none;
}

.collapsible-section > summary::-webkit-details-marker {
    display: none;
}

.collapsible-section > summary::marker {
    display: none;
}

/* Chevron icon animation */
.collapsible-icon {
    transition: transform 0.2s ease;
    font-size: 1.1em;
}

.collapsible-section[open] > summary .collapsible-icon {
    transform: rotate(90deg);
}

/* Chevron stays rotated during closing animation */
.collapsible-section.closing > summary .collapsible-icon {
    transform: rotate(0deg);
}

/* Smooth height animation using grid */
.collapsible-content {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.2s ease-out;
}

.collapsible-section[open] > .collapsible-content {
    grid-template-rows: 1fr;
}

/* When closing, keep content visible during animation */
.collapsible-section.closing > .collapsible-content {
    grid-template-rows: 0fr;
}

.collapsible-content > .collapsible-inner {
    min-height: 0;
}

/* Only apply overflow hidden during animations (open/close) */
.collapsible-section.animating > .collapsible-content > .collapsible-inner {
    overflow: hidden;
}

/* Day level styles */
.collapsible-day > summary {
    user-select: none;
}

.collapsible-day > summary:hover {
    background-color: rgba(var(--bs-primary-rgb), 0.12) !important;
}

/* Meal type level styles */
.collapsible-meal-type > summary {
    position: relative;
}

.collapsible-meal-type > summary::before {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0);
    pointer-events: none;
    border-radius: inherit;
    transition: background 0.15s;
}

.collapsible-meal-type > summary:hover::before {
    background: rgba(0, 0, 0, 0.03);
}

/* Course level styles */
.collapsible-course > summary:hover {
    background-color: color-mix(in srgb, var(--course-color) 20%, white) !important;
}

.collapsible-course > summary {
    border-bottom-left-radius: 0.25rem;
    border-bottom-right-radius: 0.25rem;
}

.collapsible-course[open] > summary {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

/* Fix dropdown overlap issues in planner */
.collapsible-day .dropdown {
    display: inline-block !important;
}

/* Project image gallery — drag & drop upload dropzone */
.gallery-dropzone {
    border: 2px dashed var(--bs-border-color, #dee2e6);
    border-radius: 0.5rem;
    padding: 2rem 1rem;
    text-align: center;
    cursor: pointer;
    background: var(--bs-tertiary-bg, #f8f9fa);
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.gallery-dropzone:hover,
.gallery-dropzone:focus-visible {
    border-color: var(--bs-primary, #0d6efd);
    background: rgba(var(--bs-primary-rgb, 13, 110, 253), 0.04);
    outline: none;
}

.gallery-dropzone--over {
    border-color: var(--bs-primary, #0d6efd);
    background: rgba(var(--bs-primary-rgb, 13, 110, 253), 0.09);
}

/* Let drag/drop + click always target the dropzone itself, never a child
   (prevents dragleave flicker when hovering the icon/labels). */
.gallery-dropzone > * {
    pointer-events: none;
}

/* Koš (trash bin): the read-only special directory at the gallery root. */
.gallery-trash-card {
    background: var(--bs-tertiary-bg, #f8f9fa);
    color: inherit;
}

.gallery-trash-card:hover {
    background: rgba(0, 0, 0, 0.05);
}

.gallery-asset--trashed img {
    opacity: 0.8;
    filter: grayscale(0.25);
}

/* Tile caption: the uploaded file name + pixel size / format, always visible
   under the thumbnail — the only way to tell two look-alike pictures of
   different sizes apart. Base and extension are separate spans so a long name
   truncates in the MIDDLE ("pozadi-tmave-mo… 1350.png" keeps its tail). */
.gallery-asset__select {
    /* The modal tile is a <button class="btn">: undo the button's centring,
       font sizing and focus/hover chrome so the caption reads like text. */
    font-size: inherit;
    line-height: inherit;
    border-radius: inherit;
}

.gallery-asset__select:hover,
.gallery-asset__select:focus-visible {
    background: rgba(var(--bs-primary-rgb, 13, 110, 253), 0.06);
}

.gallery-asset__caption {
    padding: 0.3rem 0.5rem 0.4rem;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    font-size: 0.75rem;
    line-height: 1.3;
    min-width: 0;
}

.gallery-asset__name {
    display: flex;
    min-width: 0;
    font-weight: 500;
    color: var(--bs-body-color, #212529);
}

.gallery-asset__name-base {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.gallery-asset__name-ext {
    flex: 0 0 auto;
    white-space: nowrap;
}

.gallery-asset__name-missing {
    color: var(--bs-secondary-color, #6c757d);
    font-weight: 400;
}

.gallery-asset__meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.35rem;
    margin-top: 0.1rem;
    color: var(--bs-secondary-color, #6c757d);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.gallery-asset__size {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gallery-asset__format {
    flex: 0 0 auto;
    padding: 0 0.3rem;
    border-radius: 0.25rem;
    background: var(--bs-tertiary-bg, #f1f3fa);
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.5;
}

/* Freshly uploaded thumbnail: a short highlight pulse so the user sees the
   file land in the folder they are looking at. */
.gallery-asset--new {
    outline: 2px solid var(--bs-success, #0acf97);
    outline-offset: 1px;
    animation: gallery-asset-new-pulse 1.2s ease-out 2;
}

@keyframes gallery-asset-new-pulse {
    0% { box-shadow: 0 0 0 0 rgba(10, 207, 151, 0.55); }
    100% { box-shadow: 0 0 0 12px rgba(10, 207, 151, 0); }
}

.gallery-dropzone__icon {
    display: block;
    margin-bottom: 0.5rem;
    font-size: 2.5rem;
    line-height: 1;
    color: var(--bs-primary, #0d6efd);
}

.gallery-dropzone__title {
    font-weight: 600;
    color: var(--bs-body-color, #212529);
}

/* Success toast shown after uploads finish (lives on <body>, above modals). */
.gallery-toast-container {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    pointer-events: none;
}

.gallery-toast {
    display: flex;
    align-items: center;
    padding: 0.6rem 1rem;
    border-radius: 0.375rem;
    background: var(--bs-success, #198754);
    color: #fff;
    font-size: 0.9rem;
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.2);
    opacity: 0;
    transform: translateY(-0.5rem);
    transition: opacity 0.25s ease, transform 0.25s ease;
}

.gallery-toast--show {
    opacity: 1;
    transform: translateY(0);
}

/* Selected-file preview rows (before upload is confirmed). */
.gallery-upload-item__thumb {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    object-fit: cover;
    border-radius: 0.25rem;
    background: var(--bs-tertiary-bg, #f8f9fa);
}

.gallery-upload-item__thumb--ph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--bs-secondary-color, #adb5bd);
    font-size: 1.25rem;
}

/* Let the filename column truncate instead of overflowing the row. */
.gallery-upload-item .min-w-0 {
    min-width: 0;
}

/*
 * Project dashboard — clickable module/template tiles.
 */
.module-card {
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.module-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 0.375rem 1.125rem rgba(0, 0, 0, 0.12);
}

/*
 * Mockup pages — canonical CSS-grid render.
 *
 * Every mockup layout lives on one 3-columns × 2-rows grid (spans define the
 * segments) with a fixed page aspect ratio and a uniform gap, mirroring
 * MockupPageLayout::slots(). Used by the manual preview render, the admin
 * listing thumbnails, the layout picker and the interactive editor, so all
 * surfaces show identical proportions. Gap/radius use cqw so they scale with
 * the rendered size (36 / 1380 page units and 20 / 1380 respectively).
 */
.mockup-grid-container {
    container-type: inline-size;
}

.mockup-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: 2.609cqw;
    aspect-ratio: 1380 / 798;
}

.mockup-slot {
    position: relative;
    overflow: hidden;
    border-radius: 1.45cqw;
}

.mockup-slot img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/*
 * The corner button of a mockup slot whose image carries a downloadable file.
 * Kept legible over any artwork by its own solid ground rather than a tint.
 */
.mockup-slot-download {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    display: inline-flex;
    align-items: center;
    gap: 0.35em;
    padding: 0.45em 0.7em;
    border-radius: 2em;
    background: rgba(255, 255, 255, 0.94);
    color: #313a46;
    /* Follows the page's own scale (the grid is a container) but stays
       readable on a small admin card and unobtrusive on the full manual. */
    font-size: clamp(0.625rem, 1.1cqw, 0.8125rem);
    font-weight: 700;
    line-height: 1;
    text-decoration: none;
    box-shadow: 0 0.15em 0.5em rgba(23, 27, 38, 0.25);
    transition: background-color 0.15s ease, color 0.15s ease;
}

.mockup-slot-download:hover,
.mockup-slot-download:focus {
    background: var(--bs-primary, #727cf5);
    color: #fff;
}

.mockup-slot-download i {
    font-size: 1.15em;
}

.mockup-slot-download-format {
    letter-spacing: 0.03em;
}

.mockup-slot-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    background: #f1f3f9;
    border: 1px dashed #d3d8e5;
    border-radius: inherit;
    color: #98a2b3;
    font-size: 1.25rem;
}

/*
 * Mockup page editor — layout picker (radio cards with live mini previews).
 */
.mockup-layout-picker {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 0.75rem;
}

.mockup-layout-option {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    padding: 0.625rem;
    border: 2px solid var(--bs-border-color, #dee2e6);
    border-radius: 0.5rem;
    background: var(--bs-body-bg, #fff);
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    margin-bottom: 0;
}

.mockup-layout-option:hover {
    border-color: var(--bs-primary, #727cf5);
}

.mockup-layout-option:has(.mockup-layout-radio:checked) {
    border-color: var(--bs-primary, #727cf5);
    box-shadow: 0 0 0 0.2rem rgba(114, 124, 245, 0.2);
}

.mockup-layout-option-static {
    width: 120px;
    cursor: default;
}

.mockup-layout-radio {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.mockup-mini-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: 3px;
    aspect-ratio: 1380 / 798;
}

.mockup-mini-grid > span {
    background: #d6dae7;
    border-radius: 3px;
    transition: background-color 0.15s ease;
}

.mockup-mini-grid > span.is-filled {
    background: var(--bs-primary, #727cf5);
}

.mockup-layout-count {
    font-size: 0.75rem;
    color: var(--bs-secondary-color, #98a2b3);
    text-align: center;
}

/*
 * Mockup page editor — interactive upload stage.
 */
.mockup-editor-desk {
    background: #eef0f4;
    border-radius: 0.625rem;
    padding: 1.25rem;
}

.mockup-editor-stage-container {
    container-type: inline-size;
    max-width: 980px;
    margin: 0 auto;
    background: #fff;
    border-radius: 0.375rem;
    box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.08);
    padding: 1.25rem;
}

.mockup-editor-page {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: 2.609cqw;
    aspect-ratio: 1380 / 798;
}

.mockup-editor-no-layout {
    text-align: center;
    padding: 3rem 1rem;
    font-size: 0.9375rem;
}

.mockup-editor-no-layout i {
    font-size: 2rem;
}

.mockup-editor-slot {
    position: relative;
    overflow: hidden;
    border: 2px dashed #c4cbdc;
    border-radius: 1.45cqw;
    background: #f6f7fb;
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.mockup-editor-slot:hover,
.mockup-editor-slot.is-dragover {
    border-color: var(--bs-primary, #727cf5);
    background: #eef0ff;
}

.mockup-editor-slot.has-image {
    border-style: solid;
    border-color: transparent;
    background: #e9ebf3;
}

.mockup-editor-slot.has-image.level-warning {
    border-color: var(--bs-warning, #ffbc00);
}

.mockup-editor-slot.has-image.level-danger {
    border-color: var(--bs-danger, #fa5c7c);
}

.mockup-editor-slot-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.mockup-editor-slot-empty {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.125rem;
    text-align: center;
    padding: 0.5rem;
    color: #8790a4;
    font-size: 0.8125rem;
}

.mockup-editor-slot-empty i {
    font-size: 1.5rem;
    line-height: 1;
}

.mockup-editor-slot-dims {
    font-size: 0.75rem;
}

.mockup-editor-slot-error {
    font-size: 0.75rem;
    font-weight: 600;
}

.mockup-editor-slot-removed {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    text-align: center;
    padding: 0.5rem;
    color: var(--bs-danger, #fa5c7c);
    font-size: 0.8125rem;
}

.mockup-editor-slot-removed i {
    font-size: 1.5rem;
    line-height: 1;
}

.mockup-editor-slot-number {
    position: absolute;
    top: 0.375rem;
    left: 0.375rem;
    width: 1.375rem;
    height: 1.375rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(55, 62, 80, 0.65);
    color: #fff;
    font-size: 0.6875rem;
    font-weight: 700;
}

.mockup-editor-slot-chip {
    position: absolute;
    top: 0.375rem;
    right: 0.375rem;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.125rem 0.5rem;
    border-radius: 1rem;
    font-size: 0.6875rem;
    font-weight: 700;
    color: #fff;
}

.mockup-editor-slot-chip.chip-success {
    background: var(--bs-success, #0acf97);
}

.mockup-editor-slot-chip.chip-warning {
    background: var(--bs-warning, #ffbc00);
    color: #343a40;
}

.mockup-editor-slot-chip.chip-danger {
    background: var(--bs-danger, #fa5c7c);
}

.mockup-editor-slot-meta {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 1rem 0.5rem 0.375rem;
    background: linear-gradient(transparent, rgba(23, 27, 38, 0.75));
    color: #fff;
    font-size: 0.6875rem;
    line-height: 1.3;
}

.mockup-editor-slot-actions {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    flex-wrap: wrap;
    opacity: 0;
    background: rgba(23, 27, 38, 0.35);
    transition: opacity 0.15s ease;
}

.mockup-editor-slot:hover .mockup-editor-slot-actions {
    opacity: 1;
}

.mockup-editor-slot:not(.has-image):not(.is-removed) .mockup-editor-slot-actions {
    display: none;
}

.mockup-editor-slot.is-removed .mockup-editor-slot-actions {
    background: transparent;
    align-items: flex-end;
    padding-bottom: 0.625rem;
}

.mockup-editor-slot-attach {
    position: absolute;
    top: 0.375rem;
    left: 2.0rem;
    width: 1.375rem;
    height: 1.375rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(55, 62, 80, 0.65);
    color: #fff;
    font-size: 0.75rem;
}

/*
 * Mockup page editor — the downloadable attachments, one row for the whole
 * page plus one per slot of the chosen layout. A file has no preview, only a
 * name and a size, so these are rows under the stage rather than chrome inside
 * a slot; the paperclip badge on the slot ties the two together.
 */
.mockup-download-list {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.mockup-download-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 0.375rem;
    background: var(--bs-body-bg, #fff);
    font-size: 0.8125rem;
}

.mockup-download-row-label {
    flex: 0 0 9rem;
    font-weight: 600;
}

.mockup-download-row-file {
    flex: 1 1 12rem;
    color: var(--bs-secondary-color, #98a2b3);
    overflow-wrap: anywhere;
}

.mockup-download-row-file.has-file {
    color: var(--bs-body-color, #6c757d);
    font-weight: 600;
}

.mockup-download-row-file.is-removed {
    color: var(--bs-danger, #fa5c7c);
    font-weight: 600;
}

.mockup-download-row-error {
    flex: 1 1 100%;
    font-weight: 600;
}

.mockup-download-row-actions {
    display: flex;
    gap: 0.375rem;
    margin-left: auto;
}

/*
 * Mockup page editor — sticky save bar with unsaved-changes state.
 */
.mockup-editor-savebar {
    position: sticky;
    bottom: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 1rem;
    margin-top: 1rem;
    padding: 0.75rem 1.25rem;
    background: var(--bs-body-bg, #fff);
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 0.5rem;
    box-shadow: 0 -0.125rem 0.75rem rgba(0, 0, 0, 0.06);
}

.mockup-editor-savebar > :first-child {
    margin-right: auto;
}

.mockup-editor-dirty {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    color: var(--bs-warning-text-emphasis, #997404);
    font-size: 0.8125rem;
    font-weight: 600;
}

.mockup-editor-dirty-dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--bs-warning, #ffbc00);
}

/* Canvas display-settings menu (the gear in the editor top bar). Wide enough
   that no switch label wraps, and above the sticky header's own stacking
   context so it paints over the stage. Qualified with .dropdown-menu to
   outrank the theme's own min-width/z-index on that class. */
.dropdown-menu.editor-display-menu {
    min-width: 17rem;
    z-index: 1010;
}

/* --- Template-group editor: variant rail (compact chips) ---------------- */

/* The rail card doubles as the propagation-scope indicator: a primary border
   while an edit reaches EVERY variant (mode on, every switch on, nothing
   "Nenačteno"), plain otherwise. The border is always present (transparent)
   so toggling it never shifts the stage below by its width. Two classes so
   the rule outranks the theme's `.card` border (that sheet loads after this
   one — see the .group-variant-include note). */
.card.group-variant-rail-card {
    border: 2px solid transparent;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.card.group-variant-rail-card--all {
    border-color: var(--bs-primary, #727cf5);
    /* Ring ON TOP of the theme's card shadow, not instead of it. */
    box-shadow: 0 0 0 0.2rem rgba(114, 124, 245, 0.15), var(--ct-box-shadow);
}

.group-variant-rail {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}

/* "Úprava více variant" mode switch — sits left of the chips it governs.
   Selector doubled up to outrank the theme's `.btn-sm` radius (see the
   .group-variant-include note below for why order alone does not do it). */
.group-variant-rail .group-variant-mode {
    border-radius: 2rem;
    margin-right: 0.25rem;
}

.group-variant-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    border: 2px solid transparent;
    border-radius: 2rem;
    padding: 0.125rem 0.5rem 0.125rem 0.25rem;
    background: var(--bs-light, #f8f9fa);
}

.group-variant-chip.group-variant-active {
    border-color: var(--bs-primary, #727cf5);
}

.group-variant-chip.group-variant-excluded {
    opacity: 0.5;
}

.group-variant-label {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    border: 0;
    background: transparent;
    border-radius: 2rem;
    padding: 0.125rem 0.5rem;
    font-size: 0.8125rem;
    line-height: 1.4;
    color: inherit;
    white-space: nowrap;
}

.group-variant-chip.group-variant-active .group-variant-label {
    font-weight: 600;
}

.group-variant-dirty {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: var(--bs-danger, #fa5c7c);
}

/* The per-variant opt-in switches are chrome of the multi-variant mode:
   with the mode off there is nothing to opt into, so they are not rendered
   at all (never merely dimmed — a visible switch that changes nothing is
   worse than no switch).

   Two class selectors, not one: the theme sheet (theme/css/app-saas.min.css)
   loads AFTER this file, so its `.form-check {display: block}` wins every
   single-class tie. Anything here that overrides a Bootstrap/theme utility
   needs the same treatment. */
.group-variant-chip .group-variant-include {
    display: none;
}

.group-variant-rail--multi .group-variant-chip .group-variant-include {
    display: block;
}

/* "Create group from existing template" picker tiles */
.template-source-tile {
    display: inline-block;
    padding: 0.35rem;
    border: 1px solid rgba(0, 0, 0, 0.12);
    border-radius: 0.35rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.template-source-tile:hover {
    border-color: var(--bs-primary, #727cf5);
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.12);
    transform: translateY(-2px);
}

.template-source-thumb {
    height: 110px;
    border: 1px solid rgba(0, 0, 0, 0.08);
    background-color: #f6f7fb;
}

.template-source-cta {
    color: var(--bs-primary, #727cf5);
    opacity: 0;
    transition: opacity 0.15s ease;
}

.template-source-tile:hover .template-source-cta {
    opacity: 1;
}

/* Group fill & export page (template_group_fill.html.twig) */
/* Which dimension this card is, above the image (a caption underneath reads
   as belonging to whatever follows it). */
.group-fill-preview-heading {
    padding: 0.45rem 0.75rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
    font-size: 0.75rem;
    color: var(--ct-secondary-color, #8a969c);
}

/* Background gallery picker (add/edit template + group forms): thumbnail of
   the picked gallery file next to the pick/clear buttons. */
.background-picker-thumb {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    border: 1px solid rgba(0, 0, 0, 0.125);
    border-radius: 0.25rem;
    background-size: cover;
    background-position: center;
}

/* Per-dimension PNG download in the heading — a submit button riding the
   fill form via formaction, styled as a quiet inline link. */
.group-fill-variant-download {
    border: 0;
    background: none;
    padding: 0;
    font-size: inherit;
    line-height: inherit;
    white-space: nowrap;
    color: var(--ct-link-color, #3577f1);
}

.group-fill-variant-download:hover {
    text-decoration: underline;
}

.group-fill-variant-download:disabled {
    color: inherit;
    opacity: 0.65;
}

.group-fill-preview-frame {
    position: relative;
    width: 100%;
    background: #f6f7fb;
    border-radius: 0 0 0.25rem 0.25rem;
    overflow: hidden;
}

.group-fill-preview-frame img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    transition: opacity 0.15s ease;
}

.group-fill-preview-frame.is-pending img,
.group-fill-preview-frame.is-loading img {
    opacity: 0.55;
}

.group-fill-preview-spinner {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    /* Dim the stale preview underneath so "loading" is unmissable. */
    background: rgba(255, 255, 255, 0.55);
}

.group-fill-preview-frame.is-loading .group-fill-preview-spinner {
    display: flex;
}

.group-fill-preview-error {
    position: absolute;
    inset: auto 0 0 0;
    display: none;
    padding: 0.15rem 0.5rem;
    background: rgba(250, 92, 124, 0.9);
    color: #fff;
    font-size: 11px;
    text-align: center;
}

.group-fill-preview-frame.is-error .group-fill-preview-error {
    display: block;
}

/* Image placement over a group preview.
   The layer never intercepts pointer events itself — only the per-slot grab
   boxes do — so the rest of the preview stays inert. A box exists as soon as a
   picture is chosen (it IS the grab handle); the picture inside it is revealed
   only while placing, until the fresh server render lands and takes over. */
.group-fill-placement-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.group-fill-ghost {
    position: absolute;
    overflow: hidden;
    pointer-events: auto;
    cursor: grab;
    border: 1px dashed transparent;
    border-radius: 2px;
}

.group-fill-ghost:hover,
.group-fill-ghost:focus-visible {
    border-color: rgba(114, 124, 245, 0.9);
    outline: none;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.7);
}

.group-fill-ghost:active {
    cursor: grabbing;
}

.group-fill-ghost img {
    position: absolute;
    display: block;
    max-width: none;
    object-fit: fill;
    opacity: 0;
    transform-origin: center center;
    user-select: none;
    -webkit-user-drag: none;
}

/* While placing, the stand-in replaces the (now stale) rendered pixels of that
   slot — the neutral backdrop hides the old position underneath. */
.group-fill-placement-layer.is-active .group-fill-ghost {
    background: #fff;
    border-color: rgba(114, 124, 245, 0.9);
}

.group-fill-placement-layer.is-active .group-fill-ghost img,
.group-fill-preview-frame.is-loading .group-fill-placement-layer.is-active .group-fill-ghost img,
.group-fill-preview-frame.is-pending .group-fill-placement-layer.is-active .group-fill-ghost img {
    opacity: 1;
}

/* Text echo composite (mirrors the single fill page's fill-echo-* pair):
   while the user types, the settle preview yields to the text-transparent
   base render + the client-drawn text canvas stacked above it. Placed after
   the is-pending/is-loading dim rules on purpose — the echo layers must
   never be veiled or dimmed while a settle render is on its way. */
.group-fill-echo-base,
.group-fill-echo-canvas {
    position: absolute;
    left: 0;
    top: 0;
    display: block;
    visibility: hidden;
    pointer-events: none;
}

.group-fill-echo-base {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.group-fill-preview-frame.is-echo .group-fill-echo-base,
.group-fill-preview-frame.is-echo .group-fill-echo-canvas {
    visibility: visible;
}

.group-fill-preview-frame.is-echo > img[data-group-fill-target="preview"] {
    visibility: hidden;
}

/* The echo IS the feedback — no veil, no dimming over it. */
.group-fill-preview-frame.is-echo .group-fill-preview-spinner {
    display: none;
}

.group-fill-preview-frame.is-echo.is-pending .group-fill-echo-base,
.group-fill-preview-frame.is-echo.is-loading .group-fill-echo-base {
    opacity: 1;
}

.group-fill-placement-controls {
    padding: 0.5rem 0.6rem;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 0.3rem;
    background: #fbfbfe;
}

.group-fill-placement-controls .form-range {
    margin-bottom: 0.35rem;
}

/* Dimension cards on the group fill page. The theme's 0.75rem card margin is
   too tight for a grid of previews with controls underneath — consecutive
   cards read as one column of stuff. */
.group-fill-preview-card {
    margin-bottom: 1.5rem;
}

/* One block per dimension inside a slot's placement panel, separated so
   several dimensions don't read as one long strip of sliders. */
.group-fill-variant-slot + .group-fill-variant-slot {
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
}

/* The dimension grid beside the "Vrstvy" panel (the .fill-body flex row). */
.group-fill-previews {
    flex: 1 1 auto;
    min-width: 0;
}

.group-fill-image-option {
    position: relative;
    width: 96px;
    height: 96px;
    padding: 0;
    border: 2px solid rgba(0, 0, 0, 0.08);
    border-radius: 0.3rem;
    background: #f6f7fb;
    overflow: hidden;
}

.group-fill-image-option img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.group-fill-image-option:hover,
.group-fill-image-option.selected {
    border-color: var(--bs-primary, #727cf5);
}

.group-fill-image-option span {
    position: absolute;
    inset: auto 0 0 0;
    padding: 1px 0;
    background: rgba(255, 255, 255, 0.85);
    font-size: 11px;
    text-align: center;
}

/* Photoshop-style navigator mini-map (canvas_navigator_controller): fixed
   bottom-right, auto-hidden while the whole canvas is visible. The rect is
   the viewport marker; panning happens on the map itself. */
.canvas-navigator {
    position: fixed;
    right: 18px;
    bottom: 18px;
    z-index: 1030;
    padding: 6px;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.12);
    border-radius: 6px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
}

.canvas-navigator__map {
    position: relative;
    line-height: 0;
    cursor: pointer;
    background: #f1f3fa;
}

.canvas-navigator__map:active {
    cursor: move;
}

.canvas-navigator__map canvas {
    display: block;
}

.canvas-navigator__rect {
    position: absolute;
    box-sizing: border-box;
    border: 2px solid #727cf5;
    background: rgba(114, 124, 245, 0.12);
    pointer-events: none;
}

/* WYSIWYG list lines (rich_text_editor_controller line-div rendering):
   bullets/ordinals are ::before chrome — they exist only in the editor UI,
   the export draws its own bullet objects from the shared blocks layout. */
.fill-richtext-editor .rt-line { min-height: 1em; }

.fill-richtext-editor .rt-line[data-type="ul"],
.fill-richtext-editor .rt-line[data-type="ol"],
.fill-richtext-editor .rt-line[data-type="cb"],
.fill-richtext-editor .rt-line[data-type="cbx"] {
    position: relative;
    padding-left: 1.6em;
}

.fill-richtext-editor .rt-line[data-type="ul"]::before {
    content: '\2022';
    position: absolute;
    left: 0.35em;
}

.fill-richtext-editor .rt-line[data-type="ol"]::before {
    content: attr(data-number);
    position: absolute;
    left: 0;
    font-variant-numeric: tabular-nums;
}

/* Checkbox items ('cb' unchecked / 'cbx' checked): the marker mirrors the
   export's default drawn checkbox — a rounded square in the line's text
   color, the checked one with a white check. Clicking the marker toggles
   the state (rich_text_editor_controller editorClick). */
.fill-richtext-editor .rt-line[data-type="cb"]::before,
.fill-richtext-editor .rt-line[data-type="cbx"]::before {
    content: '';
    position: absolute;
    left: 0.1em;
    top: 0.3em;
    width: 0.85em;
    height: 0.85em;
    border-radius: 0.19em;
    background-color: currentColor;
    cursor: pointer;
}

.fill-richtext-editor .rt-line[data-type="cbx"]::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.2 8.6 6.6 12 12.8 4.4' fill='none' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-size: 80% 80%;
    background-position: center;
    background-repeat: no-repeat;
}

/* ======================================================================
 * Navigation without a topbar
 *
 * The theme's `.navbar-custom` was removed from base.html.twig — the only
 * things it carried (the account dropdown, the drawer toggle) moved into the
 * left navigation. Nothing else depended on its height: `.leftside-menu` is
 * `position: fixed; top: 0` and `.content-page` never had a top padding, so
 * the page content simply starts at the window top now.
 * ====================================================================== */

/* SPECIFICITY: importmap() injects this stylesheet BEFORE the theme's
   app-saas.min.css, so an equal-specificity rule of ours LOSES. Every rule
   that competes with the theme (or with Bootstrap's `.dropup{position:
   relative}`) is therefore scoped one level deeper than what it must beat. */

/* Room for the account block at the bottom edge. Using the sidebar's own
   padding (the theme already reserved footer height here) keeps
   `#leftside-menu-container`'s `h-100` resolving to exactly the space ABOVE
   the block — percentage heights resolve against the content box — so the
   scrolling menu and the block can never overlap. */
.leftside-menu {
    --wb-leftbar-account-height: 64px;
}

.wrapper .leftside-menu {
    padding-bottom: var(--wb-leftbar-account-height);
}

.leftside-menu .leftbar-account {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--wb-leftbar-account-height);
    display: flex;
    align-items: center;
    background: var(--ct-menu-bg);
    border-top: 1px solid var(--ct-border-color);
}

/* Padding-left 19px + a 32px avatar centers the picture on the 70px condensed
   rail (35px), exactly where `.side-nav-link i` centers its icon. */
.leftside-menu .leftbar-account__toggle {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    min-width: 0;
    padding: 0.5rem 0.75rem 0.5rem 19px;
    color: var(--ct-menu-item-color);
    font-size: var(--ct-menu-item-font-size);
    text-decoration: none;
}

.leftside-menu .leftbar-account__toggle:hover,
.leftside-menu .leftbar-account__toggle:focus,
.leftside-menu .leftbar-account__toggle.show {
    color: var(--ct-menu-item-hover-color);
}

.leftside-menu .leftbar-account__name {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
}

.leftside-menu .leftbar-account__caret {
    flex-shrink: 0;
    font-size: 16px;
    line-height: 1;
}

.leftside-menu .leftbar-account__menu {
    min-width: calc(var(--ct-leftbar-width) - 1.5rem);
}

/* Icon-only sidebar states: only the avatar survives (the same way
   `.side-nav-link span` is hidden there). */
html[data-sidenav-size=condensed]:not([data-layout=topnav]) .leftbar-account__name,
html[data-sidenav-size=condensed]:not([data-layout=topnav]) .leftbar-account__caret,
html[data-sidenav-size=compact]:not([data-layout=topnav]) .leftbar-account__name,
html[data-sidenav-size=compact]:not([data-layout=topnav]) .leftbar-account__caret,
html[data-sidenav-size=sm-hover]:not([data-layout=topnav]) .leftside-menu:not(:hover) .leftbar-account__name,
html[data-sidenav-size=sm-hover]:not([data-layout=topnav]) .leftside-menu:not(:hover) .leftbar-account__caret {
    display: none;
}

/* Slim bar carrying the drawer toggle below xl (`d-xl-none` in the markup
   switches it off from 1200px up, where the sidebar is always visible). It
   takes the SIDEBAR's colours, not the theme's topbar ones: the repo ships a
   single logo asset whose wordmark is white (`assets/images/logo-lg.svg`, used
   for both the light and the dark slot), so on a white bar only the coloured
   "w" would be visible. */
.content-page .mobile-topbar {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    height: 56px;
    /* Cancel .content-page's horizontal padding so the bar spans the width. */
    margin: 0 -0.75rem;
    padding: 0 0.5rem;
    position: sticky;
    top: 0;
    z-index: 1000;
    background: var(--ct-menu-bg);
}

.content-page .mobile-topbar .button-toggle-menu {
    width: auto;
    line-height: 1;
    padding: 0 0.5rem;
    color: var(--ct-menu-item-color);
}

.content-page .mobile-topbar__logo {
    display: inline-flex;
    align-items: center;
}

/* Variant grid on the template variants page (template_variants.html.twig).
   One template's variants span wildly different aspect ratios — a 1:1 post, a
   9:16 story, an A4 portrait — so every preview gets the SAME square stage and
   is letterboxed inside it (`contain`). Sizing the card to the picture instead
   gave every column a different height and the grid read as rubble.

   Label + ⋯ menu live in a HEADER above that stage, the way the manuals cards
   do it. Both were tried below/over the picture first and both read as "beside
   the image": a footer strip detaches the label from the card, and a chip
   floated over the stage lands in the letterbox grey whenever the preview is
   narrower than the card — which is most of them, since the stage is square and
   the previews rarely are.

   Note the `.variant-card` prefix on every selector below: app.css loads BEFORE
   the theme, so an equal-specificity rule of ours loses — `.variant-card__menu`
   alone is beaten by Bootstrap's `.dropdown {position: relative}` and the menu
   lands in the card's top-LEFT corner, in flow. */
.variant-card .variant-card__link {
    display: block;
}

.variant-card .variant-card__header {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    /* Right padding reserves the ⋯ button's lane so a long label never runs
       under it. */
    padding: 0.7rem 2.5rem 0.6rem 0.9rem;
}

.variant-card .variant-card__title {
    margin: 0;
    min-width: 0;
    overflow: hidden;
    font-size: 0.8125rem;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.variant-card .variant-card__thumb {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1 / 1;
    padding: 0 0.9rem 0.9rem;
    overflow: hidden;
}

/* Soft shadow rather than a grey stage behind the picture: a variant preview
   is full-bleed artwork and is often white at the edges, so on the card's white
   body it would have no boundary at all. */
.variant-card .variant-card__thumb img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.14);
}

/* Same checkerboard the editor and the fill preview use for layer-mode
   variants, which may legitimately render onto full transparency. */
.variant-card .variant-card__thumb--transparent img {
    background-image:
        linear-gradient(45deg, #e4e7ec 25%, transparent 25%, transparent 75%, #e4e7ec 75%),
        linear-gradient(45deg, #e4e7ec 25%, transparent 25%, transparent 75%, #e4e7ec 75%);
    background-color: #fff;
    background-size: 16px 16px;
    background-position: 0 0, 8px 8px;
}

/* The whole card is one link, so it needs the affordance a button would have. */
.variant-card {
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.variant-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 0.5rem 1.2rem rgba(0, 0, 0, 0.12);
}

/* Sits in the header row's right lane (`.card` is already position: relative),
   NOT inside the link, so clicking it never navigates. */
.variant-card .variant-card__menu {
    position: absolute;
    top: 0.5rem;
    right: 0.6rem;
    z-index: 2;
}

.variant-card .variant-card__menu-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    color: var(--ct-text-muted, #98a6ad);
}

.variant-card .variant-card__menu-toggle:hover,
.variant-card .variant-card__menu-toggle:focus,
.variant-card .variant-card__menu.show .variant-card__menu-toggle {
    background: rgba(0, 0, 0, 0.05);
    color: #343a40;
}

/* --- Font choice ("Uživatel může přepínat písmo") ------------------------- */

/* The admin's per-input font checklist (text popover + "Přidat text" modal):
   fonts as group headers with a tri-state checkbox, faces beneath previewed
   in their own face, the designed face locked-checked with a badge. */
.font-choice {
    max-height: 190px;
    overflow: auto;
    border: 1px solid var(--ct-border-color, #dee2e6);
    border-radius: 0.25rem;
    padding: 0.25rem 0.5rem;
    font-size: 0.8125rem;
    background: var(--ct-input-bg, #fff);
}

.font-choice__group + .font-choice__group {
    margin-top: 0.35rem;
    padding-top: 0.35rem;
    border-top: 1px solid var(--ct-border-color, #dee2e6);
}

.font-choice__header,
.font-choice__face {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    cursor: pointer;
    line-height: 1.6;
}

.font-choice__header {
    font-weight: 600;
}

.font-choice__face {
    padding-left: 1.4rem;
}

.font-choice__name {
    font-size: 0.9375rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.font-choice__badge {
    font-size: 0.6rem;
    font-weight: 500;
    flex-shrink: 0;
}

/* The end user's whole-text font select in a plain fill popover. */
.fill-popover__font {
    width: auto;
    flex: 1 1 auto;
    min-width: 0;
}

/* --- Fonts page: type specimens ------------------------------------------- */

.font-face-row {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.6rem 0;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    position: relative;
}

.font-face-row:first-of-type {
    border-top: 0;
}

.font-face-row__body {
    flex: 1 1 auto;
    min-width: 0;
}

.font-specimen {
    font-size: 20px;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--bs-body-color, #212529);
}

.font-face-row__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.5rem;
    margin-bottom: 0.15rem;
}

.font-face-row__meta .badge {
    font-weight: 500;
}

.font-face-row__actions {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.font-face-row .dragula-handle {
    margin-top: 0.6rem;
}

.font-wire {
    font-family: var(--bs-font-monospace, monospace);
    font-size: 0.75rem;
}

/* Admin colour allowlist chips (text popover): a swatch with a check mark
   when allowed, dimmed otherwise. */
.color-choice-swatch {
    width: 24px;
    height: 24px;
    padding: 0;
    border: 2px solid rgba(0, 0, 0, 0.15);
    border-radius: 50%;
    position: relative;
    opacity: 0.45;
    cursor: pointer;
}

.color-choice-swatch--on {
    opacity: 1;
    border-color: var(--bs-primary, #0d6efd);
    box-shadow: 0 0 0 2px #fff inset;
}

.color-choice-swatch--on::after {
    content: "\2713";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    color: #fff;
    text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
}

/* Fonts page: the 100–900 weight axis per family. */
.weight-axis {
    display: flex;
    gap: 3px;
}

.weight-axis__cell {
    flex: 1 1 0;
    height: 18px;
    border-radius: 3px;
    background: var(--bs-tertiary-bg, #f1f3f5);
    border: 1px solid var(--bs-border-color, #dee2e6);
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    color: var(--bs-secondary-color, #98a6ad);
    overflow: hidden;
}

.weight-axis__cell--upright {
    background: var(--bs-body-color, #343a40);
    border-color: var(--bs-body-color, #343a40);
    color: #fff;
}

.weight-axis__cell--italic::after {
    content: "";
    position: absolute;
    right: 3px;
    top: 3px;
    bottom: 3px;
    width: 3px;
    border-radius: 2px;
    background: var(--bs-primary, #727cf5);
    transform: skewX(-18deg);
}

.weight-axis__label {
    pointer-events: none;
}
