/* Shared design system for every server-rendered page in this app —
   design tokens, the topbar/nav, and the handful of components (cards,
   buttons, tabs, tables, the edit-popup overlay) that were previously
   copy-pasted into every template's own <style> block. Page-specific
   layout (column widths, one-off colors, page-specific components like
   a particular table's column groups) stays in that page's own <style>,
   loaded after this stylesheet so it can still override where needed. */

:root {
    --bg: #f8fafc;
    --surface: #ffffff;
    --border: #e6e7eb;
    --text: #1a1a1a;
    --text-muted: #6b7280;
    --navy: #002f66;
    --navy-deep: #0f172a;
    --yellow: #fde047;
    --radius: 12px;
    --shadow: 0 1px 2px rgba(16,24,40,0.04), 0 1px 3px rgba(16,24,40,0.06);
    --sidebar-w: 190px;
}
* { box-sizing: border-box; }
body {
    font-family: Verdana, Geneva, sans-serif;
    margin: 0;
    background: var(--surface);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
}
/* Only a page that actually renders the sidebar (every page except the
   login screen) gets the matching gutter — :has() lets this stay a
   single shared rule instead of every page's own <style> needing it. */
body:has(.topbar) { padding-left: var(--sidebar-w); transition: padding-left .16s ease, padding-top .16s ease; }
/* Collapsed reclaims the sidebar's width entirely. */
body:has(.topbar.collapsed) { padding-left: 0; }
/* ---------- sidebar (was a horizontal topbar) ---------- */
.topbar {
    position: fixed; top: 0; left: 0; bottom: 0; width: var(--sidebar-w);
    background: var(--yellow); border-right: 1px solid rgba(0,47,102,0.15);
    display: flex; flex-direction: column; overflow-y: auto; overflow-x: hidden;
    padding: 0.6rem 0.5rem 0.8rem; z-index: 30;
    transition: width .16s ease, padding .16s ease;
}
/* Collapsed: leaves the layout entirely (width 0) so the page reclaims
   the space — the persistent .header-nav-toggle (below) stays on
   screen either way so it's obvious there's a menu to bring back. */
.topbar.collapsed { width: 0; padding: 0.6rem 0 0.8rem; border-right-color: transparent; }
.topbar.collapsed .brand-row, .topbar.collapsed nav { opacity: 0; pointer-events: none; }
.sidebar-collapse-btn {
    flex: none; width: 1.5rem; height: 1.5rem; border-radius: 6px; border: 1px solid rgba(0,47,102,0.25);
    background: transparent; color: var(--navy); cursor: pointer; font-size: 0.8rem; font-family: inherit;
    display: flex; align-items: center; justify-content: center; margin-left: auto;
}
.sidebar-collapse-btn:hover { background: rgba(0,47,102,0.08); }
/* Reopen control for a collapsed sidebar — icon + the current page's
   own name (see _header_pill.html, included as the first child of
   each page's own .month-nav — a real part of the fixed header now,
   not a separately-floating element, so it can never visually drift
   apart from it while scrolling or during the collapse animation).
   Doubles as both "what page is this" (now that the page's own <h1>
   is gone — its text became this button's label) and the sidebar
   toggle; hidden once the sidebar is already open (see
   body:has(.topbar:not(.collapsed)) below), since the sidebar's own
   collapse button covers that job then. */
.header-nav-toggle {
    display: flex; align-items: center; gap: 0.45rem;
    border: 1px solid rgba(0,47,102,0.3); border-radius: 999px;
    background: var(--yellow); color: var(--navy); cursor: pointer; font-family: inherit;
    font-size: 0.85rem; font-weight: 700; letter-spacing: -0.01em;
    padding: 0.35rem 0.9rem 0.35rem 0.35rem; box-shadow: var(--shadow);
}
/* Only needed to reopen a collapsed sidebar — once it's open, the
   sidebar's own collapse button (.sidebar-collapse-btn) already covers
   that job, so this pill would just be a redundant label. */
