/* 09-gallery - part of the former static/style.css. * * The stylesheet is split by area, but the split is mechanical: rules were * moved verbatim and NOT regrouped. The numeric prefix is the load order, and * that order is what preserves the cascade. A rule sits in the file its * original position fell into, so an area's rules are not all in its file and * a file is not only that area's rules. * * Do not reorder the tags in static/index.html, and do not move a rule * between files: with 1,151 redeclarations and 1,886 !important in this * cascade, either can change which declaration wins. * * static/index.html and the PRECACHE list in static/sw.js must agree on this * order. tests/test_static_stylesheet_manifest.py and * tests/test_css_computed_style_snapshot.py fail if that drifts. */ .email-reader-actions-inline.email-reader-actions-compact > .reader-action-overflowed { display: none !important; } /* ── Archive browser ── */ .archive-list { grid-template-columns: 1fr !important; gap: 4px !important; } .archive-row { flex-direction: row !important; } .archive-row .doclib-card-header { flex: 1; padding: 8px 10px; } .archive-row .doclib-card-title { flex: 1; min-width: 0; } /* Archive column layout */ .archive-header { display: flex; align-items: center; padding: 4px 10px; gap: 6px; font-size: 9px; font-weight: 600; opacity: 0.35; text-transform: uppercase; letter-spacing: 0.5px; } .archive-col-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-left: -4px; } .archive-col-msgs { width: 40px; flex-shrink: 0; text-align: left; margin-left: -8px; } .archive-col-model { width: 90px; flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .archive-col-time { width: 55px; flex-shrink: 0; text-align: right; } .archive-col-menu { width: 24px; flex-shrink: 0; } .archive-menu-btn { background: transparent; border: none; color: var(--fg); opacity: 0.3; cursor: pointer; padding: 4px; line-height: 0; border-radius: 4px; transition: background .15s, opacity .15s; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; margin-left: 4px; vertical-align: middle; height: 100%; } .archive-menu-btn svg { display: block; margin-top: -6px; } .archive-menu-btn:hover { background: color-mix(in srgb, var(--fg) 10%, transparent); opacity: 1; } /* ── Gallery (image library) ── */ .gallery-modal-content { width: min(820px, 94vw); max-height: 92vh; font-size: 12px; } /* Keep the Gallery frame anchored while tabs or bulk-selection controls change the content height. Centering the frame made every shorter tab move the whole window vertically. */ @media (min-width: 769px) { #calendar-modal, #gallery-modal, #tasks-modal, #memory-modal, #doclib-modal, #compare-model-overlay, #research-overlay, #theme-modal, #settings-modal, #email-lib-modal, #cookbook-modal { align-items: flex-start; padding-top: 4vh; box-sizing: border-box; } } /* While the Edit tab is active the editor needs a *definite* height so the right-side tool panel (`.ge-right-panel { overflow-y:auto }`) scrolls INTERNALLY. Without this the modal only has `max-height`, so `.gallery-editor { height:100% }` can't resolve — the editor grows to its full content height, overflows the modal body, and the modal-body scrollbar that appears can't be wheel-scrolled because the editor's `overscroll-behavior:contain` blocks the chain. Pinning a real height here makes the panel bounded and scrollable as designed. Scoped to the editor view (matched via the container's inline `display:flex`) so the Photos/Albums views keep sizing to their content. */ .gallery-modal-content:has(#gallery-editor-container[style*="flex"]) { height: 92vh; } /* The editor tab owns the modal body's scroll context. Keep the landing chooser and the mounted editor inside the available sheet height so a long saved-project list cannot push the launch controls below the viewport. */ .gallery-modal-content:has(#gallery-editor-container[style*="flex"]) > .modal-body { display: flex; min-height: 0; overflow: hidden; } .gallery-modal-content:has(#gallery-editor-container[style*="flex"]) #gallery-editor-container { min-height: 0; overflow: hidden; } /* Photo-detail view sizing (issue #314). The detail view is rendered as a `position:absolute; inset:0` overlay *inside* `.gallery-images-container`, painted over the photo grid. Because it's absolutely positioned it can't contribute to the container's height — the container (and therefore the overlay's `inset:0` box) collapses to the height of the grid sitting behind it. When the library only has a few photos that grid is short, so the detail view is crushed: the image is clipped and the metadata sidebar (`overflow-y:auto`) is squeezed into a tiny, internally-scrolling strip. (With a large library the grid is tall, which is why it looked fine in the demo video but cramped for users with few photos.) Fix: when the detail view is open, hide the grid-view siblings and drop the overlay into normal flow. The container — and the window, up to its 92vh max-height — then sizes to the detail's own content (image + metadata), so nothing is clipped or squeezed regardless of how many photos exist. Scoped via the detail element's inline `display:flex` so the grid / albums views keep sizing to their own content. Works on both desktop and the mobile full-screen sheet. */ #gallery-images-container:has(> #gallery-detail[style*="flex"]) > *:not(#gallery-detail) { display: none !important; } #gallery-images-container:has(> #gallery-detail[style*="flex"]) > #gallery-detail { position: static; } /* Containing block for the photo-detail overlay — keeps it inside the body so it sits below the modal header and the tab strip instead of covering them. */ .gallery-images-container { position: relative; } .gallery-modal-content .modal-header h4 { font-size: 1rem; } .gallery-stats { font-size: 10px; color: color-mix(in srgb, var(--fg) 50%, transparent); margin-bottom: 4px; } .gallery-toolbar { display: flex; gap: 6px; margin-bottom: 4px; align-items: center; } .gallery-toolbar-break { display: none; } /* Keep the hint anchored while independently seating the input in the row. */ .gallery-search-wrap { position: relative; flex: 1; min-width: 0; display: flex; top: 0; } .gallery-search { position: relative; top: 4px; flex: 1; width: 100%; padding: 4px 8px; padding-right: 78px; /* room for the enter hint */ background: var(--bg); color: var(--fg); border: 1px solid var(--border); border-radius: 6px; font: inherit; font-size: 12px; outline: none; } .gallery-search-enter-hint { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 600; color: var(--accent, var(--red)); pointer-events: none; opacity: 0; transition: opacity 0.12s; white-space: nowrap; } /* Show the hint only once the user has typed something (placeholder gone). */ .gallery-search:not(:placeholder-shown) ~ .gallery-search-enter-hint { opacity: 0.95; } .gallery-search:focus { border-color: var(--red); } .gallery-model-filter, .gallery-sort { padding: 4px 8px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font: inherit; font-size: 11px; cursor: pointer; position: relative; top: 4px; } .gallery-selection-bar { margin-bottom: 4px; position: relative; top: 2px; padding-right: 18px; } .gallery-tag-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px; } .gallery-chip { height: 28px; box-sizing: border-box; padding: 0 13px; display: inline-flex; align-items: center; justify-content: center; gap: 4px; border-radius: 14px; font-size: 12px; line-height: 1; border: 1px solid var(--border); background: none; color: var(--fg); cursor: pointer; transition: border-color 0.15s, background 0.15s; } .gallery-chip:hover { border-color: var(--red); } .gallery-chip.active { background: color-mix(in srgb, var(--red) 15%, transparent); border-color: color-mix(in srgb, var(--red) 40%, transparent); color: var(--red); } /* Album chips row */ .gallery-album-chips { display: flex; gap: 4px; flex-wrap: wrap; padding: 0 0 3px; } .gallery-chip-fav { color: var(--red); } .gallery-chip-fav.active { background: color-mix(in srgb, var(--red) 15%, transparent); border-color: var(--red); } .gallery-chip-add { opacity: 0.5; font-size: 14px; padding: 2px 10px; } /* Active-album indicator — appears when the Photos grid is filtered to one album. */ .gallery-chip-active-album, .gallery-active-filter-pill { display: inline-flex; align-items: center; gap: 4px; background: color-mix(in srgb, var(--red) 14%, transparent); border-color: color-mix(in srgb, var(--red) 45%, transparent); position: relative; top: 6px; } .gallery-active-filter-pill { background: color-mix(in srgb, var(--fg) 7%, transparent); border-color: color-mix(in srgb, var(--fg) 18%, transparent); color: color-mix(in srgb, var(--fg) 86%, transparent); } .gallery-clear-all-filters { height: 28px; border-style: dashed; opacity: 0.72; } .gallery-clear-all-filters:hover { opacity: 1; color: var(--red); } .gallery-chip-clear { background: none; border: none; color: inherit; opacity: 0.6; cursor: pointer; padding: 0; margin-left: 1px; width: 12px; height: 12px; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; line-height: 1; position: relative; top: -4px; } .gallery-chip-clear:hover { opacity: 1; } /* Favorite button on card */ /* Video cards: