* { box-sizing: border-box; }

html, body {
    height: 100%;
    margin: 0;
    padding: 0;
    font-family: var(--font-sans);
    font-size: var(--text-base);
    line-height: 1.45;
    color: var(--quindi-text);
    background: var(--quindi-bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

button { font-family: inherit; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.ambient-backdrop {
    position: fixed; inset: 0; z-index: -1; overflow: hidden;
    /* "Paper": the Excalidraw dot grid over the aurora glow — content pages
       read as sheets torn from the same sketchbook as the canvas. */
    background:
        var(--dot-grid),
        radial-gradient(60% 50% at 0% 0%, rgba(0, 214, 143, 0.12), transparent 60%),
        radial-gradient(50% 40% at 100% 100%, rgba(0, 214, 143, 0.07), transparent 70%),
        var(--quindi-bg);
    background-size: var(--dot-grid-size) var(--dot-grid-size), auto, auto, auto;
}
/* The editor is full-bleed over a white canvas: the green ambient glow would
   bleed around the chrome and fight the canvas, so the backdrop flattens to
   plain page bg when an .editor-shell follows it (EditorLayout). */
.ambient-backdrop:has(+ .editor-shell) {
    background: var(--quindi-bg);
}

/* L'orizzonte: l'editor è full-bleed, niente grid layout. Lo chrome (seam,
   rail, header, panel, palette) vive in `position: fixed` SOPRA il canvas. */
.editor-shell {
    position: fixed;
    inset: 0;
}

.page-shell {
    min-height: 100vh;
}

.main { position: absolute; inset: 0; min-height: 0; }

.excalidraw-host { position: absolute; inset: 0; }

/* Small inline spinner (e.g. the "Converting PDF…" indicator). */
@keyframes quindi-spin { to { transform: rotate(360deg); } }
.quindi-spin,
.quindi-pdf-tool-btn.is-busy svg { animation: quindi-spin 0.8s linear infinite; transform-origin: center; }
@media (prefers-reduced-motion: reduce) {
    .quindi-spin,
    .quindi-pdf-tool-btn.is-busy svg { animation: none; }
}
.loading {
    position: absolute; inset: 0;
    display: grid; place-items: center;
    color: var(--quindi-text-soft);
}

/* `.editor-canvas` con margine + bordo arrotondato è specifico dello shared
   view (read-only). L'editor full-bleed lo override-a a inset:0/no-radius
   tramite la regola `.editor-shell .editor-canvas` qui sotto. */
.editor-canvas {
    position: absolute; inset: 0;
    border-radius: var(--radius-xl);
    overflow: hidden;
    box-shadow: var(--glass-shadow);
    background: var(--canvas-bg);
}
.editor-shell .editor-canvas {
    inset: 0;
    border-radius: 0;
    box-shadow: none;
}

.brand {
    display: flex; align-items: center; gap: 10px;
    font-weight: 600;
    letter-spacing: -0.01em;
}
.brand h1,
.brand .brand-name {
    font-family: var(--font-hand);
    font-size: 26px;
    margin: 0;
    font-weight: 400;
    letter-spacing: 0;
    color: var(--quindi-text);
}
.brand-mark {
    width: 30px; height: 30px;
    border-radius: var(--radius-sm);
    background: var(--accent);
    display: grid; place-items: center;
    color: #050505;
    font-family: var(--font-hand);
    font-weight: 400; font-size: 18px;
    line-height: 1;
    text-decoration: none;
    transition: transform 0.12s ease;
}
.brand-mark:hover { transform: scale(1.05); text-decoration: none; }
/* When the brand mark holds the logo image (dashboard), drop the green tile —
   the logo is itself green on transparent — and let it fill the box. The letter
   variant (admin "A") keeps the tiled look. */
.brand-mark:has(img) { background: transparent; border-radius: 0; }
.brand-mark img { width: 100%; height: 100%; object-fit: contain; display: block; }

.search-input {
    width: 100%;
    padding: 8px 12px;
    border: var(--sketch-border-w) solid var(--sketch-border);
    background: var(--quindi-surface-2);
    color: var(--quindi-text);
    border-radius: var(--sketch-radius-sm);
    font-size: 13px;
    outline: none;
}
.search-input:focus { border-color: var(--accent); }
/* Wrapper that anchors the clear (×) button inside the dashboard search input. */
.search-box {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.search-box .search-input { padding-right: 30px; }
.search-clear {
    position: absolute;
    right: 6px;
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--quindi-text-soft);
    cursor: pointer;
}
.search-clear:hover { color: var(--quindi-text); background: rgba(255, 255, 255, 0.06); }
/* Search scope segmented toggle (title-only vs title + content). */
.search-scope {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.03);
    flex: 0 0 auto;
}
.search-scope-btn {
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--quindi-text-soft);
    font: inherit;
    font-size: 12px;
    font-weight: 600;
    padding: 5px 10px;
    cursor: pointer;
    white-space: nowrap;
}
.search-scope-btn:hover { color: var(--quindi-text); }
.search-scope-btn.is-active { color: #0A0A0A; background: var(--accent); }
.search-hint {
    flex: 1 1 180px;
    min-width: 0;
    color: var(--quindi-text-soft);
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Narrow screens: the hint only restates what the All/Titles chips already say
   and was truncated mid-word — the chips alone carry the meaning. */
@media (max-width: 700px) {
    .search-hint { display: none; }
}


.toast {
    position: fixed; bottom: 24px; left: 50%;
    transform: translateX(-50%);
    padding: 10px 16px;
    border-radius: var(--radius-md);
    font-size: 13px;
    z-index: 1000;
    background: var(--quindi-surface);
    border: 1px solid var(--quindi-border-soft);
    color: var(--quindi-text);
    box-shadow: var(--elev-2);
    animation: toast-in 0.25s ease;
}
@supports (backdrop-filter: blur(1px)) {
    .toast {
        background: var(--glass-tint);
        backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
        -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
    }
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* Update-incoming banner: a fixed top strip broadcast to every connected user
   (UpdateAnnouncementBanner.razor → UpdateAnnouncementService). Opaque surface + amber
   accent line — a heads-up, not an error. No backdrop-filter: it sits over the doc
   gallery and we don't want the thumbnails re-compositing behind it. */
.update-banner {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 1100;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 16px;
    background: var(--quindi-surface);
    border-bottom: 2px solid #f5b450;
    color: var(--quindi-text);
    font-size: 13px;
    font-weight: 500;
    box-shadow: 0 2px 14px rgba(0, 0, 0, 0.22);
    animation: update-banner-in 0.25s ease;
}
@keyframes update-banner-in { from { transform: translateY(-100%); } to { transform: translateY(0); } }
.update-banner-icon { flex: 0 0 auto; color: #f5b450; }
.update-banner-text { flex: 1 1 auto; line-height: 1.4; white-space: pre-wrap; }
.update-banner-close {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px;
    padding: 0;
    border: none; background: transparent; cursor: pointer;
    color: var(--quindi-text-soft); border-radius: 6px;
}
.update-banner-close:hover { background: rgba(255, 255, 255, 0.08); color: var(--quindi-text); }

/* Dashboard / Trash / settings-style pages */
.dashboard {
    /* Fluid on large screens — the old hard 1200px cap wasted full-screen width and squeezed the
       topbar actions onto a second row. The 1720px cap + fluid side padding use the width (the
       gallery auto-fills more columns) while keeping card sizes and line lengths sane on ultra-wide. */
    padding: 32px clamp(24px, 3.5vw, 52px);
    max-width: 1720px;
    margin: 0 auto;
}
.dashboard-header {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 24px;
}
/* Page-level top bar: brand on the far left, actions on the right. Sits above
   the sidebar+content grid so the "Q + Quindi.Excalidraw" lockup owns the
   top-left of the viewport regardless of how the dashboard body is laid out.
   Floating chrome: it sticks 12px under the viewport edge and earns the soft
   glass treatment (blur over --glass-tint) so the gallery scrolls beneath it.
   No overflow:hidden here — the bells' and Export's dropdowns extend past it. */
.page-topbar {
    position: sticky;
    top: 12px;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 24px;
    padding: 6px 12px;
    background: var(--quindi-surface);
    border: 1px solid var(--quindi-border-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--elev-2);
}
@supports (backdrop-filter: blur(1px)) {
    .page-topbar {
        background: var(--glass-tint);
        backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
        -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
    }
}
/* Grouped icon command-strip. wrap is kept only as a safety net for narrow windows — the condensed
   (icon-only) actions plus the widened .dashboard keep everything on ONE row at full-screen widths;
   the two-row problem was insufficient width, not the wrapping policy. */
.dashboard-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; align-items: center; }
.action-group { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; }
/* Hairline between functional groups (view · navigate · content · utility · status) — it encodes
   the grouping, it is not decoration. Dashed: a pencil tick, not a ruled line. */
.action-sep {
    width: 0;
    height: 20px;
    border-left: var(--sketch-border-w) dashed rgba(255, 255, 255, 0.18);
    flex: 0 0 auto;
    margin: 0 4px;
}
/* Icon-over-label: every action button in the strip stacks its icon over a short name, so the names
   stay visible while the buttons stay narrow enough to keep the row on ONE line. The S/M/L size
   selector and the identity chip aren't glass-buttons, so they keep their own layout. */
.dashboard-actions .glass-button,
.dashboard-actions .thumb-toggle {
    flex-direction: column;
    gap: 3px;
    height: auto;
    min-width: 52px;
    padding: 6px 8px 5px;
    line-height: 1.1;
    text-align: center;
    flex: 0 0 auto;
}
.dashboard-actions .glass-button > span,
.dashboard-actions .thumb-toggle > span {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.01em;
}
/* The Export dropdown caret is a horizontal affordance — drop it in the stacked strip (clicking the
   button still opens the menu). */
.dashboard-actions .export-menu-caret { display: none; }


/* Anchor for the Export dropdown: the .tag-picker-menu inside is positioned
   absolute against this wrapper, not against the toolbar. */
.export-menu-anchor { position: relative; display: inline-flex; }
.export-menu-anchor .export-menu-caret { opacity: 0.7; margin-left: 2px; }
.export-menu-anchor .glass-button.is-active { background: var(--quindi-surface-2); }
/* Override the tag-picker-menu's default top-right offset (it sits inside a
   row of buttons, not the page corner) so it drops directly under the button. */
.export-menu { right: 0; top: calc(100% + 6px); min-width: 220px; }
.user-chip {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-left: 16px;
    padding: 6px 12px;
    background: var(--quindi-surface-2);
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    font-size: 13px;
    line-height: 1;
    max-width: 280px;
    flex: 0 0 auto;
}
/* Identity sticker: the user's initial in the hand font on a green tile,
   pinned at a slight tilt like the gallery cards. */
.user-chip-avatar {
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    flex: 0 0 auto;
    background: var(--accent);
    color: var(--quindi-on-accent);
    font-family: var(--font-hand);
    font-size: 14px;
    line-height: 1;
    border-radius: 8px 10px 7px 10px / 10px 7px 10px 8px;
    transform: rotate(-4deg);
}
.user-chip-name {
    font-weight: 600;
    color: var(--quindi-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Truncate the name first so the sign-out button keeps its full width. */
    min-width: 0;
    flex: 0 1 auto;
}
.user-chip-sep {
    width: 1px;
    height: 14px;
    background: var(--quindi-border);
    flex: 0 0 auto;
}
.user-chip-form { margin: 0; display: inline-flex; flex: 0 0 auto; }
.user-chip-signout { padding: 0; font-size: 13px; white-space: nowrap; }
.notification-bell { position: relative; }
.notification-bell-icon {
    position: relative;
    display: inline-grid;
    place-items: center;
}
.notification-badge {
    position: absolute;
    top: -9px;
    right: -10px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    display: inline-grid;
    place-items: center;
    border-radius: var(--radius-sm);
    background: var(--quindi-red, #E5484D);
    color: #fff;
    border: 1px solid rgba(10, 10, 10, 0.78);
    font-size: 9px;
    font-weight: 800;
    line-height: 1;
}

/* Release-notes ("Novita") heads-up bar — the same fixed top-strip pattern as
   the update announcement above, tinted with the brand accent: amber warns a
   restart is imminent, green just says "there's new stuff to see". Shown only
   while the running version is newer than the one this browser last acknowledged
   (ReleaseNotesBell.razor); tapping it opens the notes panel, the x dismisses it.
   The wrapper is display:contents so it adds no box inside the action row — both
   the bar and the panel anchor to the viewport on their own. */
.novita-root { display: contents; }
.novita-banner {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 1090;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px 8px 16px;
    background: var(--quindi-surface);
    border-bottom: 2px solid var(--accent);
    color: var(--quindi-text);
    box-shadow: 0 2px 14px rgba(0, 0, 0, 0.22);
    animation: update-banner-in 0.25s ease;
}
.novita-banner-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0;
    padding: 4px 6px;
    border: none;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    text-align: left;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.novita-banner-main:hover { background: rgba(0, 214, 143, 0.08); }
.novita-banner-main:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; }
.novita-banner-icon { flex: 0 0 auto; display: inline-grid; place-items: center; color: var(--accent); }
.novita-banner-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.4;
}
.novita-banner-text b { font-weight: 700; }
.novita-banner-cta {
    color: var(--accent);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.novita-banner-close {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px;
    padding: 0;
    border: none; background: transparent; cursor: pointer;
    color: var(--quindi-text-soft); border-radius: 6px;
}
.novita-banner-close:hover { background: rgba(255, 255, 255, 0.08); color: var(--quindi-text); }

/* The notes drop straight under the bar as a speech bubble with a pointer up to the
   strip — instead of a detached top-right dialog — so it reads as "this bar's detail".
   The bar stays put as the bubble's anchor while it is open. */
.novita-bubble {
    position: fixed;
    top: 52px;
    left: 16px;
    z-index: 1200;
    width: min(420px, calc(100vw - 32px));
    padding: 16px;
    border-radius: var(--radius-md);
    background: var(--quindi-surface);
    border: 1px solid var(--quindi-border);
    color: var(--quindi-text);
    box-shadow: var(--glass-shadow);
    animation: release-notes-drop 0.18s ease;
}
.novita-bubble::before {
    /* The little tail pointing up at the bar. */
    content: "";
    position: absolute;
    top: -7px;
    left: 28px;
    width: 12px;
    height: 12px;
    background: var(--quindi-surface);
    border-left: 1px solid var(--quindi-border);
    border-top: 1px solid var(--quindi-border);
    transform: rotate(45deg);
}
.novita-bubble .release-notes-list {
    /* Only the version list scrolls; the bubble box (and its tail) stay put. */
    max-height: min(52vh, 420px);
    overflow-y: auto;
}
/* Transparent catcher so a click anywhere off the bubble closes it. Sits below the bar
   (z 1090) so the bar's own buttons keep working while the bubble is open. */
.novita-bubble-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1080;
    background: transparent;
}
@media (max-width: 700px) {
    .novita-bubble { left: 12px; right: 12px; width: auto; }
}
.release-notes-panel {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 1200;
    width: min(420px, calc(100vw - 32px));
    max-height: min(520px, calc(100vh - 96px));
    overflow: auto;
    padding: 16px;
    border-radius: var(--radius-md);
    background: var(--quindi-surface);
    border: 1px solid var(--quindi-border);
    color: var(--quindi-text);
    box-shadow: var(--glass-shadow);
    animation: release-notes-drop 0.18s ease;
}
.release-notes-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--quindi-border);
}
.release-notes-head h2 {
    margin: 0;
    font-size: 16px;
    line-height: 1.2;
}
.release-notes-head p {
    margin: 4px 0 0;
    color: var(--quindi-text-soft);
    font-size: 12px;
    line-height: 1.35;
}
.release-notes-close {
    flex: 0 0 auto;
    display: inline-grid;
    place-items: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 999px;
    background: transparent;
    color: var(--quindi-text-soft);
    cursor: pointer;
}
.release-notes-close:hover {
    border-color: var(--quindi-border);
    color: var(--quindi-text);
}
.release-notes-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding-top: 12px;
}
.release-notes-item {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    background: var(--quindi-surface-2);
}
.release-notes-item header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}
.release-notes-item strong {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
}
.release-notes-item header span {
    color: var(--quindi-text-soft);
    font-size: 11px;
    white-space: nowrap;
}
/* Release-note body is admin-authored Markdown rendered to HTML (ReleaseNotesMarkdown).
   Style the block elements Markdig can emit; collapse the outer margins so a note hugs
   its card. */
.release-notes-md {
    color: var(--quindi-text);
    font-size: 13px;
    line-height: 1.5;
}
.release-notes-md > :first-child { margin-top: 0; }
.release-notes-md > :last-child { margin-bottom: 0; }
.release-notes-md p { margin: 0 0 8px; }
.release-notes-md ul,
.release-notes-md ol { margin: 0 0 8px; padding-left: 18px; }
.release-notes-md li { margin: 2px 0; }
.release-notes-md h1,
.release-notes-md h2,
.release-notes-md h3,
.release-notes-md h4 { margin: 12px 0 6px; font-size: 14px; font-weight: 700; line-height: 1.25; }
.release-notes-md a { color: var(--accent); text-underline-offset: 2px; }
.release-notes-md strong { color: #fff; font-weight: 700; }
.release-notes-md code {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
    font-size: 12px;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid var(--quindi-border);
    border-radius: 4px;
    padding: 1px 5px;
}
.release-notes-md pre {
    background: var(--quindi-surface-2);
    border: 1px solid var(--quindi-border);
    border-radius: 8px;
    padding: 10px 12px;
    overflow: auto;
}
.release-notes-md pre code { background: none; border: 0; padding: 0; }
.release-notes-md blockquote {
    margin: 0 0 8px;
    padding: 4px 10px;
    border-left: 2px solid var(--accent);
    color: var(--quindi-text-soft);
}
.release-notes-md hr { border: 0; border-top: 1px solid var(--quindi-border); margin: 10px 0; }
.release-notes-empty {
    padding: 18px 4px 4px;
    color: var(--quindi-text-soft);
    font-size: 13px;
    line-height: 1.45;
}
@keyframes release-notes-drop {
    from { opacity: 0; transform: translateY(-6px); }
    to { opacity: 1; transform: translateY(0); }
}

/* Admin-only "schema behind code" alarm in the top bar. Red, with a slow pulse so it
   reads as an alert that needs action, not just another nav button. */
.system-health-alert {
    color: #fff;
    border-color: rgba(229, 72, 77, 0.55);
    background: linear-gradient(180deg, rgba(229, 72, 77, 0.28), rgba(229, 72, 77, 0.16));
    animation: system-health-pulse 2s ease-in-out infinite;
}
.system-health-alert:hover {
    background: linear-gradient(180deg, rgba(229, 72, 77, 0.4), rgba(229, 72, 77, 0.24));
    border-color: rgba(229, 72, 77, 0.8);
}
.system-health-alert-icon { display: inline-grid; place-items: center; }
@keyframes system-health-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0); }
    50% { box-shadow: 0 0 0 4px rgba(229, 72, 77, 0.22); }
}
@media (prefers-reduced-motion: reduce) {
    .system-health-alert,
    .novita-banner,
    .release-notes-panel { animation: none; }
}