body:has(.topbar:not(.collapsed)) .header-nav-toggle { display: none; }
.header-nav-toggle .brand-logo { width: 1.6rem; height: 1.6rem; display: block; border-radius: 50%; flex: none; }
.header-nav-toggle span { white-space: nowrap; }
/* A handful of simple pages (Roles, WhatsApp Setup, ...) have no
   .month-nav to merge the pill into — _header_pill.html gives those
   this extra class instead, restoring the old floating-over-the-page
   behavior exactly as it worked before the merge, since there's
   nothing there to merge it into. */
.header-nav-toggle-floating {
    position: fixed; top: 0.6rem; left: calc(var(--sidebar-w) + 0.85rem); z-index: 32;
    transition: left .16s ease;
}
body:has(.topbar.collapsed) .header-nav-toggle-floating { left: 0.85rem; }
/* Breadcrumb (see _crumb.html/_crumb_render.html) — the plain-text "A →
   B" separator used inside .header-nav-toggle (a <button>, so a real
   nested <a> isn't valid there), plus the real clickable Bootstrap
   breadcrumb .crumb_render.html can render instead when linked=True is
   passed from somewhere that isn't a <button> — same split as
   stitch360's tracker/_nav.html. */
.crumb-sep { font-style: normal; opacity: 0.55; margin: 0 0.2rem; }
.crumb-nav { display: inline-flex; min-width: 0; max-width: 100%; overflow: hidden; }
.crumb-nav .breadcrumb {
    --bs-breadcrumb-divider: "\2192"; display: flex; flex-wrap: nowrap; align-items: center;
    margin: 0; padding: 0; min-width: 0; overflow: hidden;
    font-size: inherit; font-weight: inherit; font-family: inherit;
}
.crumb-nav .breadcrumb-item { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crumb-nav .breadcrumb-item::before { color: var(--navy); opacity: 0.55; padding-right: 0.4rem; }
.crumb-nav .breadcrumb-item a { color: var(--navy); text-decoration: none; }
.crumb-nav .breadcrumb-item a:hover { text-decoration: underline; }
.crumb-nav .breadcrumb-item.active { color: var(--navy); overflow: hidden; text-overflow: ellipsis; }
/* .page is the scroll container now, not body — the header (.month-nav,
   see below) is truly position: fixed, outside .page entirely, so
   nothing reserves its space in normal flow any more. margin-top pushes
   .page's content below it; height + overflow bound .page to exactly
   the remaining viewport so IT scrolls, not body. --header-h is the
   fixed header's real rendered height, measured at runtime (see
   _htmx_scripts.html) since it varies page to page (one row vs two,
   wrapped content on a narrow screen, a messages banner appearing).
   body .page (element + class) is specific enough to win over every
   page's own local "body .page { padding: ...; width: ...; }" shorthand
   regardless of source order — same trick used elsewhere in this file —
   though here there's nothing to actually fight: no page sets these
   particular properties locally, so this is purely additive. */
body .page {
    margin-top: var(--header-h, 0px);
    height: calc(100vh - var(--header-h, 0px));
    overflow: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
    box-sizing: border-box;
}
.brand-row { display: flex; align-items: center; gap: 0.3rem; padding: 0.15rem 0.15rem 0.6rem 0.3rem; }
.topbar .brand {
    display: flex; align-items: center; gap: 0.4rem; font-weight: 700; font-size: 0.86rem;
    letter-spacing: -0.01em; color: var(--navy); text-decoration: none; min-width: 0;
}
.topbar .brand .brand-logo { width: 20px; height: 20px; display: block; border-radius: 4px; flex: none; }
.topbar nav { display: flex; flex-direction: column; gap: 0.05rem; flex: 1 1 auto; }
.topbar nav .nav-spacer { flex: 1 1 auto; }
.topbar nav form { margin-top: auto; padding-top: 0.5rem; border-top: 1px solid rgba(0,47,102,0.15); }
.topbar nav a {
    color: var(--navy); text-decoration: none; font-size: 0.72rem; font-weight: 600;
    padding: 0.32rem 0.45rem; border-radius: 6px; transition: background .15s ease, color .15s ease;
}
.topbar nav a:hover, .topbar nav a.current { background: var(--navy); color: var(--yellow); }
.topbar nav button {
    display: block; width: 100%; text-align: left;
    color: var(--navy); background: none; border: none; cursor: pointer;
    font-size: 0.72rem; font-weight: 600; font-family: inherit;
    padding: 0.32rem 0.45rem; border-radius: 6px; transition: background .15s ease, color .15s ease;
}
.topbar nav button:hover { background: var(--navy); color: var(--yellow); }
/* Sidebar section groups (Attendance/Payroll/Cash/Piece Rate) — a real
   Bootstrap accordion (see _topbar.html: data-bs-toggle/data-bs-parent),
   same component and reskin approach as stitch360's tracker/_nav.html:
   transparent background and navy/yellow brand colors instead of
   Bootstrap's default white-card accordion look, sized to sit flush in
   the narrow yellow sidebar. */
.topbar nav .accordion {
    --bs-accordion-bg: transparent; --bs-accordion-border-width: 0; --bs-accordion-border-radius: 6px;
    --bs-accordion-inner-border-radius: 6px;
    --bs-accordion-btn-padding-x: 0.45rem; --bs-accordion-btn-padding-y: 0.32rem;
    --bs-accordion-body-padding-x: 0; --bs-accordion-body-padding-y: 0.15rem;
    --bs-accordion-btn-color: var(--navy); --bs-accordion-active-color: var(--yellow);
    --bs-accordion-active-bg: var(--navy);
    --bs-accordion-btn-focus-box-shadow: 0 0 0 0.2rem rgba(0,47,102,0.25);
    --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23002f66'%3e%3cpath fill-rule='evenodd' d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
    --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fde047'%3e%3cpath fill-rule='evenodd' d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}
.topbar nav .accordion-item + .accordion-item { margin-top: 0.18rem; }
.topbar nav .accordion-button {
    font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
    box-shadow: none !important;
}
.topbar nav .accordion-button.current:not(:hover) { background: var(--navy); color: var(--yellow); }
.topbar nav .accordion-button::after { width: 0.7rem; height: 0.7rem; background-size: 0.7rem; }
.topbar nav .accordion-body { display: flex; flex-direction: column; gap: 0.02rem; }
.topbar nav .accordion-body a { display: block; padding: 0.32rem 0.45rem 0.32rem 0.85rem; border-radius: 6px; font-size: 0.72rem; font-weight: 600; color: var(--navy); text-decoration: none; line-height: 1.2; }
.topbar nav .accordion-body a:hover { background: var(--navy); color: var(--yellow); }
.topbar nav .accordion-body a.current { background: var(--navy); color: var(--yellow); }
.topbar nav .accordion-body .nav-dropdown-divider { height: 1px; background: rgba(0,47,102,0.35); margin: 0.4rem 0.4rem; }
.topbar nav .accordion-body .nav-dropdown-group-alt {
    background: rgba(0,47,102,0.16); border-radius: 6px; padding: 0.2rem;
}

h1 { font-size: 1.35rem; margin: 0; letter-spacing: -0.01em; color: var(--navy); }
.page-header-row { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1.25rem; }

@media (max-width: 700px) {
    /* The desktop collapse-to-nothing state doesn't apply here — the
       drawer open/close (via transform, below) is the only concept of
       "hidden" at this width. Without this reset, a .collapsed class
       carried over from a desktop-width visit in the same browser (the
       cookie persists across both) would fight the open drawer's
       transform with a stale width:0/no-padding, leaving it open but
       zero-width. */
    .topbar.collapsed { width: var(--sidebar-w); padding: 0.6rem 0.5rem 0.8rem; border-right-color: rgba(0,47,102,0.15); }
    .topbar.collapsed .brand-row, .topbar.collapsed nav { opacity: 1; pointer-events: auto; }
    .topbar { transform: translateX(-100%); transition: transform .16s ease; }
    .topbar.open { transform: translateX(0); box-shadow: 0 0 0 1px rgba(0,0,0,0.05), 8px 0 24px rgba(0,0,0,0.18); }
    body:has(.topbar) { padding-left: 0; }
    /* .month-nav spans edge-to-edge at this width (no sidebar gutter to
       track — the drawer overlays instead) — body:has(.topbar) (element
       + class) is specific enough to beat the plain .month-nav rule
       below it in the file regardless of source order, same trick used
       elsewhere in this file. Left padding clears the hamburger toggle
       (below), which still floats on its own here rather than merging
       into the header the way the desktop pill does — there's little
       room for a labeled pill at this width, and the drawer itself
       already shows context once opened. */
    body:has(.topbar) .month-nav { left: 0; right: 0; padding-left: 2.6rem; }
    /* The hamburger toggle is the only show/hide control at this width
       — the desktop header-nav-toggle pill and in-sidebar collapse
       button would be redundant (tapping outside the open drawer
       already closes it). */
    .header-nav-toggle { display: none !important; }
    .sidebar-collapse-btn { display: none; }
    .sidebar-mobile-toggle {
        position: fixed; top: 0.6rem; left: 0.6rem; z-index: 32; width: 2.2rem; height: 2.2rem;
        border-radius: 8px; border: 1px solid var(--navy); background: var(--yellow); color: var(--navy);
        font-weight: 800; font-size: 1rem; display: flex; align-items: center; justify-content: center;
    }
    /* Hidden once the drawer is open — tapping outside it (or a nav
       link) already closes it, so a hamburger floating on top of the
       open drawer would be redundant, same reasoning as the desktop
       header-nav-toggle pill above. */
    body:has(.topbar.open) .sidebar-mobile-toggle { display: none; }
}
@media (min-width: 701px) { .sidebar-mobile-toggle { display: none; } }

.msg { padding: 0.6rem 0.9rem; border-radius: 8px; margin-bottom: 0.9rem; font-size: 0.85rem; }
.msg.success { background: #e6f4ea; color: #1e7e34; }
.msg.error { background: #fdecea; color: #b3261e; }
.msg.info { background: #e8f0fe; color: var(--navy); }

/* display:block explicitly — Bootstrap has its own .card component
   (display:flex; flex-direction:column) that this app's plain,
   pre-existing .card class name now collides with now that Bootstrap's
   CSS loads on every page; every other Bootstrap .card property this
   already overrides below (background/border/radius/shadow/padding),
   but display wasn't previously a concern. */
.card { display: block; background: none; border: none; border-radius: 0; box-shadow: none; padding: 0; margin-bottom: 1.5rem; }

.tab-row { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
/* Both states built from the logo's own navy + yellow, not a neutral
   gray — inactive/rest is navy text on a pale yellow tint, active
   flips to the solid navy-bg/yellow-text look used everywhere else
   for a selected state (.accordion-button.current, nav-pills, etc). */
.tab-btn {
    padding: 0.5rem 1rem; border-radius: 999px; font-size: 0.85rem; font-weight: 600;
    text-decoration: none; color: var(--navy); background: rgba(253,224,71,0.18);
    border: 1px solid rgba(0,47,102,0.15); cursor: pointer; font-family: inherit;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.tab-btn:hover { background: rgba(253,224,71,0.4); }
.tab-btn.active { background: var(--navy); color: var(--yellow); border-color: var(--navy); }
.tab-btn:disabled { opacity: 0.6; cursor: default; }

.action-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 1rem; }
.post-btn {
    background: var(--navy); color: var(--yellow); font-weight: 700; border: none;
    padding: 0.55rem 1.1rem; border-radius: 8px; cursor: pointer; font-size: 0.85rem; text-decoration: none;
    display: inline-block;
}
.post-btn:hover { background: var(--navy-deep); }
.search-input {
    padding: 0.5rem 0.8rem; border: 1px solid var(--border); border-radius: 8px;
    font-size: 0.85rem; font-family: inherit; min-width: 300px;
}
.search-input:focus { outline: none; border-color: var(--navy); }

/* Floating labels (Google Forms' text-question look — label alone at
   rest, floats to the top-left on focus/fill) are Bootstrap's own
   built-in .form-floating component (wrap a .form-control/.form-select
   + <label>, input before label, any placeholder text to trigger
   :placeholder-shown — see the pages that use it); no custom CSS
   needed here, just the navy-tinted focus label in bootstrap-theme.css. */

.icon-btn {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    width: 2.1rem; height: 2.1rem; padding: 0; border-radius: 50%;
    color: var(--text-muted); background: var(--bg); border: 1px solid var(--border);
    cursor: pointer; text-decoration: none; font-family: inherit;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.icon-btn:hover { background: var(--navy); color: var(--yellow); border-color: var(--navy); }
.icon-btn:disabled { opacity: 0.6; cursor: default; }
/* Persistent "currently open" state for a toggleable icon-btn (e.g. a
   Legend panel toggle) — same solid navy/yellow fill as :hover, but
   sticks while the thing it opened stays open instead of only showing
   on mouseover. */
.icon-btn.active { background: var(--navy); color: var(--yellow); border-color: var(--navy); }
.icon-btn svg { display: block; }
.icon-btn[data-tooltip]:hover::after {
    content: attr(data-tooltip);
    position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
    margin-bottom: 8px; background: var(--navy-deep); color: #fff;
    font-size: 0.75rem; font-weight: 600; white-space: nowrap;
    padding: 0.35rem 0.65rem; border-radius: 6px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.25); z-index: 50; pointer-events: none;
}

.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 10px; }
/* Tables expand to their full height. The page is the single vertical
   scroll area across the app; wrappers do not create nested scroll traps.
   Wide grids use the page's horizontal scroll on smaller screens. */
body .page .table-wrap,
body .page .table-responsive,
body .page .grid-scroll,
body .page .log-table-wrap {
    max-height: none;
    overflow: visible;
    overscroll-behavior: auto;
}
/* Summary grids use the page scroll too; let them grow to their full height. */
.page .table-wrap.summary-table-wrap {
    flex: 0 0 auto;
    min-height: 0;
    max-height: none;
}
body .page .table-wrap thead th,
body .page .table-responsive thead th,
body .page .grid-scroll thead th,
body .page .log-table-wrap thead th,
body .page .table-wrap table tr:first-child th,
body .page .table-responsive table tr:first-child th,
body .page .grid-scroll table tr:first-child th,
body .page .log-table-wrap table tr:first-child th {
    position: sticky;
    top: 0;
    z-index: 15;
}
body .page .table-wrap thead tr:nth-child(2) th,
body .page .table-responsive thead tr:nth-child(2) th,
body .page .grid-scroll thead tr:nth-child(2) th,
body .page .log-table-wrap thead tr:nth-child(2) th {
    top: var(--thead-row1-h, 0px);
    z-index: 16;
}
/* Header cells that also pin to the left need to sit above both the
   ordinary header cells and the sticky body columns at their crossing. */
body .page .table-wrap thead th[class*="col-"],
body .page .table-responsive thead th[class*="col-"],
body .page .grid-scroll thead th.label { z-index: 18; }
/* border-separate, not collapse: border-collapse + position:sticky thead
   is a known browser rendering bug — a scrolled-past row can stay
   painted (a "ghost") just above the stuck header instead of being
   cleanly clipped away, especially visible on a table with its own
   colored group rows. Found and fixed individually on several pages
   before landing here as the shared default; border-spacing:0 keeps
   cell spacing looking the same as collapse did. */
table.simple { border-collapse: separate; border-spacing: 0; width: 100%; }
/* A single shared table treatment across Attendance and Piece Rate pages.
   The page qualifier also keeps these defaults consistent with older
   page-local rules while leaving semantic cell classes free to add color. */
.page table.simple th, .page table.simple td {
    padding: 0.5rem 0.65rem;
    font-size: 0.84rem;
    line-height: 1.35;
    text-align: left;
    vertical-align: middle;
    border: 0;
    border-right: 1px solid #6b7280;
    border-bottom: 1px solid #6b7280;
    white-space: nowrap;
}
.page table.simple tr > :first-child { border-left: 1px solid #6b7280; }
.page table.simple thead tr:first-child > * { border-top: 1px solid #6b7280; }
body .page table.simple th,
body .page table.month th,
body .page table.month th[class] { border-color: #6b7280; }
.page table.simple th {
    background: #fff4bf;
    color: var(--navy-deep);
    font-weight: 700;
    font-size: 0.76rem;
    text-transform: uppercase;
    letter-spacing: 0.035em;
}
/* Let long headings use multiple lines instead of forcing wide columns.
   This covers both list grids (.simple) and month/day data grids. */
body .page table th,
body .page table.simple th,
body .page table.month th,
body .page table.month th[class] { white-space: normal; overflow-wrap: anywhere; }
.page table.simple tbody tr:nth-child(even) { background-color: #f8fafc; }
.page table.simple tbody tr:hover td:not([class]) { background-color: #eef4fb; }
.page table.simple td.num, .page table.simple th.num { text-align: right; }
/* Summary tables contain many cells with their own semantic fills
   (attendance flags, rate warnings, hierarchy levels). Tint every cell on
   hover without replacing those underlying colors. */
body .page #operatorSummaryGrid tbody tr:hover > td,
body .page #managementSummaryGrid tbody tr:hover > td,
body .page #styleSummaryGrid tbody tr:hover > td {
    box-shadow: inset 0 0 0 9999px rgba(59, 130, 246, 0.2) !important;
}

.style-image-icon, .style-image-icon-placeholder {
    display: inline-flex;
    width: 24px;
    height: 24px;
    flex: none;
    align-items: center;
    justify-content: center;
    vertical-align: middle;
    margin-right: 0.35rem;
    border-radius: 5px;
}
.style-image-icon { object-fit: cover; border: 1px solid var(--border); }
.style-image-icon-placeholder { background: #eef1f5; font-size: 0.9rem; }

/* Hover previews for small style/template thumbnails. The fixed overlay
   stays visible outside scrollable table containers. */
.image-hover-zoom {
    position: fixed;
    z-index: 10000;
    display: none;
    width: min(320px, calc(100vw - 24px));
    height: min(320px, calc(100vh - 24px));
    padding: 6px;
    object-fit: contain;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.28);
    pointer-events: none;
}
.image-hover-zoom.is-visible { display: block; }

/* Fixed page header bar + shared table header positioning.
   .month-nav is a sibling placed immediately BEFORE .page in the
   markup (not nested inside it — see e.g. style_list.html), spanning
   the content area beside the sidebar; left tracks the sidebar's own
   width/collapse state exactly like body's own padding-left does.
   table headers stick to the page's single scroll area. Page-specific
   styles continue to own each table's colors and spacing. */
.month-nav { position: fixed; top: 0; left: var(--sidebar-w); right: 0; z-index: 20; transition: left .16s ease; }
body:has(.topbar.collapsed) .month-nav { left: 0; }

.empty-state { color: var(--text-muted); font-size: 0.9rem; padding: 1rem 0; }

/* Shared edit-popup overlay mechanics — each page's own <style> still
   defines .edit-modal itself (size varies a lot: a 3-field balance
   editor vs. a full scrollable employee form), just not this part. */
.edit-overlay {
    position: fixed; inset: 0; background: rgba(17,17,20,0.45); backdrop-filter: blur(2px);
    display: none; align-items: center; justify-content: center; z-index: 1000;
}
.edit-overlay.open { display: flex; }
