.canvas-wrapper {
    display: inline-block;
    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);
}

/* 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);
}

/* 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. */
.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;
}

.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;
}

/* 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. */
.container-zone {
    position: absolute;
    z-index: 899;
    pointer-events: none;
    border: 1.5px dashed #39afd1;
    border-radius: 3px;
    background: rgba(57, 175, 209, 0.04);
}

.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: #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: #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: #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: #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;
}

/* ---- 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;
}

/* 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. */
.fill-form.fill-js .fill-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.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 together with the dashed border (the highlight toggle). */
.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;
}

/* Visible with the highlight border, and always for an overflowing member. */
.fill-form.fill-js.fill-highlight-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) {
    .fill-body {
        flex-direction: column;
    }

    .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;
}

/* 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);
}

/* "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;
}

.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;
}

.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 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);
}

/* --- Template-group editor: variant rail + miniatures ------------------- */

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

.group-variant-card {
    border: 2px solid transparent;
    border-radius: 0.375rem;
    padding: 0.375rem;
    background: var(--bs-light, #f8f9fa);
}

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

.group-variant-card.group-variant-excluded {
    opacity: 0.45;
    filter: grayscale(0.8);
}

.group-variant-thumb {
    position: relative;
    display: flex;
    justify-content: center;
    cursor: pointer;
}

.group-variant-thumb canvas {
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 0.25rem;
    background: #fff;
    max-width: 160px;
}

.group-variant-dirty {
    position: absolute;
    top: -0.3rem;
    right: -0.3rem;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    background: var(--bs-danger, #fa5c7c);
    border: 1px solid #fff;
}

/* "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) */
.group-fill-preview-frame {
    position: relative;
    width: 100%;
    background: #f6f7fb;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 0.25rem 0.25rem 0 0;
    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;
}

.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;
}

.group-fill-image-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;
}

.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;
}