.dashboard-panel { padding: 32px; }
/* Empty states are blank canvas sheets: dot-grid paper in a dashed pencil
   frame, message in the hand font — an invitation to draw, not a dead end. */
.empty-state {
    display: flex; flex-direction: column; gap: 16px; align-items: center;
    color: var(--quindi-text-soft);
    padding: 48px 40px;
    font-family: var(--font-hand);
    font-size: 18px;
    text-align: center;
    background:
        radial-gradient(circle, rgba(255, 255, 255, 0.04) 1px, transparent 1.4px) 0 0 / 18px 18px,
        var(--quindi-surface-0);
    border: var(--sketch-border-w) dashed var(--sketch-border);
    border-radius: var(--sketch-radius-a);
}
.empty-state p { margin: 0; }
/* Section headings (Favorites, My documents, Shared …) — Excalifont with a
   green brush-stroke underline: the sketchbook's own handwriting. */
.section-title {
    font-family: var(--font-hand);
    font-size: 26px;
    font-weight: 400;
    letter-spacing: 0;
    margin: 0 0 16px;
    padding-bottom: 8px;
    color: var(--quindi-text);
    background-image: var(--squiggle);
    background-repeat: repeat-x;
    background-position: 0 100%;
    background-size: auto 6px;
}
/* Hand-scribbled document count next to a section title — data, not décor:
   with the tag eye toggles a section can show fewer cards than it owns. */
.section-count {
    display: inline-grid;
    place-items: center;
    min-width: 26px;
    height: 24px;
    margin-left: 6px;
    padding: 0 8px;
    vertical-align: 4px;
    border: 1.5px dashed rgba(0, 214, 143, 0.4);
    border-radius: var(--radius-sm);
    color: var(--accent);
    font-size: 14px;
    line-height: 1;
}
.doc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 14px;
}

/* ----------------------------------------------------------------------
   Visual gallery — replaces the textual grid on the dashboard. Cards lay
   out on an aligned CSS grid: every thumb is a fixed 16/10 box and the
   title/tag rows reserve a min-height, so cards line up row-by-row for an
   "operational workspace" feel rather than a ragged masonry. auto-fill +
   minmax keeps the column width (~280px) the columns layout used.
   ---------------------------------------------------------------------- */
.doc-gallery {
    display: grid;
    /* Column width — and therefore the preview thumbnail size — is driven by
       --gallery-col-min, set by the dashboard's S/M/L preview-size control on
       the enclosing .doc-main (see .doc-main.thumbs-*). Falls back to the
       medium default when no size class is present. The min(…, 100%) guard keeps
       the Large setting from forcing a column wider than the viewport (which
       would overflow horizontally) on narrow screens. */
    grid-template-columns: repeat(auto-fill, minmax(min(var(--gallery-col-min, 280px), 100%), 1fr));
    gap: 16px;
    align-items: start;
}

/* Preview-size classes on .doc-main cascade --gallery-col-min into every
   .doc-gallery inside it (Favorites, My documents, Shared …). Persisted per
   user via localStorage; see PreviewSizeControl / Index.razor. */
.doc-main.thumbs-s { --gallery-col-min: 200px; }
.doc-main.thumbs-m { --gallery-col-min: 280px; }
.doc-main.thumbs-l { --gallery-col-min: 380px; }

/* Small also shrinks the card text (and tightens the caption padding to match)
   so the denser grid reads as genuinely small, not full-size type crammed into
   narrow columns. Higher specificity than the base .gallery-* rules, so the
   later source position of those defaults is irrelevant. */
.doc-main.thumbs-s .gallery-overlay { padding: 9px 12px 11px; gap: 3px; }
.doc-main.thumbs-s .gallery-title { font-size: 12.5px; }
.doc-main.thumbs-s .gallery-meta { font-size: 10px; }
.doc-main.thumbs-s .gallery-search-context { font-size: 10px; }
.doc-main.thumbs-s .gallery-tags { min-height: 20px; }
.doc-main.thumbs-s .tag-chip { font-size: 10px; padding: 1px 7px; }

.gallery-card {
    position: relative;
    /* Top-lit gradient instead of a flat fill, so the card reads as a physical
       surface the drawing is mounted on rather than a void tile on the page. */
    background: linear-gradient(180deg, var(--quindi-card-top), var(--quindi-card-bottom));
    /* Pencilled corners + a slightly playful rest tilt, alternating per card so
       the grid looks pinned by hand; hover straightens the card to attention. */
    border: var(--sketch-border-w) solid var(--sketch-border);
    border-radius: var(--sketch-radius-a);
    box-shadow: var(--elev-2);
    /* overflow stays visible so absolutely-positioned popovers (MoveToFolderMenu)
       can extend past the card. The thumb and overlay clip their own rounded
       corners independently. */
    transition: transform 0.18s cubic-bezier(0.2, 0.7, 0.2, 1), border-color 0.18s ease, box-shadow 0.18s ease;
}
.doc-gallery .gallery-card:nth-child(odd)  { transform: rotate(-0.4deg); }
.doc-gallery .gallery-card:nth-child(even) { transform: rotate(0.3deg); border-radius: var(--sketch-radius-b); }
.doc-gallery .gallery-card:nth-child(odd):hover,
.doc-gallery .gallery-card:nth-child(even):hover { transform: translateY(-4px) rotate(0deg); }
/* 1px catch-light along the top edge of the mat — sells the lit surface. */
.gallery-card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.gallery-card:hover {
    transform: translateY(-4px);
    border-color: rgba(0, 214, 143, 0.55);
    /* Green "spotlight" pick-up: accent ring + soft green wash + depth, instead
       of a flat black shadow that vanishes against the near-black page. */
    box-shadow: 0 0 0 1px rgba(0, 214, 143, 0.35), 0 10px 22px rgba(0, 214, 143, 0.14), 0 22px 48px rgba(0, 0, 0, 0.55);
}
/* The mounted plate lights up under the same spotlight on hover. */
.gallery-card:hover .gallery-thumb {
    box-shadow: 0 0 0 1px rgba(0, 214, 143, 0.45), 0 6px 16px rgba(0, 0, 0, 0.4);
}
.gallery-card.is-archived { opacity: 0.65; }
/* While the tag picker menu is open the card must stack above siblings.
   Cards have position:relative but no z-index by default, so the next card in
   DOM order paints over any absolute child (the menu) that extends past the
   card box. Elevating only the card with the open menu keeps the painting
   intact without affecting hover/transform stacking on the rest of the grid. */
.gallery-card.is-menu-open { z-index: 10; }

/* Freeze the hover transform while the picker menu is open.
   The :hover translateY(-2px) below applies a non-none transform, which by
   CSS spec turns the card into the containing block for its `position: fixed`
   descendants — including .tag-picker-backdrop (inset:0). The backdrop then
   collapses from "covers the viewport" to "covers the card", letting clicks
   on the rest of the grid bypass the close handler and navigate away mid-pick.
   The visible symptom is a flicker when hover toggles and a much worse one
   when the user switches browser tabs and returns (forced repaint snaps
   :hover/:focus-within state, snapping the backdrop's coordinate system with
   it). Disabling transform on the open card eliminates the containing-block
   flip; border/shadow stay so the card still feels active. */
.gallery-card.is-menu-open,
.gallery-card.is-menu-open:hover { transform: none; }

/* Keep the actions strip visible while the menu is open. Without this rule
   the backdrop intercepts the mouse, the card loses :hover, .gallery-actions
   fades to opacity 0, and the "Assegna tag" button (the one the user just
   clicked) ghosts out under their cursor. */
.gallery-card.is-menu-open .gallery-actions {
    opacity: 1;
    pointer-events: auto;
}

.gallery-card-link {
    display: block;
    color: inherit;
    text-decoration: none;
}
.gallery-card-link:hover { text-decoration: none; }

/* Two states managed by JS in quindi.js → quindi.thumbnails:
   • is-placeholder (default markup): the inline ThumbPlaceholder fills the box
   • has-thumb: an <img src=blob:...> with the cached scene image, loaded inert */
.gallery-thumb {
    position: relative;
    /* Inset on a dark mat so the white preview reads as a mounted print, not a
       raw white block bleeding into the card's rounded corners. */
    margin: 10px 10px 0;
    aspect-ratio: 16 / 10;
    /* Excalidraw's default scene background is white; matching it here keeps the
       cached preview visually continuous with the editor view. The placeholder paints
       on a slightly darker surface to make "no preview yet" obviously different. */
    background: #ffffff;
    overflow: hidden;
    border-radius: var(--radius-sm);
    /* Hairline frame + soft drop = the drawing is matted and mounted. */
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(0, 0, 0, 0.06), 0 4px 10px rgba(0, 0, 0, 0.35);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--quindi-text-soft);
}
.gallery-thumb.is-placeholder {
    /* Faint dot grid echoes Excalidraw's canvas grid, so an empty/loading plate
       still feels like a canvas waiting rather than a dead grey box. */
    background:
        radial-gradient(circle at center, rgba(255, 255, 255, 0.05) 1px, transparent 1.4px) 0 0 / 16px 16px,
        var(--quindi-surface-2);
}
.gallery-thumb.is-placeholder::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.62), transparent);
    transform: translateX(-100%);
    animation: thumb-skeleton-sweep 1.4s ease-in-out infinite;
    pointer-events: none;
}
.gallery-thumb.is-placeholder .thumb-placeholder {
    width: 60%;
    max-width: 200px;
    opacity: 0.55;
    color: var(--quindi-text-soft);
}
.gallery-thumb.has-thumb img.gallery-thumb-img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: contain;
    user-select: none;
    -webkit-user-drag: none;
}

