        .project-location-map {
            position: relative;
            overflow: hidden;
            background: #eef2f1;
            isolation: isolate;
            cursor: crosshair;
        }
        .project-location-map .leaflet-pane,
        .project-location-map .leaflet-control { z-index: 1; }
        .project-location-map .leaflet-control-attribution { font-size: 8px; }
        .project-image-placeholder {
            background-color: #e8f5e9;
            background-image: linear-gradient(135deg, rgba(46,125,50,.10), rgba(46,125,50,.03)), url('/assets/map-placeholder.svg');
            background-size: cover;
            background-position: center;
        }
        .vl-landing-upload-trigger {
            position: relative;
            isolation: isolate;
            background: #fff;
            color: var(--color-primary);
            border: 1px solid rgba(245, 124, 0, .18);
            box-shadow: 0 8px 24px rgba(15, 23, 42, .09);
        }
        .vl-editor-actions { grid-column: 1 / -1; }
        .vl-editor-action-card {
            min-height: 132px;
            background: rgba(255,255,255,.94);
            border: 1px solid rgba(148,163,184,.18);
            box-shadow: 0 8px 30px rgba(15,23,42,.07);
        }
        .vl-editor-action-card:hover {
            border-color: rgba(245,124,0,.34);
            box-shadow: 0 12px 38px rgba(15,23,42,.11);
        }
        .vl-upload-ring {
            --vl-upload-progress: 0%;
            position: relative;
            width: 50px;
            height: 50px;
            border-radius: 999px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            flex: none;
            background: rgba(245,124,0,.09);
        }
        .vl-upload-ring::before {
            content: '';
            position: absolute;
            inset: -4px;
            border-radius: inherit;
            background: conic-gradient(#f57c00 var(--vl-upload-progress), rgba(245,124,0,.14) 0);
            opacity: 0;
            transition: opacity .2s ease, background .2s ease;
            -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 3px));
            mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 3px));
        }
        .vl-landing-upload-trigger.is-uploading .vl-upload-ring::before { opacity: 1; }
        .vl-viewer-upload-ring { background: transparent; }
        .vl-landing-upload-trigger.is-uploading .vl-viewer-upload-ring { background: rgba(245,124,0,.09); }
        .vl-landing-upload-trigger.is-uploading { cursor: wait; }
        .vl-landing-upload-trigger.is-uploading .vl-upload-main-icon { animation: gallery-upload-pulse 1.1s ease-in-out infinite; }
        .vl-landing-upload-progress-track {
            height: 4px;
            border-radius: 999px;
            overflow: hidden;
            background: rgba(245,124,0,.12);
        }
        .vl-landing-upload-progress-fill {
            width: 0;
            height: 100%;
            border-radius: inherit;
            background: linear-gradient(90deg, #f59e0b, #f97316);
            transition: width .25s ease;
        }
        .image-source-popover {
            --image-source-arrow-x: 50%;
            z-index: var(--layer-context-menu);
            width: min(320px, calc(100vw - 16px));
            padding: 10px;
            overflow: visible;
            color: #334155;
            background: rgba(255,255,255,.98);
            border: 1px solid rgba(148,163,184,.22);
            border-radius: 22px;
            box-shadow: 0 22px 60px -18px rgba(15,23,42,.32), 0 8px 22px -12px rgba(15,23,42,.18);
            animation: image-source-popover-in .16s ease-out both;
        }
        #gallery-style-menu,
        #vl-project-actions-menu { z-index: var(--layer-context-menu); }
        .image-source-popover::before,
        .image-source-popover::after {
            content: '';
            position: absolute;
            left: var(--image-source-arrow-x);
            width: 14px;
            height: 14px;
            transform: translateX(-50%) rotate(45deg);
        }
        .image-source-popover::before {
            z-index: -1;
            background: rgba(148,163,184,.28);
        }
        .image-source-popover::after { background: #fff; }
        .image-source-popover[data-placement="below"]::before,
        .image-source-popover[data-placement="below"]::after { top: -7px; }
        .image-source-popover[data-placement="above"]::before,
        .image-source-popover[data-placement="above"]::after { bottom: -7px; }
        .image-source-option-copy strong { display: block; font-size: 12px; font-weight: 900; }
        .image-source-option-copy small { display: block; margin-top: 2px; font-size: 9px; color: #94a3b8; line-height: 1.55; }
        .image-source-options { display: grid; gap: 5px; }
        .image-source-option {
            width: 100%;
            min-height: 58px;
            display: flex;
            align-items: center;
            gap: 11px;
            padding: 8px 9px;
            border: 1px solid transparent;
            border-radius: 15px;
            text-align: right;
            transition: background .16s ease, border-color .16s ease, transform .16s ease;
        }
        .image-source-option:hover,
        .image-source-option:focus-visible {
            outline: none;
            background: rgba(245,124,0,.07);
            border-color: rgba(245,124,0,.2);
            transform: translateX(-2px);
        }
        .image-source-option-icon {
            width: 38px;
            height: 38px;
            border-radius: 13px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            flex: none;
            color: var(--color-primary);
            background: var(--color-surface-container);
            font-size: 20px;
        }
        .image-source-option-copy { flex: 1; min-width: 0; }
        .image-source-chevron { flex: none; color: #cbd5e1; font-size: 18px; }
        @keyframes image-source-popover-in {
            from { opacity: 0; transform: translateY(-4px) scale(.98); }
            to { opacity: 1; transform: translateY(0) scale(1); }
        }
        .image-source-popover[data-placement="above"] { transform-origin: bottom center; }
        .image-source-popover[data-placement="below"] { transform-origin: top center; }
        @media (max-width: 420px) {
            .image-source-popover { border-radius: 19px; }
            .image-source-option { min-height: 54px; }
        }
        @keyframes gallery-upload-pulse {
            0%, 100% { transform: scale(1); opacity: 1; }
            50% { transform: scale(.88); opacity: .65; }
        }
        /* Drag-and-drop of local images/folders onto a landing project card. */
        .vl-project-card.vl-card-dragover {
            border-color: var(--color-primary);
            box-shadow: 0 0 0 3px rgba(46,125,50,.25), 0 18px 40px -12px rgba(46,125,50,.45);
            transform: translateY(-2px);
        }
        .vl-project-card.vl-card-dragover::after {
            content: "";
            position: absolute;
            inset: 6px;
            border: 2px dashed rgba(46,125,50,.55);
            border-radius: 1.25rem;
            pointer-events: none;
            z-index: 5;
        }
        .vl-card-drop-overlay {
            position: absolute;
            inset: 0;
            z-index: 20;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 1rem;
            border-radius: inherit;
            /* Keep the project cover recognizable while its name is hidden by the
               upload state. The controls remain legible without washing the card out. */
            background: rgba(253,253,253,.68);
            backdrop-filter: blur(.75px);
            -webkit-backdrop-filter: blur(.75px);
        }
        .vl-card-drop-inner { width: 100%; max-width: 220px; text-align: center; }
        .vl-card-drop-ring {
            width: 54px; height: 54px; margin: 0 auto .6rem;
            border-radius: 999px;
            display: flex; align-items: center; justify-content: center;
            background: conic-gradient(var(--color-primary) calc(var(--vl-card-progress,0) * 1%), rgba(46,125,50,.12) 0);
            transition: background .25s ease;
        }
        .vl-card-drop-ring > span {
            width: 42px; height: 42px; border-radius: 999px; background: var(--color-surface);
            display: flex; align-items: center; justify-content: center;
            font-size: 12px; font-weight: 900; color: var(--color-primary);
        }
        .vl-card-drop-status { font-size: 11px; font-weight: 700; color: #4b5563; margin-bottom: .1rem; }
        .vl-card-drop-sub { font-size: 10px; color: #9ca3af; margin-bottom: .7rem; }
        .vl-card-drop-cancel {
            display: inline-flex; align-items: center; gap: .25rem;
            font-size: 11px; font-weight: 800; color: #dc2626;
            padding: .3rem .9rem; border-radius: 999px;
            background: rgba(220,38,38,.08);
        }
        .vl-card-drop-cancel:hover { background: rgba(220,38,38,.16); }
        .vl-card-drop-cancel:disabled { opacity: .5; cursor: default; }
        :root {
            color-scheme: only light;
            --color-primary: #2E7D32;
            --color-primary-container: #C8E6C9;
            --color-on-primary: #ffffff;
            --color-surface: #fdfdfd;
            --color-surface-container: #f0f4f1;
            --bg-gradient: linear-gradient(135deg, #fdfdfd 0%, #e8f5e9 100%);
            --layer-content: 1;
            --layer-sticky: 40;
            --layer-page-scrim: 80;
            --layer-header-active: 120;
            --layer-dropdown-wrapper: 130;
            --layer-dropdown: 140;
            --layer-context-menu: 220;
            --layer-modal: 1000;
        }

        /* The application is intentionally light-only. Native select popovers can
           otherwise inherit macOS/browser dark mode even when the control itself is
           rendered on a light surface. This also covers selects inserted by JS. */
        select,
        select option,
        select optgroup {
            color-scheme: only light !important;
            background-color: var(--color-surface);
            color: #1f2937;
        }

        .theme-summer { --color-primary: #F57C00; --color-primary-container: #FFE0B2; --color-surface-container: #fff3e0; --bg-gradient: linear-gradient(135deg, #fdfdfd 0%, #fff3e0 100%); }
        .theme-autumn { --color-primary: #D84315; --color-primary-container: #FFCCBC; --color-surface-container: #fbe9e7; --bg-gradient: linear-gradient(135deg, #fdfdfd 0%, #fbe9e7 100%); }
        .theme-winter { --color-primary: #1565C0; --color-primary-container: #BBDEFB; --color-surface-container: #e3f2fd; --bg-gradient: linear-gradient(135deg, #fdfdfd 0%, #e3f2fd 100%); }

        body { background: var(--bg-gradient); background-attachment: fixed; transition: background 0.8s ease, color 0.3s; }
        .transition-expressive { transition-property: color, background-color, border-color, box-shadow, opacity, transform, filter, width; transition-duration: 0.4s; transition-timing-function: cubic-bezier(0.2, 0, 0, 1); }
        .material-symbols-rounded { font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; font-size: 22px; }
        .no-scrollbar::-webkit-scrollbar { display: none; }
        .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
        .thin-scrollbar { scrollbar-width: thin; scrollbar-color: #cbd5e1 transparent; }
        .thin-scrollbar::-webkit-scrollbar { width: 4px; }
        .thin-scrollbar::-webkit-scrollbar-track { background: transparent; }
        .thin-scrollbar::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 999px; }
        .view-section { display: none; opacity: 0; transition: opacity 0.3s ease; }
        .view-section.active { display: flex; opacity: 1; }
        .project-edge-nav {
            position: fixed;
            top: 0;
            bottom: 0;
            width: clamp(56px, 7vw, 128px);
            z-index: 8;
            display: none;
            align-items: center;
            justify-content: center;
            pointer-events: none;
            transition: background 0.28s ease;
        }
        #dashboard-view.active ~ .project-edge-nav { display: flex; pointer-events: auto; }
        .project-edge-nav.left { left: 0; }
        .project-edge-nav.right { right: 0; }
        .project-edge-nav.left:hover { background: linear-gradient(90deg, rgba(46, 125, 50, 0.14), rgba(46, 125, 50, 0)); }
        .project-edge-nav.right:hover { background: linear-gradient(270deg, rgba(46, 125, 50, 0.14), rgba(46, 125, 50, 0)); }
        .project-edge-button {
            position: absolute;
            top: 33.333%;
            height: 33.333%;
            min-height: 128px;
            width: 54px;
            border: 1px solid rgba(46, 125, 50, 0.22);
            background: rgba(255, 255, 255, 0.82);
            color: var(--color-primary);
            box-shadow: 0 12px 30px -16px rgba(15, 23, 42, 0.35);
            opacity: 0;
            transform: translateX(0) scale(0.96);
            transition: all 0.24s ease;
            backdrop-filter: blur(14px);
        }
        .project-edge-button:hover,
        .project-edge-button:focus-visible {
            opacity: 1;
            transform: translateX(0) scale(1);
        }
        .project-edge-nav.left .project-edge-button { border-radius: 0 999px 999px 0; left: 0; }
        .project-edge-nav.right .project-edge-button { border-radius: 999px 0 0 999px; right: 0; }
        .project-edge-button:disabled {
            opacity: 0;
            pointer-events: none;
        }
        @media (max-width: 1535px) {
            .project-edge-nav { display: none !important; }
        }
        
        .glass-panel { background: rgba(255, 255, 255, 0.7); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.5); transition: background 0.3s; }

        /* Keep the calendar visually consistent with the other neutral dashboard
           modules. A previous orange override made it look selected at all times. */
        #module-calendar {
            background: var(--color-surface);
            border: 1px solid rgba(0, 0, 0, 0.08) !important;
        }

        .calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; direction: rtl; }
        .calendar-day { aspect-ratio: 1.22; display: flex; align-items: center; justify-content: center; border-radius: 50%; cursor: pointer; transition: all 0.2s; font-size: 0.875rem; position: relative; user-select: none; }
        .calendar-day:hover { background: var(--color-primary-container); color: var(--color-primary); opacity: 1 !important; }
        .calendar-day.active { background: var(--color-primary); color: var(--color-on-primary) !important; font-weight: bold; box-shadow: 0 4px 12px rgba(0,0,0,0.15); opacity: 1 !important; }
        .calendar-day.has-report::after { content: ''; position: absolute; bottom: 4px; width: 4px; height: 4px; border-radius: 50%; background-color: var(--color-primary); }
        
        .calendar-day.today-marker { background-color: var(--color-primary-container); color: var(--color-primary) !important; font-weight: 900; border-radius: 12px; border: 1px solid var(--color-primary); opacity: 1 !important; }
        .calendar-day:hover .gregorian-date { opacity: 0.8; color: inherit; }
        .calendar-day.weekly-off { color: #e11d48 !important; opacity: .72 !important; }
        .calendar-day.official-holiday {
            color: #be123c !important;
            background: #fff1f2;
            border: 1px solid #fecdd3;
            border-radius: 12px;
            font-weight: 900;
            opacity: 1 !important;
        }
        .calendar-day.today-marker.official-holiday { border-color: #fb7185; box-shadow: inset 0 0 0 1px #fff, 0 3px 9px rgba(225,29,72,.14); }
        #calendar-holiday-legend { max-height: 132px; overflow-y: auto; }
        .calendar-holiday-heading { display: flex; align-items: center; gap: 6px; color: #be123c; }
        .calendar-holiday-heading .material-symbols-rounded { font-size: 16px; }
        .calendar-holiday-heading strong { font-size: 11px; }
        .calendar-holiday-heading > span:last-child { margin-right: auto; color: #9ca3af; font-size: 9px; }
        #calendar-holiday-legend ul { display: grid; gap: 5px; margin-top: 8px; }
        #calendar-holiday-legend li { display: flex; align-items: flex-start; gap: 7px; font-size: 10px; color: #6b7280; line-height: 1.55; }
        #calendar-holiday-legend li b { min-width: 23px; border-radius: 7px; background: #fff1f2; color: #be123c; text-align: center; }
        .calendar-holiday-empty { display: flex; align-items: center; gap: 6px; color: #9ca3af; font-size: 10px; }
        .calendar-holiday-empty .material-symbols-rounded { font-size: 16px; }

        /* Project-list controls collapse into one tidy toolbar on phones. Search
           grows in-place on focus while sort and export remain icon buttons. */
        @media (max-width: 767.98px) {
            #vl-project-toolbar,
            #vl-material-toolbar,
            #vl-service-toolbar,
            #vl-report-toolbar {
                flex-direction: row;
                align-items: center;
                gap: 0.625rem;
            }
            #vl-sort-container,
            #vl-project-export-container,
            #vl-material-sort-container,
            #vl-material-export-container,
            #vl-service-sort-container,
            #vl-service-export-container,
            #vl-report-sort-container,
            #vl-report-export-container {
                flex: 0 0 3.5rem;
            }
            #vl-sort-container > button,
            #vl-project-export-container > button,
            #vl-material-sort-container > button,
            #vl-material-export-container > button,
            #vl-service-sort-container > button,
            #vl-service-export-container > button,
            #vl-report-sort-container > button,
            #vl-report-export-container > button {
                width: 3.5rem;
                padding-inline: 0;
                justify-content: center;
            }
            #vl-sort-label,
            #vl-material-sort-label,
            #vl-service-sort-label,
            #vl-report-sort-label,
            #vl-report-export-label { display: none; }
            #vl-project-search-wrap,
            #vl-material-search-wrap,
            #vl-service-search-wrap,
            #vl-report-search-wrap {
                flex: 1 1 auto;
                min-width: 3.5rem;
            }
            #vl-project-search,
            #vl-material-search,
            #vl-service-search,
            #vl-report-search {
                padding-right: 3.5rem;
                padding-left: 1rem;
                color: inherit;
                caret-color: var(--color-primary);
                cursor: text;
            }
            #vl-project-search-wrap > .material-symbols-rounded,
            #vl-material-search-wrap > .material-symbols-rounded,
            #vl-service-search-wrap > .material-symbols-rounded,
            #vl-report-search-wrap > .material-symbols-rounded {
                right: 1rem;
            }
            #vl-calendar-toggle-btn { display: none !important; }
            #vl-service-year-container { flex: 0 0 auto; }
            #vl-service-year-container > button {
                padding-inline: 0.75rem;
                font-size: 0.75rem;
                gap: 0.25rem;
            }
        }

        /* The "analytics" lead card in the landing grids gets a tonal (primary-
           tinted) surface so it reads as a summary, not another data card —
           Material-3 "filled tonal" look, same radius/shadow as its siblings. */
        .vl-analytics-card {
            background: linear-gradient(150deg,
                color-mix(in srgb, var(--color-primary) 10%, var(--color-surface)) 0%,
                color-mix(in srgb, var(--color-primary) 4%, var(--color-surface)) 55%,
                var(--color-surface) 100%);
            border: 1.5px solid color-mix(in srgb, var(--color-primary) 28%, transparent);
        }
        .vl-analytics-card:hover {
            border-color: color-mix(in srgb, var(--color-primary) 55%, transparent);
        }

        /* ---------- Viewer lockdown ----------
           body.role-readonly is set while a viewer-tier user has a project open (see
           applyRoleUIRestrictions). Every affordance that would create/edit/delete
           project DATA is hidden; layout-only interactions (module drag/resize,
           expanding, exports) and the private-comments button stay available. The
           server remains the real enforcement boundary — this is UX, not security. */
        body.role-readonly #save-btn,
        body.role-readonly #save-status-text,
        body.role-readonly [onclick*="openGlobalSettings"],
        body.role-readonly [onclick*="openEditInfoModal"],
        body.role-readonly [onclick*="openActivityModal"],
        body.role-readonly [onclick*="deleteActivity"],
        body.role-readonly [onclick*="openWBSSettingsModal"],
        body.role-readonly .act-handle,
        body.role-readonly [onclick*="addKanbanTask"],
        body.role-readonly [onclick*="submitKanbanPersonAdd"],
        body.role-readonly [onclick*="renameKanbanPerson"],
        body.role-readonly [onclick*="openKanbanAssignModal"],
        body.role-readonly #kanban-people-add-row,
        body.role-readonly [onclick*="editKanbanTask"],
        body.role-readonly [onclick*="deleteKanbanTask"],
        body.role-readonly [onclick*="removeKanbanPerson"],
        body.role-readonly [onclick*="unassignKanbanPerson"],
        body.role-readonly [onclick*="toggleKanbanPin"],
        body.role-readonly [onclick*="openImageSourceMenu"],
        body.role-readonly [onclick*="deleteGalleryItem"],
        body.role-readonly [onclick*="openGalleryTagEditor"],
        body.role-readonly [onclick*="bulkTagGallerySelected"],
        body.role-readonly [onclick*="bulkDateGallerySelected"],
        body.role-readonly [onclick*="bulkDeleteGallerySelected"],
        body.role-readonly [onclick*="openGallerySettingsModal"],
        body.role-readonly .gallery-check-btn,
        body.role-readonly [onclick*="documents-upload"],
        body.role-readonly [onclick*="deleteDocument"],
        body.role-readonly [onclick*="openAddFinancialItem"],
        body.role-readonly [onclick*="openSingleMaterialEdit"],
        body.role-readonly [onclick*="openSingleServiceEdit"],
        body.role-readonly [onclick*="deleteMaterial"],
        body.role-readonly [onclick*="deleteService"],
        body.role-readonly [onclick*="removeMaterialTransfer"],
        body.role-readonly [onclick*="openMaterialTransferHub"],
        body.role-readonly [onclick*="openMaterialTransferEdit"],
        body.role-readonly [onclick*="dismissLowStock"],
        body.role-readonly [onclick*="saveFinancialEstimate"],
        body.role-readonly [onclick*="openFinancialModal"],
        body.role-readonly [onclick*="'se-srv-invoice-upload'"],
        body.role-readonly [onclick*="removeServiceInvoice"],
        body.role-readonly [onclick*="openInvoiceEdit"],
        body.role-readonly [onclick*="deleteInvoice"],
        body.role-readonly [onclick*="generateAiReport"],
        body.role-readonly [onclick*="openAiReportSettings"],
        body.role-readonly [onclick*="openProjectWeatherSettings"],
        body.role-readonly [onclick*="removeAiReportLogo"],
        body.role-readonly [onclick*="removeAiReportCover"],
        body.role-readonly [onclick*="deleteArchivedReport"] {
            display: none !important;
        }
        /* Slideshow settings stay OPEN for viewers on purpose: their tweaks (music,
           image picks, style) save to a browser-local copy in js/slideshow.js and
           never touch the editor's own project setup. */
        /* The daily-report modal doubles as the read view of a calendar day: keep it
           openable (and exportable) but strip every mutating control — add note/image,
           per-item delete ("…").remove()) and visibility toggles, save — and freeze
           the note textareas. */
        body.role-readonly #modal-report [onclick*="addReportNote"],
        body.role-readonly #modal-report [onclick*="saveReportData"],
        body.role-readonly #modal-report [onclick*="toggleVisibility"],
        body.role-readonly #modal-report [onclick*=".remove()"] { display: none !important; }
        body.role-readonly #modal-report textarea { pointer-events: none; }
        /* Inline value editors that live outside any modal */
        body.role-readonly [oninput*="liveUpdateActivityDOM"],
        body.role-readonly .gallery-caption-input,
        body.role-readonly #fin-estimate { pointer-events: none; }

        /* Dashboard cards for modules outside a viewer's admin-granted scope
           (toggled per-card in applyRoleUIRestrictions from the server's
           allowedModules list). */
        .viewer-module-hidden { display: none !important; }
        /* Viewers the admin granted edit rights to: full edit affordances inside
           their granted modules, but never project-level controls — settings,
           project-info editing, or sharing stay owner/editor-only. */
        body.role-viewer-edit [onclick*="openGlobalSettings"],
        body.role-viewer-edit #module-info [onclick*="openEditInfoModal"],
        body.role-viewer-edit #profile-menu-panel [onclick*="openAccessModal"] { display: none !important; }

        /* A module the viewer may SEE but not EDIT (view/edit are separate admin grants):
           neutralize the mutating controls inside that one card while leaving view-only
           actions (expand, export) working. The server is the real boundary; this just
           avoids offering controls whose save would be rejected. */
        .viewer-module-readonly [onclick*="add"],
        .viewer-module-readonly [onclick*="Add"],
        .viewer-module-readonly [onclick*="delete"],
        .viewer-module-readonly [onclick*="Delete"],
        .viewer-module-readonly [onclick*="remove"],
        .viewer-module-readonly [onclick*="Remove"],
        .viewer-module-readonly [onclick*="Settings"],
        .viewer-module-readonly [onclick*="openEditInfoModal"],
        .viewer-module-readonly [onclick*="openActivityModal"],
        .viewer-module-readonly [onclick*="openFinancialModal"],
        .viewer-module-readonly [onclick*="openInvoiceEdit"],
        .viewer-module-readonly [onclick*="openMaterialTransfer"],
        .viewer-module-readonly [onclick*="saveFinancialEstimate"],
        .viewer-module-readonly [onclick*="dismissLowStock"],
        .viewer-module-readonly [onclick*="generateAiReport"],
        .viewer-module-readonly [onclick*="Upload"],
        .viewer-module-readonly [onclick*="upload"],
        .viewer-module-readonly .act-handle,
        .viewer-module-readonly .module-drag-handle,
        .viewer-module-readonly .module-resize-handle,
        .viewer-module-readonly .gallery-check-btn { display: none !important; }
        .viewer-module-readonly input,
        .viewer-module-readonly textarea,
        .viewer-module-readonly select,
        .viewer-module-readonly .gallery-caption-input,
        .viewer-module-readonly [oninput*="liveUpdateActivityDOM"] { pointer-events: none !important; }

        .modal-overlay {
            position: fixed;
            inset: 0;
            width: auto;
            height: 100vh;
            height: 100dvh;
            overflow-x: hidden;
            overflow-y: auto;
            overscroll-behavior: contain;
            background: rgba(0,0,0,0.6);
            backdrop-filter: blur(4px);
            -webkit-backdrop-filter: blur(4px);
            isolation: isolate;
            z-index: var(--modal-z, var(--layer-modal)) !important;
            display: none;
            opacity: 0;
            transition: opacity 0.24s ease;
        }
        .modal-overlay.active { display: flex; opacity: 1; }
        .modal-overlay.items-center { align-items: safe center; }
        .modal-overlay.justify-center { justify-content: safe center; }
        .modal-overlay > :first-child { min-width: 0; margin-block: auto; }
        .modal-overlay.active[data-modal-top="false"] {
            pointer-events: none;
            background: transparent !important;
            -webkit-backdrop-filter: none !important;
            backdrop-filter: none !important;
        }
        body.modal-open { overscroll-behavior: none; }
        #dashboard-view > header {
            position: relative;
            z-index: var(--layer-sticky);
        }
        body.notif-focus #dashboard-view > header,
        body.profile-focus #dashboard-view > header,
        body.profile-focus #viewer-landing-view > header,
        body.profile-focus #admin-view > header {
            z-index: var(--layer-header-active);
        }
        #notif-wrapper {
            z-index: var(--layer-dropdown-wrapper);
        }
        #profile-menu-wrapper,
        #vl-profile-menu-wrapper,
        #admin-profile-menu-wrapper {
            z-index: var(--layer-dropdown-wrapper);
        }
        #notif-panel {
            z-index: var(--layer-dropdown) !important;
            background: rgba(255, 255, 255, 0.94);
            backdrop-filter: blur(28px) saturate(180%);
            -webkit-backdrop-filter: blur(28px) saturate(180%);
            border: 1px solid rgba(255, 255, 255, 0.55);
            box-shadow: 0 24px 70px -28px rgba(15, 23, 42, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.7);
        }
        #notif-panel .border-b {
            border-color: rgba(15, 23, 42, 0.08) !important;
        }
        #profile-menu-panel,
        #vl-profile-menu-panel,
        #admin-profile-menu-panel {
            z-index: var(--layer-dropdown) !important;
        }
        #admin-profile-menu-panel {
            background: rgba(255, 255, 255, 0.94);
            backdrop-filter: blur(28px) saturate(180%);
            -webkit-backdrop-filter: blur(28px) saturate(180%);
            border-color: rgba(255, 255, 255, 0.6) !important;
            box-shadow: 0 24px 70px -28px rgba(15, 23, 42, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.72);
        }
        #notif-dim-backdrop {
            position: fixed;
            inset: 0;
            z-index: var(--layer-page-scrim);
            background: rgba(15, 23, 42, 0.08);
            backdrop-filter: blur(10px) saturate(135%);
            -webkit-backdrop-filter: blur(10px) saturate(135%);
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.32s cubic-bezier(0.22, 1, 0.36, 1);
        }
        #notif-dim-backdrop.active {
            opacity: 1;
            pointer-events: auto;
        }
        #dashboard-scroll-area {
            transition: filter 0.32s cubic-bezier(0.22, 1, 0.36, 1);
        }
        body.notif-focus #dashboard-scroll-area,
        body.profile-focus #dashboard-scroll-area,
        body.profile-focus #viewer-landing-view > *:not(header) {
            filter: brightness(0.99) saturate(103%);
            transition: filter 0.32s cubic-bezier(0.22, 1, 0.36, 1);
        }
        .notif-row {
            cursor: pointer;
            transition: transform 0.18s ease, box-shadow 0.18s ease;
        }
        .notif-row:hover {
            transform: translateY(-1px);
            box-shadow: 0 10px 24px -18px rgba(15, 23, 42, 0.55);
        }
        .notif-dismiss-btn {
            width: 22px;
            height: 22px;
            border-radius: 999px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            color: currentColor;
            opacity: 0.55;
            transition: opacity 0.18s ease, background 0.18s ease;
        }
        .notif-dismiss-btn:hover {
            opacity: 1;
            background: rgba(15, 23, 42, 0.08);
        }
        .accordion-content { max-height: 0; overflow: hidden; transition: max-height 0.4s cubic-bezier(0, 1, 0, 1); }
        .accordion-item.open .accordion-content { max-height: 5000px; transition: max-height 0.6s ease-in-out; }
        .accordion-item.open .chevron { transform: rotate(180deg); }

        @keyframes pulse-border {
            0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
            70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); }
            100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
        }
        @keyframes bell-shake {
            0%, 100% { transform: rotate(0deg); }
            10% { transform: rotate(-18deg); }
            20% { transform: rotate(18deg); }
            30% { transform: rotate(-14deg); }
            40% { transform: rotate(14deg); }
            50% { transform: rotate(-8deg); }
            60% { transform: rotate(8deg); }
            70% { transform: rotate(-4deg); }
            80% { transform: rotate(4deg); }
            90% { transform: rotate(0deg); }
        }
        @keyframes notif-row-in {
            from { opacity: 0; transform: translateX(16px); }
            to   { opacity: 1; transform: translateX(0); }
        }
        #landing-notif-bell-icon { animation: bell-shake 1.6s ease-in-out infinite; display: inline-block; transform-origin: top center; }
        .landing-notif-row { animation: notif-row-in 0.35s ease both; }
        #save-btn {
            --save-progress: 0%;
            width: 48px;
            height: 40px;
            padding-inline: 0;
            overflow: hidden;
            color: #16a34a;
            border-color: rgba(22, 163, 74, 0.22);
            transition: width 0.28s ease, background-color 0.22s ease, color 0.22s ease, border-color 0.22s ease, box-shadow 0.22s ease;
        }
        #save-btn.save-dirty {
            color: var(--color-primary);
            border-color: rgba(249, 115, 22, 0.28);
        }
        #save-btn:hover,
        #save-btn:focus-visible {
            width: 112px;
            color: var(--color-on-primary);
            background: var(--color-primary);
            border-color: var(--color-primary);
        }
        #save-btn.save-saving {
            width: 112px;
            color: transparent;
            border-color: rgba(34, 197, 94, 0.45);
            background: #eefdf4;
            pointer-events: none;
        }
        #save-btn .save-btn-content {
            position: relative;
            z-index: 2;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            width: 100%;
            transition: opacity 0.18s ease;
        }
        #save-btn.save-saving .save-btn-content { opacity: 0; }
        #save-btn .save-label {
            max-width: 0;
            opacity: 0;
            overflow: hidden;
            white-space: nowrap;
            transition: max-width 0.24s ease, opacity 0.18s ease;
        }
        #save-btn:hover .save-label,
        #save-btn:focus-visible .save-label {
            max-width: 56px;
            opacity: 1;
        }
        #save-btn .save-progress-track {
            position: absolute;
            inset: 0;
            z-index: 1;
            opacity: 0;
            background: #ecfdf3;
            transition: opacity 0.18s ease;
        }
        #save-btn.save-saving .save-progress-track { opacity: 1; }
        #save-btn .save-progress-fill {
            position: absolute;
            inset: 0 auto 0 0;
            width: var(--save-progress);
            border-radius: inherit;
            background:
                repeating-linear-gradient(115deg, rgba(255,255,255,0.28) 0 3px, rgba(187,247,208,0.12) 3px 6px),
                linear-gradient(90deg, #86efac, #34d399);
            background-size: 14px 14px, 100% 100%;
            animation: save-stripes 0.75s linear infinite;
            transition: width 0.32s cubic-bezier(0.22, 1, 0.36, 1);
        }
        @keyframes save-stripes {
            from { background-position: 0 0, 0 0; }
            to { background-position: 14px 0, 0 0; }
        }
        #save-btn .save-progress-percent {
            position: absolute;
            inset: 0;
            z-index: 3;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
            font-weight: 800;
            direction: ltr;
            color: #ffffff;
            text-shadow: 0 1px 2px rgba(0,0,0,0.18);
            pointer-events: none;
        }

        #save-status-text { display: none; }
        @media (min-width: 640px) { #save-status-text.is-dirty, #save-status-text.has-sync-state { display: block; } }

        input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
        input[type="number"] { -moz-appearance: textfield; }
        .fin-fmt-btn.selected { border-color: var(--color-primary) !important; background: rgba(46,125,50,0.08) !important; color: var(--color-primary) !important; }
        .fin-exp-check { display: flex; align-items: center; gap: 6px; background: var(--color-surface-container); border-radius: 10px; padding: 6px 10px; font-size: 11px; font-weight: 600; cursor: pointer; border: 1.5px solid transparent; transition: all 0.2s; user-select: none; }
        .fin-exp-check:has(input:checked) { border-color: var(--color-primary); background: rgba(46,125,50,0.07); color: var(--color-primary); }
        .fin-exp-check input[type=checkbox] { accent-color: var(--color-primary); width: 14px; height: 14px; cursor: pointer; }
        /* format toggle chips — styling is driven purely by the checkbox state so
           selection can never get visually stuck on one option */
        .fin-fmt-btn { border: 2px solid #e5e7eb; color: #6b7280; font-weight: 700; position: relative; }
        .fin-fmt-btn:hover { border-color: #d1d5db; }
        .fin-fmt-btn:has(input:checked) { border-color: var(--color-primary); background: rgba(46,125,50,0.06); color: var(--color-primary); font-weight: 900; }
        .fin-fmt-btn:has(input:checked)::after { content: 'check_circle'; font-family: 'Material Symbols Rounded'; position: absolute; top: 6px; left: 8px; font-size: 15px; color: var(--color-primary); }
        .fin-exp-card { background: var(--color-surface); border: 1px solid rgba(0,0,0,0.07); border-radius: 16px; overflow: hidden; }
        .fin-exp-card > summary { list-style: none; display: flex; justify-content: space-between; align-items: center; padding: 10px 14px; cursor: pointer; user-select: none; }
        .fin-exp-card > summary::-webkit-details-marker { display: none; }
        .fin-exp-card[open] > summary .chevron { transform: rotate(180deg); }

        .is-printing .hidden-print { display: none !important; }
        .is-printing #calendar-header-row { justify-content: center !important; }
        .module-hidden { display: none !important; }
        .sortable-ghost { opacity: 0.4; background: var(--color-surface-container); }
        .dashboard-sortable-column { min-height: 80px; }
        .module-drag-handle {
            cursor: grab;
            color: #94a3b8;
            border-radius: 8px;
            transition: color 0.18s ease, background 0.18s ease;
            width: 28px;
            height: 28px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
        }
        .module-drag-handle:hover {
            color: var(--color-primary);
            background: rgba(46, 125, 50, 0.08);
        }
        .module-drag-handle:active { cursor: grabbing; }
        /* Drag-drop-to-upload feedback for the gallery/documents cards — see
           setupModuleDropZones in dashboard-layout.js. */
        .module-drop-active {
            outline: 2px dashed var(--color-primary);
            outline-offset: -4px;
            background-color: rgba(224, 123, 53, 0.06);
        }
        .module-header-actions {
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.18s ease, color 0.18s ease, background 0.18s ease;
        }
        .dashboard-draggable-module:hover .module-header-actions,
        .dashboard-draggable-module:focus-within .module-header-actions {
            opacity: 1;
            pointer-events: auto;
        }
        /* The desktop gallery search is a hover affordance, like the other header
           tools. Focus must not pin it open after the pointer leaves the module. */
        @media (hover: hover) and (min-width: 640px) {
            #gallery-desktop-search-wrap {
                opacity: 0;
                visibility: hidden;
                pointer-events: none;
                transform: translateY(-2px);
                transition: opacity .18s ease, visibility .18s ease, transform .18s ease;
            }
            #module-gallery:hover #gallery-desktop-search-wrap {
                opacity: 1;
                visibility: visible;
                pointer-events: auto;
                transform: translateY(0);
            }
        }
        /* On mobile there's no hover to reveal these, and by this breakpoint the header
           only holds the handful of primary buttons plus the "⋮" more-menu trigger (see
           setupMobileModuleActionMenus in dashboard-layout.js) — so just show them. */
        @media (max-width: 1023.98px) {
            .module-header-actions { opacity: 1; pointer-events: auto; }
            /* No hover on touch — the WBS row's edit/delete buttons must always show,
               not just reveal on a desktop-style :hover the device can't produce. */
            .group\/act button { opacity: 1; pointer-events: auto; }
            /* Modules stack as direct children of #pdf-export-wrapper on mobile. If it
               stays a CSS grid, each module becomes its own grid row sized exactly to its
               own content — leaving #module-info's sticky positioning no room within its
               own row to actually stick as the page scrolls (a well-known grid+sticky
               interaction). A flex column gives it the same tall shared containing block
               its desktop column (.dashboard-sortable-column, also flex) already has. */
            #pdf-export-wrapper { display: flex; flex-direction: column; }
            /* The header stays fixed in place — no scroll-driven hide/reveal. Drop the
               scroll area's top padding so the sticky project-info card (order:1) pins
               flush under the header: a sticky element leaves a band equal to the
               container's padding-top above it while stuck, and that band is exactly
               where other modules used to ghost through. 0 padding = no gap, no ghost. */
            #dashboard-scroll-area { padding-top: 0 !important; }
        }
        .module-header-actions > button,
        .module-header-actions > .relative > button,
        .module-header-actions .module-drag-handle {
            width: 32px !important;
            height: 32px !important;
            border-radius: 8px !important;
            background: transparent !important;
            color: #64748b !important;
            box-shadow: none !important;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            transition: color 0.18s ease, transform 0.18s ease !important;
        }
        .module-header-actions > button:hover,
        .module-header-actions > .relative > button:hover,
        .module-header-actions .module-drag-handle:hover {
            background: transparent !important;
            color: var(--color-primary) !important;
        }
        .module-header-actions > button:hover.text-red-600,
        .module-header-actions > .relative > button:hover.text-red-600 {
            color: #dc2626 !important;
        }
        .module-header-actions > button:hover.text-orange-600,
        .module-header-actions > .relative > button:hover.text-orange-600 {
            color: #ea580c !important;
        }
        .module-header-actions > button:hover.text-green-700,
        .module-header-actions > .relative > button:hover.text-green-700 {
            color: #15803d !important;
        }
        .module-header-actions > button:hover.text-purple-600,
        .module-header-actions > .relative > button:hover.text-purple-600 {
            color: #9333ea !important;
        }
        .module-header-actions > button:hover.text-primary,
        .module-header-actions > .relative > button:hover.text-primary {
            color: var(--color-primary) !important;
        }
        .module-header-actions .material-symbols-rounded {
            color: inherit !important;
            font-size: 22px !important;
            line-height: 1 !important;
        }
        /* Project-information controls sit over photography instead of a conventional
           module header. Give them a stable Material-style white container that survives
           the mobile action-menu reparenting, rather than inheriting tinted legacy fills. */
        #module-info .info-control-button,
        #module-info > .module-header-actions > .module-mobile-menu-wrap > button {
            width: 36px !important;
            height: 36px !important;
            border-radius: 12px !important;
            background: #ffffff !important;
            color: #475569 !important;
            border: 1px solid rgba(226, 232, 240, 0.95) !important;
            box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08) !important;
            display: inline-flex !important;
            align-items: center;
            justify-content: center;
        }
        #module-info .info-control-button:hover,
        #module-info > .module-header-actions > .module-mobile-menu-wrap > button:hover {
            background: #ffffff !important;
            color: var(--color-primary) !important;
            border-color: rgba(245, 124, 0, 0.28) !important;
            transform: translateY(-1px);
        }
        #module-info .info-control-button .material-symbols-rounded,
        #module-info > .module-header-actions > .module-mobile-menu-wrap > button .material-symbols-rounded {
            font-size: 20px !important;
        }
        /* "Picked up" cue: SortableJS adds .sortable-chosen once the touch
           press-and-hold delay elapses, so on mobile the user can tell the
           item is now draggable (vs. a quick swipe, which keeps scrolling). */
        .sortable-chosen {
            transform: scale(1.02);
            box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
            transition: transform 120ms ease, box-shadow 120ms ease;
        }
        .module-sortable-ghost {
            opacity: 0.45;
            border: 2px dashed rgba(245, 158, 11, 0.55) !important;
            background: rgba(245, 158, 11, 0.08) !important;
        }
        #module-info {
            position: sticky;
            top: 0;
            z-index: 15;
        }
        /* ── Per-module sticky headers ──────────────────────────────────────────
           A module's own header (and, where present, the toolbar row right under
           it) sticks just below the pinned project-info card once scrolling
           through a tall module (e.g. a long gallery) would otherwise carry it
           off-screen — its actions/search/batch-controls stay reachable without
           scrolling back up through everything already seen.
           `--sticky-header-top` is kept up to date in JS (updatePinnedHeaderOffset
           in dashboard-layout.js) as module-info's height changes. Requires the
           module's own box to not clip via overflow-hidden (see the modules that
           had it — their inner scroll area now carries the rounded bottom corner
           instead, via .module-sticky-round-b). */
        .module-sticky-round-b { border-radius: 0 0 1.5rem 1.5rem; }
        /* Desktop-only: on mobile #module-info is NOT pinned (position: static, see
           the max-width:1023.98px block below) — a module header sticking to the
           height module-info WOULD have occupied had nothing real above it to sit
           under, so it froze partway down the screen instead of at the top. Module
           headers only stick where the project-info card is actually pinned. */
        @media (min-width: 1024px) {
            .module-header-sticky {
                position: sticky;
                top: var(--sticky-header-top, 76px);
                z-index: 14;
                background: #fdfdfd;
            }
        }
        /* Mobile: #module-info scrolls away with the rest of the stream (see the
           max-width:1023.98px block below), so there's no pinned band to sit under —
           each module header sticks straight to the top of the scroll area instead,
           same as #module-info does on desktop. */
        @media (max-width: 1023.98px) {
            .module-header-sticky {
                position: sticky;
                top: 0;
                z-index: 14;
                background: #fdfdfd;
            }
        }
        /* On desktop, the slideshow and financial modules pin to the top edge once
           scrolling brings them there; scrolling back up releases them and reveals
           the modules above (progress chart / calendar) again. */
        @media (min-width: 1024px) {
            #module-slider,
            #module-financial {
                position: sticky;
                top: 0;
                z-index: 15;
            }
            /* While pinned, keep the financial card inside the viewport — its
               inner lists scroll, so capping the card height is safe. */
            body.dash-scrolled #module-financial {
                max-height: calc(100vh - 110px);
            }
            /* While scrolled, pinned cards get a solid face so modules passing
               behind can't ghost through the translucent glass. */
            body.dash-scrolled #module-info,
            body.dash-scrolled #module-slider,
            body.dash-scrolled #module-financial {
                background: #fdfdfd;
            }
        }
        /* ── Pinned-region shield: a page-coloured gradient from the header edge down
           past the project-info card, masking modules that scroll up behind the pinned
           ones. Fully solid — no fade — so nothing scrolling up behind it (in columns
           with no sticky header of their own, e.g. the calendar module) is ever
           partially visible through the gap. ── */
        #pinned-shield { display: none; }
        @media (min-width: 1024px) {
            #pinned-shield {
                display: block;
                position: sticky;
                top: 0;
                height: 0;
                z-index: 10;
                pointer-events: none;
            }
            #pinned-shield::before {
                content: '';
                position: absolute;
                top: -24px;
                left: -24px;
                right: -24px;
                height: var(--pinned-shield-h, 340px);
                background: #fdfdfd;
                opacity: 0;
                transition: opacity 0.35s ease;
            }
            body.dash-scrolled #pinned-shield::before { opacity: 1; }
        }
        /* ── Mobile: once scrolled, the pinned project-info card gets a solid face and
           a taller gradient reaching up to the (possibly hidden) header edge, so other
           modules can't ghost through the gap while scrolling underneath it. ── */
        @media (max-width: 1023.98px) {
            body.dash-scrolled #module-info { background: #fdfdfd; }
            body.dash-scrolled #module-info::before {
                top: -120px;
                height: 132px;
                background: linear-gradient(
                    to bottom,
                    #fdfdfd 0%,
                    #fdfdfd 78%,
                    rgba(253, 253, 253, 0.6) 92%,
                    rgba(253, 253, 253, 0) 100%
                );
            }
        }
        /* ── Pinned-module top shield: solid-to-transparent gradient above the card ── */
        #module-info::before {
            content: '';
            position: absolute;
            left: -40px;
            right: -40px;
            top: -80px;
            height: 88px;
            pointer-events: none;
            /* solid surface colour at the very top, fading to transparent at the bottom
               so it blends cleanly into the module card edge                          */
            background: linear-gradient(
                to bottom,
                #fdfdfd  0%,
                #fdfdfd  55%,
                rgba(253, 253, 253, 0.55) 82%,
                rgba(253, 253, 253, 0)    100%
            );
            z-index: -1;
        }

        /* ── Pinned-module bottom skirt: glass-morphic gradient below the card ── */
        #module-info::after {
            content: '';
            position: absolute;
            left: 4px;
            right: 4px;
            top: calc(100% - 12px);
            height: 110px;
            pointer-events: none;
            background: linear-gradient(
                to bottom,
                rgba(253, 253, 253, 0.92) 0%,
                rgba(253, 253, 253, 0)    100%
            );
            backdrop-filter: blur(20px);
            -webkit-backdrop-filter: blur(20px);
            -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.96) 0%, rgba(0,0,0,0) 100%);
            mask-image: linear-gradient(to bottom, rgba(0,0,0,0.96) 0%, rgba(0,0,0,0) 100%);
            border-radius: 0 0 28px 28px;
            z-index: 5;
        }
        /* Project information is a regular dashboard module. Legacy sticky masks are
           disabled; pinning is now an explicit user choice like the other modules. */
        #module-info { position: relative; top: auto; z-index: auto; }
        #module-info::before,
        #module-info::after,
        #pinned-shield { display: none !important; }
        @media (min-width: 1024px) {
            .module-header-sticky { top: 0; }
        }
        .resizable-module { position: relative; }
        .module-resize-handle {
            position: absolute;
            left: 24px;
            right: 24px;
            bottom: 0;
            height: 14px;
            cursor: ns-resize;
            z-index: 12;
            opacity: 0;
            transition: opacity 0.18s ease;
            touch-action: none;
        }
        .module-resize-handle::before {
            content: '';
            position: absolute;
            left: 50%;
            bottom: 4px;
            width: 56px;
            height: 4px;
            transform: translateX(-50%);
            border-radius: 999px;
            background: rgba(46, 125, 50, 0.34);
            box-shadow: 0 1px 5px rgba(15, 23, 42, 0.16);
        }
        .resizable-module:hover .module-resize-handle,
        .module-resize-handle:focus-visible,
        body.module-resizing .module-resize-handle {
            opacity: 1;
        }
        body.module-resizing {
            user-select: none;
            cursor: ns-resize;
        }
        
        .kanban-col { min-height: 90px; overflow-y: auto; }
        .kanban-drag-handle {
            position: absolute; left: 4px; top: 4px; z-index: 5;
            width: 36px; height: 36px; border-radius: 10px;
            display: inline-flex; align-items: center; justify-content: center;
            color: #94a3b8; background: rgba(248,250,252,.92);
            cursor: grab; touch-action: none;
        }
        .kanban-drag-handle:active { cursor: grabbing; color: var(--color-primary); }
        .kanban-drag-handle .material-symbols-rounded { font-size: 19px; }
        @media (hover: hover) and (pointer: fine) {
            .kb-task > .kanban-drag-handle {
                opacity: 0;
                visibility: hidden;
                pointer-events: none;
                transition: opacity .16s ease, visibility .16s ease;
            }
            .kb-task:hover > .kanban-drag-handle,
            .kb-task:focus-within > .kanban-drag-handle {
                opacity: 1;
                visibility: visible;
                pointer-events: auto;
            }
        }
        button, [role="button"] { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
        .modal-overlay.active { overscroll-behavior: contain; }
        @media (pointer: coarse) {
            .kanban-drag-handle, .module-drag-handle, .settings-order-handle, .editor-note-drag-handle {
                min-width: 44px; min-height: 44px;
            }
        }
        @media (max-width: 767.98px) {
            .text-\[8px\], .text-\[9px\], .text-\[10px\] { font-size: 11px !important; line-height: 1.45 !important; }
        }
        .editor-note-priority-urgent { background: linear-gradient(135deg, #fff1f2, #fff7ed); border-color: #fecdd3; }
        .editor-note-priority-important { background: linear-gradient(135deg, #fff7ed, #fffbeb); border-color: #fed7aa; }
        .editor-note-priority-normal { background: linear-gradient(135deg, #eff6ff, #f8fafc); border-color: #bfdbfe; }
        .editor-note-drag-handle { cursor: grab; touch-action: none; }
        .editor-note-drag-handle:active { cursor: grabbing; }
        .editor-note-sortable-ghost { opacity: .35; }
        .editor-note-sortable-chosen { box-shadow: 0 18px 36px rgba(15,23,42,.16); }
        .editor-notes-page-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 20px;
            margin-bottom: 20px;
        }
        .editor-notes-title-group {
            display: flex;
            align-items: center;
            gap: 12px;
            min-width: 0;
        }
        .editor-notes-back-button {
            width: 44px;
            height: 44px;
            flex: 0 0 44px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            border: 1px solid #e5e7eb;
            border-radius: 14px;
            color: #64748b;
            background: rgba(255,255,255,.94);
            box-shadow: 0 4px 12px rgba(15,23,42,.08);
            transition: color .18s ease, border-color .18s ease, background .18s ease, transform .18s ease, box-shadow .18s ease;
        }
        .editor-notes-back-button:hover {
            color: var(--color-primary);
            border-color: rgba(249,115,22,.28);
            background: #fff7ed;
            transform: translateX(2px);
            box-shadow: 0 7px 16px rgba(249,115,22,.12);
        }
        .editor-notes-back-button:focus-visible {
            outline: 3px solid rgba(249,115,22,.18);
            outline-offset: 2px;
        }
        .editor-notes-back-button .material-symbols-rounded { font-size: 22px; line-height: 1; }
        .editor-note-project-menu {
            position: absolute;
            top: calc(100% + 6px);
            right: 0;
            left: 0;
            z-index: 40;
            max-height: 224px;
            overflow-y: auto;
            padding: 6px;
            border: 1px solid #e5e7eb;
            border-radius: 14px;
            background: #fff;
            box-shadow: 0 16px 34px rgba(15,23,42,.16);
        }
        .editor-note-project-option {
            width: 100%;
            padding: 10px 12px;
            border-radius: 10px;
            color: #374151;
            font-size: 12px;
            font-weight: 700;
            text-align: right;
        }
        .editor-note-project-option:hover,
        .editor-note-project-option:focus-visible { color: var(--color-primary); background: #fff7ed; outline: none; }
        @media (max-width: 767.98px) {
            .editor-notes-page-header { align-items: stretch; gap: 14px; }
            .editor-notes-title-group { width: 100%; }
            .editor-notes-page-header > .editor-notes-add-button { width: 100%; }
        }
        .kb-task.is-pinned { border-color: rgba(249, 115, 22, 0.55) !important; box-shadow: inset 3px 0 0 #f97316, 0 2px 8px rgba(15, 23, 42, 0.08); }
        .settings-module-row .pin-toggle-btn { opacity: 0; transition: opacity .15s ease, color .15s ease, background-color .15s ease; }
        .settings-module-row:hover .pin-toggle-btn { opacity: 1; }
        .pin-toggle-btn.is-pinned { opacity: 1 !important; color: #ea580c; background-color: rgba(234, 88, 12, 0.1); }
        .pin-toggle-btn[disabled] { cursor: default; opacity: 1 !important; color: #dc2626; background-color: rgba(220, 38, 38, 0.1); }
        .settings-order-handle { touch-action: none; cursor: grab; }
        .settings-order-handle:active { cursor: grabbing; }
        @media (max-width: 1023.98px) {
            #settings-modules-list .pin-toggle-btn { display: none !important; }
        }
        @media (min-width: 1024px) {
            .settings-order-handle { display: none !important; }
        }
        .kanban-style-classic .kanban-column { background: var(--color-surface-container); }
        .kanban-style-clean .kanban-column { background: rgba(255,255,255,0.72); border-style: dashed; }
        .kanban-style-bold .kanban-column { background: linear-gradient(180deg, rgba(255,247,237,0.95), rgba(255,255,255,0.78)); border-color: rgba(249,115,22,0.28); }
        /* Give the default board enough vertical room for several useful task rows;
           columns keep their own scroll once the module reaches its max height. */
        #module-kanban { min-height: 400px; }
        #modal-module-expand.active #module-kanban {
            max-height: none !important;
            height: 100%;
            min-height: 0;
        }
        #modal-module-expand.active #module-kanban .kanban-board-grid {
            min-height: 0;
            height: 100%;
        }
        #modal-module-expand.active #module-kanban .kanban-col {
            max-height: none;
        }
        #modal-module-expand.active #module-gallery {
            max-height: none !important;
            height: 100%;
            min-height: 0;
        }
        #modal-module-expand.active #module-gallery > .overflow-y-auto {
            max-height: none !important;
            min-height: 0;
        }
        #modal-module-expand.active #module-gallery #gallery-print-wrapper {
            min-height: 100%;
        }
        #modal-module-expand.active [data-module-expand-button] {
            display: none !important;
        }
        #modal-module-expand.active .resizable-module {
            height: 100% !important;
            max-height: none !important;
        }
        #modal-module-expand.active .module-resize-handle {
            display: none !important;
        }
        #module-info .info-expand-only { display: none; }
        #modal-module-expand.active #module-info .info-expand-only:not(.hidden) { display: flex; }
        #module-invoice .invoice-expand-only { display: none; }
        #modal-module-expand.active #module-invoice .invoice-expand-only { display: flex; }
        @media (max-width: 1023.98px) {
            /* The three desktop columns flatten into one mobile module stream. Use
               one deliberate gap here so modules from the same and different
               desktop columns always have identical spacing. */
            #pdf-export-wrapper {
                gap: 1rem !important;
            }
            #col-right,
            #col-middle,
            #col-left {
                display: contents;
            }
            #col-right > .dashboard-draggable-module,
            #col-middle > .dashboard-draggable-module,
            #col-left > .dashboard-draggable-module {
                margin-top: 0 !important;
                margin-bottom: 0 !important;
            }

            /* On mobile the project-info card is NOT pinned — it scrolls away with the
               rest of the stream. Drop the sticky positioning and the sticky-card masks
               (the ::before/::after skirt), which only made sense while it was pinned. */
            #module-info::before,
            #module-info::after {
                display: none;
            }
            #module-info {
                position: static;
                top: auto;
            }
            #module-info { order: 1; }
            #module-kanban { order: 2; }
            #module-charts { order: 3; }
            #module-slider { order: 4; }
            #module-financial { order: 5; }
            #module-gallery { order: 6; }
            #module-invoice { order: 7; }
            #module-calendar { order: 8; }
            #module-wbs { order: 9; }
            /* Documents and AI report always close the mobile page. Without an
               explicit order they defaulted to 0 and jumped above everything,
               including the project info card. */
            #module-documents { order: 10; }
            #module-report { order: 11; }

            /* ── Project info shows its full desktop card on mobile too (it's no
               longer pinned there, so the old compact 56px identity bar is gone);
               the base flex-col classes stack the image above the details. ── */

            /* ── Touch UX ──
               Order is CSS-driven on mobile, so drag-reorder is meaningless and its
               handle only fights scrolling; desktop-saved pixel heights don't fit a
               phone either, so modules size to content with a viewport cap. */
            .module-drag-handle { display: none !important; }
            .module-resize-handle { display: none !important; }
            /* Gallery alone has no useful module-expansion action on phones. */
            #module-gallery [data-gallery-desktop-only] { display: none !important; }
            #module-gallery #gallery-desktop-search-wrap { display: none !important; }
            .resizable-module {
                height: auto !important;
                max-height: 75vh !important;
            }
            /* Module "⋮" menu expands its icons inline in the header row (desktop-like),
               with a small slide-in — not as a vertical dropdown panel. */
            .module-mobile-menu-panel { display: none; }
            .module-mobile-menu-panel.open {
                display: flex;
                align-items: center;
                gap: 4px;
                animation: module-menu-in 0.22s cubic-bezier(0.22, 1, 0.36, 1) both;
            }
            @keyframes module-menu-in {
                from { opacity: 0; transform: translateX(-8px); }
                to   { opacity: 1; transform: translateX(0); }
            }
            /* Landing tab bar: no icons on phones — thin vertical separators between
               tabs instead — and every tab flexes to share the full width, so the
               whole bar fits one screen without horizontal scrolling. */
            #vl-tabs-bar { width: 100%; }
            #vl-tabs-bar > button {
                flex: 1 1 0;
                min-width: 0;
                justify-content: center;
                padding-inline: 0.125rem !important;
                /* Scales with viewport width so it also holds up on narrower real
                   devices (~360px) than what a resized desktop browser simulates,
                   not just the ~390-430px width common phones test at. */
                font-size: clamp(9.5px, 3vw, 12px);
                white-space: nowrap;
                overflow: hidden;
                position: relative;
            }
            #vl-tabs-bar > button > span {
                overflow: hidden;
                text-overflow: ellipsis;
                max-width: 100%;
            }
            #vl-tabs-bar > button .material-symbols-rounded { display: none; }
            #vl-tabs-bar > button:not(:first-child)::before {
                content: '';
                position: absolute;
                inset-inline-start: -2.5px;
                top: 50%;
                transform: translateY(-50%);
                width: 1px;
                height: 18px;
                background: #e2e8f0;
            }
            .vl-editor-action-card { min-height: 118px; }
        }

        /* ==================== اسلایدشو نسخه ۲ (موزیک‌محور) ==================== */
        #module-slider {
            height: auto !important;
            min-height: 0 !important;
            max-height: none !important;
            overflow: visible !important;
        }
        #ss-card-stage,
        .ss-preview-wrap {
            position: relative;
            width: 100%;
            height: auto !important;
            min-height: 0 !important;
            max-height: none !important;
            flex: 0 0 auto !important;
            aspect-ratio: 16 / 9 !important;
            overflow: hidden;
            border-radius: 1rem;
            background: #0f172a;
        }
        .ss-preview-wrap canvas { display: block; width: 100% !important; height: 100% !important; }
        .ss-card-btn {
            position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
            width: 40px; height: 40px; border-radius: 9999px;
            background: rgba(15, 23, 42, 0.55); color: #fff; backdrop-filter: blur(4px);
            display: flex; align-items: center; justify-content: center;
            opacity: 0; transition: opacity 0.2s ease, background 0.2s ease, transform 0.2s ease;
            z-index: 20; cursor: pointer; border: none;
        }
        .ss-preview-wrap:hover .ss-card-btn { opacity: 1; }
        .ss-card-btn:hover { background: rgba(15, 23, 42, 0.78); transform: translate(-50%, -50%) scale(1.08); }
        #ss-player-stage { position: absolute; inset: 0; overflow: hidden; background: #000; }
        #ss-player-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
        .ss-chrome { transition: opacity 0.45s ease; }
        .ss-chrome.ss-hidden { opacity: 0 !important; pointer-events: none; }
        #modal-slider-player.ss-nocursor { cursor: none; }
        .ss-ai-loader {
            position: absolute; top: 26px; left: 50%; transform: translateX(-50%); z-index: 40;
            display: none; align-items: center; gap: 10px;
            background: rgba(10, 14, 25, 0.75); border: 1px solid rgba(251, 191, 36, 0.35);
            color: #fbbf24; font-size: 12px; font-weight: bold; padding: 9px 18px;
            border-radius: 9999px; backdrop-filter: blur(10px); pointer-events: none;
        }
        .ss-ai-loader.show { display: flex; }
        .ss-ai-spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid rgba(251, 191, 36, 0.25); border-top-color: #fbbf24; animation: ss-spin 0.8s linear infinite; }
        .ss-mini-spinner { display: inline-block; width: 10px; height: 10px; border-radius: 50%; border: 2px solid rgba(100, 116, 139, 0.25); border-top-color: #64748b; animation: ss-spin 0.8s linear infinite; flex-shrink: 0; }
        .ss-mini-spinner-ai { border-color: rgba(139, 92, 246, 0.25); border-top-color: #8b5cf6; }
        @keyframes ss-spin { to { transform: rotate(360deg); } }
        .ss-tab-btn {
            display: flex; align-items: center; gap: 4px; padding: 8px 14px;
            font-size: 12px; font-weight: bold; color: #6b7280;
            border-bottom: 2px solid transparent; margin-bottom: -1px;
            transition: color 0.15s, border-color 0.15s; cursor: pointer; background: none; border-top: none; border-left: none; border-right: none;
        }
        .ss-tab-btn:hover { color: var(--color-primary); }
        .ss-tab-btn.active { color: var(--color-primary); border-bottom-color: var(--color-primary); }
        .dark .ss-tab-btn { color: #94a3b8; }
        .dark .ss-tab-btn.active { color: var(--color-primary); }
        .ss-preset-card {
            position: relative; text-align: right; border: 2px solid transparent;
            border-radius: 1rem; padding: 12px; cursor: pointer;
            transition: border-color 0.15s, transform 0.15s;
        }
        .ss-preset-card:hover { transform: translateY(-2px); }
        .ss-preset-card.active { border-color: var(--color-primary); }
        #ss-preset-grid .ss-preset-card {
            aspect-ratio: 1 / 1; min-width: 0; padding: 8px;
            display: flex; flex-direction: column; align-items: center; justify-content: center;
            text-align: center; border-radius: 0.85rem; overflow: hidden;
        }
        #ss-preset-grid .ss-preset-card .material-symbols-rounded { font-size: 21px; }
        #ss-preset-grid .ss-preset-card p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
        .gal-pill {
            border: 2px solid #e5e7eb; border-radius: 0.85rem; padding: 10px 0;
            text-align: center; font-weight: 700; font-size: 12px; color: #6b7280;
            cursor: pointer; transition: border-color 0.15s, color 0.15s, background 0.15s;
            background: var(--color-surface);
        }
        .gal-pill:hover { border-color: #d1d5db; }
        .gal-pill.active { border-color: var(--color-primary); background: rgba(46,125,50,0.06); color: var(--color-primary); font-weight: 900; }
        .gal-stepper-btn {
            width: 30px; height: 30px; border-radius: 0.6rem; background: var(--color-surface);
            display: flex; align-items: center; justify-content: center; transition: background 0.15s, color 0.15s;
        }
        .gal-stepper-btn:hover { background: var(--color-primary); color: #fff; }
        .ss-export-bar-track { width: 100%; height: 8px; border-radius: 9999px; background: rgba(148, 163, 184, 0.25); overflow: hidden; }
        #ss-export-bar { height: 100%; width: 0%; border-radius: 9999px; background: linear-gradient(90deg, #ef4444, #f59e0b); transition: width 0.3s ease; }

        
        .export-muted { opacity: 0.4; border: 1px dashed #ef4444 !important; background-color: rgba(239, 68, 68, 0.05) !important; }
        .is-printing .export-muted { display: none !important; }
        /* Always-visible determinate upload bar under the gallery header. */
        .gallery-upload-bar {
            display: none;
            padding: 10px 16px 12px;
            background: linear-gradient(180deg, rgba(255,247,237,0.95), rgba(255,247,237,0.6));
            border-bottom: 1px solid rgba(249,115,22,0.18);
        }
        .gallery-upload-bar.active { display: block; }
        .gallery-upload-bar-head {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 8px;
            font-size: 12px;
            font-weight: 800;
            color: #b45309;
        }
        .gallery-upload-bar-icon { font-size: 18px; color: #f97316; }
        .gallery-upload-bar-text {
            flex: 1;
            min-width: 0;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .gallery-upload-bar-pct { font-variant-numeric: tabular-nums; font-weight: 900; }
        .gallery-upload-bar-track {
            height: 6px;
            border-radius: 999px;
            background: rgba(249,115,22,0.16);
            overflow: hidden;
        }
        .gallery-upload-bar-fill {
            display: block;
            width: var(--gallery-upload-progress, 0%);
            height: 100%;
            border-radius: inherit;
            background:
                repeating-linear-gradient(115deg, rgba(255,255,255,0.34) 0 4px, rgba(251,191,36,0.12) 4px 8px),
                linear-gradient(90deg, #fbbf24, #f97316);
            background-size: 16px 16px, 100% 100%;
            animation: gallery-upload-stripes 0.8s linear infinite;
            transition: width 0.28s ease;
        }
        .gallery-upload-bar.is-done .gallery-upload-bar-fill { animation: none; }
        .gallery-upload-bar.is-done .gallery-upload-bar-head { color: #15803d; }
        .gallery-upload-bar.is-done .gallery-upload-bar-icon { color: #16a34a; }
        .gallery-upload-bar.is-error .gallery-upload-bar-head { color: #b91c1c; }
        .gallery-upload-bar.is-error .gallery-upload-bar-icon { color: #dc2626; }
        .gallery-upload-bar.is-error .gallery-upload-bar-track { background: rgba(220,38,38,0.16); }
        .gallery-upload-bar.is-error .gallery-upload-bar-fill {
            background: #dc2626;
            animation: none;
        }
        /* Manual-arrange (drag-to-reorder) mode. */
        .gallery-arrange-grip {
            position: absolute;
            top: 8px;
            left: 8px;
            z-index: 11;
            width: 44px;
            height: 44px;
            border-radius: 999px;
            background: rgba(255,255,255,0.92);
            color: var(--color-primary);
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: 0 1px 5px rgba(0,0,0,0.18);
            cursor: grab;
            touch-action: none;
        }
        .gallery-arrange-grip .material-symbols-rounded { font-size: 18px; }
        .gallery-media-card.gallery-arrange-on { cursor: grab; }
        /* Suppress native touch-scroll on the drag surface so a finger drag reorders
           instead of scrolling the page. Uses a stable class (not [data-src], which the
           lazy-loader strips once the image paints). */
        .gallery-drag-surface { touch-action: pan-y; }
        /* The lifted card is transform-positioned under the pointer every frame, so
           it must not have a transition of its own; the shadow/outline is what marks
           it as "the one you are holding". */
        .gallery-media-card.gallery-dragging {
            opacity: 0.97;
            box-shadow: 0 18px 36px rgba(0,0,0,0.24);
            outline: 2px solid var(--color-primary);
            outline-offset: 2px;
            position: relative;
            z-index: 30;
            transition: none;
            cursor: grabbing;
            will-change: transform;
        }
        /* Displaced neighbours (and the dropped card on release) glide to their new
           slot instead of snapping, so the move stays readable. */
        .gallery-media-card.gallery-arrange-shift {
            transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.3, 1);
        }
        /* A brief ring on the card that was just dropped: after a reorder the grid
           otherwise looks identical and it is easy to lose track of which image moved. */
        @keyframes gallery-arrange-settle {
            0%   { box-shadow: 0 0 0 3px rgba(245,124,0,0.55), 0 8px 20px rgba(245,124,0,0.22); }
            70%  { box-shadow: 0 0 0 3px rgba(245,124,0,0.35), 0 4px 12px rgba(245,124,0,0.12); }
            100% { box-shadow: 0 0 0 0 rgba(245,124,0,0); }
        }
        .gallery-media-card.gallery-arrange-settled {
            animation: gallery-arrange-settle 1.4s ease-out;
        }
        @media (prefers-reduced-motion: reduce) {
            .gallery-media-card.gallery-arrange-shift { transition: none; }
            .gallery-media-card.gallery-arrange-settled { animation: none; outline: 2px solid var(--color-primary); }
        }
        /* Legacy pill/bar styles — still used by the comment-attachment uploader
           (#comment-upload-status / #comment-upload-progress). The gallery itself now
           uses .gallery-upload-bar above. */
        .gallery-upload-status {
            display: none;
            align-items: center;
            gap: 6px;
            margin-right: 6px;
            padding: 4px 8px;
            border-radius: 999px;
            background: rgba(255, 247, 237, 0.95);
            color: #d97706;
            font-size: 10px;
            font-weight: 900;
            line-height: 1;
            white-space: nowrap;
        }
        .gallery-upload-status.active { display: inline-flex; }
        .gallery-upload-status .material-symbols-rounded {
            font-size: 14px;
            animation: gallery-upload-spin 1s linear infinite;
        }
        .gallery-upload-progress {
            display: none;
            position: relative;
            height: 3px;
            border-radius: 999px;
            overflow: hidden;
            background: rgba(245, 158, 11, 0.15);
        }
        .gallery-upload-progress.active { display: block; }
        .gallery-upload-progress-fill {
            display: block;
            width: var(--gallery-upload-progress, 0%);
            height: 100%;
            border-radius: inherit;
            background:
                repeating-linear-gradient(115deg, rgba(255,255,255,0.34) 0 4px, rgba(251,191,36,0.12) 4px 8px),
                linear-gradient(90deg, #fbbf24, #f97316);
            background-size: 16px 16px, 100% 100%;
            animation: gallery-upload-stripes 0.8s linear infinite;
            transition: width 0.28s ease;
        }
        body.gallery-arranging-active { user-select: none; }
        body.gallery-arranging-active * { cursor: grabbing !important; }
        .document-upload-status {
            display: none;
            align-items: center;
            gap: 6px;
            margin-right: 6px;
            padding: 4px 8px;
            border-radius: 999px;
            background: rgba(255, 247, 237, 0.95);
            color: #d97706;
            font-size: 10px;
            font-weight: 900;
            line-height: 1;
            white-space: nowrap;
        }
        .document-upload-status.active { display: inline-flex; }
        .document-upload-status .material-symbols-rounded {
            font-size: 14px;
            animation: gallery-upload-spin 1s linear infinite;
        }
        .document-upload-progress {
            display: none;
            position: absolute;
            left: 16px;
            right: 16px;
            bottom: 0;
            height: 3px;
            border-radius: 999px;
            overflow: hidden;
            background: rgba(245, 158, 11, 0.15);
        }
        .document-upload-progress.active { display: block; }
        .document-upload-progress-fill {
            display: block;
            width: var(--document-upload-progress, 0%);
            height: 100%;
            border-radius: inherit;
            background:
                repeating-linear-gradient(115deg, rgba(255,255,255,0.34) 0 4px, rgba(251,191,36,0.12) 4px 8px),
                linear-gradient(90deg, #fbbf24, #f97316);
            background-size: 16px 16px, 100% 100%;
            animation: gallery-upload-stripes 0.8s linear infinite;
            transition: width 0.28s ease;
        }
        @keyframes gallery-upload-spin {
            from { transform: rotate(0deg); }
            to { transform: rotate(360deg); }
        }
        @keyframes gallery-upload-stripes {
            from { background-position: 0 0, 0 0; }
            to { background-position: 16px 0, 0 0; }
        }
        .gallery-caption-input {
            direction: rtl;
            text-align: center;
            line-height: 1.55;
            white-space: pre-wrap;
            overflow-wrap: anywhere;
            word-break: normal;
        }
        .gallery-style-indicator {
            position: absolute;
            left: 8px;
            bottom: 8px;
            z-index: 10;
            display: inline-flex;
            flex-direction: row;
            align-items: center;
            justify-content: flex-start;
            gap: 0;
            width: auto;
            max-width: 32px;
            height: 32px;
            padding: 6px;
            overflow: hidden;
            direction: rtl;
            color: #fff;
            background: rgba(17, 24, 39, 0.58);
            border: 1px solid rgba(255, 255, 255, 0.3);
            border-radius: 999px;
            box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
            -webkit-backdrop-filter: blur(10px) saturate(135%);
            backdrop-filter: blur(10px) saturate(135%);
            transition: max-width 0.24s ease, background-color 0.2s ease, box-shadow 0.2s ease;
        }
        .gallery-style-indicator-icon {
            flex: 0 0 18px;
            width: 18px;
            font-size: 17px;
            line-height: 1;
        }
        .gallery-style-indicator-label {
            min-width: 0;
            max-width: 0;
            overflow: hidden;
            opacity: 0;
            white-space: nowrap;
            font-size: 10px;
            font-weight: 900;
            line-height: 1;
            transition: max-width 0.24s ease, opacity 0.16s ease, margin-left 0.24s ease;
        }
        .gallery-media-frame:hover .gallery-style-indicator,
        .gallery-style-indicator:focus-visible {
            max-width: min(240px, calc(100% - 16px));
            background: rgba(17, 24, 39, 0.76);
            box-shadow: 0 6px 18px rgba(0, 0, 0, 0.24);
            outline: none;
        }
        .gallery-media-frame:hover .gallery-style-indicator-label,
        .gallery-style-indicator:focus-visible .gallery-style-indicator-label {
            max-width: 190px;
            margin-left: 6px;
            opacity: 1;
        }
        @media (prefers-reduced-motion: reduce) {
            .gallery-style-indicator,
            .gallery-style-indicator-label { transition: none; }
        }
        #hub-transfer-to,
        #hub-transfer-to option {
            color-scheme: light;
            background-color: #ffffff;
            color: #1f2937;
        }
        .gallery-tag-pill {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            max-width: 240px;
            padding: 4px 12px;
            border-radius: 999px;
            border: 1px solid rgba(245, 158, 11, 0.24);
            background: rgba(255, 247, 237, 0.92);
            color: #b45309;
            font-size: 11px;
            font-weight: 900;
            line-height: 1.4;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            cursor: pointer;
            transition: all 0.18s ease;
        }
        .gallery-tag-pill:hover { background: rgba(254, 243, 199, 0.92); border-color: rgba(245, 158, 11, 0.42); }
        .gallery-tag-edit { font-size: 12px; line-height: 1; color: #d97706; padding: 0 1px; display: inline-flex; }
        /* Large galleries keep all searchable/editable cards in the DOM, but the
           browser can skip painting/layout inside off-screen cards until needed. */
        .gallery-media-card { content-visibility: auto; contain-intrinsic-size: auto 330px; }
        .is-printing .gallery-media-card { content-visibility: visible; contain-intrinsic-size: none; }
        .gallery-tag-remove { font-size: 12px; line-height: 1; color: #d97706; padding: 0 1px; }
        .gallery-tag-icon-btn {
            width: 28px;
            height: 28px;
            border-radius: 999px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            color: #d97706;
            background: transparent;
            transition: all 0.18s ease;
        }
        .gallery-tag-icon-btn:hover { background: rgba(245, 158, 11, 0.1); }
        .gallery-tag-editor {
            min-width: 118px;
            max-width: 170px;
            border-radius: 999px;
            border: 1px solid rgba(245, 158, 11, 0.26);
            background: rgba(255, 247, 237, 0.94);
            color: #b45309;
            padding: 3px 9px;
        }
        .gallery-tag-editor input { width: 100%; min-width: 0; background: transparent; outline: none; color: #b45309; font-size: 11px; font-weight: 900; }
        #gallery-active-tag-filter .gallery-tag-pill { background: rgba(255, 247, 237, 0.94); border-color: rgba(245, 158, 11, 0.28); color: #b45309; font-size: 11px; }
        .gallery-meta-row { direction: ltr; justify-content: space-between; flex-wrap: wrap; row-gap: 4px; }
        /* Let the tag claim the row's free width (wrapping the date to its own line
           when needed) instead of squeezing the tag text into an early ellipsis. */
        .gallery-meta-row .gallery-tags-row { flex: 1 1 auto; }
        .gallery-tag-pill { max-width: 100%; }
        .gallery-meta-date { direction: ltr; unicode-bidi: embed; }
        .is-printing #gallery-grid {
            display: grid !important;
            grid-template-columns: repeat(3, 1fr) !important;
            gap: 12px !important;
            page-break-inside: auto !important;
        }
        
        .is-printing #gallery-grid > div {
            padding: 14px !important;
            page-break-inside: avoid !important;
            break-inside: avoid !important;
            display: flex !important;
            flex-direction: column !important;
            grid-column: span 1 !important;
        }
        /* Static PDF/Word reports are image documents. Uploaded videos stay in the
           interactive gallery and are intentionally omitted from these snapshots. */
        .is-printing #gallery-grid > .gallery-video-entry { display: none !important; }
        
        @media print {
            #gallery-grid {
                display: grid !important;
                grid-template-columns: repeat(3, 1fr) !important;
                gap: 12px !important;
            }
            
            #gallery-grid > div {
                page-break-inside: avoid !important;
                break-inside: avoid !important;
                display: flex !important;
                flex-direction: column !important;
            }
            #gallery-grid > .gallery-video-entry { display: none !important; }
        }
        .is-printing #gallery-grid .gallery-caption-input {
            padding: 6px 12px !important;
            min-height: 52px;
            height: auto !important;
            line-height: 1.55 !important;
            box-sizing: border-box;
            text-align: center !important;
            overflow: visible !important;
            border: 1px solid transparent !important;
        }
        .is-printing #gallery-grid .gallery-meta-row {
            padding: 6px 8px !important;
            box-sizing: border-box;
        }
        .is-printing #gallery-grid .gallery-tag-icon-btn,
        .is-printing #gallery-grid .gallery-tag-editor { display: none !important; }
        .is-printing:not(.print-gallery-tags) #gallery-grid .gallery-tags-row { display: none !important; }
        .is-printing:not(.print-gallery-tags) #gallery-grid .gallery-meta-row { justify-content: center !important; }
        .is-printing.print-gallery-tags #gallery-grid .gallery-meta-row.has-tags {
            display: grid !important;
            grid-template-columns: minmax(12px, 1fr) auto minmax(12px, 1fr) auto minmax(12px, 1fr);
            align-items: center;
            justify-content: stretch !important;
            column-gap: 0 !important;
        }
        .is-printing.print-gallery-tags #gallery-grid .gallery-meta-row.has-tags .gallery-tags-row {
            grid-column: 2;
            justify-content: center !important;
            min-width: 0;
        }
        .is-printing.print-gallery-tags #gallery-grid .gallery-meta-row.has-tags .gallery-meta-date {
            grid-column: 4;
            justify-content: center !important;
        }

        /* PDF export is rendered from dedicated, fixed-size A4 pages instead of the
           continuously scrolling gallery. This keeps every configured row intact. */
        .gallery-pdf-export-root {
            position: fixed;
            left: -10000px;
            top: 0;
            width: var(--gallery-pdf-page-width);
            margin: 0;
            padding: 0;
            color: #334155;
            background: #fff;
            direction: rtl;
            font-family: Tahoma, Arial, sans-serif;
            z-index: -1;
        }
        .gallery-pdf-page {
            width: var(--gallery-pdf-page-width);
            height: var(--gallery-pdf-page-height);
            box-sizing: border-box;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            background: #fff;
            break-inside: avoid-page;
            page-break-inside: avoid;
        }
        .gallery-pdf-page-break {
            break-before: page;
            page-break-before: always;
        }
        .gallery-pdf-header {
            flex: 0 0 18mm;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            border-bottom: 0.25mm solid #e2e8f0;
            text-align: center;
        }
        .gallery-pdf-header h1 {
            margin: 0 0 1.2mm;
            color: #0f5132;
            font-size: 16pt;
            line-height: 1.2;
            font-weight: 900;
        }
        .gallery-pdf-header p {
            margin: 0;
            color: #64748b;
            font-size: 8.5pt;
            line-height: 1.3;
            font-weight: 700;
        }
        .gallery-pdf-grid {
            flex: 1 1 auto;
            min-height: 0;
            padding-top: 3mm;
            box-sizing: border-box;
            display: grid;
            grid-template-columns: repeat(var(--gallery-pdf-columns), minmax(0, 1fr));
            grid-template-rows: repeat(var(--gallery-pdf-rows), minmax(0, 1fr));
            gap: 3mm;
        }
        .gallery-pdf-card {
            min-width: 0;
            min-height: 0;
            overflow: hidden;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
            align-items: stretch;
            padding: 2.2mm;
            border: 0.25mm solid #e2e8f0;
            border-radius: 2.2mm;
            background: #fff;
            break-inside: avoid;
            page-break-inside: avoid;
        }
        .gallery-pdf-card-empty { border-color: transparent; }
        .gallery-pdf-image-wrap {
            flex: 1 1 auto;
            min-height: 8mm;
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 1.5mm;
            background: #f8fafc;
        }
        .gallery-pdf-image-wrap img {
            display: block;
            width: 100%;
            height: 100%;
            object-fit: contain;
        }
        .gallery-pdf-caption {
            flex: 0 0 auto;
            margin-top: 1.5mm;
            color: #334155;
            line-height: 1.35;
            font-weight: 800;
            text-align: center;
            white-space: pre-wrap;
            overflow-wrap: anywhere;
        }
        .gallery-pdf-meta {
            flex: 0 0 auto;
            margin-top: 1mm;
            color: #047857;
            font-size: 6.8pt;
            line-height: 1.25;
            font-weight: 800;
            text-align: center;
            direction: rtl;
            overflow-wrap: anywhere;
        }
        
        /* طراحی چارت لاین هزینه/برآورد */
        .cost-progress-container { position: relative; width: 100%; height: 14px; background-color: #e5e7eb; border-radius: 999px; margin-top: 2.5rem; margin-bottom: 1rem; }
        .cost-progress-bar { height: 100%; border-radius: 999px; transition: all 0.5s ease; position: relative; z-index: 5;}
        .dark .cost-progress-container { background-color: #334155; }
        .estimate-marker { position: absolute; top: 50%; transform: translate(50%, -50%); width: 18px; height: 18px; background-color: #3b82f6; border: 3px solid var(--color-surface); border-radius: 50%; z-index: 10; box-shadow: 0 2px 4px rgba(0,0,0,0.2); }
        .estimate-label { position: absolute; top: -26px; left: 50%; transform: translateX(-50%); white-space: nowrap; font-size: 10px; font-weight: bold; color: #3b82f6; background: var(--color-surface); padding: 2px 6px; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); opacity: 0; pointer-events: none; transition: opacity 0.15s ease; }
        .estimate-marker:hover .estimate-label { opacity: 1; }
        .unit-suggest-box { position: absolute; top: 100%; right: 0; left: 0; margin-top: 2px; background: var(--color-surface); border: 1px solid #e2e8f0; border-radius: 10px; box-shadow: 0 8px 20px rgba(0,0,0,0.12); z-index: 60; max-height: 160px; overflow-y: auto; display: none; }
        .unit-suggest-box.open { display: block; }
        .unit-suggest-item { padding: 6px 10px; font-size: 12px; cursor: pointer; text-align: center; color: #475569; }
        .unit-suggest-item:hover, .unit-suggest-item.active { background: #fff7ed; color: #c2410c; font-weight: bold; }
        .png-export-glass {
            backdrop-filter: blur(18px) saturate(1.15);
            -webkit-backdrop-filter: blur(18px) saturate(1.15);
        }
        .png-export-title {
            letter-spacing: 0;
        }

        /* ---- AI Report module ---- */
        .ai-period-tab { color: #9ca3af; }
        .ai-period-tab.active { background: #fff; color: var(--md-primary, #e07b35); box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
        .ai-settings-tab-btn { color: #6b7280; background: transparent; }
        .ai-settings-tab-btn.active { background: #fff; color: var(--md-primary, #e07b35); box-shadow: 0 1px 5px rgba(0,0,0,0.08); }
        .ai-settings-tab-pane.hidden { display: none; }
        .ai-rep-doc { direction: rtl; text-align: right; font-size: 12px; line-height: 1.9; color: #374151; }
        .ai-rep-doc h1 { font-size: 17px; font-weight: 900; color: #1f2937; margin: 14px 0 6px; }
        .ai-rep-doc h2 { font-size: 14px; font-weight: 800; color: #b45309; margin: 16px 0 6px; padding-bottom: 4px; border-bottom: 1px solid #f3e8d8; }
        .ai-rep-doc h3 { font-size: 12.5px; font-weight: 700; color: #374151; margin: 10px 0 4px; }
        .ai-rep-doc p { margin: 4px 0; }
        .ai-rep-doc ul, .ai-rep-doc ol { margin: 4px 18px 8px 0; padding: 0; }
        .ai-rep-doc li { margin: 2px 0; }
        .ai-rep-doc table { width: 100%; border-collapse: collapse; margin: 8px 0; font-size: 11px; }
        .ai-rep-doc th { background: #fdf3e7; color: #92400e; font-weight: 800; padding: 6px; border: 1px solid #f0dcc3; }
        .ai-rep-doc td { padding: 5px 6px; border: 1px solid #eee2d1; }
        .ai-rep-doc strong { color: #1f2937; }
        .ai-rep-cover-mini { background: linear-gradient(135deg, #fff7ed 0%, #fef3c7 55%, #ffedd5 100%); }
        #ai-report-preview::-webkit-scrollbar { width: 4px; }
        #ai-report-preview::-webkit-scrollbar-thumb { background: #e5e7eb; border-radius: 4px; }

        /* ---- Truncate-with-reveal rows: a long label ellipsizes, but a trailing
           badge (e.g. a percentage in parentheses) always stays visible and a value
           on the other side stays separated by padding. On hover (mice/trackpads)
           the ellipsized text slides to reveal itself in full; on touch devices,
           which have no hover, it slides automatically since the user can't hover
           to trigger it. See initMarqueeTruncate() in js/utils.js. ---- */
        .mq-row { display: flex; align-items: center; min-width: 0; gap: 6px; }
        .mq-clip { position: relative; overflow: hidden; white-space: nowrap; min-width: 0; flex: 1 1 auto; }
        .mq-clip .mq-text { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: top; }
        .mq-clip.mq-overflow .mq-text { max-width: none; text-overflow: clip; will-change: transform; }
        @media (hover: hover) {
            .mq-clip.mq-overflow:hover .mq-text { animation: mq-slide var(--mq-duration, 4.5s) ease-in-out infinite; }
        }
        @media (hover: none) {
            .mq-clip.mq-overflow .mq-text { animation: mq-slide var(--mq-duration, 4.5s) ease-in-out infinite; }
        }
        .mq-suffix, .mq-value { flex-shrink: 0; }
        .mq-suffix { color: #9ca3af; }
        .mq-value { margin-inline-start: auto; padding-inline-start: 6px; }
        /* Keep project labels in landing material/service cards pinned to the
           logical (right/RTL) edge, while quantities remain a stable left column. */
        .vl-project-row { direction: rtl; text-align: right; }
        .vl-project-row .mq-clip { text-align: right; }
        .vl-project-row .mq-text { display: block; width: 100%; text-align: right; }

        /* Dashboard module headers reserve a dedicated, shrinkable title lane so
           long names never overlap the icon or the module action buttons. */
        .dashboard-draggable-module .module-title-marquee {
            min-width: 0;
            flex: 1 1 auto;
            overflow: hidden;
        }
        .dashboard-draggable-module:hover .module-title-marquee .mq-clip.mq-overflow .mq-text,
        .dashboard-draggable-module:focus-within .module-title-marquee .mq-clip.mq-overflow .mq-text {
            animation: mq-slide var(--mq-duration, 4.5s) ease-in-out infinite;
        }
        #calendar-project-title.module-title-marquee {
            display: flex;
            align-items: center;
            justify-content: flex-start;
            padding-inline: 0;
        }
        .calendar-module-topbar {
            display: flex;
            align-items: center;
            gap: 8px;
            min-width: 0;
        }
        .calendar-module-topbar #calendar-project-title {
            position: static;
            min-width: 0;
            flex: 1 1 auto;
            text-align: right;
            white-space: nowrap;
            overflow: hidden;
        }
        .calendar-module-topbar .module-header-actions {
            flex: 0 0 auto;
        }

        /* ---- Segmented spend bar (services analytics card): each colored slice
           shows its label + amount in a small tooltip on hover, matching the
           .estimate-marker/.estimate-label pattern used for the financial chart. ---- */
        .svc-seg { position: relative; }
        .svc-seg .svc-seg-tip { position: absolute; bottom: 100%; right: 50%; transform: translate(50%, -6px); white-space: nowrap; background: #1f2937; color: #fff; font-size: 10px; font-weight: 700; padding: 4px 8px; border-radius: 8px; opacity: 0; pointer-events: none; transition: opacity 0.15s ease; z-index: 20; }
        .svc-seg:hover { filter: brightness(1.08); }
        .svc-seg:hover .svc-seg-tip { opacity: 1; }

        /* ---- Pill selects (documents module filter bar): a plain <select> relies on
           the browser's own dropdown arrow, which doesn't reserve any space for
           itself — in a narrow pill it sits on top of the selected option's text.
           This draws our own arrow and pads the text away from it instead. ---- */
        .pill-select {
            appearance: none; -webkit-appearance: none; -moz-appearance: none;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%239ca3af'%3E%3Cpath fill-rule='evenodd' d='M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z' clip-rule='evenodd'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: left 0.5rem center;
            background-size: 14px;
            padding-left: 1.7rem !important;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        /* Chromium's customizable picker bypasses the macOS dark native menu and lets
           the opened option surface follow the app's light-only design. Browsers that
           do not support it keep the strengthened color-scheme fallback above. */
        @supports (appearance: base-select) {
            select {
                appearance: base-select !important;
                -webkit-appearance: base-select !important;
            }
            ::picker(select) {
                appearance: base-select;
                color-scheme: only light;
                background: #ffffff;
                color: #1f2937;
                border: 1px solid #e2e8f0;
                border-radius: 14px;
                box-shadow: 0 16px 36px rgba(15, 23, 42, 0.16);
                padding: 6px;
            }
            select option,
            select optgroup {
                background: #ffffff;
                color: #1f2937;
            }
            select option {
                border-radius: 9px;
                padding: 8px 12px;
            }
            select option:hover,
            select option:focus,
            select option:checked {
                background: color-mix(in srgb, var(--color-primary) 12%, white);
                color: #1f2937;
            }
            .pill-select::picker-icon {
                display: none;
            }
        }
        @keyframes mq-slide {
            0%, 12% { transform: translateX(0); }
            50%, 62% { transform: translateX(var(--mq-shift, 0px)); }
            100% { transform: translateX(0); }
        }

        /* ── Mobile motion diet ──────────────────────────────────────────────
           On phones, decorative/idle animations and the long (0.4s) transitions
           burn CPU/GPU — and the looping ones hold that cost the whole time the
           screen just sits there — for no functional gain. Strip them here only;
           desktop keeps every animation. This is safe: no JS depends on
           transitionend/animationend (only requestAnimationFrame), and menus/
           modals reveal via display/.hidden toggles, not the transition itself.
           Active-work feedback (loading spinners, save/upload progress stripes)
           is deliberately NOT touched — it must keep moving to signal work. */
        @media (max-width: 1023.98px) {
            *, *::before, *::after {
                transition-duration: 0.01ms !important;
                transition-delay: 0ms !important;
                scroll-behavior: auto !important;
            }
            /* Looping/idle & entrance animations that don't signal active work. */
            .animate-pulse,
            #landing-notif-bell-icon,
            .landing-notif-row,
            .mq-clip.mq-overflow .mq-text,
            .mq-clip.mq-overflow:hover .mq-text,
            .module-mobile-menu-panel.open {
                animation: none !important;
            }
        }

        /* ════════════════ Phone aspect-ratio fit (16:9 and 19.5:9 screens) ═══════════════
           Real mobile browsers make 100vh the LARGEST viewport (URL bar hidden), so any
           h-screen/overflow-hidden shell leaves its bottom strip behind the browser chrome —
           and because the page itself never scrolls (inner areas do), the bar never
           collapses and that strip is permanently invisible. dvh tracks the actually
           visible height on every aspect ratio; vh stays as the fallback for old engines. */
        #dashboard-view,
        #admin-view {
            height: 100vh;
            height: 100dvh;
            min-height: 0;
        }
        #login-view,
        #landing-view {
            min-height: 100vh;
            min-height: 100dvh;
        }
        /* Full-screen overlays (slideshow player) get the same treatment. */
        #modal-slider-player.modal-overlay {
            height: 100vh;
            height: 100dvh;
        }

        /* Project cover preview: this is deliberately a presentation frame only.
           The image itself is not cropped or rewritten to this aspect ratio. */
        #modal-edit-info .edit-info-media {
            width: 100%;
            height: auto;
            aspect-ratio: 16 / 9;
            min-height: 180px;
            max-height: 300px;
            border: 2px dashed rgba(148, 163, 184, 0.55);
            border-radius: 1.25rem;
            background: #f8fafc;
            transition: border-color 160ms ease, background-color 160ms ease, transform 160ms ease;
        }
        #modal-edit-info #edit-info-image {
            background-size: contain;
            background-position: center;
            background-repeat: no-repeat;
        }
        #modal-edit-info .edit-info-media.is-dragover {
            border-color: #ff7a00;
            background: #fff7ed;
            transform: scale(0.995);
        }
        #modal-edit-info .edit-info-drop-hint {
            background: linear-gradient(to top, rgba(15, 23, 42, 0.72), transparent);
        }
        /* Home-indicator/notch clearance for the floating action buttons; env() is 0
           on devices without insets, so this is inert everywhere else. */
        #comments-btn {
            bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px)) !important;
        }

        @media (max-width: 767.98px) {
            /* ── Dashboard header: at ≤400px the fixed-width items (home 40 + pill +
               save 48 + bell 40 + profile 40 + gaps + padding) exceeded the viewport and
               pushed the profile button off-screen. Tighten paddings/gaps and give the
               project pill a viewport-scaled, shrinkable title lane so the row always
               fits — on 320px devices just as on 360/390/430px ones. ── */
            #dashboard-header { padding-inline: 0.75rem; }
            #dashboard-header > div { gap: 0.375rem; }
            #dashboard-header > div > div { gap: 0.375rem; min-width: 0; }
            #dashboard-project-switcher { min-width: 0; flex: 1 1 auto; }
            #dashboard-project-pill { max-width: 100%; padding-inline: 0.625rem; }
            #dashboard-project-title { max-width: clamp(56px, 22vw, 110px); }
            #dashboard-project-search-input { width: clamp(56px, 22vw, 110px); }

            /* ── Viewer event/photo lightbox on mobile: the desktop sizing (fixed
               62vh stage + object-cover to fill it edge-to-edge) doesn't fit here —
               needs a smaller card, and no forced-box cropping. Letting the image
               size itself (width/height: auto, capped by max-width/max-height) means
               the box shrinks to exactly the image's own aspect ratio instead of a
               fixed rectangle with the image floating inside it — that's what
               produces blank top/bottom bars with object-fit: contain, since the
               *box* stays a fixed shape even though the image itself doesn't crop.
               Portrait gets a phone-width card; landscape (where vw is generous and
               vh is tight) gets a much wider one, sized off both viewport axes so it
               scales with the actual screen instead of a hardcoded breakpoint. */
            #modal-vl-event > div { max-width: min(92vw, 26rem); max-height: 78vh; padding: 1rem; }
            #vl-event-image-stage { height: auto; max-height: 60vh; display: flex; justify-content: center; }
            #vl-event-image { width: auto; height: auto; max-width: 100%; max-height: 60vh; object-fit: contain; }

            /* The bell's dropdown is wider than the space left of the bell once the
               header actually fits, so span it across the header instead: the header is
               position:relative, so making the wrapper static re-anchors the absolute
               panel to the header itself. z-index moves to the panel (the static wrapper
               can no longer stack) so the notif-focus elevation keeps working. */
            #notif-wrapper { position: static; }
            #notif-panel {
                left: 0.75rem;
                right: 0.75rem;
                top: calc(100% + 4px);
                width: auto;
                max-width: none;
            }

            /* ── Kanban: all three columns stay visible side-by-side, no swiping.
               Cards render as a one-word AI keyword on mobile (see kanban.js), so a
               third of the screen width per column is enough. The module height is
               capped so the whole board fits a 19.5:9 phone screen; each column
               scrolls internally when it holds more cards than fit. Applies both
               inline and inside the expand modal (the node moves with its id). ── */
            .kanban-board-grid { gap: 0.375rem; }
            .kanban-board-grid .kanban-column { padding: 0.3rem; min-height: 0; }
            #module-kanban { min-height: 360px; max-height: min(72vh, 640px); }
            .kb-task-mobile { cursor: pointer; }

            /* ── Edit-info modal: the 96px square thumbnail looks dull on a phone —
               let the project image span the modal's full width there. Module views
               are untouched; this is the settings dialog only. ── */
            #modal-edit-info .edit-info-media {
                width: 100%;
                height: auto;
                min-height: 150px;
                aspect-ratio: 16 / 9;
                border-radius: 1rem;
            }
            /* ── Financial: the desktop-saved module height left only ~2 resource cards
               visible on a phone. Force it ~5cm taller here (the !important also beats
               any inline height applySavedModuleHeights carried over from desktop). ── */
            #module-financial {
                height: auto !important;
                min-height: 730px !important;
                max-height: 850px !important;
            }

            /* ── Modal tab bars (material/service edit, slideshow settings): fixed
               paddings clipped the last tab at ≤370px with no way to reach it. Let the
               bar scroll horizontally (scrollbar hidden, snap per tab) and drop the
               padding a notch so 3-4 tabs usually fit outright. ── */
            #mat-tabs,
            #srv-tabs,
            #ss-tabs {
                overflow-x: auto;
                flex-wrap: nowrap;
                scrollbar-width: none;
                -webkit-overflow-scrolling: touch;
                scroll-snap-type: x proximity;
            }
            #mat-tabs::-webkit-scrollbar,
            #srv-tabs::-webkit-scrollbar,
            #ss-tabs::-webkit-scrollbar { display: none; }
            #mat-tabs > button,
            #srv-tabs > button,
            #ss-tabs > button {
                flex: 0 0 auto;
                white-space: nowrap;
                scroll-snap-align: start;
                padding-inline: 0.625rem;
            }

            /* ── Material-transfer hub: the filter row (search + 2 selects) is wider
               than a phone; the search input's long placeholder blocks shrinking. Wrap
               to two lines: full-width search, selects sharing the second row. ── */
            #modal-material-transfer-hub .flex.items-center.gap-2 { flex-wrap: wrap; }
            #modal-material-transfer-hub #hub-mat-search { min-width: 0; }
            #modal-material-transfer-hub .flex-1.relative { flex: 1 1 100%; }
            #hub-mat-project-filter,
            #hub-mat-sort { flex: 1 1 0; min-width: 0; max-width: none; }

            /* ── Landing report-type cards: icon-beside-label needs more than a third
               of a small phone's width, so the row clipped. Stack icon over label. ── */
            #vl-report-type-list > button {
                flex-direction: column;
                align-items: center;
                text-align: center;
                gap: 0.375rem;
                padding: 0.625rem 0.25rem;
                min-width: 0;
            }
            #vl-report-type-list > button > span:last-child { width: 100%; }
            #vl-report-type-list > button .block.font-black {
                font-size: 10.5px;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
        }
        /* Landscape phones have generous width and tight height — give the event/
           photo lightbox a much wider card and a slightly taller image allowance,
           on top of the portrait-oriented defaults set in the block above. */
        @media (max-width: 767.98px) and (orientation: landscape) {
            #modal-vl-event > div { max-width: min(92vw, 56rem); max-height: 88vh; }
            #vl-event-image-stage { max-height: 70vh; }
            #vl-event-image { max-height: 70vh; }
        }
        /* Landing directory details use the full mobile viewport. These directories
           can contain many projects/transactions, so a content-sized bottom sheet
           wastes most of the screen and leaves too little room for the table. */
        @media (max-width: 600px) {
            #modal-vl-material-detail,
            #modal-vl-service-detail {
                align-items: stretch;
                padding: 0;
            }
            #modal-vl-material-detail > div,
            #modal-vl-service-detail > div {
                height: 100dvh !important;
                max-height: 100dvh;
                max-width: none;
                padding: max(12px, env(safe-area-inset-top)) 12px max(10px, env(safe-area-inset-bottom));
                border-radius: 0;
                background:
                    radial-gradient(circle at 88% 0, rgba(249, 115, 22, .09), transparent 150px),
                    var(--color-surface, #fff);
            }
            #modal-vl-material-detail > div > div:first-child,
            #modal-vl-service-detail > div > div:first-child {
                position: relative;
                padding-bottom: 14px;
                margin-bottom: 12px;
                border-bottom-color: rgba(249, 115, 22, .13);
            }
            #modal-vl-material-detail > div > div:first-child > div:first-child,
            #modal-vl-service-detail > div > div:first-child > div:first-child {
                width: 100%;
                padding-left: 86px;
            }
            #modal-vl-material-detail > div > div:first-child > div:last-child,
            #modal-vl-service-detail > div > div:first-child > div:last-child {
                position: absolute;
                left: 0;
                top: 0;
                z-index: 2;
            }
            #vl-material-detail-icon,
            #vl-service-detail-icon {
                width: 42px;
                height: 42px;
                border-radius: 14px;
                box-shadow: 0 8px 22px rgba(249, 115, 22, .12);
            }
            #vl-material-detail-title,
            #vl-service-detail-title {
                font-size: 16px;
            }
            #vl-material-detail-cost,
            #vl-service-detail-cost {
                max-width: 100%;
                overflow: hidden;
            }
            #vl-material-detail-cost [data-detail-cost-text],
            #vl-service-detail-cost [data-detail-cost-text] {
                overflow: hidden;
                text-overflow: ellipsis;
            }
            #vl-material-detail-back:not(.hidden) + #vl-material-detail-icon,
            #vl-service-detail-back:not(.hidden) + #vl-service-detail-icon {
                display: none;
            }
            #vl-material-detail-list-view,
            #vl-service-detail-list-view {
                flex: 1 1 0%;
                max-height: none;
                border: 0;
                border-radius: 18px;
                background: rgba(248, 250, 252, .72);
                padding: 6px;
            }
            #vl-material-detail-txn-view,
            #vl-service-detail-txn-view {
                flex: 1 1 0%;
                max-height: none;
            }
            #vl-material-detail-txn-view > div:last-child,
            #vl-service-detail-txn-view > div:last-child {
                max-height: none;
                border-radius: 18px;
            }
            #vl-material-detail-list-view table,
            #vl-service-detail-list-view table,
            #vl-material-detail-txn-view table,
            #vl-service-detail-txn-view table {
                font-size: 12px;
            }
            #vl-material-detail-list-view thead,
            #vl-service-detail-list-view thead,
            #vl-material-detail-txn-view thead,
            #vl-service-detail-txn-view thead {
                background: linear-gradient(135deg, #fff7e8, #fff1d5);
            }
            #vl-material-detail-list-view th,
            #vl-service-detail-list-view th,
            #vl-material-detail-txn-view th,
            #vl-service-detail-txn-view th {
                padding: 10px 8px;
                font-size: 10px;
                line-height: 1.5;
            }
            #vl-material-detail-list-view td,
            #vl-service-detail-list-view td,
            #vl-material-detail-txn-view td,
            #vl-service-detail-txn-view td {
                padding: 11px 8px;
                line-height: 1.65;
            }
            #vl-material-detail-list-view tbody tr:nth-child(odd),
            #vl-service-detail-list-view tbody tr:nth-child(odd),
            #vl-material-detail-txn-view tbody tr:nth-child(odd),
            #vl-service-detail-txn-view tbody tr:nth-child(odd) {
                background: rgba(255, 255, 255, .82);
            }
            #vl-material-detail-list-view tbody tr,
            #vl-service-detail-list-view tbody tr {
                box-shadow: inset 3px 0 0 rgba(249, 115, 22, .18);
            }
            #vl-material-detail-txn-summary > div {
                padding: 12px;
                gap: 10px;
                background: linear-gradient(135deg, rgba(249,115,22,.07), rgba(46,125,50,.05));
            }
        }

        /* Warehouse-origin transfers intentionally have their own visual language.
           These selectors are activated only by JS on the exact-lot warehouse flow,
           leaving ordinary project transfers untouched. */
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-shell,
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-shell {
            background: #f7fbf9;
            border: 1px solid rgba(16, 185, 129, .2);
            box-shadow: 0 28px 80px rgba(2, 44, 34, .32);
        }
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-header,
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-header {
            color: white;
            border-color: rgba(255, 255, 255, .1);
            background:
                radial-gradient(circle at 8% -20%, rgba(251, 191, 36, .22), transparent 34%),
                linear-gradient(135deg, #022c22 0%, #064e3b 58%, #065f46 100%);
        }
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-title,
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-title { color: #fff; }
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-close,
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-close {
            width: 40px;
            height: 40px;
            border-radius: 999px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            color: rgba(255,255,255,.72);
            background: rgba(255,255,255,.09);
        }
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-close:hover,
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-close:hover { color: white; background: rgba(255,255,255,.16); }
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-kicker,
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-kicker { display: flex; }
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-filterbar {
            margin: 0 -4px -2px;
            padding: 10px;
            border-radius: 18px;
            background: rgba(255,255,255,.1);
            border: 1px solid rgba(255,255,255,.1);
        }
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-filterbar input,
        #modal-material-transfer-hub.warehouse-transfer-theme .warehouse-transfer-filterbar select {
            background: rgba(255,255,255,.94);
            border-color: rgba(167,243,208,.75);
        }
        #modal-material-transfer-hub.warehouse-transfer-theme #hub-mat-list { background: linear-gradient(180deg, #f0fdf7 0%, #f8faf9 100%); }
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-source-summary { display: flex; }
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-form { background: linear-gradient(180deg, #f0fdf4 0%, #f8faf9 100%); }
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-form label { color: #047857; }
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-form input,
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-form select {
            background: rgba(255,255,255,.96);
            border-color: #a7f3d0;
            border-radius: 14px;
            min-height: 42px;
        }
        #modal-hub-transfer-pick.warehouse-transfer-theme .warehouse-transfer-footer { background: #ecfdf5; border-color: #d1fae5; }
        #modal-hub-transfer-pick.warehouse-transfer-theme #hub-transfer-submit { background: #047857; box-shadow: 0 10px 24px rgba(4,120,87,.25); }
        #modal-hub-transfer-pick.warehouse-transfer-theme #hub-transfer-submit:hover { background: #065f46; }

        /* Weather intelligence. Project-local Chabahar-inspired images rotate daily
           while the dark blend keeps the operational message visually dominant. */
        .weather-alert-card {
            position: relative; overflow: hidden; color: #fff; background-color: #172033;
            background-image: url('assets/weather/alert-coast.webp');
            background-size: cover; background-position: center; background-blend-mode: multiply;
            border: 1px solid rgba(255,255,255,.16); box-shadow: 0 18px 44px rgba(15,23,42,.22);
        }
        .weather-alert-card::before { content: ''; position: absolute; inset: 0; background: rgba(12,20,35,.48); pointer-events: none; }
        .weather-alert-card.weather-severity-critical { background-color: #651f2b; }
        .weather-alert-card.weather-severity-high { background-color: #71390e; }
        .weather-alert-card.weather-severity-watch { background-color: #164e63; }
        .weather-alert-card.weather-urgent { background-color: #8f1628; border-color: rgba(254,202,202,.55); box-shadow: 0 18px 52px rgba(127,29,29,.34); }
        .weather-alert-content { position: relative; z-index: 2; }
        .weather-alert-toolbar { display: flex; align-items: center; gap: .55rem; min-width: 0; }
        .weather-kicker { letter-spacing: .08em; }
        .weather-alert-dismiss, .weather-alert-action { width: 2rem; height: 2rem; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; border: 1px solid rgba(255,255,255,.16); background: rgba(255,255,255,.12); color: rgba(255,255,255,.9); transition: background .18s ease, transform .18s ease; }
        .weather-alert-dismiss:hover, .weather-alert-action:hover { background: rgba(255,255,255,.23); transform: translateY(-1px); }
        .weather-urgent-indicator { display: none; align-items: center; gap: .3rem; color: #fee2e2; font-size: .62rem; font-weight: 900; white-space: nowrap; }
        .weather-alert-card.weather-urgent .weather-alert-dismiss { display: none; }
        .weather-alert-card.weather-urgent .weather-urgent-indicator { display: inline-flex; }
        .weather-ticker-shell { display: none; min-width: 0; flex: 1 1 auto; overflow: hidden; border-inline: 1px solid rgba(255,255,255,.14); direction: ltr; }
        .weather-ticker-track { display: flex; align-items: center; gap: 2rem; width: max-content; padding-inline: 1.25rem; animation: weather-ticker 56s linear infinite; white-space: nowrap; font-size: .7rem; color: rgba(255,255,255,.9); direction: ltr; }
        .weather-ticker-item { display: inline-flex; align-items: center; gap: .35rem; direction: rtl; }
        .weather-ticker-item::after { content: '•'; margin-inline-start: 1.65rem; color: #fbbf24; }
        @keyframes weather-ticker { from { transform: translateX(-50%); } to { transform: translateX(0); } }
        .weather-alert-card.weather-collapsed .weather-alert-content { padding: .65rem 1rem; }
        .weather-alert-card.weather-collapsed .weather-expanded, .weather-alert-card.weather-collapsed .weather-toolbar-meta { display: none; }
        .weather-alert-card.weather-collapsed .weather-ticker-shell { display: block; }
        .weather-alert-card.weather-collapsed .weather-alert-toolbar { min-height: 2rem; margin-bottom: 0 !important; }
        .weather-alert-card.weather-collapsed #landing-weather-toggle { background: rgba(255,255,255,.22); }
        .weather-metric-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
        .weather-metric { padding: .7rem .9rem; border-inline-start: 1px solid rgba(255,255,255,.15); }
        .weather-metric:first-child { border-inline-start: 0; }
        .weather-metric strong { display: block; font-size: .95rem; line-height: 1.25; }
        .weather-metric span { display: block; margin-top: .2rem; font-size: .62rem; color: rgba(255,255,255,.68); }
        .weather-risk-list li { position: relative; padding-right: 1rem; }
        .weather-risk-list li::before { content: ''; position: absolute; right: 0; top: .55em; width: .36rem; height: .36rem; border-radius: 50%; background: #fbbf24; }
        .weather-motion { position: absolute; inset: 0; z-index: 1; overflow: hidden; pointer-events: none; opacity: .55; }
        .weather-rain-drop { position: absolute; top: -20%; width: 1px; height: 20px; background: rgba(191,219,254,.72); transform: rotate(12deg); animation: weather-rain 1.6s linear infinite; }
        .weather-wind-leaf { position: absolute; right: -8%; color: rgba(253,230,138,.72); font-size: 15px; animation: weather-leaf 5.5s linear infinite; }
        @keyframes weather-rain { to { transform: translate(-70px, 620px) rotate(12deg); } }
        @keyframes weather-leaf { 0% { transform: translateX(0) rotate(0); } 100% { transform: translateX(-115vw) translateY(55px) rotate(-430deg); } }
        @media (prefers-reduced-motion: reduce) { .weather-rain-drop, .weather-wind-leaf { animation: none; display: none; } .weather-ticker-track { animation: none; } }
        #module-weather { position: relative; overflow: hidden; color: #fff; background-color: #164e63; background-image: url('/assets/weather/alert-coast.webp'); background-size: cover; background-position: center; background-blend-mode: multiply; border-color: rgba(255,255,255,.16); }
        #module-weather::before { content: ''; position: absolute; inset: 0; background: rgba(10,24,39,.57); pointer-events: none; }
        #module-weather::after { content: ''; position: absolute; inset: 0 auto 0 0; width: 4px; background: #38bdf8; }
        #module-weather.is-alerting { background-color: #71390e; }
        #module-weather.is-critical { background-color: #8f1628; box-shadow: 0 18px 40px rgba(127,29,29,.23); }
        #module-weather.is-alerting::after { background: #fbbf24; }
        #module-weather.is-critical::after { background: #fecaca; }
        .project-weather-content { position: relative; z-index: 2; }
        .project-weather-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .25rem; }
        .project-weather-metrics > div { text-align: center; padding: .6rem .25rem; border-inline-start: 1px solid rgba(255,255,255,.14); }
        .project-weather-metrics > div:first-child { border-inline-start: 0; }
        #module-weather .project-weather-metrics { border-color: rgba(255,255,255,.13); background: rgba(0,0,0,.18); }
        #module-weather .project-weather-metrics .material-symbols-rounded { color: rgba(255,255,255,.78) !important; }
        #module-weather .project-weather-metrics strong { color: #fff !important; }
        #module-weather .project-weather-metrics span:last-child { color: rgba(255,255,255,.62) !important; }
        #modal-weekly-weather { background: rgba(2,8,23,.62); backdrop-filter: blur(14px) saturate(1.15); }
        .weekly-weather-dialog { position: relative; isolation: isolate; width: min(72rem, calc(100vw - 2rem)); color: #fff; background-color: rgba(9,18,34,.68); background-size: cover; background-position: center; border: 1px solid rgba(255,255,255,.2); box-shadow: 0 34px 90px rgba(0,0,0,.48), inset 0 1px rgba(255,255,255,.13); backdrop-filter: blur(28px) saturate(1.25); }
        .weekly-weather-dialog::before { content: ''; position: absolute; inset: 0; z-index: 0; background: linear-gradient(135deg, rgba(4,12,28,.78), rgba(14,32,51,.48) 55%, rgba(4,12,28,.72)); pointer-events: none; }
        .weekly-weather-header, .weekly-weather-body { position: relative; z-index: 3; }
        .weekly-weather-header { border-color: rgba(255,255,255,.13); background: rgba(255,255,255,.045); }
        .weekly-weather-close { color: rgba(255,255,255,.78); border: 1px solid rgba(255,255,255,.14); background: rgba(255,255,255,.08); transition: transform .2s ease, background .2s ease; }
        .weekly-weather-close:hover { color: #fff; background: rgba(255,255,255,.18); transform: rotate(5deg) scale(1.04); }
        .weekly-weather-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: .65rem; }
        .weekly-weather-day { position: relative; min-width: 0; overflow: hidden; border: 1px solid rgba(255,255,255,.16); border-radius: 1.25rem; padding: 1rem .65rem; text-align: center; color: #fff; background: linear-gradient(145deg, rgba(255,255,255,.16), rgba(255,255,255,.055)); box-shadow: inset 0 1px rgba(255,255,255,.17), 0 12px 30px rgba(0,0,0,.14); backdrop-filter: blur(18px) saturate(1.2); transition: transform .25s ease, border-color .25s ease, background .25s ease; animation: weekly-card-arrive .55s both; animation-delay: calc(var(--day-index) * 55ms); }
        .weekly-weather-day::before { content: ''; position: absolute; inset: 0; background: linear-gradient(120deg, transparent 20%, rgba(255,255,255,.09), transparent 70%); transform: translateX(110%); transition: transform .55s ease; }
        .weekly-weather-day:hover { transform: translateY(-5px); border-color: rgba(255,255,255,.34); background: linear-gradient(145deg, rgba(255,255,255,.22), rgba(255,255,255,.08)); }
        .weekly-weather-day:hover::before { transform: translateX(-110%); }
        .weekly-weather-day.is-danger { border-color: rgba(251,191,36,.58); box-shadow: inset 0 1px rgba(255,255,255,.18), 0 15px 34px rgba(120,53,15,.24); }
        .weekly-weather-day.is-today { border-color: rgba(103,232,249,.72); background: linear-gradient(145deg, rgba(34,211,238,.22), rgba(255,255,255,.06)); }
        .weekly-weather-icon { display: inline-flex; align-items: center; justify-content: center; width: 3.2rem; height: 3.2rem; border-radius: 999px; color: #e0f2fe; background: rgba(255,255,255,.1); box-shadow: inset 0 1px rgba(255,255,255,.2); animation: weekly-icon-float 3.8s ease-in-out infinite; }
        .weekly-weather-stats { display: grid; grid-template-columns: 1fr 1fr; gap: .3rem; margin-top: .8rem; }
        .weekly-weather-stat { display: flex; align-items: center; justify-content: center; gap: .2rem; padding: .35rem .2rem; border-radius: .65rem; font-size: .58rem; color: rgba(255,255,255,.72); background: rgba(3,10,24,.18); white-space: nowrap; }
        .weekly-weather-risk { display: inline-flex; align-items: center; gap: .2rem; margin-top: .55rem; padding: .28rem .48rem; border-radius: 999px; color: #fef3c7; background: rgba(180,83,9,.3); border: 1px solid rgba(251,191,36,.28); font-size: .56rem; font-weight: 900; }
        .weekly-weather-scene { position: absolute; inset: 0; z-index: 1; overflow: hidden; pointer-events: none; opacity: .78; }
        .weekly-scene-rain .weekly-rain-drop { position: absolute; top: -18%; left: var(--x); width: 1.5px; height: var(--length); border-radius: 999px; background: linear-gradient(to bottom, transparent, rgba(186,230,253,.85)); transform: rotate(10deg); animation: weekly-rain-fall var(--speed) linear infinite; animation-delay: var(--delay); }
        .weekly-scene-snow .weekly-snow-flake { position: absolute; top: -8%; left: var(--x); width: var(--size); height: var(--size); border-radius: 50%; background: rgba(255,255,255,.86); filter: blur(.3px); animation: weekly-snow-fall var(--speed) linear infinite; animation-delay: var(--delay); }
        .weekly-cloud { position: absolute; width: var(--size); height: calc(var(--size) * .28); top: var(--y); right: calc(var(--size) * -1); border-radius: 999px; background: rgba(226,232,240,.13); filter: blur(7px); animation: weekly-cloud-drift var(--speed) linear infinite; animation-delay: var(--delay); }
        .weekly-wind-line { position: absolute; top: var(--y); right: -30%; width: var(--width); height: 1px; border-radius: 999px; background: linear-gradient(to left, transparent, rgba(224,242,254,.62), transparent); animation: weekly-wind-sweep var(--speed) ease-in-out infinite; animation-delay: var(--delay); }
        .weekly-fog-band { position: absolute; right: -20%; width: 140%; height: 16%; top: var(--y); border-radius: 50%; background: rgba(226,232,240,.11); filter: blur(18px); animation: weekly-fog-breathe var(--speed) ease-in-out infinite alternate; }
        .weekly-sun-glow { position: absolute; width: 38rem; height: 38rem; left: -12rem; top: -18rem; border-radius: 50%; background: radial-gradient(circle, rgba(253,224,71,.28), rgba(251,146,60,.08) 42%, transparent 70%); animation: weekly-sun-pulse 5s ease-in-out infinite; }
        .weekly-lightning { position: absolute; inset: 0; background: rgba(224,242,254,.22); opacity: 0; animation: weekly-lightning-flash 7s steps(1) infinite; }
        @keyframes weekly-card-arrive { from { opacity: 0; transform: translateY(16px) scale(.97); } to { opacity: 1; transform: none; } }
        @keyframes weekly-icon-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
        @keyframes weekly-rain-fall { to { transform: translate(-90px, 820px) rotate(10deg); } }
        @keyframes weekly-snow-fall { to { transform: translate(-55px, 800px) rotate(240deg); } }
        @keyframes weekly-cloud-drift { to { transform: translateX(-150vw); } }
        @keyframes weekly-wind-sweep { 0% { transform: translateX(0); opacity: 0; } 20%,70% { opacity: 1; } 100% { transform: translateX(-145vw); opacity: 0; } }
        @keyframes weekly-fog-breathe { to { transform: translateX(-8%) scaleY(1.25); opacity: .55; } }
        @keyframes weekly-sun-pulse { 50% { transform: scale(1.08); opacity: .72; } }
        @keyframes weekly-lightning-flash { 0%, 91%, 94%, 100% { opacity: 0; } 92%, 95% { opacity: .38; } 93% { opacity: .08; } }
        @media (prefers-reduced-motion: reduce) { .weekly-weather-day, .weekly-weather-icon, .weekly-rain-drop, .weekly-snow-flake, .weekly-cloud, .weekly-wind-line, .weekly-fog-band, .weekly-sun-glow, .weekly-lightning { animation: none !important; } }
        .report-day-weather { border: 1px solid #bae6fd; background: #f0f9ff; border-radius: 1.1rem; padding: .8rem 1rem; margin-bottom: 1rem; }
        .report-day-weather-stats { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .35rem; }
        .report-day-weather-stats > span { display: flex; align-items: center; justify-content: center; gap: .25rem; min-width: 0; padding: .35rem; border-radius: .7rem; background: rgba(255,255,255,.72); color: #475569; font-size: .66rem; white-space: nowrap; }
        .weather-mini-icon { position: absolute; right: 2px; top: 2px; font-size: 13px !important; color: #0284c7; opacity: 1; filter: drop-shadow(0 1px 1px white); }
        .calendar-day:has(.weather-mini-icon) { opacity: 1 !important; }
        @media (max-width: 639px) {
            .weather-alert-toolbar { flex-wrap: wrap; }
            .weather-ticker-shell { order: 5; flex-basis: 100%; height: 1.7rem; border-inline: 0; border-top: 1px solid rgba(255,255,255,.12); padding-top: .35rem; }
            .weather-alert-card.weather-collapsed .weather-alert-content { padding: .55rem .7rem; }
            .weather-alert-card.weather-collapsed .weather-urgent-indicator { font-size: 0; }
            .weather-alert-card.weather-collapsed .weather-urgent-indicator .material-symbols-rounded { font-size: 15px; }
            .weather-metric-row, .project-weather-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            .weather-metric:nth-child(3), .project-weather-metrics > div:nth-child(3) { border-inline-start: 0; }
            .weather-metric:nth-child(n+3) { border-top: 1px solid rgba(255,255,255,.15); }
            .project-weather-metrics > div:nth-child(n+3) { border-top: 1px solid rgba(255,255,255,.14); }
            .weekly-weather-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            .report-day-weather-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            #module-weather { order: 2; }
            #module-kanban { order: 3; } #module-charts { order: 4; } #module-slider { order: 5; }
            #module-financial { order: 6; } #module-gallery { order: 7; } #module-invoice { order: 8; }
            #module-calendar { order: 9; } #module-wbs { order: 10; } #module-documents { order: 11; } #module-report { order: 12; }
        }
        @media (min-width: 640px) and (max-width: 1023px) { .weekly-weather-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