@keyframes thumb-skeleton-sweep {
    0% { transform: translateX(-100%); }
    60%, 100% { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
    .gallery-thumb.is-placeholder::after { animation: none; }
}

.gallery-overlay {
    padding: 11px 14px 13px;
    /* No divider, transparent fill: the caption sits on the card's own mat,
       like a label under a mounted plate, not a separate panel. */
    border-radius: 0 0 var(--radius-md) var(--radius-md);
    display: flex;
    flex-direction: column;
    gap: 4px;
    background: transparent;
}
.gallery-title {
    font-weight: 600;
    font-size: 14px;
    line-height: 1.35;
    color: var(--quindi-text);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
    /* No min-height: a one-line title stays one line tall, so the card doesn't
       carry an empty reserved second line. line-clamp still caps long titles at
       two lines. Card bottoms go slightly ragged across a row, but the fixed
       16:10 thumbnails already carry the grid's alignment. */
}
.gallery-title mark.search-hl {
    background: rgba(0, 214, 143, 0.28);
    color: inherit;
    border-radius: 3px;
    padding: 0 1px;
}
.gallery-meta {
    font-size: 11px;
    color: var(--quindi-text-soft);
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
    letter-spacing: 0.01em;
}
.gallery-metarow {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-width: 0;
}
.gallery-metarow .gallery-meta {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Live-presence avatars — who is currently inside the document. */
.gallery-presence { display: inline-flex; align-items: center; flex: 0 0 auto; }
.gallery-presence-avatar {
    width: 20px;
    height: 20px;
    margin-left: -6px;
    border-radius: 999px;
    display: inline-grid;
    place-items: center;
    font-size: 9px;
    font-weight: 700;
    color: #0A0A0A;
    border: 1.5px solid var(--quindi-surface);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
}
.gallery-presence-avatar:first-child { margin-left: 0; }
.gallery-presence-avatar.is-more { background: var(--quindi-surface-2); color: var(--quindi-text); }
.gallery-search-context {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    font-size: 11px;
    line-height: 1.25;
    color: var(--quindi-text-soft);
}
.gallery-search-label {
    flex: 0 0 auto;
    padding: 2px 5px;
    border: 1px solid rgba(0, 214, 143, 0.32);
    border-radius: var(--radius-sm);
    color: var(--accent);
    background: rgba(0, 214, 143, 0.08);
    font-weight: 650;
}
.gallery-search-snippet {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gallery-search-snippet mark.search-hl {
    background: rgba(0, 214, 143, 0.28);
    color: inherit;
    border-radius: 3px;
    padding: 0 1px;
}

/* Read-only document viewer (/viewer/{id}) — full-page Excalidraw, no UI chrome,
   opened by the eye button on a dashboard card. */
.viewer-host {
    position: fixed;
    inset: 0;
    background: var(--quindi-bg);
}
.viewer-host .editor-canvas {
    position: absolute;
    inset: 0;
    border-radius: 0;
    box-shadow: none;
}
/* View mode still renders Excalidraw's menu / zoom / footer; this is a pure preview,
   so hide the whole UI layer and leave just the canvas (mouse pan/zoom still work). */
.viewer-host .excalidraw .layer-ui__wrapper { display: none !important; }
.viewer-close {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 5;
    display: inline-grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: 999px;
    border: 1px solid var(--quindi-border);
    background: rgba(16, 16, 16, 0.9);
    color: var(--quindi-text);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
    transition: color 0.12s ease, border-color 0.12s ease;
}
.viewer-close:hover { border-color: var(--accent); color: #fff; }

/* The "Watching/Following presentation" badge was removed on purpose: audience
   surfaces (/watch, /follow) stay chrome-free like /present itself. */

/* Guest-link pill on /present (bottom-left). Fully visible for the first seconds so
   the presenter can grab the link before screen-sharing, then fades to near-invisible
   to stay out of the audience's eye; hover or keyboard focus brings it back. */
.present-guest-pill {
    position: fixed;
    left: calc(16px + var(--safe-left));
    bottom: calc(16px + var(--safe-bottom, 0px));
    z-index: 90;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    background: var(--glass-bg-dark);
    color: var(--quindi-text);
    font: inherit;
    font-size: 12.5px;
    cursor: pointer;
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    animation: present-guest-fade 0.5s ease 12s forwards;
}
.present-guest-pill:hover,
.present-guest-pill:focus-visible {
    animation: none;
    opacity: 1;
    border-color: var(--accent);
}
@keyframes present-guest-fade { to { opacity: 0.15; } }
@media (prefers-reduced-motion: reduce) {
    .present-guest-pill { animation: none; opacity: 0.5; }
    .present-guest-pill:hover, .present-guest-pill:focus-visible { opacity: 1; }
}

/* Laser pill on /present (bottom-right) — same hover-reveal contract as the guest pill:
   near-invisible so it stays out of the screen-share, back on hover/focus. While the
   laser is on it keeps a soft accent so the presenter can find the off switch. */
.present-laser-pill {
    position: fixed;
    right: calc(16px + var(--safe-right, 0px));
    bottom: calc(16px + var(--safe-bottom, 0px));
    z-index: 90;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    background: var(--glass-bg-dark);
    color: var(--quindi-text-soft);
    font-family: var(--font-hand);
    font-size: 12px;
    cursor: pointer;
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    animation: present-guest-fade 0.5s ease 12s forwards;
}
.present-laser-pill:hover,
.present-laser-pill:focus-visible {
    animation: none;
    opacity: 1;
    border-color: var(--accent);
}
.present-laser-pill.is-on {
    animation: none;
    opacity: 0.85;
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-soft);
}
@media (prefers-reduced-motion: reduce) {
    .present-laser-pill { animation: none; opacity: 0.5; }
    .present-laser-pill:hover, .present-laser-pill:focus-visible, .present-laser-pill.is-on { opacity: 1; }
}

/* Remote presenter laser pointer — a fixed-position glowing dot on <body>, positioned in
   client coords from the local viewport. Signature laser red; sits above everything. */
.quindi-laser-dot {
    position: fixed;
    top: 0;
    left: 0;
    width: 20px;
    height: 20px;
    margin: -10px 0 0 -10px;
    border-radius: 999px;
    background: radial-gradient(circle, rgba(255, 68, 58, 0.95) 0%, rgba(255, 68, 58, 0.55) 45%, transparent 72%);
    box-shadow: 0 0 14px 5px rgba(255, 68, 58, 0.5);
    pointer-events: none;
    z-index: 2147483000;
    opacity: 0;
    transition: opacity 0.18s ease;
    will-change: left, top;
}
.quindi-laser-dot.is-visible { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    .quindi-laser-dot { transition: none; }
}

/* Ephemeral presenter ink (the "underline") — a client-space SVG overlay on <body>, above
   the canvas but below the laser dot, non-interactive. Highlighter amber; fades out when the
   stroke ends (transition matches INK_FADE_MS in the interop). */
.quindi-ink-overlay {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    pointer-events: none;
    z-index: 2147482000;
    overflow: visible;
}
.quindi-ink-stroke {
    fill: none;
    stroke: rgba(255, 176, 32, 0.85);
    stroke-width: 6;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.9;
    transition: opacity 4s ease;
}
.quindi-ink-stroke.is-fading { opacity: 0; }

/* Editor: badge on frames that carry a speaker note. Client-space markers on <body> (like the
   laser/ink layers), non-interactive, glued to each noted frame's top-right corner. */
.quindi-note-badge-layer {
    position: fixed;
    inset: 0;
    pointer-events: none;
    /* Canvas annotation, not presentation chrome: above the board and Excalidraw's own
       islands (single-digit z), but BELOW every Quindi chrome layer (seam starts at 50)
       and every modal backdrop — unlike the laser/ink layers, which deliberately sit on
       top of everything while presenting. A near-max z here let badges bleed through the
       Present modal's backdrop. */
    z-index: 45;
}
.quindi-note-badge {
    position: fixed;
    top: 0;
    left: 0;
    transform: translate(-50%, -50%);
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: var(--accent, #4c6ef5);
    color: #fff;
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.35);
    pointer-events: none;
}
.quindi-note-badge svg { width: 13px; height: 13px; }

@media (prefers-reduced-motion: reduce) {
    .quindi-ink-stroke { transition: opacity 0.4s ease; }
}

/* ── Phone remote viewfinder (/remote) ──────────────────────────────────────
   A full-bleed mirror of the board with Excalidraw's own UI hidden, driven by a
   simple dark dock. Colours echo the marks: green "driving", red laser, amber
   underline. Needs on-device QA — CI has no phone renderer. */
.remote-host {
    position: fixed;
    inset: 0;
    background: var(--quindi-bg);
    overflow: hidden;
    /* Single source of truth for the dock's height, so anything stacked above it (the speaker
       notes panel) can sit clear of it instead of guessing with a constant that matched no
       real device — the old hardcoded 96px was 14px short on any phone with a home indicator,
       and the dock's opaque gradient then clipped the bottom of the note. */
    --remote-dock-h: calc(56px + 24px + var(--safe-bottom));
}
.remote-host .editor-canvas,
.remote-host .excalidraw-host { position: absolute; inset: 0; }
.remote-host .excalidraw .layer-ui__wrapper,
.remote-host .excalidraw .App-top-bar,
.remote-host .excalidraw .App-bottom-bar,
.remote-host .excalidraw .App-welcome-screen,
.remote-host .excalidraw .App-menu,
.remote-host .excalidraw .App-toolbar,
.remote-host .excalidraw .mobile-toolbar,
.remote-host .excalidraw .excalidraw-ui-top-left,
.remote-host .excalidraw .excalidraw-ui-top-right,
.remote-host .excalidraw .layer-ui__wrapper__top-right,
.remote-host .excalidraw .disable-zen-mode,
.remote-host .excalidraw .dropdown-menu,
.remote-host .excalidraw .context-menu,
.remote-host .excalidraw .context-menu-popover,
.remote-host .excalidraw .Modal,
.remote-host .excalidraw .Dialog {
    display: none !important;
}

.remote-topbar {
    position: fixed;
    top: calc(10px + var(--safe-top));
    left: calc(12px + var(--safe-left));
    z-index: 70;
    pointer-events: none;
}
.remote-live {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    border-radius: var(--radius-sm);
    background: var(--glass-bg-dark);
    border: 1px solid var(--quindi-border);
    color: var(--quindi-text-soft);
    font-family: var(--font-hand);
    font-size: 12px;
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
}
.remote-live-dot {
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--accent);
    animation: present-pulse 2s ease-out infinite;
}

.remote-dock {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 70;
    display: flex;
    justify-content: center;
    gap: 10px;
    padding: 12px calc(12px + var(--safe-right)) calc(12px + var(--safe-bottom)) calc(12px + var(--safe-left));
    background: linear-gradient(to top, rgba(10, 10, 10, 0.92), transparent);
}
.remote-btn {
    flex: 1 1 0;
    /* Without min-width:0 a flex item keeps its content's intrinsic width, so a crowded dock
       (6–7 controls) overflows a narrow phone; centred, the spill pushes the first button
       (Prev) off the left edge. min-width:0 lets flex-shrink actually work. */
    min-width: 0;
    max-width: 96px;
    min-height: 56px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    border-radius: var(--radius-lg);
    background: var(--quindi-surface);
    border: 1px solid var(--quindi-border);
    color: var(--quindi-text);
    font-family: var(--font-hand);
    font-size: 12px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.remote-btn svg { width: 22px; height: 22px; flex: 0 0 auto; }
/* Keep a label on one clipped line rather than letting a long word ("Overview", "By position")
   spill past its shrunken button and collide with a neighbour. */
.remote-btn span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.remote-btn.is-on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.remote-btn-laser.is-on { border-color: #FF443A; color: #FF443A; background: rgba(255, 68, 58, 0.14); }
.remote-btn-underline.is-on { border-color: var(--quindi-amber); color: var(--quindi-amber); background: var(--quindi-amber-soft); }
.remote-btn-clear { color: var(--quindi-text-soft); }
.remote-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.remote-btn:disabled { opacity: 0.4; cursor: default; }
/* The slides dock packs the most controls (Prev, count, Next, order, notes, overview, exit),
   so it needs the tightest spacing to stay on one row. */
.remote-dock-slides { align-items: center; gap: 6px; }
/* Also the "recentre on this slide" button — hence the button reset and a real touch height. */
.remote-slide-count {
    flex: 0 1 auto;
    min-width: 52px;
    min-height: 44px;
    text-align: center;
    font-family: var(--font-hand);
    font-size: 15px;
    font-weight: 600;
    color: var(--quindi-text);
    padding: 0 2px;
    background: none;
    border: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.remote-slide-count:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-md); }
.remote-slide-count:disabled { cursor: default; opacity: 0.4; }
.remote-slide-count:not(:disabled):active { opacity: 0.65; }
.remote-btn-exit { color: var(--quindi-text-soft); }

/* Narrow phones: shrink the dock chrome so a full slides row (up to 7 items) fits without spill. */
@media (max-width: 400px) {
    .remote-host { --remote-dock-h: calc(52px + 24px + var(--safe-bottom)); }
    .remote-dock { gap: 6px; padding-left: calc(8px + var(--safe-left)); padding-right: calc(8px + var(--safe-right)); }
    .remote-dock-slides { gap: 4px; }
    .remote-btn { min-height: 52px; border-radius: var(--radius-md); font-size: 11px; }
    .remote-btn svg { width: 20px; height: 20px; }
    .remote-slide-count { min-width: 44px; font-size: 14px; }
}

/* Short landscape — Excalidraw's own second phone breakpoint. Height is the scarce axis here,
   and the width-keyed rule above doesn't fire at 852x393, so the dock kept its full portrait
   metrics and ate a quarter of the screen. Lay each button out as icon + label on one row
   instead (there is width to spare), which nearly halves the dock. */
@media (max-height: 499px) and (max-width: 999px) {
    .remote-host { --remote-dock-h: calc(44px + 20px + var(--safe-bottom)); }
    .remote-dock { gap: 8px; padding-top: 10px; padding-bottom: calc(10px + var(--safe-bottom)); }
    /* A narrow AND short viewport (split screen) matches the <=400px block too, and this one
       wins on source order — so restate the tighter slides-row gap it asked for. */
    .remote-dock-slides { gap: 6px; }
    .remote-btn {
        flex-direction: row;
        gap: 6px;
        min-height: 44px;
        max-width: 132px;
        border-radius: var(--radius-md);
        font-size: 12px;
    }
    .remote-btn svg { width: 18px; height: 18px; }
    .remote-slide-count { min-height: 44px; }
}

/* Terminal state after the pairing session is revoked or expires. Covers the dock (70) and the
   overview (80) — nothing on this page can drive the room any more. */
.remote-ended {
    position: fixed;
    inset: 0;
    z-index: 90;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: calc(24px + var(--safe-top)) calc(28px + var(--safe-right)) calc(24px + var(--safe-bottom)) calc(28px + var(--safe-left));
    text-align: center;
    background: var(--quindi-bg, #0a0a0a);
    color: var(--quindi-text);
    font-family: var(--font-hand);
}
.remote-ended svg { color: var(--quindi-text-soft); }
.remote-ended h1 { margin: 0; font-size: 20px; font-weight: 600; }
.remote-ended p { margin: 0; max-width: 30ch; font-size: 15px; line-height: 1.5; color: var(--quindi-text-soft); }

/* Presenter view: the current slide's speaker note + next-slide label + timer, above the dock. */
.remote-notes {
    position: fixed;
    left: 0; right: 0;
    /* Clear of the dock whatever its real height is, rather than a constant that undershot it. */
    bottom: calc(var(--remote-dock-h) + 8px);
    z-index: 30;
    margin: 0 calc(12px + var(--safe-right)) 0 calc(12px + var(--safe-left));
    max-height: 42vh;
    /* dvh so iOS Safari's collapsing toolbars don't leave the panel taller than the space it has. */
    max-height: 42dvh;
    display: flex;
    flex-direction: column;
    background: rgba(10, 10, 10, 0.92);
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-lg);
    backdrop-filter: blur(8px);
    color: var(--quindi-text);
}
.remote-notes-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 14px;
    border-bottom: 1px solid var(--quindi-border);
    font-family: var(--font-hand);
}
.remote-notes-timer { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.remote-notes-next { font-size: 13px; color: var(--quindi-text-soft); }
.remote-notes-body {
    overflow-y: auto;
    padding: 14px;
    font-size: 17px;
    line-height: 1.55;
    white-space: pre-wrap;
    -webkit-overflow-scrolling: touch;
}
.remote-notes-empty { color: var(--quindi-text-soft); font-style: italic; }

/* Speaker-note editor (a small modal on the canvas, built in excalidraw-interop.js). */
.quindi-fn-backdrop {
    position: fixed;
    inset: 0;
    z-index: 100000;
    display: grid;
    place-items: center;
    background: rgba(0, 0, 0, 0.55);
    padding: 24px;
}
.quindi-fn-modal {
    width: min(520px, 94vw);
    background: var(--quindi-surface, #141414);
    color: var(--quindi-text, #f4f6f9);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.1));
    border-radius: var(--radius-lg);
    box-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);
    padding: 16px;
    font-family: var(--font-sans);
}
.quindi-fn-title { font-weight: 600; font-size: 14px; margin-bottom: 10px; }
.quindi-fn-text {
    width: 100%;
    box-sizing: border-box;
    resize: vertical;
    padding: 10px 12px;
    border-radius: var(--radius-md);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.15));
    background: var(--quindi-bg, #0a0a0a);
    color: inherit;
    font: inherit;
    font-size: 14px;
    line-height: 1.5;
}
.quindi-fn-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.quindi-fn-btn {
    padding: 8px 14px;
    border-radius: 8px;
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.18));
    background: none;
    color: inherit;
    cursor: pointer;
    font: inherit;
    font-size: 13px;
}
.quindi-fn-btn--primary {
    background: var(--accent, #00d68f);
    border-color: var(--accent, #00d68f);
    color: #04140d;
    font-weight: 600;
}

/* Slide overview: a full-bleed grid of frame thumbnails to jump to any slide.
   z-index 80 puts it above the dock (70) and the live pill (70): as a sibling at 40 it was
   painted UNDER them, so the dock covered the last row of thumbnails and — worse — kept
   intercepting taps aimed there, hitting Next or the destructive Exit instead of a slide. */
.remote-overview {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: flex;
    flex-direction: column;
    background: var(--quindi-bg, #0a0a0a);
}
.remote-overview-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* viewport-fit=cover means inset:0 reaches under the status bar / sensor housing. */
    padding: calc(14px + var(--safe-top)) calc(16px + var(--safe-right)) 14px calc(16px + var(--safe-left));
    color: var(--quindi-text);
    font-family: var(--font-hand);
    font-size: 15px;
    border-bottom: 1px solid var(--quindi-border);
}
.remote-overview-close {
    border: none; background: none; color: var(--quindi-text); cursor: pointer;
    /* 44px is the minimum comfortable touch target; 6px of padding around a 22px icon is not. */
    min-width: 44px;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: -6px -10px -6px 0;
    -webkit-tap-highlight-color: transparent;
}
.remote-overview-grid {
    flex: 1;
    overflow-y: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 12px;
    padding: 16px calc(16px + var(--safe-right)) calc(16px + var(--safe-bottom)) calc(16px + var(--safe-left));
    -webkit-overflow-scrolling: touch;
}
.remote-slide-cell {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 6px;
    border: 2px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: var(--quindi-surface);
    cursor: pointer;
    color: var(--quindi-text);
}
.remote-slide-cell.is-current { border-color: var(--accent); }
/* Note marker: a frame carrying a speaker note gets a badge over its thumbnail corner. */
.remote-slide-cell-note {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.35);
}
.remote-slide-cell-note svg { width: 14px; height: 14px; }
.remote-slide-cell img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: contain;
    background: #fff;
    border-radius: 8px;
}
.remote-slide-cell-blank {
    width: 100%;
    aspect-ratio: 4 / 3;
    display: grid;
    place-items: center;
    background: rgba(255, 255, 255, 0.05);
    border-radius: 8px;
    font-size: 24px;
    font-weight: 600;
    color: var(--quindi-text-soft);
}
.remote-slide-cell-label {
    font-family: var(--font-hand);
    font-size: 12px;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media (prefers-reduced-motion: reduce) {
    .remote-live-dot { animation: none; }
}
.viewer-error {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 14px;
    color: var(--quindi-text-soft);
    font-family: var(--font-sans);
}

/* Help manual modal (the "?" button). Markdown body, readable sizes. */
.manual-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1300;
    background: var(--scrim);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}
.manual {
    position: fixed;
    z-index: 1301;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(760px, calc(100vw - 32px));
    max-height: min(86vh, 900px);
    display: flex;
    flex-direction: column;
    background: var(--quindi-surface);
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--glass-shadow);
    overflow: hidden;
    animation: release-notes-drop 0.18s ease;
}
.manual-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--quindi-border);
}
.manual-head h2 { margin: 0; font-size: 18px; }
.manual-close {
    flex: 0 0 auto;
    display: inline-grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 999px;
    background: transparent;
    color: var(--quindi-text-soft);
    cursor: pointer;
}
.manual-close:hover { border-color: var(--quindi-border); color: var(--quindi-text); }
.manual-body { padding: 20px; overflow-y: auto; }
.manual-md { color: var(--quindi-text); font-size: 14.5px; line-height: 1.65; }
.manual-md > :first-child { margin-top: 0; }
.manual-md h1 { font-size: 22px; margin: 0 0 10px; font-weight: 700; line-height: 1.2; }
.manual-md h2 { font-size: 17px; margin: 24px 0 8px; font-weight: 700; }
.manual-md h3 { font-size: 14.5px; margin: 16px 0 6px; font-weight: 700; color: #fff; }
.manual-md p { margin: 0 0 10px; }
.manual-md ul, .manual-md ol { margin: 0 0 10px; padding-left: 20px; }
.manual-md li { margin: 4px 0; }
.manual-md a { color: var(--accent); }
.manual-md strong { color: #fff; font-weight: 700; }
.manual-md code {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
    font-size: 12.5px;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid var(--quindi-border);
    border-radius: 4px;
    padding: 1px 5px;
}
.manual-md hr { border: 0; border-top: 1px solid var(--quindi-border); margin: 18px 0; }
.manual-md blockquote {
    margin: 0 0 10px;
    padding: 6px 12px;
    border-left: 2px solid var(--accent);
    color: var(--quindi-text-soft);
}
@media (prefers-reduced-motion: reduce) {
    .manual { animation: none; }
}
.gallery-tags {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    margin-top: 4px;
    /* Reserve one chip row even when the card has no tags, so tagged and
       untagged cards keep the same height — same intent as the title
       min-height above. Cards with 2+ chip rows still grow (flex-wrap). */
    min-height: 22px;
}

/* Pills overlaid top-left on the thumbnail (status badge). The badges row
   is a pure presentational layer; pointer-events on the strip itself are
   off so it doesn't intercept the underlying link's click. */
.gallery-badges {
    position: absolute;
    /* Tucked inside the mounted plate's top-left corner. */
    top: 18px;
    left: 18px;
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    pointer-events: none;
    /* Reserve space on the right so badges never overlap the action cluster. */
    max-width: calc(100% - 120px);
    z-index: 1;
}
.gallery-status {
    pointer-events: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 8px;
    border-radius: var(--radius-sm);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    background: rgba(20, 20, 20, 0.78);
    color: var(--quindi-text);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    border: 1px solid rgba(255, 255, 255, 0.06);
    white-space: nowrap;
}
.gallery-status.is-archived {
    background: rgba(229, 72, 77, 0.18);
    color: var(--quindi-red, #F08080);
    border-color: rgba(229, 72, 77, 0.35);
}
.gallery-status.is-published {
    background: rgba(0, 214, 143, 0.18);
    color: var(--accent, #00D68F);
    border-color: rgba(0, 214, 143, 0.35);
}
/* Sharing badge — used in two flavors:
   - "With me" on docs whose OwnerId differs from the current user;
   - "By me · N" on docs the current user owns and has shared with others.
   The element is a <button> (clickable to open the collaborators popover) so we
   need explicit cursor + reset of the default button background; the popover
   itself anchors to .gallery-status-anchor (a position:relative wrapper). */
.gallery-status-anchor {
    position: relative;
    display: inline-flex;
    pointer-events: auto;
}
.gallery-status.is-shared {
    background: rgba(91, 154, 255, 0.18);
    color: #8FB6FF;
    border-color: rgba(91, 154, 255, 0.35);
    cursor: pointer;
    /* font-family already inherits via the global `button` reset; pin size/weight
       explicitly. (A bare `font: inherit` here used to pull in the 16px body
       default, making this chip far larger than the sibling status badges.) */
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
}
.gallery-status.is-shared:hover {
    background: rgba(91, 154, 255, 0.28);
    border-color: rgba(91, 154, 255, 0.55);
}
.gallery-status.is-shared:focus-visible {
    outline: 1px solid var(--accent);
    outline-offset: 1px;
}
/* Filled, higher-contrast variant scoped to /shared so the dashboard `/`
   keeps the subtler translucent badge. */
.shared-page .gallery-status.is-shared {
    background: var(--accent);
    color: #0A0A0A;
    border-color: var(--accent);
}
.shared-page .gallery-status.is-shared:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
}

.shared-page-shell {
    padding: 0 24px 80px;
}
.shared-stats {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 10px;
    margin: 0 0 14px;
}
.shared-stats > div {
    min-width: 0;
    padding: 10px 12px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.025);
}
.shared-stat-value {
    display: block;
    color: var(--quindi-text);
    font-size: 18px;
    font-weight: 760;
    line-height: 1;
}
.shared-stat-label {
    display: block;
    margin-top: 4px;
    color: var(--quindi-text-soft);
    font-size: 11px;
    font-weight: 650;
}
.shared-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 14px;
}
.shared-tabs {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.03);
}
.shared-tab,
.shared-segment {
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--quindi-text-soft);
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    padding: 7px 10px;
    cursor: pointer;
    white-space: nowrap;
}
.shared-tab span {
    display: inline-grid;
    place-items: center;
    min-width: 18px;
    padding: 1px 5px;
    margin-left: 6px;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.08);
    color: var(--quindi-text-soft);
    font-size: 10px;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}
.shared-tab:hover,
.shared-segment:hover {
    color: var(--quindi-text);
    background: rgba(255, 255, 255, 0.06);
}
.shared-tab.is-active,
.shared-segment.is-active {
    color: #0A0A0A;
    background: var(--accent);
}
.shared-tab.is-active span {
    background: rgba(10, 10, 10, 0.18);
    color: rgba(10, 10, 10, 0.78);
}
.shared-search {
    max-width: 360px;
    padding-left: 34px;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238B95A5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><path d='m21 21-4.3-4.3'/></svg>");
    background-repeat: no-repeat;
    background-position: 12px center;
}
.shared-workbench {
    display: grid;
    grid-template-columns: minmax(320px, 0.92fr) minmax(380px, 1.08fr);
    gap: 16px;
    align-items: start;
}
.shared-list,
.shared-detail-panel {
    min-height: 480px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: var(--quindi-surface);
}
.shared-list {
    display: flex;
    flex-direction: column;
    padding: 8px;
    gap: 6px;
}
/* Positioning context for the absolutely-placed favorite star, which is a
   sibling of the row <button> (not nested) so it never selects the row. */
.shared-row-wrap { position: relative; }
.shared-row {
    position: relative;
    width: 100%;
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 10px 44px 10px 10px;
    /* Button reset — this is a real <button> for native Enter/Space activation. */
    margin: 0;
    font: inherit;
    -webkit-appearance: none;
    appearance: none;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: transparent;
    color: inherit;
    text-align: left;
    cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease;
}
.shared-row:focus-visible {
    outline: 1px solid var(--accent);
    outline-offset: 2px;
}
/* Scene preview — mirrors .gallery-thumb / .recents-item-thumb so
   quindi.thumbnails.attach (swaps is-placeholder → has-thumb and injects
   img.gallery-thumb-img) renders the same, just sized for a list row. */
.shared-row-thumb {
    position: relative;
    flex: 0 0 auto;
    width: 116px;
    aspect-ratio: 16 / 10;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: #ffffff;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--quindi-text-soft);
    transition: box-shadow 0.12s ease;
}
.shared-row-thumb.is-placeholder { background: var(--quindi-surface-2); }
.shared-row-thumb.is-placeholder::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
    transform: translateX(-100%);
    animation: thumb-skeleton-sweep 1.4s ease-in-out infinite;
    pointer-events: none;
}
.shared-row-thumb.is-placeholder .thumb-placeholder {
    width: 66%;
    opacity: 0.5;
    color: var(--quindi-text-soft);
}
.shared-row-thumb.has-thumb img.gallery-thumb-img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: contain;
    user-select: none;
    -webkit-user-drag: none;
}
@media (prefers-reduced-motion: reduce) {
    .shared-row-thumb.is-placeholder::after { animation: none; }
}
.shared-row-body {
    min-width: 0;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding-top: 1px;
}
.shared-row-star {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 28px;
    height: 28px;
    display: grid;
    place-items: center;
    padding: 0;
    margin: 0;
    -webkit-appearance: none;
    appearance: none;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.035);
    color: var(--quindi-text-soft);
    cursor: pointer;
}
.shared-row-star:hover,
.shared-row-star.is-on {
    color: var(--quindi-amber);
    border-color: rgba(255, 194, 102, 0.4);
    background: var(--quindi-amber-soft);
}
.shared-row-star:focus-visible {
    outline: 1px solid var(--accent);
    outline-offset: 2px;
}
.shared-row:hover {
    background: rgba(255, 255, 255, 0.045);
    border-color: rgba(255, 255, 255, 0.08);
}
.shared-row.is-active {
    background: var(--share-accent-soft);
    border-color: rgba(0, 214, 143, 0.38);
}
.shared-row.is-active::before {
    content: "";
    position: absolute;
    left: -1px;
    top: 8px;
    bottom: 8px;
    width: 3px;
    border-radius: 2px;
    background: var(--accent);
}
/* The mounted preview lights up on hover / selection, matching the dashboard cards. */
.shared-row:hover .shared-row-thumb {
    box-shadow: 0 0 0 1px rgba(0, 214, 143, 0.45), 0 2px 8px rgba(0, 0, 0, 0.35);
}
.shared-row.is-active .shared-row-thumb {
    box-shadow: 0 0 0 1px rgba(0, 214, 143, 0.6), 0 2px 8px rgba(0, 0, 0, 0.4);
}
.shared-row-main {
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.shared-row-title {
    color: var(--quindi-text);
    font-size: 14px;
    font-weight: 650;
    line-height: 1.35;
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.shared-row-meta,
.shared-person-email,
.shared-person-date,
.shared-link-date,
.shared-detail-meta {
    color: var(--quindi-text-soft);
    font-size: 11px;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}
.shared-row-badges,
.shared-row-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}
.shared-row-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.06);
    color: var(--quindi-text-soft);
    font-size: 11px;
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
}
.shared-row-badge.is-with {
    color: var(--accent);
    background: var(--share-accent-soft);
}
.shared-row-badge.is-by {
    color: var(--quindi-amber);
    background: var(--quindi-amber-soft);
}
.shared-list-empty,
.shared-detail-loading,
.shared-readonly-note {
    color: var(--quindi-text-soft);
    padding: 18px;
    text-align: center;
}
/* Section empty state — a quiet dashed placeholder that reads as "nothing here
   yet, add something above" rather than a loading line. */
.shared-detail-empty-row {
    color: var(--quindi-text-soft);
    font-size: 12px;
    line-height: 1.4;
    padding: 14px;
    text-align: center;
    border: 1px dashed var(--quindi-border);
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.015);
}
.shared-detail-panel {
    position: sticky;
    top: 18px;
    padding: 18px;
}
.shared-detail-empty {
    min-height: 480px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: var(--quindi-text-soft);
    gap: 8px;
}
.shared-detail-empty h2,
.shared-detail-header h2 {
    margin: 0;
    color: var(--quindi-text);
    font-size: 20px;
    line-height: 1.25;
    overflow-wrap: anywhere;
}
.shared-detail-empty p { margin: 0; max-width: 280px; }
.shared-detail-empty-icon {
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    border-radius: var(--radius-md);
    color: var(--accent);
    background: var(--share-accent-soft);
}
.shared-detail-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 14px;
    margin-bottom: 16px;
}
.shared-detail-kicker {
    display: inline-block;
    margin: 0 0 6px 0;
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    background: var(--share-accent-soft);
    color: var(--accent);
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.shared-open-link,
.shared-icon-button {
    width: 32px;
    height: 32px;
    display: inline-grid;
    place-items: center;
    border-radius: var(--radius-sm);
    border: 1px solid var(--quindi-border);
    color: var(--quindi-text);
    background: rgba(255, 255, 255, 0.04);
    cursor: pointer;
    flex: 0 0 auto;
}
.shared-open-link:hover,
.shared-icon-button:hover {
    border-color: rgba(255, 255, 255, 0.18);
    background: rgba(255, 255, 255, 0.08);
}
.shared-icon-button:disabled {
    opacity: 0.55;
    cursor: default;
}
.shared-detail-section {
    border-top: 1px solid var(--quindi-border);
    padding-top: 16px;
    margin-top: 16px;
}
.shared-section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}
.shared-section-header h3 {
    margin: 0;
    color: var(--quindi-text);
    font-size: 14px;
}
.shared-section-header span {
    color: var(--quindi-text-soft);
    font-size: 12px;
}
/* Share composer — the email field (grant only) sits above an actions row that
   pairs the access/lifetime selector with the primary button, so the action
   reads as part of one unit instead of a stack of full-width slabs. */
.shared-grant-form,
.shared-link-create {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 12px;
}
.shared-grant-actions,
.shared-link-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px;
}
/* Segmented selectors — matched to .shared-tabs so the toggle reads as one
   control (subtle tray, active segment filled with the accent). */
.shared-role-toggle,
.shared-ttl-toggle {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.03);
}
.shared-person-list,
.shared-link-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.shared-person-row,
.shared-link-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.025);
}
.shared-avatar {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    display: inline-grid;
    place-items: center;
    color: #0A0A0A;
    background: var(--accent);
    font-size: 11px;
    font-weight: 800;
    flex: 0 0 auto;
}
.shared-person-list > .shared-person-row:nth-child(5n+2) .shared-avatar {
    background: var(--quindi-amber);
    color: #0A0A0A;
}
.shared-person-list > .shared-person-row:nth-child(5n+3) .shared-avatar {
    background: #B58CFF;
    color: #fff;
}
.shared-person-list > .shared-person-row:nth-child(5n+4) .shared-avatar {
    background: #FF7A7A;
    color: #fff;
}
.shared-person-list > .shared-person-row:nth-child(5n+5) .shared-avatar {
    background: #4FD1C5;
    color: #0A0A0A;
}
.shared-person-meta,
.shared-link-meta {
    min-width: 0;
    flex: 1;
}
.shared-person-name,
.shared-link-id {
    color: var(--quindi-text);
    font-size: 13px;
    font-weight: 650;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.shared-role {
    color: var(--quindi-text-soft);
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
}
.shared-issued-link {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px;
    padding: 26px 10px 10px;
    margin-bottom: 12px;
    border: 1px solid rgba(0, 214, 143, 0.24);
    border-radius: var(--radius-sm);
    background: rgba(0, 214, 143, 0.08);
}
.shared-issued-link::before {
    content: "New link ready";
    position: absolute;
    top: 8px;
    left: 12px;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--accent);
}

@media (max-width: 920px) {
    .shared-toolbar {
        align-items: stretch;
        flex-direction: column;
    }
    .shared-stats {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    .shared-tabs {
        width: 100%;
        overflow-x: auto;
    }
    .shared-search {
        max-width: none;
    }
    .shared-workbench {
        grid-template-columns: 1fr;
    }
    .shared-detail-panel {
        position: static;
        min-height: 0;
    }

    /* Stack the brand over the action cluster; the cluster wraps left-aligned and drops its group
       separators (they read as noise once the row breaks). */
    .page-topbar {
        flex-direction: column;
        align-items: stretch;
        gap: 12px;
    }
    .dashboard-actions { justify-content: flex-start; }
    .action-sep { display: none; }
}

.notifications-shell {
    padding: 0 24px 80px;
}
.notifications-tabs {
    margin-bottom: 14px;
}
.notifications-list {
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: var(--quindi-surface);
    overflow: hidden;
}
.notification-row {
    position: relative;
    width: 100%;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 10px;
    align-items: center;
    padding: 14px 16px;
    border-bottom: 1px solid var(--quindi-border);
    background: transparent;
    color: inherit;
}
.notification-main {
    min-width: 0;
    width: 100%;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 12px;
    align-items: start;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    text-align: left;
    cursor: pointer;
}
.notification-row:last-child { border-bottom: 0; }
.notification-row:hover {
    background: rgba(255, 255, 255, 0.04);
}
.notification-row.is-unread {
    background: rgba(0, 214, 143, 0.055);
}
.notification-kind {
    min-width: 62px;
    padding: 3px 8px;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.06);
    color: var(--quindi-text-soft);
    font-size: 10px;
    font-weight: 800;
    text-align: center;
    text-transform: uppercase;
}
.notification-kind.is-share {
    color: var(--accent);
    background: var(--share-accent-soft);
}
.notification-kind.is-mention {
    color: #B58CFF;
    background: rgba(181, 140, 255, 0.14);
}
.notification-kind.is-revoked {
    color: var(--quindi-red, #F08080);
    background: rgba(229, 72, 77, 0.14);
}
.notification-copy {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.notification-title-line {
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.notification-title {
    min-width: 0;
    color: var(--quindi-text);
    font-size: 14px;
    font-weight: 700;
    overflow-wrap: anywhere;
}
.notification-body {
    color: var(--quindi-text-soft);
    font-size: 13px;
    line-height: 1.4;
    overflow-wrap: anywhere;
}
.notification-time {
    color: var(--quindi-text-soft);
    font-size: 11px;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}
.notification-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 3px rgba(0, 214, 143, 0.12);
    flex: 0 0 auto;
}
.notification-delete {
    width: 30px;
    height: 30px;
    margin-top: 1px;
    color: var(--quindi-text-soft);
}
.notification-delete:hover {
    color: var(--quindi-red, #F08080);
    border-color: rgba(240, 128, 128, 0.4);
    background: rgba(229, 72, 77, 0.12);
}
.glass-button.danger {
    color: var(--quindi-red, #F08080);
}
.glass-button.danger:hover {
    border-color: rgba(240, 128, 128, 0.4);
    background: rgba(229, 72, 77, 0.12);
}

@media (max-width: 640px) {
    .shared-stats {
        grid-template-columns: 1fr;
    }
    .notification-row {
        grid-template-columns: minmax(0, 1fr) auto;
    }
    .notification-main {
        grid-template-columns: minmax(0, 1fr);
    }
    .notification-kind {
        grid-column: 1 / -1;
        width: max-content;
    }
    .notification-copy {
        grid-column: 1;
    }
    .notification-delete {
        grid-column: 2;
        grid-row: 1 / span 2;
    }
}

/* Collaborator popover — anchored under the sharing badge. Backdrop catches
   outside clicks so the popover dismisses without an extra JS listener. */
.collaborator-popover-backdrop {
    position: fixed;
    inset: 0;
    z-index: 10;
    background: transparent;
}
.collaborator-popover {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    min-width: 240px;
    max-width: 320px;
    padding: 8px;
    background: rgba(20, 20, 20, 0.96);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: var(--radius-md);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    z-index: 11;
    display: flex;
    flex-direction: column;
    gap: 4px;
    color: var(--quindi-text);
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    font-size: 12px;
}
.collaborator-empty {
    padding: 10px 8px;
    color: var(--quindi-text-soft);
    text-align: center;
}
.collaborator-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
}
.collaborator-row:hover {
    background: rgba(255, 255, 255, 0.04);
}
.collaborator-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: rgba(91, 154, 255, 0.22);
    color: #8FB6FF;
    font-size: 11px;
    font-weight: 600;
    flex: 0 0 auto;
}
.collaborator-meta {
    flex: 1 1 auto;
    min-width: 0;
}
.collaborator-name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.collaborator-email {
    color: var(--quindi-text-soft);
    font-size: 11px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.collaborator-granted {
    color: var(--quindi-text-soft);
    font-size: 10px;
    margin-top: 1px;
    opacity: 0.85;
}
.collaborator-role {
    flex: 0 0 auto;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--quindi-text-soft);
    padding: 2px 6px;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.06);
}
.collaborator-remove {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: transparent;
    border: 1px solid transparent;
    color: var(--quindi-text-soft);
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
.collaborator-remove:hover {
    background: rgba(229, 72, 77, 0.18);
    color: var(--quindi-red, #F08080);
    border-color: rgba(229, 72, 77, 0.35);
}
.collaborator-remove:disabled {
    opacity: 0.4;
    cursor: progress;
}

/* Top-right action cluster — hidden until hover/focus to keep the card
   visually clean until the user interacts. */
.gallery-actions {
    position: absolute;
    /* Tucked inside the mounted plate's top-right corner (matches .gallery-badges). */
    top: 16px;
    right: 16px;
    display: flex;
    gap: 4px;
    /* Never let the six-icon cluster spill past the card: cap the row to the card
       width minus the 16px insets on each side (so it stays inside the mounted
       thumbnail, which is itself inset 10px). It stays a SINGLE row — on a narrow
       card the buttons shrink to fit (see .gallery-action flex) instead of wrapping
       to a second row or bleeding out over the edge / neighbouring cards. */
    max-width: calc(100% - 32px);
    justify-content: flex-end;
    z-index: 2;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
}
.gallery-card:hover .gallery-actions,
.gallery-card:focus-within .gallery-actions,
.gallery-actions.has-active-favorite {
    opacity: 1;
    pointer-events: auto;
}
/* Favorited card at rest: the gold star stays as the favorite indicator, but the
   rest of the cluster (tag / rename / duplicate / delete) collapses until the card
   is hovered, focused, or has its tag menu open — otherwise every favorited card
   would show its full toolbar with no pointer over it. */
.gallery-actions.has-active-favorite .gallery-action:not(.gallery-favorite) {
    opacity: 0;
    pointer-events: none;
}
.gallery-card:hover .gallery-actions.has-active-favorite .gallery-action:not(.gallery-favorite),
.gallery-card:focus-within .gallery-actions.has-active-favorite .gallery-action:not(.gallery-favorite),
.gallery-card.is-menu-open .gallery-actions.has-active-favorite .gallery-action:not(.gallery-favorite) {
    opacity: 1;
    pointer-events: auto;
}
.gallery-action {
    /* 28px at rest, but allowed to shrink (flex-shrink:1, min-width:0) so all six
       icons stay on ONE row inside a narrow card instead of overflowing; aspect-ratio
       keeps them square while they shrink. */
    flex: 0 1 28px;
    min-width: 0;
    aspect-ratio: 1 / 1;
    display: grid; place-items: center;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(16, 16, 16, 0.86);
    color: var(--quindi-text-soft);
    border-radius: var(--radius-sm);
    cursor: pointer;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease, opacity 0.12s ease;
}
/* Hover backgrounds stay opaque so the chip reads against bright thumbnails:
   replacing the base rgba(20,20,20,0.8) with a translucent overlay turned
   the icons see-through over light scene previews. The translucent rgba is
   layered on top of a darker base instead of replacing it. */
.gallery-action:hover {
    background: rgba(40, 40, 40, 0.92);
    color: var(--quindi-text);
    border-color: rgba(255, 255, 255, 0.18);
}
.gallery-action.is-destructive:hover {
    background: rgba(60, 24, 26, 0.92);
    color: var(--quindi-red, #F08080);
    border-color: rgba(229, 72, 77, 0.55);
}
/* The favorite star anchors at the far right of the cluster: a favorited card at
   rest shows it flush in the top-right corner, and on hover the other actions
   expand to its left instead of shoving the star inward. */
.gallery-favorite { order: 1; }
.gallery-favorite.is-on {
    color: var(--quindi-amber);
    border-color: rgba(255, 194, 102, 0.38);
    background: rgba(54, 39, 17, 0.92);
}
.gallery-action:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }

/* ── Selection mode (dashboard bulk actions) ──────────────────────────────────
   The whole card becomes a toggle; the per-card hover actions step aside for a
   single checkmark, and a selected card gets an accent ring. */
.gallery-card.is-selectable { cursor: pointer; }
.gallery-card.is-selectable .gallery-actions { display: none; }

/* Phone: the six-icon action cluster (favorite / tag / folder / rename / duplicate
   / delete) is sized for a roomy desktop card; on a narrow phone card it crowds and
   overflows the corner. Shrink the buttons, icons, and gap so all six sit inside. */
@media (max-width: 599px) {
    .gallery-actions { gap: 3px; }
    .gallery-action { flex-basis: 22px; }
    .gallery-action svg { width: 12px; height: 12px; }
}

/* Touch devices have no hover, so the hover-revealed action cluster would never appear (a tap
   falls through to the card link). Reveal it at rest on coarse pointers — without this the whole
   per-card tag/move/rename/duplicate/delete set (and the mobile icon sizing above) is unreachable.
   Out-specifies the base opacity rules and does NOT touch .is-selectable's display:none, so bulk
   select is unaffected. */
@media (hover: none) {
    .gallery-card .gallery-actions { opacity: 1; pointer-events: auto; }
    .gallery-card .gallery-actions.has-active-favorite .gallery-action:not(.gallery-favorite) {
        opacity: 1;
        pointer-events: auto;
    }
}

/* Corner button (bottom-right) that peeks the document preview on hover / tap. */
.gallery-preview-btn {
    position: absolute;
    bottom: 10px;
    right: 12px;
    z-index: 2;
    width: 28px;
    height: 28px;
    display: grid;
    place-items: center;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(16, 16, 16, 0.86);
    color: var(--quindi-text-soft);
    border-radius: var(--radius-sm);
    cursor: pointer;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.gallery-card:hover .gallery-preview-btn,
.gallery-card:focus-within .gallery-preview-btn { opacity: 1; pointer-events: auto; }
.gallery-preview-btn:hover { color: var(--quindi-text); border-color: var(--accent); }
.gallery-preview-btn:focus-visible { opacity: 1; pointer-events: auto; outline: 1px solid var(--accent); outline-offset: 1px; }
.gallery-select {
    position: absolute;
    top: 16px;
    right: 16px;
    z-index: 3;
    /* Purely visual: let clicks fall through to the card so the checkmark toggles too. */
    pointer-events: none;
    width: 24px;
    height: 24px;
    display: grid;
    place-items: center;
    border-radius: 999px;
    border: 1.5px solid rgba(255, 255, 255, 0.55);
    background: rgba(16, 16, 16, 0.86);
    color: transparent;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.gallery-select.is-on {
    background: var(--accent);
    border-color: var(--accent);
    color: #0A0A0A;
}
.gallery-card.is-selected { border-color: var(--accent); }
.gallery-card.is-selected::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    box-shadow: 0 0 0 1px var(--accent), 0 0 0 4px rgba(0, 214, 143, 0.16);
}

/* Bulk action bar shown above the gallery while in select mode. */
.bulk-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 12px 10px 16px;
    margin-bottom: 16px;
    border: 1px solid rgba(0, 214, 143, 0.35);
    border-radius: var(--radius-md);
    background: linear-gradient(180deg, rgba(0, 214, 143, 0.10), rgba(0, 214, 143, 0.04));
    box-shadow: var(--glass-shadow-soft);
}
.bulk-bar-count { font-size: 13px; font-weight: 650; color: var(--quindi-text); }
.bulk-bar-spacer { flex: 1 1 auto; }
.bulk-bar-anchor { position: relative; display: inline-flex; }

/* Dashboard import queue. JS uploads one file at a time and streams each job's
   status into this compact operational strip. */
.import-queue {
    margin: -4px 0 16px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: var(--radius-md);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.018));
    box-shadow: var(--glass-shadow-soft);
    overflow: hidden;
}
.import-queue-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 12px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.import-queue-title {
    font-size: 13px;
    font-weight: 700;
    color: var(--quindi-text);
}
.import-queue-summary {
    margin-top: 2px;
    font-size: 12px;
    color: var(--quindi-text-soft);
}
.import-queue-clear {
    flex: 0 0 auto;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--quindi-text-soft);
    font: inherit;
    font-size: 12px;
    font-weight: 650;
    padding: 6px 9px;
    cursor: pointer;
}
.import-queue-clear:hover { color: var(--quindi-text); border-color: rgba(255, 255, 255, 0.18); }
.import-queue-list {
    display: grid;
}
.import-queue-item {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 12px;
    align-items: center;
    padding: 10px 12px;
}
.import-queue-item + .import-queue-item {
    border-top: 1px solid rgba(255, 255, 255, 0.055);
}
.import-queue-main { min-width: 0; }
.import-queue-file {
    color: var(--quindi-text);
    font-size: 13px;
    font-weight: 650;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.import-queue-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 8px;
    margin-top: 3px;
    color: var(--quindi-text-soft);
    font-size: 11px;
}
.import-queue-progress {
    height: 4px;
    margin-top: 8px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
}
.import-queue-progress span {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--accent);
    transition: width 0.14s ease;
}
.import-queue-item.is-failed .import-queue-progress span { background: #ff5c7a; }
.import-queue-item.is-canceled .import-queue-progress span { background: var(--quindi-text-soft); }
.import-queue-error {
    margin-top: 6px;
    color: #ff9aaa;
    font-size: 12px;
    line-height: 1.35;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.import-queue-actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.import-queue-action {
    display: inline-grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.035);
    color: var(--quindi-text-soft);
    cursor: pointer;
}
.import-queue-action:hover {
    color: var(--quindi-text);
    border-color: rgba(0, 214, 143, 0.45);
}

@media (max-width: 720px) {
    .import-queue-head,
    .import-queue-item {
        grid-template-columns: 1fr;
        align-items: stretch;
    }
    .import-queue-head {
        display: grid;
    }
    .import-queue-actions {
        justify-content: flex-end;
    }
}

/* Compact-card variant — driven by the dashboard's "Anteprime" toggle. The thumb
   wrapper isn't rendered at all in this mode; the card collapses to its title +
   meta + tags + actions strip. Visually closer to the legacy text-only grid but
   reusing the same component / layout / interactions. */
.gallery-card.is-compact .gallery-overlay {
    border-top: none;
    padding: 14px 16px 14px;
    border-radius: var(--radius-md);
}
.gallery-card.is-compact .gallery-actions {
    /* No thumbnail to overlay against — actions are on the card surface itself
       so they need to read against --quindi-surface, not against arbitrary
       scene colors. The hover-only reveal stays. */
    background: transparent;
    /* No mounted plate to tuck inside — sit at the card's own corner. */
    top: 8px;
    right: 8px;
}
.gallery-card.is-compact .gallery-action {
    background: var(--quindi-surface);
    border-color: var(--quindi-border);
}
.gallery-card.is-compact .gallery-badges {
    /* Without a thumb the absolute-positioned badges would overlap the title.
       Move them into the overlay flow as inline pills. */
    position: static;
    padding: 0 16px;
    margin-top: -6px;
    margin-bottom: 6px;
    max-width: 100%;
}
.gallery-card.is-compact .gallery-status {
    background: rgba(255, 255, 255, 0.04);
    backdrop-filter: none;
}

/* Header toggle — eye / eye-off icon + label, persisted to localStorage. Sits
   inline with the other dashboard-actions buttons but lighter than a full
   GlassButton because it's a preference, not a primary action. */
.thumb-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--quindi-border);
    background: transparent;
    color: var(--quindi-text-soft);
    font: inherit;
    font-size: 12px;
    cursor: pointer;
    transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
.thumb-toggle:hover {
    color: var(--quindi-text);
    border-color: rgba(255, 255, 255, 0.18);
}
.thumb-toggle.is-on {
    color: var(--accent);
    border-color: rgba(0, 214, 143, 0.35);
    background: rgba(0, 214, 143, 0.08);
}
.thumb-toggle:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }
.thumb-toggle svg { display: block; }

/* Preview-size segmented control — S / M / L. Sits next to .thumb-toggle and
   scales the gallery cards via --gallery-col-min. Styled as a compact pill of
   equal-height segments to match the toggle beside it. */
/* Preview-size control — a "Size" trigger (icon-over-label like the rest of the strip) that opens a
   small S/M/L popover, shown beside the Previews eye when previews are on. */
.thumb-size-anchor { position: relative; display: inline-flex; }
.thumb-size-backdrop { position: fixed; inset: 0; z-index: 60; }
.thumb-size-pop {
    position: absolute;
    top: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 61;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 4px;
    min-width: 148px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: var(--quindi-surface-2);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}
.thumb-size-pop-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--quindi-text-soft);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
    text-align: left;
}
.thumb-size-pop-item:hover { background: rgba(255, 255, 255, 0.06); color: var(--quindi-text); }
.thumb-size-pop-item.is-active { color: var(--accent); }
.thumb-size-pop-item:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }
.thumb-size-pop-key {
    display: inline-grid;
    place-items: center;
    width: 22px;
    height: 22px;
    flex: 0 0 auto;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.06);
    font-weight: 700;
    font-size: 11px;
}
.thumb-size-pop-item.is-active .thumb-size-pop-key { background: var(--accent); color: #0a0a0a; }
.thumb-size-pop-name { flex: 1 1 auto; }

/* Drop overlay — full-viewport target rendered on top of everything when the
   user drags files onto the page. The toggle is `body.has-drag-over` set by
   quindi.dashboard's drag handlers; default opacity:0 + pointer-events:none
   keep the element invisible and click-through when no drag is in flight. */
.drop-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(10, 10, 10, 0.55);
    backdrop-filter: blur(4px);
    border: 3px dashed var(--accent, #00D68F);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s ease;
}
body.has-drag-over .drop-overlay {
    opacity: 1;
}
.drop-overlay-message {
    font-size: 22px;
    color: var(--quindi-text, #F4F6F9);
    text-align: center;
    line-height: 1.4;
    font-weight: 600;
}
.drop-overlay-message span {
    display: block;
    margin-top: 6px;
    font-size: 13px;
    font-weight: 400;
    color: var(--quindi-text-soft, #8B95A5);
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

/* Build stamp — discreet bottom-right marker so users can quote a version when
   reporting issues. Format YYYYMMDD-sha is baked into the assembly by the
   ResolveBuildMetadata MSBuild target in Quindi.Excalidraw.Web.Blazor.csproj. */
.build-stamp {
    position: fixed;
    bottom: 8px;
    right: 12px;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
    font-size: 11px;
    color: var(--quindi-text-soft, #8B95A5);
    opacity: 0.55;
    pointer-events: none;
    user-select: text;
    z-index: 1;
    letter-spacing: 0.02em;
}

/* Upstream attribution: small footer centrato in basso che da' credito al
   progetto Excalidraw open-source su cui Quindi.Excalidraw e' costruito.
   pointer-events:auto solo sul link cosi' il resto del footer non intercetta
   eventi sul canvas / sulla dashboard. */
.powered-by {
    position: fixed;
    bottom: 8px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 11px;
    color: var(--quindi-text-soft, #8B95A5);
    opacity: 0.55;
    pointer-events: none;
    z-index: 1;
    letter-spacing: 0.02em;
    white-space: nowrap;
}
.powered-by a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    pointer-events: auto;
}
.powered-by a:hover { color: var(--accent); }
/* Phone widths: the centered attribution line is wider than the viewport and sat
   on top of the build stamp — keep the attribution (ellipsized to fit), drop the
   stamp (diagnostic detail, still visible on desktop). */
@media (max-width: 700px) {
    .powered-by {
        max-width: calc(100vw - 24px);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .build-stamp { display: none; }
}
.doc-card-row { position: relative; }
.doc-card-row:nth-child(odd) .doc-card  { transform: rotate(-0.3deg); }
.doc-card-row:nth-child(even) .doc-card { transform: rotate(0.25deg); border-radius: var(--sketch-radius-b); }
.doc-card-row:hover .row-action { opacity: 1; pointer-events: auto; }
.doc-card {
    padding: 20px;
    height: 104px;
    color: inherit;
    text-decoration: none;
    transition: transform 0.18s cubic-bezier(0.2, 0.7, 0.2, 1), border-color 0.18s ease, box-shadow 0.18s ease;
    display: flex; flex-direction: column; justify-content: space-between; gap: 6px;
    /* Same top-lit surface + catch-light as the gallery cards — one card family. */
    background: linear-gradient(180deg, var(--quindi-card-top), var(--quindi-card-bottom));
    border: var(--sketch-border-w) solid var(--sketch-border);
    border-radius: var(--sketch-radius-a);
    box-shadow: var(--elev-2);
}
.doc-card:hover {
    transform: translateY(-4px);
    text-decoration: none;
    border-color: rgba(0, 214, 143, 0.55);
    box-shadow: 0 0 0 1px rgba(0, 214, 143, 0.35), 0 10px 22px rgba(0, 214, 143, 0.14), 0 22px 48px rgba(0, 0, 0, 0.55);
}
/* Same specificity as the nth-child rest tilts above, later in source: hover
   straightens the card and lifts it, overriding the rotation. */
.doc-card-row .doc-card:hover { transform: translateY(-4px) rotate(0deg); }
.doc-card-title {
    font-weight: 600; font-size: 15px;
    padding-right: 32px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}
.doc-card-meta { font-size: 12px; color: var(--quindi-text-soft); }

/* Action buttons used inside dashboard cards / rail panel doc rows. */
.row-action {
    position: absolute;
    right: 8px; top: 50%;
    transform: translateY(-50%);
    width: 24px; height: 24px;
    display: grid; place-items: center;
    border: none;
    background: transparent;
    color: var(--quindi-text-soft);
    border-radius: var(--radius-sm);
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.row-action:hover { background: rgba(255,255,255,0.08); color: var(--quindi-text); }
.row-action.is-destructive:hover { background: rgba(229, 72, 77, 0.15); color: var(--quindi-red); }
.row-action.is-rename:hover { background: rgba(0, 214, 143, 0.15); color: var(--accent); }
.row-action:focus-visible { opacity: 1; pointer-events: auto; outline: 1px solid var(--accent); outline-offset: 1px; }
.card-action { right: 12px; top: 12px; transform: none; width: 28px; height: 28px; }
/* When a card has both rename + delete, lay them side by side instead of stacked. */
.card-action-rename { right: 48px; }

/* Make Excalidraw canvas inherit the rounded corners (only in shared view). */
.shared-canvas-host .excalidraw-host,
.shared-canvas-host .excalidraw-host .excalidraw,
.shared-canvas-host .excalidraw-host .excalidraw-container,
.shared-canvas-host .excalidraw-host .App { border-radius: inherit; }
.shared-canvas-host .excalidraw-host .excalidraw .layer-ui__wrapper { border-radius: inherit; }

/* Generic small text-button used in rail panel footer + bulk bar. */
.link-btn {
    background: none; border: none;
    padding: 4px 0;
    font: inherit; font-size: 12px;
    color: var(--quindi-text-soft);
    cursor: pointer;
    text-decoration: none;
}
.link-btn:hover { color: var(--accent); text-decoration: none; }
.link-btn.danger { color: var(--quindi-red); }
.link-btn.danger:hover { color: #ff6b70; }
/* Warn variant — amber link, leggermente piu' brillante su hover. Usato per
   azioni che non sono distruttive ma vogliono ancora un richiamo visivo
   distinto dal verde brand (es. Sign out, che porta fuori sessione ma non
   distrugge nulla). */
.link-btn.warn { color: var(--quindi-amber); }
.link-btn.warn:hover { color: #FFC447; }
.link-btn[disabled] { opacity: 0.4; cursor: not-allowed; }

.row-check {
    position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
    width: 14px; height: 14px;
    accent-color: var(--accent);
    cursor: pointer;
}
.panel-doc-row.bulk .panel-doc { padding-left: 32px; }
.panel-doc-row.bulk.selected { background: var(--accent-soft); border-radius: var(--radius-sm); }

/* Trash — override the dashboard .doc-card fixed 104px height: the trash card stacks
   a title, meta and TWO action buttons, which doesn't fit, so the flex column was
   shrinking the (overflow:hidden) title to near-zero height and clipping it away.
   Auto height + a non-shrinking title makes the document name visible again; the
   content is centered so the recovery card reads cleanly. */
.trash-card {
    position: relative;          /* anchors the multi-select checkbox */
    height: auto;
    min-height: 156px;          /* uniform card height regardless of title length */
    gap: 6px;
    justify-content: center;    /* center the stacked content vertically */
    align-items: center;
    text-align: center;
}
.trash-card .doc-card-title {
    padding-right: 0;   /* no corner action button to clear in the trash card */
    flex: 0 0 auto;     /* never shrink/clip the document name */
    width: 100%;
    min-height: 2.6em;  /* reserve two lines so 1- and 2-line titles line up */
}
.trash-card .doc-card-meta { flex: 0 0 auto; }
.trash-check {
    position: absolute; top: 10px; left: 10px;
    width: 16px; height: 16px;
    accent-color: var(--accent);
    cursor: pointer;
}
.trash-card.selected { border-color: var(--accent); }
.trash-card .trash-actions {
    display: flex;
    gap: 8px;
    margin-top: 8px;
    justify-content: center;
}

/* Share modal */
.share-options { margin: 12px 0; }
.share-label { display: block; font-size: 12px; color: var(--quindi-text-soft); margin-bottom: 8px; }
.share-ttl { display: flex; gap: 6px; flex-wrap: wrap; }
.ttl-pill {
    padding: 6px 12px;
    border: 1px solid var(--quindi-border);
    background: var(--quindi-surface-2);
    color: var(--quindi-text);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font: inherit; font-size: 12px;
    transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.ttl-pill:hover { border-color: var(--accent); }
.ttl-pill.active { background: var(--accent); color: #050505; border-color: var(--accent); }
.share-link {
    display: flex; gap: 8px; align-items: center;
    margin-top: 16px;
}
.share-link .search-input { flex: 1; cursor: pointer; }
.share-meta { color: var(--quindi-text-soft); font-size: 12px; margin: 8px 0 0; }

.share-tabs {
    display: flex; gap: 6px;
    margin: 0 0 16px;
    border-bottom: 1px solid var(--quindi-border);
}
.share-tab {
    padding: 8px 14px;
    background: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    color: var(--quindi-text-soft);
    cursor: pointer;
    font: inherit; font-size: 13px;
    transition: color 0.12s ease, border-color 0.12s ease;
}
.share-tab:hover { color: var(--quindi-text); }
.share-tab.active { color: var(--quindi-text); border-bottom-color: var(--accent); }

.share-grant-form {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 8px; align-items: center;
    margin-bottom: 16px;
}
.share-grant-form .search-input { width: 100%; }

.share-active { margin-top: 16px; }
.share-active-header {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 8px;
}
.share-active-status { color: var(--quindi-text-soft); font-size: 12px; }
.share-active-list {
    list-style: none; padding: 0; margin: 0;
    display: flex; flex-direction: column; gap: 6px;
}
.share-active-item {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px;
    padding: 8px 12px;
    background: var(--quindi-surface-2);
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
}
.share-active-meta {
    display: flex; flex-direction: column;
    min-width: 0;
}
.share-active-id { font-size: 13px; color: var(--quindi-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.share-active-expires { font-size: 11px; color: var(--quindi-text-soft); }

/* Shared (read-only) view */
.shared-view-shell {
    min-height: 100vh;
    display: grid;
    grid-template-rows: 56px 1fr;
}
.shared-topbar {
    display: flex; align-items: center; justify-content: space-between;
    padding: 0 18px;
    border-bottom: 1px solid var(--quindi-border);
    background: var(--quindi-surface);
}
.shared-topbar .brand { color: inherit; text-decoration: none; }
.shared-badge {
    padding: 4px 10px;
    background: var(--quindi-surface-2);
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    font-size: 12px;
    color: var(--quindi-text-soft);
}
.shared-canvas-host {
    position: relative;
    min-height: 0;
}
.shared-canvas-host .editor-canvas {
    position: absolute; inset: 14px;
}

/* Embedded board — chrome-free, full-bleed canvas for iframes (/embed/{token}).
   No topbar/brand/margin, unlike the shared view: the embedding host page
   already provides the surrounding context. */
.embed-host {
    position: fixed; inset: 0;
    background: var(--quindi-bg);
}
.embed-host .editor-canvas {
    position: absolute; inset: 0;
}
.embed-host .loading,
.embed-host .embed-error {
    position: absolute; inset: 0;
    display: grid; place-items: center;
    padding: 24px; text-align: center;
    color: var(--quindi-text-soft);
}

/* Standalone pages (login, settings, notfound) */
.standalone-page {
    min-height: 100vh;
    display: grid; place-items: center;
    padding: 32px;
}
.standalone-card {
    width: 100%; max-width: 480px;
    background: var(--quindi-surface);
    border: var(--sketch-border-w) solid var(--sketch-border);
    border-radius: var(--sketch-radius-a);
    padding: 32px;
    box-shadow: var(--elev-3);
}
.standalone-card h1 {
    margin: 0 0 8px;
    font-family: var(--font-hand);
    font-size: 34px;
    font-weight: 400;
    letter-spacing: 0;
}

/* FocusOnNavigate (Routes.razor) programmatically focuses the page's <h1>
   on every navigation so screen readers announce the new section. Without
   this rule Chrome paints its blue keyboard-focus rectangle around the
   title on every page (visible on /admin, /login, the dashboard, etc.).
   :not(:focus-visible) is *not* enough: Chrome treats focus that lands on
   a fresh document as focus-visible (the navigation might have been
   keyboard-triggered), so the exemption never matches after a navigation.
   Drop the ring unconditionally on h1 — h1 has no tabindex so it isn't in
   the keyboard focus order, and the screen-reader announcement that the
   focus enables is independent of the visual outline. */
h1:focus { outline: none; }
.standalone-card p {
    margin: 0 0 20px; color: var(--quindi-text-soft); font-size: 14px;
}

/* Sign-in form (Login.razor). The page renders SSR inside the dark
   standalone-card, so each control needs explicit dark styling: the
   earlier inline styles relied on undefined vars (--text-muted,
   --border-soft) and light backgrounds that clashed with the card. */
.login-form { margin-top: 18px; }
.login-label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    color: var(--quindi-text-soft);
}
.login-input {
    width: 100%;
    padding: 12px 14px;
    border: var(--sketch-border-w) solid var(--sketch-border);
    background: var(--quindi-surface-2);
    color: var(--quindi-text);
    border-radius: var(--sketch-radius-sm);
    font-size: 15px;
    outline: none;
}
.login-input::placeholder { color: var(--quindi-text-soft); }
.login-input:focus { border-color: var(--accent); }
.login-actions { display: flex; justify-content: center; gap: 8px; margin-top: 16px; }
.login-actions .glass-button { padding: 9px 22px; font-size: 13px; }
.login-alert {
    margin-top: 12px;
    padding: 10px 14px;
    border-radius: var(--radius-md);
    border: 1px solid var(--quindi-red);
    background: rgba(229, 72, 77, 0.14);
    color: var(--quindi-red);
    font-size: 14px;
}
.login-link { color: var(--quindi-text-soft); text-decoration: none; }
.login-link:hover { color: var(--quindi-text); text-decoration: underline; }
p.login-resend { margin-top: 16px; }
p.login-hint { margin-top: 18px; font-size: 12px; }
p.login-hint code {
    background: var(--quindi-surface-2);
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    padding: 1px 5px;
    font-size: 11.5px;
}
/* The QR door: a white sticky note taped to the card's edge — always on the
   table next to the email form. quindi.js drives the state classes
   (is-idle / is-live / is-terminal / is-failed); without JS the note's form
   falls back to the classic POST + redirect flow. */
.login-page .brand-name {
    font-family: var(--font-hand);
    font-size: 17px;
    font-weight: 400;
}
.login-page::before {
    /* soft spotlight so the card + note pair sits in a lit spot of the board */
    content: "";
    position: fixed; inset: 0;
    pointer-events: none;
    background: radial-gradient(ellipse 70% 60% at 50% 42%, transparent 0%, rgba(0, 0, 0, 0.5) 100%);
}
.login-stage { position: relative; width: 100%; max-width: 480px; }
.login-card { position: relative; z-index: 1; }

.qr-note {
    position: absolute; z-index: 2;
    top: 30px; right: -200px;
    width: 236px;
    background: #fff;
    border-radius: 2px 3px 2px 4px;
    padding: 18px 16px 12px;
    transform: rotate(-3.4deg);
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.45), 0 18px 40px rgba(0, 0, 0, 0.55);
    text-align: center;
}
/* the tape — one milky strip across the top-left corner */
.qr-note::before {
    content: "";
    position: absolute;
    top: -12px; left: -32px;
    width: 84px; height: 25px;
    transform: rotate(-45deg);
    background: rgba(196, 202, 210, 0.34);
    box-shadow:
        0 2px 5px rgba(0, 0, 0, 0.35),
        inset 0 1px 0 rgba(255, 255, 255, 0.35),
        inset 0 -1px 0 rgba(255, 255, 255, 0.25);
}
p.qr-note-title {
    margin: 0 0 12px;
    font-family: var(--font-hand);
    font-size: 17px;
    color: #0c1014;
}
.qr-note-frame {
    display: grid; place-items: center;
    padding: 0 6px;
    aspect-ratio: 1;
}
.qr-note-frame > * { grid-area: 1 / 1; }
.qr-note-frame img { display: block; width: 100%; height: auto; }
.qr-note-frame img:not([src]) { display: none; }
.qr-note-placeholder { width: 62%; height: auto; color: #d7dce2; }
.qr-note.is-live .qr-note-placeholder { display: none; }
p.qr-note-status {
    display: flex; align-items: center; justify-content: center; gap: 7px;
    min-height: 17px;
    margin: 10px 0 0;
    font-size: 12px;
    color: #5a6472;
}
.qr-note.is-live .qr-note-status::before {
    content: "";
    flex: none;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--accent);
    animation: qr-note-pulse 1.6s ease-in-out infinite;
}
@keyframes qr-note-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.35; transform: scale(0.72); }
}
p.qr-note-countdown {
    margin: 4px 0 0;
    font-size: 11.5px;
    color: #98a1ad;
    font-variant-numeric: tabular-nums;
}
.qr-note.is-terminal .qr-note-frame,
.qr-note.is-failed .qr-note-frame { opacity: 0.38; }
.qr-note.is-terminal p.qr-note-status,
.qr-note.is-failed p.qr-note-status { color: var(--quindi-red); }
.qr-note-start { display: flex; justify-content: center; margin-top: 10px; }
/* author `display` would otherwise beat the UA's [hidden] rule */
.qr-note-start[hidden] { display: none; }
.qr-note-start .glass-button { padding: 6px 14px; font-size: 12.5px; }
p.qr-note-hint {
    margin: 10px 0 0;
    font-family: var(--font-hand);
    font-size: 12.5px;
    color: #7a8492;
}

/* keep the card + note pair centred as one composition on wide screens */
@media (min-width: 900px) {
    .login-stage { transform: translateX(-96px); }
}
@media (max-width: 899px) {
    .login-stage { display: flex; flex-direction: column; align-items: center; }
    .login-card { width: 100%; }
    .qr-note {
        position: static;
        width: min(260px, 100%);
        margin: 26px auto 0;
        transform: rotate(-1.4deg);
    }
    /* Stacked, the note hangs under the card: the tape belongs on the seam
       between the two, not off the note's outer corner. */
    .qr-note::before {
        top: -13px; left: 50%;
        width: 92px; height: 26px;
        transform: translateX(-50%) rotate(-2.5deg);
    }
}
@media (prefers-reduced-motion: reduce) {
    .qr-note.is-live .qr-note-status::before { animation: none; }
}

/* Bug report modal — single-field form, posted to /bug-reports via fetch.
   The textarea owns most of the vertical space; the footer pins the
   character counter on the left and the action buttons on the right. */
.bug-report-help {
    margin: 0 0 12px;
    font-size: 13px;
    color: var(--quindi-text-soft);
    line-height: 1.45;
}
.bug-report-textarea {
    width: 100%;
    box-sizing: border-box;
    padding: 10px 12px;
    background: var(--quindi-surface-2);
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-sm);
    color: var(--quindi-text);
    font-family: inherit;
    font-size: 13px;
    line-height: 1.5;
    resize: vertical;
    min-height: 140px;
    outline: none;
}
.bug-report-textarea:focus {
    border-color: var(--accent);
}
.bug-report-textarea:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}
.bug-report-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 14px;
}
.bug-report-count {
    font-size: 11px;
    color: var(--quindi-text-soft);
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

/* ── Markdown blocks ─────────────────────────────────────────────────────────
   A markdown block is an Excalidraw embeddable rendered by excalidraw-interop.js
   (renderMarkdownEmbeddable): a sandboxed iframe + an edit affordance, authored
   and edited through the .quindi-md-* modal. */
.quindi-md-embeddable {
    --quindi-md-frame-inset: 4px;
    position: relative;
    width: 100%;
    height: 100%;
}
.quindi-md-embeddable > iframe.excalidraw__embeddable {
    position: absolute;
    inset: var(--quindi-md-frame-inset);
    width: calc(100% - var(--quindi-md-frame-inset) - var(--quindi-md-frame-inset));
    height: calc(100% - var(--quindi-md-frame-inset) - var(--quindi-md-frame-inset));
    border: none;
    /* The iframe sits inset inside the rough frame, so its corner radius is the
       element radius reduced by that inset — same shape as the canvas punch-out
       and the SVG-export foreignObject clip. The full radius here would round
       the content more than the hole and expose background slivers at the
       corners. */
    border-radius: max(0px, calc(var(--embeddable-radius, 6px) - var(--quindi-md-frame-inset)));
    /* Set inline by renderMarkdownEmbeddable: custom note bg, else the theme
       body color — so mounting never flashes the opposite theme's surface. */
    background: var(--quindi-md-surface, #fff);
}
.quindi-md-deferred {
    position: absolute;
    inset: var(--quindi-md-frame-inset);
    display: grid;
    place-items: center;
    overflow: hidden;
    padding: 0 10px;
    text-align: center;
    color: var(--quindi-text-soft, #8b95a5);
    background: var(--quindi-md-surface, #fff);
    border-radius: max(0px, calc(var(--embeddable-radius, 6px) - var(--quindi-md-frame-inset)));
    font: 600 12px/1.3 var(--font-sans, system-ui, sans-serif);
}
/* Parent-driven hover: the viewport controller flags clickable note content
   (TOC entries, links, the Read pill) and the canvas cursor follows; a
   selected note's body advertises that dragging it moves the element. */
.quindi-md-cursor-pointer canvas.excalidraw__canvas { cursor: pointer !important; }
.quindi-md-cursor-move canvas.excalidraw__canvas { cursor: move !important; }
/* Soft-lock badge: shown on a markdown block while another collaborator edits it.
   Top-LEFT: the top-right corner belongs to the in-note Read pill. */
.quindi-md-editing-badge {
    position: absolute;
    top: 6px;
    left: 6px;
    max-width: calc(100% - 12px);
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.5;
    color: #07140f;
    background: var(--accent, #00D68F);
    border-radius: var(--radius-sm);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
    pointer-events: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    z-index: 1;
}

/* Hide Excalidraw's hyperlink popover for markdown blocks: their link is an
   internal sentinel (md.quindi.local), not something the user should see or edit.
   :has() scopes this to that link only, so real embeddables keep their popover. */
.excalidraw-hyperlinkContainer:has(a.excalidraw-hyperlinkContainer-link[href*="md.quindi.local"]) {
    display: none !important;
}
/* Same sentinel treatment for PDF attachments (pdf.quindi.local). */
.excalidraw-hyperlinkContainer:has(a.excalidraw-hyperlinkContainer-link[href*="pdf.quindi.local"]) {
    display: none !important;
}
/* Same sentinel treatment for document links (doc.quindi.local). */
.excalidraw-hyperlinkContainer:has(a.excalidraw-hyperlinkContainer-link[href*="doc.quindi.local"]) {
    display: none !important;
}
/* Same sentinel treatment for spreadsheets (sheet.quindi.local). */
.excalidraw-hyperlinkContainer:has(a.excalidraw-hyperlinkContainer-link[href*="sheet.quindi.local"]) {
    display: none !important;
}

/* "Markdown" button injected into the shape toolbar (.App-toolbar). Sized + styled
   like a native tool button so it blends in with the shape tools; the M↓ glyph
   carries the brand accent. Excalidraw vars let it follow the editor theme. */
.quindi-md-tool-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--lg-button-size, 2.25rem);
    height: var(--lg-button-size, 2.25rem);
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: var(--border-radius-lg, 0.75rem);
    background: transparent;
    /* Match the native tool icons: same monochrome color, themed light/dark. */
    color: var(--color-on-surface, #1b1b1f);
    cursor: pointer;
}
.quindi-md-tool-btn:hover { background: var(--button-hover-bg, rgba(0, 0, 0, 0.06)); }
.quindi-md-tool-btn:active { background: var(--button-active-bg, rgba(0, 0, 0, 0.1)); }
.quindi-lasso-tool-btn.is-active {
    background: var(--color-surface-primary-container, #e3e2fe);
    color: var(--color-on-primary-container, #1b1b1f);
}
.quindi-md-tool-btn:focus-visible {
    outline: 2px solid var(--accent, #00D68F);
    outline-offset: -2px;
}
.quindi-md-tool-btn__icon {
    display: block;
    /* Match the native toolbar tool icons: same SVG box (--lg-icon-size, 1.25rem
       on desktop / 1rem on small screens) so the M↓ glyph reads at the same size
       as the eraser / shapes icons instead of the smaller fixed 22×14 it had. */
    width: var(--lg-icon-size, 1.25rem);
    height: var(--lg-icon-size, 1.25rem);
}
/* The chain glyph reads optically larger than its siblings at the same box size —
   shrink just the doc-link button a touch. */
.quindi-doclink-btn .quindi-md-tool-btn__icon {
    width: calc(var(--lg-icon-size, 1.25rem) * 0.85);
    height: calc(var(--lg-icon-size, 1.25rem) * 0.85);
}

/* Hide the external links row in Excalidraw's Help dialog (Documentation, Blog,
   "Found an issue? Submit", YouTube) — irrelevant for this self-hosted instance.
   The dialog renders in the top document, so this app stylesheet reaches it. */
.excalidraw .HelpDialog__header { display: none !important; }

/* Long-form Markdown reader. The canvas block stays a compact, passive preview;
   this surface owns document scrolling, index navigation, search and print/PDF. */
.quindi-md-reader-backdrop {
    position: fixed;
    inset: 0;
    z-index: 100002;
    display: grid;
    place-items: center;
    padding: 18px;
    background: var(--scrim, rgba(0, 0, 0, 0.68));
}
.quindi-md-reader {
    display: flex;
    flex-direction: column;
    width: min(1240px, 96vw);
    height: min(920px, calc(100vh - 36px));
    height: min(920px, calc(100dvh - 36px));
    /* Never let the preferred desktop minimum push the modal outside a short
       landscape/split-screen viewport. The dvh declaration corrects mobile
       browser chrome while the vh value remains the compatibility fallback;
       36px reserves the backdrop's 18px padding on both edges. */
    min-height: min(320px, calc(100vh - 36px));
    min-height: min(320px, calc(100dvh - 36px));
    overflow: hidden;
    color: var(--quindi-text, #f4f6f9);
    background: var(--quindi-surface, #141414);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.12));
    border-radius: var(--radius-md, 12px);
    box-shadow: 0 22px 72px rgba(0, 0, 0, 0.58);
    font-family: var(--font-sans, system-ui, sans-serif);
}
.quindi-md-reader-backdrop--fullscreen {
    padding: 0;
    place-items: stretch;
}
.quindi-md-reader.quindi-md-reader--fullscreen {
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    min-height: 0;
    max-width: none;
    max-height: none;
    border: 0;
    border-radius: 0;
}
.quindi-md-reader__header {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 56px;
    padding: 10px 12px 10px 16px;
    border-bottom: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.1));
}
.quindi-md-reader__title {
    flex: 1 1 180px;
    min-width: 100px;
    margin: 0;
    overflow: hidden;
    font-size: 15px;
    font-weight: 650;
    line-height: 1.3;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.quindi-md-reader__search {
    width: clamp(150px, 22vw, 300px);
    height: 34px;
    padding: 0 10px;
    color: var(--quindi-text, #f4f6f9);
    background: var(--quindi-bg, #0a0a0a);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.15));
    border-radius: 7px;
    outline: none;
}
.quindi-md-reader__search:focus {
    border-color: var(--accent, #00d68f);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent, #00d68f) 22%, transparent);
}
.quindi-md-reader__count {
    min-width: 48px;
    color: var(--quindi-text-soft, #8b95a5);
    font-size: 11px;
    text-align: center;
}
.quindi-md-reader__icon,
.quindi-md-reader__close,
.quindi-md-reader__action {
    height: 34px;
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.13));
    border-radius: 7px;
    color: var(--quindi-text, #f4f6f9);
    background: var(--quindi-surface-2, #1c1c1c);
    cursor: pointer;
}
.quindi-md-reader__icon,
.quindi-md-reader__close {
    display: inline-grid;
    width: 34px;
    padding: 0;
    place-items: center;
    font-size: 17px;
}
.quindi-md-reader__close { margin-left: 2px; font-size: 24px; font-weight: 300; }
.quindi-md-reader__fullscreen { font-size: 18px; }
.quindi-md-reader__action { padding: 0 11px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.quindi-md-reader__action--primary {
    color: #07140f;
    background: var(--accent, #00d68f);
    border-color: transparent;
}
.quindi-md-reader__icon:hover,
.quindi-md-reader__close:hover,
.quindi-md-reader__action:hover { filter: brightness(1.14); }
.quindi-md-reader__icon:focus-visible,
.quindi-md-reader__close:focus-visible,
.quindi-md-reader__action:focus-visible {
    outline: 2px solid var(--accent, #00d68f);
    outline-offset: 2px;
}
.quindi-md-reader__action:disabled { opacity: 0.65; cursor: wait; }
.quindi-md-reader__frame {
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
    border: 0;
    background: #fff;
}
@media (max-width: 860px) {
    .quindi-md-reader-backdrop { padding: 0; }
    .quindi-md-reader { width: 100vw; height: 100vh; height: 100dvh; min-height: 0; border: 0; border-radius: 0; }
    .quindi-md-reader__header { flex-wrap: wrap; }
    .quindi-md-reader__title { flex-basis: calc(100% - 44px); }
    .quindi-md-reader__search { flex: 1 1 180px; width: auto; }
    .quindi-md-reader__action { padding-inline: 8px; }
}
@media (max-width: 560px) {
    .quindi-md-reader__count { min-width: 38px; }
    .quindi-md-reader__action[data-qmd-reader-action="copy"] { display: none; }
    .quindi-md-reader__action--primary { flex: 1 1 auto; }
}

/* Authoring / editing modal — floats above the whole editor. Dark Quindi chrome
   (header tabs + formatting toolbar) wrapping a WHITE WYSIWYG surface that mirrors
   exactly how the note renders on the canvas and in exports. */
.quindi-md-backdrop {
    position: fixed;
    inset: 0;
    z-index: 100000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--scrim, rgba(0, 0, 0, 0.6));
    padding: 24px;
}
.quindi-md-modal {
    display: flex;
    flex-direction: column;
    width: min(840px, 96vw);
    height: min(660px, 90vh);
    background: var(--quindi-surface, #141414);
    color: var(--quindi-text, #F4F6F9);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.08));
    border-radius: var(--radius-md, 12px);
    box-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);
    overflow: hidden;
    font-family: var(--font-sans, system-ui, sans-serif);
}
.quindi-md-modal__header {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.08));
}
.quindi-md-modal__title { font-weight: 600; font-size: 15px; margin-right: auto; }
/* Segmented Visuale / Sorgente switch. */
.quindi-md-tabs {
    display: inline-flex;
    padding: 2px;
    gap: 2px;
    background: var(--quindi-bg, #0A0A0A);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.12));
    border-radius: var(--radius-sm, 8px);
}
.quindi-md-tab {
    padding: 5px 12px;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--quindi-text-soft, #8B95A5);
    background: transparent;
    border: 0;
    border-radius: 6px;
    cursor: pointer;
}
.quindi-md-tab:hover { color: var(--quindi-text, #F4F6F9); }
.quindi-md-tab.is-active {
    color: var(--quindi-text, #F4F6F9);
    background: var(--quindi-surface-2, #1C1C1C);
    box-shadow: inset 0 0 0 1px var(--quindi-border, rgba(255, 255, 255, 0.1));
}
/* Fullscreen toggle in the header. */
.quindi-md-fs-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 7px;
    background: transparent;
    color: var(--quindi-text-soft, #8B95A5);
    cursor: pointer;
}
.quindi-md-fs-btn:hover { background: var(--quindi-surface-2, #1C1C1C); color: var(--quindi-text, #F4F6F9); }
.quindi-md-fs-btn svg { width: 18px; height: 18px; }
.quindi-md-fs-btn[aria-pressed="true"] { color: var(--accent, #00D68F); }

/* Formatting toolbar. */
.quindi-md-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.08));
}
.quindi-md-tb-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 7px;
    background: transparent;
    color: var(--quindi-text, #F4F6F9);
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
}
.quindi-md-tb-btn--text { font-weight: 700; font-size: 13px; }
.quindi-md-tb-btn--text i { font-style: italic; }
.quindi-md-tb-btn--text s { text-decoration: line-through; }
.quindi-md-tb-btn:hover { background: var(--quindi-surface-2, #1C1C1C); }
.quindi-md-tb-btn.is-active {
    background: color-mix(in srgb, var(--accent, #00D68F) 22%, transparent);
    color: var(--accent, #00D68F);
}
.quindi-md-tb-btn:focus-visible {
    outline: 2px solid var(--accent, #00D68F);
    outline-offset: -2px;
}
.quindi-md-tb-ic { display: block; width: 20px; height: 20px; }
.quindi-md-tb-divider {
    width: 1px;
    align-self: stretch;
    margin: 4px 6px;
    background: var(--quindi-border, rgba(255, 255, 255, 0.1));
}

/* Contextual table toolbar — shown only when the caret is inside a table. */
.quindi-md-tabletools {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
    padding: 7px 12px;
    background: var(--quindi-bg, #0A0A0A);
    border-bottom: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.08));
}
.quindi-md-tabletools[hidden] { display: none; }
.quindi-md-tt-btn {
    padding: 4px 9px;
    font-size: 12px;
    font-weight: 500;
    color: var(--quindi-text, #F4F6F9);
    background: var(--quindi-surface, #141414);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.12));
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
}
.quindi-md-tt-btn:hover { background: var(--quindi-surface-2, #1C1C1C); border-color: var(--accent, #00D68F); }

/* Fullscreen — fill the viewport, drop the rounded card chrome. */
.quindi-md-modal.quindi-md-modal--fullscreen {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    max-width: none;
    border-radius: 0;
}

.quindi-md-modal__body { flex: 1; min-height: 0; display: flex; }
/* WYSIWYG surface — a white "page" that renders the note exactly as it appears on
   the canvas, so editing is truly what-you-see-is-what-you-get. */
.quindi-md-editor {
    flex: 1;
    min-height: 0;
    overflow: auto;
    background: #ffffff;
}
.quindi-md-editor .ProseMirror {
    position: relative;
    min-height: 100%;
    padding: 22px 26px;
    color: #1b1b1f;
    font-size: 15px;
    line-height: 1.6;
    outline: none;
    word-wrap: break-word;
    white-space: pre-wrap;
    -webkit-font-variant-ligatures: none;
    font-variant-ligatures: none;
}
.quindi-md-editor .ProseMirror > :first-child { margin-top: 0; }
.quindi-md-editor .ProseMirror h1,
.quindi-md-editor .ProseMirror h2,
.quindi-md-editor .ProseMirror h3,
.quindi-md-editor .ProseMirror h4,
.quindi-md-editor .ProseMirror h5,
.quindi-md-editor .ProseMirror h6 { line-height: 1.25; margin: 1.2em 0 0.5em; font-weight: 600; }
.quindi-md-editor .ProseMirror h1 { font-size: 1.7em; }
.quindi-md-editor .ProseMirror h2 { font-size: 1.4em; }
.quindi-md-editor .ProseMirror h3 { font-size: 1.2em; }
/* Same scale as the on-canvas block (QuindiMarkdown.body in entry.js): without
   explicit sizes h5/h6 fall to browser defaults, smaller than the body text. */
.quindi-md-editor .ProseMirror h4 { font-size: 1.05em; }
.quindi-md-editor .ProseMirror h5 { font-size: 0.95em; }
.quindi-md-editor .ProseMirror h6 { font-size: 0.85em; }
.quindi-md-editor .ProseMirror h1,
.quindi-md-editor .ProseMirror h2 { border-bottom: 1px solid #e6e6e9; padding-bottom: 0.25em; }
.quindi-md-editor .ProseMirror p,
.quindi-md-editor .ProseMirror ul,
.quindi-md-editor .ProseMirror ol,
.quindi-md-editor .ProseMirror blockquote,
.quindi-md-editor .ProseMirror pre { margin: 0 0 0.8em; }
.quindi-md-editor .ProseMirror ul,
.quindi-md-editor .ProseMirror ol { padding-left: 1.5em; }
/* TipTap wraps each list item's text in a <p>; zero its margin so items render
   tight, matching the on-canvas (marked) list rendering. */
.quindi-md-editor .ProseMirror li > p { margin: 0; }
.quindi-md-editor .ProseMirror a { color: #1971c2; text-decoration: none; cursor: pointer; }
.quindi-md-editor .ProseMirror code {
    background: #f4f4f6;
    padding: 0.15em 0.35em;
    border-radius: 4px;
    font-size: 0.9em;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.quindi-md-editor .ProseMirror pre {
    background: #f4f4f6;
    padding: 12px 14px;
    border-radius: 8px;
    overflow: auto;
    white-space: pre-wrap;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.quindi-md-editor .ProseMirror pre code { background: none; padding: 0; }
.quindi-md-editor .ProseMirror blockquote {
    margin-left: 0;
    padding: 0.2em 1em;
    border-left: 3px solid #e6e6e9;
    color: #5c5c66;
}
.quindi-md-editor .ProseMirror hr {
    border: none;
    border-top: 1px solid #e6e6e9;
    margin: 1.2em 0;
}
.quindi-md-editor .ProseMirror img { max-width: 100%; }
/* Task lists (GFM checkboxes). */
.quindi-md-editor .ProseMirror ul[data-type="taskList"] { list-style: none; padding-left: 0.2em; }
.quindi-md-editor .ProseMirror ul[data-type="taskList"] li { display: flex; align-items: flex-start; gap: 0.5em; }
.quindi-md-editor .ProseMirror ul[data-type="taskList"] li > label { margin-top: 0.2em; user-select: none; }
.quindi-md-editor .ProseMirror ul[data-type="taskList"] li > label input { accent-color: var(--accent, #00D68F); cursor: pointer; }
.quindi-md-editor .ProseMirror ul[data-type="taskList"] li > div { flex: 1; min-width: 0; }
.quindi-md-editor .ProseMirror ul[data-type="taskList"] li > div > p { margin: 0; }
/* Tables. */
.quindi-md-editor .ProseMirror .tableWrapper { overflow-x: auto; margin: 0 0 0.8em; }
.quindi-md-editor .ProseMirror table { border-collapse: collapse; table-layout: fixed; width: 100%; }
.quindi-md-editor .ProseMirror th,
.quindi-md-editor .ProseMirror td {
    border: 1px solid #e6e6e9;
    padding: 0.4em 0.6em;
    vertical-align: top;
    box-sizing: border-box;
    position: relative;
    min-width: 3em;
}
.quindi-md-editor .ProseMirror th { background: #f7f7f9; font-weight: 600; text-align: left; }
.quindi-md-editor .ProseMirror .selectedCell:after {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(25, 113, 194, 0.12);
    pointer-events: none;
    z-index: 2;
}
/* Empty-state placeholder (TipTap Placeholder extension). */
.quindi-md-editor .ProseMirror p.is-editor-empty:first-child::before,
.quindi-md-editor .ProseMirror .is-empty:first-child::before {
    content: attr(data-placeholder);
    color: #9aa0aa;
    float: left;
    height: 0;
    pointer-events: none;
}
/* ProseMirror essentials (not pulled from prosemirror's own stylesheet), scoped. */
.quindi-md-editor .ProseMirror-selectednode { outline: 2px solid #1971c2; }
.quindi-md-editor .ProseMirror-gapcursor {
    display: none;
    pointer-events: none;
    position: absolute;
}
.quindi-md-editor .ProseMirror-gapcursor:after {
    content: "";
    display: block;
    position: absolute;
    top: -2px;
    width: 20px;
    border-top: 1px solid #1b1b1f;
}
.quindi-md-editor .ProseMirror-focused .ProseMirror-gapcursor { display: block; }

/* Dark editing surface — mirrors QuindiMarkdown.body's dark palette so the editor
   matches a markdown block on a dark Excalidraw canvas (set by interop when
   appState.theme === "dark"). */
.quindi-md-editor.quindi-md-editor--dark { background: #232329; }
.quindi-md-editor--dark .ProseMirror { color: #e3e3e8; }
.quindi-md-editor--dark .ProseMirror h1,
.quindi-md-editor--dark .ProseMirror h2 { border-bottom-color: #3a3a44; }
.quindi-md-editor--dark .ProseMirror a { color: #8ab4f8; }
.quindi-md-editor--dark .ProseMirror code,
.quindi-md-editor--dark .ProseMirror pre { background: #2c2c34; }
.quindi-md-editor--dark .ProseMirror blockquote { border-left-color: #3a3a44; color: #a0a0ab; }
.quindi-md-editor--dark .ProseMirror hr { border-top-color: #3a3a44; }
.quindi-md-editor--dark .ProseMirror th,
.quindi-md-editor--dark .ProseMirror td { border-color: #3a3a44; }
.quindi-md-editor--dark .ProseMirror th { background: #2c2c34; }
.quindi-md-editor--dark .ProseMirror p.is-editor-empty:first-child::before,
.quindi-md-editor--dark .ProseMirror .is-empty:first-child::before { color: #6b6b75; }

/* Raw markdown ("Sorgente" tab) — code editor look on the dark surface. */
.quindi-md-source {
    flex: 1;
    border: none;
    outline: none;
    resize: none;
    padding: 18px 22px;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
    font-size: 13px;
    line-height: 1.6;
    color: var(--quindi-text, #F4F6F9);
    background: var(--quindi-bg, #0A0A0A);
}
.quindi-md-source::placeholder { color: var(--quindi-text-soft, #8B95A5); }

/* Slash (/) command menu — appended to <body>, floats above the modal. */
.quindi-md-slash {
    position: fixed;
    z-index: 100001;
    min-width: 220px;
    max-height: 300px;
    overflow: auto;
    padding: 6px;
    background: var(--quindi-surface, #141414);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.12));
    border-radius: var(--radius-sm, 10px);
    box-shadow: 0 12px 36px rgba(0, 0, 0, 0.5);
    font-family: var(--font-sans, system-ui, sans-serif);
}
.quindi-md-slash__item {
    display: flex;
    flex-direction: column;
    gap: 1px;
    width: 100%;
    text-align: left;
    padding: 7px 10px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    color: var(--quindi-text, #F4F6F9);
    cursor: pointer;
}
.quindi-md-slash__item:hover,
.quindi-md-slash__item.is-active { background: var(--quindi-surface-2, #1C1C1C); }
.quindi-md-slash__item.is-active { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent, #00D68F) 50%, transparent); }
.quindi-md-slash__title { font-size: 13px; font-weight: 500; }
.quindi-md-slash__hint { font-size: 11.5px; color: var(--quindi-text-soft, #8B95A5); }
.quindi-md-slash__empty { padding: 8px 10px; font-size: 12.5px; color: var(--quindi-text-soft, #8B95A5); }

.quindi-md-modal__footer {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 18px;
    border-top: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.08));
}
.quindi-md-font {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--quindi-text-soft, #8B95A5);
}
.quindi-md-font-select {
    font-family: var(--font-sans, system-ui, sans-serif);
    font-size: 13px;
    color: var(--quindi-text, #F4F6F9);
    background: var(--quindi-bg, #0A0A0A);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.16));
    border-radius: var(--radius-sm, 8px);
    padding: 6px 8px;
    cursor: pointer;
}
.quindi-md-font-select:focus-visible {
    outline: 2px solid var(--accent, #00D68F);
    outline-offset: 1px;
}
.quindi-md-hint {
    margin-right: auto;
    font-size: 12px;
    color: var(--quindi-text-soft, #8B95A5);
}
.quindi-md-btn {
    padding: 8px 16px;
    font-size: 13px;
    font-weight: 500;
    border-radius: var(--radius-sm, 8px);
    border: 1px solid transparent;
    cursor: pointer;
}
.quindi-md-btn--ghost {
    background: transparent;
    border-color: var(--quindi-border, rgba(255, 255, 255, 0.16));
    color: var(--quindi-text, #F4F6F9);
}
.quindi-md-btn--ghost:hover { background: var(--quindi-surface-2, #1C1C1C); }
.quindi-md-btn--primary {
    background: var(--accent, #00D68F);
    color: #07140F;
}
.quindi-md-btn--primary:hover { background: var(--accent-hover, #00B879); }
.quindi-md-btn--danger {
    background: var(--quindi-red, #E5484D);
    color: #fff;
}
.quindi-md-btn--danger:hover { background: #C93A3F; }

/* Unsaved-changes bar (requestClose in excalidraw-interop.js): sits over the
   footer inside the modal so the question is impossible to miss but nothing
   else is disturbed. */
.quindi-md-confirm {
    position: absolute;
    inset: auto 0 0 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    padding: 12px 18px;
    background: var(--quindi-surface-2, #1C1C1C);
    border-top: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.08));
}
.quindi-md-confirm > span {
    margin-right: auto;
    font-size: 13px;
    font-weight: 600;
}

/* Focus visibility parity: every chrome control shows the accent ring the
   toolbar buttons already had. */
.quindi-md-tab:focus-visible,
.quindi-md-fs-btn:focus-visible,
.quindi-md-btn:focus-visible,
.quindi-md-tt-btn:focus-visible {
    outline: 2px solid var(--accent, #00D68F);
    outline-offset: 1px;
}

/* The editing surfaces scroll with the same thin scrollbar as the live note. */
.quindi-md-editor,
.quindi-md-source {
    scrollbar-width: thin;
    scrollbar-color: rgba(136, 136, 136, 0.5) transparent;
}

/* Phones: the footer packs many controls — wrap them, go full-bleed, and drop
   the keyboard-shortcut hint (no hardware keyboard to use it with). */
.quindi-md-modal { position: relative; }
.quindi-md-modal__footer { flex-wrap: wrap; row-gap: 8px; }
@media (max-width: 599px) {
    .quindi-md-backdrop { padding: 0; }
    .quindi-md-modal {
        width: 100vw;
        height: 100dvh;
        max-height: 100dvh;
        border: 0;
        border-radius: 0;
    }
    .quindi-md-hint { display: none; }
}
@media (pointer: coarse) {
    .quindi-md-tb-btn,
    .quindi-md-tt-btn,
    .quindi-md-fs-btn,
    .quindi-md-tab {
        min-width: 40px;
        min-height: 40px;
    }
}

/* ── PDF attachments ─────────────────────────────────────────────────────────
   A PDF attachment is an embeddable rendered by excalidraw-interop.js
   (renderPdfEmbeddable): the self-hosted /pdf-viewer iframe, uploaded via the
   toolbar popover or canvas drag&drop, opened fullscreen in a quindi-md modal. */
.quindi-pdf-embeddable {
    position: relative;
    width: 100%;
    height: 100%;
}
.quindi-pdf-embeddable > iframe.excalidraw__embeddable {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: none;
    border-radius: var(--embeddable-radius, 6px);
    /* Set inline by renderPdfEmbeddable to match the viewer theme while it loads. */
    background: var(--quindi-pdf-embed-bg, #2b2b30);
}

/* Spreadsheet editor modal: a scrim + one big framed /sheet-editor page. Same z-plane as the
   markdown editor and speaker-note modals (100000): a lost connection overlay must still win. */
.quindi-sheet-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 100000;
    display: grid;
    place-items: center;
    background: rgba(0, 0, 0, 0.55);
    padding: 24px;
}
.quindi-sheet-modal-frame {
    /* Wide on purpose. The editor's toolbar is one flat row (no tabs), and Univer decides in JS
       how much of it fits: anything past the container's width is moved into a "more" menu.
       Measured against the pinned bundle, every tool fits from ~1576px of toolbar width, so the
       old 1240px cap was what pushed items out of sight — not the number of tools. The grid
       gets the extra room too; 96vw still keeps the scrim visible on smaller screens. */
    width: min(1600px, 96vw);
    height: min(880px, 92vh);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.1));
    border-radius: var(--radius-lg);
    background: var(--quindi-surface, #141414);
    box-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);
}
@media (max-width: 599px) {
    /* Phones: the editor takes the whole screen — a windowed grid is unusable there. */
    .quindi-sheet-modal-backdrop { padding: 0; }
    .quindi-sheet-modal-frame { width: 100vw; height: 100dvh; border-radius: 0; border: none; }
}

/* Spreadsheet preview: an inert srcDoc table (see sheet-embed.js). pointer-events: none is the
   contract — clicks select/move the ELEMENT like a doc-link card, and double-click reaches the
   host to open the editor; all in-sheet interaction lives on the /sheet-editor page. */
.quindi-sheet-embeddable {
    position: relative;
    width: 100%;
    height: 100%;
}
.quindi-sheet-embeddable > iframe.excalidraw__embeddable {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: none;
    border-radius: var(--embeddable-radius, 6px);
    background: transparent;
    pointer-events: none;
}

/* Anchored popover from the toolbar button (fixed like the markdown color pops;
   above the canvas, below the modal scrim's z-index: 100000). Quindi tokens: the
   old --popup-background-color reference never resolved outside .excalidraw and
   left a white outlier over the dark chrome. */
.quindi-pdf-popover {
    position: fixed;
    z-index: 99999;
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: 240px;
    padding: 12px;
    border-radius: var(--radius-md);
    background: var(--quindi-surface, #141414);
    color: var(--quindi-text, #F4F6F9);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.08));
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
    font-family: var(--font-sans, system-ui, sans-serif);
}
.quindi-pdf-popover__title { font-weight: 600; }
.quindi-pdf-popover__hint { font-size: 12px; color: var(--quindi-text-soft, #8B95A5); }
.quindi-pdf-popover .quindi-md-btn { align-self: stretch; }

/* Filename pill over the embed (renderPdfEmbeddable) — display only. */
.quindi-pdf-embeddable__name {
    position: absolute;
    top: 6px;
    left: 6px;
    max-width: calc(100% - 12px);
    padding: 3px 9px;
    border-radius: var(--radius-sm);
    font: 500 11px/1.4 var(--font-sans, system-ui, sans-serif);
    background: rgba(20, 20, 20, 0.72);
    color: #F4F6F9;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    pointer-events: none;
    z-index: 1;
}

/* Non-modal feedback toasts (quindiToast in excalidraw-interop.js). */
.quindi-toasts {
    position: fixed;
    top: 64px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 100001;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    pointer-events: none;
}
.quindi-toast {
    padding: 10px 14px;
    border-radius: var(--radius-md);
    background: var(--quindi-surface, #141414);
    color: var(--quindi-text, #F4F6F9);
    border: 1px solid var(--quindi-border-soft, rgba(255, 255, 255, 0.04));
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.4);
    font: 500 13px/1.4 var(--font-sans, system-ui, sans-serif);
    max-width: min(80vw, 480px);
    /* Toasts wrap. The recovery messages are the ones that matter most here — "this sheet was
       deleted while you were editing… copy anything you need before closing" — and it is the
       SECOND sentence that prevents the data loss. nowrap + ellipsis cut it off, so the user
       saw the alarm and not the instruction. Clamped so a long name still cannot grow one
       without bound. */
    overflow: hidden;
    overflow-wrap: anywhere;
    white-space: pre-line;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    -webkit-box-orient: vertical;
}
@supports (backdrop-filter: blur(1px)) {
    .quindi-toast {
        background: var(--glass-tint, rgba(20, 20, 20, 0.72));
        backdrop-filter: blur(var(--glass-blur, 18px)) saturate(var(--glass-saturate, 140%));
        -webkit-backdrop-filter: blur(var(--glass-blur, 18px)) saturate(var(--glass-saturate, 140%));
    }
}
.quindi-toast--error { border-color: rgba(229, 72, 77, 0.55); }

/* Fullscreen viewer modal (double-click): header chrome + the full-mode iframe. */
.quindi-pdf-viewer-modal { padding: 0; }
.quindi-pdf-viewer-modal .quindi-md-modal__title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.quindi-pdf-viewer-frame {
    flex: 1;
    min-height: 0;
    width: 100%;
    border: none;
    display: block;
}

/* ── Document links ──────────────────────────────────────────────────────────
   A doc-link is an embeddable rendered by excalidraw-interop.js
   (renderDocLinkEmbeddable): a React-rendered anchor linking /d/{id}, inserted
   via the toolbar picker modal below. Keeping this as normal DOM (not an iframe)
   preserves the editable surface without painting another frame over Excalidraw. */
.excalidraw__embeddable__outer:has(.quindi-doclink-embeddable) {
    /* Generic embeddables pad their content by strokeWidth (inline style in
       App.tsx). A doc-link card IS the full element surface — frame and fill are
       painted by its SVG — so the card must span the whole element box. The
       !important beats the non-important inline padding. */
    padding: 0 !important;
}
.quindi-doclink-embeddable {
    --quindi-doclink-radius: 0px;
    --quindi-doclink-ink: #1e1e1e;
    --quindi-doclink-accent: #6965db;
    --quindi-doclink-surface: #fff;
    --quindi-doclink-shot-bg: rgba(0, 0, 0, .05);
    --quindi-doclink-shot-border: rgba(0, 0, 0, .10);
    position: relative;
    width: 100%;
    height: 100%;
    container: quindi-doclink / size;
    /* Clip the content at the same corner radius the SVG frame draws, so the
       rounded border and the card content always agree (native-element look). */
    overflow: hidden;
    border-radius: var(--quindi-doclink-radius) !important;
    background: transparent;
}
.quindi-doclink-surface {
    position: absolute;
    z-index: 0;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    pointer-events: none;
}
.quindi-doclink-card {
    position: relative;
    z-index: 1;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    padding: 6px;
    color: var(--quindi-doclink-ink);
    text-decoration: none;
    border-radius: inherit;
}
.quindi-doclink-card:hover .quindi-doclink-card__inner { opacity: .82; }
.quindi-doclink-card__inner {
    display: flex;
    align-items: center;
    gap: 12px;
    max-width: 100%;
    min-width: 0;
}
.quindi-doclink-card__shot {
    position: relative;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 72px;
    max-height: 100%;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    font-size: 26px;
    background: var(--quindi-doclink-shot-bg);
    border: 1px solid var(--quindi-doclink-shot-border);
    border-radius: 8px;
}
.quindi-doclink-card__shot img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.quindi-doclink-card__text {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.quindi-doclink-card__title {
    overflow: hidden;
    color: var(--quindi-doclink-ink);
    font-size: 15px;
    font-weight: 650;
    line-height: 1.25;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.quindi-doclink-card__subtitle {
    display: flex;
    align-items: center;
    gap: 5px;
    color: var(--quindi-doclink-ink);
    font-size: 12px;
    opacity: .62;
}
.quindi-doclink-card__go {
    color: var(--quindi-doclink-accent);
    font-weight: 600;
    opacity: 1;
}
@container quindi-doclink (min-height: 130px) {
    .quindi-doclink-card__shot { height: 104px; }
    .quindi-doclink-card__title { font-size: 17px; }
    .quindi-doclink-card__subtitle { font-size: 13px; }
}
@container quindi-doclink (min-height: 200px) {
    .quindi-doclink-card__inner { flex-direction: column; gap: 14px; text-align: center; }
    .quindi-doclink-card__shot { height: 120px; }
    .quindi-doclink-card__text { align-items: center; }
    .quindi-doclink-card__subtitle { justify-content: center; }
}
@container quindi-doclink (max-height: 72px) {
    .quindi-doclink-card__shot { height: 40px; font-size: 18px; border-radius: 6px; }
    .quindi-doclink-card__title { font-size: 13px; }
    .quindi-doclink-card__subtitle { display: none; }
}
@container quindi-doclink (max-height: 44px) {
    .quindi-doclink-card__shot { height: 26px; font-size: 13px; border-width: 0; }
    .quindi-doclink-card__inner { gap: 8px; }
    .quindi-doclink-card__title { font-size: 12px; }
}

/* Picker modal: search + scrollable result rows. Height hugs the content — the
   base .quindi-md-modal pins a fixed 660px that left a huge empty area when few
   documents match; the list scrolls only past ~7 rows. */
.quindi-md-modal.quindi-doclink-picker {
    width: min(560px, 96vw);
    height: auto;
    max-height: min(640px, 86vh);
}
.quindi-doclink-picker__body {
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    min-height: 0;
}
.quindi-doclink-picker__search {
    flex: 0 0 auto;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.16));
    background: var(--quindi-surface-2, #1C1C1C);
    color: var(--quindi-text, #F4F6F9);
    font: inherit;
    outline: none;
}
.quindi-doclink-picker__search:focus { border-color: var(--accent, #00D68F); }
.quindi-doclink-picker__results {
    flex: 0 1 auto;
    min-height: 0;
    max-height: min(400px, 46vh);
    overflow-y: auto;
    overflow-x: hidden; /* keep clipped row edges from leaking horizontal artifacts */
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.quindi-doclink-picker__state {
    padding: 24px 8px;
    text-align: center;
    color: var(--quindi-text-dim, #9a9aa4);
    font-size: 13px;
}
.quindi-doclink-row {
    /* flex: none — the results column is a flex container with max-height: without
       this, rows SHRINK below their content instead of overflowing into a scrollbar,
       which clipped the 44px thumbnails to half height. */
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: transparent;
    color: var(--quindi-text, #F4F6F9);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.quindi-doclink-row:hover,
.quindi-doclink-row:focus-visible {
    background: var(--quindi-surface-2, #1C1C1C);
    border-color: var(--quindi-border, rgba(255, 255, 255, 0.12));
    outline: none;
}
.quindi-doclink-row__shot {
    flex: 0 0 44px;
    width: 44px;
    height: 44px;
    border-radius: 6px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--quindi-surface-2, #1C1C1C);
    border: 1px solid var(--quindi-border, rgba(255, 255, 255, 0.08));
}
.quindi-doclink-row__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.quindi-doclink-row__glyph { font-size: 20px; }
.quindi-doclink-row__txt { min-width: 0; display: flex; flex-direction: column; }
.quindi-doclink-row__t {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.quindi-doclink-row__s { font-size: 12px; color: var(--quindi-text-dim, #9a9aa4); }

/* ── Dashboard hierarchy refinement ─────────────────────────────────────── */
.dashboard > .page-topbar {
    min-height: 56px;
    padding: 6px 12px;
}
.dashboard > .page-topbar .brand { flex: 0 1 auto; min-width: 0; }
.dashboard > .page-topbar .brand h1 {
    font-size: 20px;
    white-space: nowrap;
}
.dashboard-actions {
    flex-wrap: nowrap;
    gap: 8px;
}
.dashboard-actions .action-sep,
.dashboard-secondary-actions { display: none; }
.dashboard-actions .glass-button,
.dashboard-actions .thumb-toggle {
    flex-direction: row;
    gap: 6px;
    min-width: 0;
    min-height: 36px;
    padding: 0 11px;
}
.dashboard-actions .glass-button > span,
.dashboard-actions .thumb-toggle > span { font-size: 12px; }
.dashboard-new { white-space: nowrap; }
.dashboard-view-controls {
    padding: 3px;
    gap: 2px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.025);
}
.dashboard-menu { position: relative; flex: 0 0 auto; }
.dashboard-menu > summary { list-style: none; cursor: pointer; }
.dashboard-menu > summary::-webkit-details-marker { display: none; }
.dashboard-menu[open] > summary {
    color: var(--accent);
    border-color: rgba(0, 214, 143, 0.38);
    background: var(--accent-soft);
}
.dashboard-menu-popover {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    z-index: 90;
    width: 228px;
    padding: 8px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    border: 1px solid var(--quindi-border);
    border-radius: var(--radius-md);
    background: rgba(20, 20, 20, 0.97);
    box-shadow: var(--elev-3);
    backdrop-filter: blur(18px);
}
.dashboard-menu-item {
    width: 100%;
    min-height: 40px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    padding: 0 12px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--quindi-text);
    font: inherit;
    font-size: 13px;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
.dashboard-menu-item:hover,
.dashboard-menu-item:focus-visible {
    background: var(--quindi-surface-2);
    color: var(--accent);
    outline: 2px solid transparent;
}
.dashboard-menu-item.is-danger { color: #F08080; }
.dashboard-signout-form { margin: 0; }
.dashboard-more-view {
    display: none;
    padding: 4px 4px 8px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--quindi-border);
}
.dashboard-menu-label {
    display: block;
    margin: 0 4px 6px;
    color: var(--quindi-text-soft);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
}
.dashboard-menu-export,
.dashboard-menu-export .export-menu-anchor { width: 100%; }
.dashboard-menu-export .glass-button {
    width: 100%;
    min-height: 40px;
    justify-content: flex-start;
    background: transparent;
    border-color: transparent;
}
.dashboard-menu-export .glass-button > span { font-size: 13px; }
.user-chip {
    width: 38px;
    height: 38px;
    padding: 0;
    margin-left: 0;
    justify-content: center;
}
.user-chip-form { display: none; }
.dashboard-logout { display: none; }

/* Wide dashboard: keep the original icon-over-label command strip. The compact New + More
   hierarchy remains active below this breakpoint, where the full icon set would wrap. */
@media (min-width: 1180px) {
    .dashboard-new,
    .dashboard-more { display: none; }

    .dashboard-actions .action-sep { display: block; }
    .dashboard-secondary-actions { display: inline-flex; }

    .dashboard-actions {
        gap: 6px;
    }

    .dashboard-view-controls {
        padding: 0;
        gap: 8px;
        border: 0;
        border-radius: 0;
        background: transparent;
    }

    .dashboard-actions .glass-button,
    .dashboard-actions .thumb-toggle {
        flex-direction: column;
        gap: 3px;
        min-width: 48px;
        min-height: 48px;
        padding: 5px 6px 4px;
        line-height: 1.1;
        text-align: center;
    }

    .dashboard-actions .glass-button > span,
    .dashboard-actions .thumb-toggle > span {
        font-size: 10px;
        font-weight: 600;
        letter-spacing: .01em;
    }

    .user-chip {
        width: auto;
        height: auto;
        padding: 6px 12px;
        margin-left: 16px;
        justify-content: flex-start;
    }
    .dashboard-logout { display: inline-flex; }
    .user-chip-form { display: inline-flex; }
}

.dashboard-footer {
    max-width: 1720px;
    margin: 8px auto 0;
    padding: 20px clamp(24px, 3.5vw, 52px) 28px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    color: var(--quindi-text-soft);
    font-size: 11px;
}
.dashboard-footer .powered-by,
.dashboard-footer .build-stamp {
    position: static;
    transform: none;
    opacity: .64;
    max-width: none;
    pointer-events: auto;
}

/* WCAG 2.5.8: pointer targets never fall below 24 CSS px. */
.gallery-action { flex-basis: 28px; min-width: 28px; min-height: 28px; }
.gallery-preview-btn { width: 32px; height: 32px; }
.gallery-action:focus-visible,
.gallery-preview-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 920px) {
    .dashboard { padding: 14px 12px 20px; }
    .dashboard > .page-topbar {
        position: sticky;
        top: 8px;
        flex-direction: row;
        align-items: center;
        gap: 8px;
        margin-bottom: 16px;
    }
    .dashboard > .page-topbar .brand h1 { font-size: 16px; }
    .dashboard-actions { flex: 1 1 auto; justify-content: flex-end; min-width: 0; }
    .dashboard-view-controls { display: none; }
    .dashboard-more-view { display: block; }
    .dashboard-new span:last-child { display: inline; }
    .dashboard-status { gap: 2px; }
    .dashboard-menu-popover {
        position: fixed;
        top: 76px;
        right: 12px;
        width: min(280px, calc(100vw - 24px));
        max-height: calc(100vh - 92px);
        overflow-y: auto;
    }
    .dashboard-footer {
        padding: 16px 16px 24px;
        flex-direction: column;
        align-items: flex-start;
    }
    .dashboard-footer .powered-by { white-space: normal; }
    .dashboard-footer .build-stamp { display: block; }
}

@media (max-width: 540px) {
    .dashboard > .page-topbar .brand h1 { font-size: 14px; }
    .dashboard > .page-topbar .brand { flex: 0 0 auto; }
    .dashboard > .page-topbar .brand-mark { flex: 0 0 auto; }
    .dashboard-new { width: 44px; min-width: 44px !important; padding: 0 !important; justify-content: center; }
    .dashboard-new span:last-child { display: none; }
    .dashboard-actions { gap: 5px; }
    .dashboard-actions .glass-button,
    .dashboard-actions .thumb-toggle { min-height: 44px; }
    .notification-bell,
    .system-health-alert { width: 44px; min-width: 44px !important; padding: 0 !important; justify-content: center; }
    .notification-bell > span:last-child,
    .system-health-alert > span:last-child { display: none; }
    .dashboard-more > summary span:first-child { font-size: 13px; }
    .dashboard-more > summary span:last-child { display: none; }
    .user-chip { width: 44px; height: 44px; }
    .gallery-actions { gap: 4px; }
    .gallery-action { flex-basis: 32px; min-width: 32px; min-height: 32px; }
}
