/* GX-10: HTTPS endpoints banner, list of clickable URLs the user
   can try from the client device to unlock WebGPU + multi-thread
   WASM. Styled like the per-rig FOV picker pills so it doesn't look
   like a primary action (it's a one-time-per-device step). */
.https-info-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 8px 0 4px;
}
.https-link {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border: 1px solid var(--border, #2a3142);
    border-radius: 999px;
    background: rgba(34, 197, 94, 0.08);
    color: var(--accent-success, #4ade80);
    font-family: var(--font-mono);
    font-size: 12px;
    text-decoration: none;
    white-space: nowrap;
    transition: background 120ms ease, border-color 120ms ease;
}
.https-link:hover {
    background: rgba(34, 197, 94, 0.16);
    border-color: var(--accent-success, #4ade80);
}
/* GX-11b: collapsed cert-info <details> shown when the user is
   already on a secure origin. Minimal, just enough to confirm
   the secure-context check passed without taking real estate. */
.https-info-collapsed {
    padding: 8px 12px;
    background: rgba(34, 197, 94, 0.06);
    border: 1px solid rgba(34, 197, 94, 0.18);
    border-radius: 6px;
}
.https-info-collapsed summary {
    cursor: pointer;
    font-size: 12px;
    color: var(--accent-success, #4ade80);
}
.https-info-collapsed summary::-webkit-details-marker { display: none; }
.https-info-collapsed[open] {
    background: rgba(34, 197, 94, 0.04);
}
/* SIM-9: same compact details treatment as .https-info-collapsed
   but in neutral muted instead of green (this isn't a "good news"
   thing, it's an opt-in feature whose absence is the default). */
.sim-collapsed.settings-section {
    padding: 8px 12px;
    background: rgba(255, 255, 255, 0.025);
    border: 1px solid var(--border, #2a3142);
    border-radius: 6px;
    box-shadow: none;
}
.sim-collapsed summary {
    cursor: pointer;
    font-size: 13px;
    list-style: none;
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px;
}
.sim-collapsed summary::-webkit-details-marker { display: none; }
.sim-collapsed[open] {
    background: rgba(255, 255, 255, 0.04);
}

/* Connection lost banner */
.connection-banner {
    position: fixed;
    top: var(--statusbar-height);
    left: 0;
    right: 0;
    z-index: 1000;
    background: var(--danger);
    color: #fff;
    text-align: center;
    padding: 6px 12px;
    font-size: 13px;
    font-weight: 500;
    animation: pulse-banner 2s ease-in-out infinite;
}

/* NETUX-5: the second line carries the part the operator cannot deduce, that
   the host is still running the session. Smaller, and it does not pulse: the
   alarm is the first line's job, this one is the explanation. */
.connection-banner-why {
    display: block;
    font-weight: 400;
    font-size: 12px;
    opacity: 0.92;
    margin-top: 2px;
}

/* NETUX-2/5: a degraded link is amber, not red. Red is for "gone", and using
   it for "slow" is how a network wobble gets read as equipment failure. */
.connection-banner.is-slow {
    background: var(--warn, #d98c00);
    animation: none;
}

@keyframes pulse-banner {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.7; }
}

/* ---- NETUX-2: stale telemetry ----------------------------------------
   When the status stream stops, every live number on screen freezes while
   still looking authoritative: the mount coordinates stop moving, the cooler
   sits at one temperature, the guiding trace flatlines. That reads as broken
   equipment. Fading the surfaces that carry host-owned numbers says "this
   view is old" without hiding anything the operator may still want to read.
   Deliberately not applied to controls: buttons stay at full contrast,
   because the operator can still press them and the host will still obey. */
body.link-slow .activity-bar,
body.link-slow .status-bar-tray-items .status-indicator,
body.link-offline .activity-bar,
body.link-offline .status-bar-tray-items .status-indicator {
    transition: opacity 0.4s ease;
}
body.link-slow .activity-bar,
body.link-slow .status-bar-tray-items .status-indicator { opacity: 0.72; }
body.link-offline .activity-bar,
body.link-offline .status-bar-tray-items .status-indicator { opacity: 0.5; }

/* The link chip is the one thing in the activity bar that must NOT fade:
   it is the explanation for everything else fading. */
body.link-slow .activity-bar .activity-link,
body.link-offline .activity-bar .activity-link { opacity: 1; }

/* The preview is the strongest "everything is fine" signal on the page, so
   it gets an explicit label rather than only a fade. */
body.link-offline .preview-pane { position: relative; }
body.link-offline .preview-pane::after {
    content: attr(data-stale-label);
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 5;
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 500;
    color: #fff;
    background: rgba(0, 0, 0, 0.62);
    border: 1px solid var(--warn, #d98c00);
    pointer-events: none;
}

/* ---- NETUX-4: link chip in the activity bar -------------------------- */
.activity-link {
    display: flex;
    align-items: center;
    gap: 3px;
    padding: 0 4px;
    font-size: 11px;
    line-height: 1;
    white-space: nowrap;
}
.activity-link-icon { opacity: 0.75; }
.activity-link.link-ok   { color: var(--text-muted); }
.activity-link.link-warn { color: var(--warn, #d98c00); font-weight: 600; }
.activity-link.link-bad  { color: var(--danger); font-weight: 600; }

/* Toast notifications */
.toast-container {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1100;
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-width: 340px;
}

@media (max-width: 768px) {
    .toast-container {
        bottom: 70px;
        right: 10px;
        left: 10px;
        max-width: none;
    }
}

.toast {
    padding: 10px 16px;
    border-radius: var(--radius);
    font-size: 13px;
    cursor: pointer;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
    transition: opacity 0.3s, transform 0.3s;
    /* Toasts sit over the live image preview most of the time and the
       previous 0.15 alpha made them hard to read against bright stars
       or galaxy cores. Bump the colour tints up to 0.92 and layer them
       over the app-tertiary background so the tint stays subtle but
       the text is fully opaque against a solid backdrop. */
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.toast-info {
    background: var(--bg-tertiary);
    border-left: 3px solid var(--accent);
    color: var(--text-primary);
}

.toast-ok {
    background:
        linear-gradient(rgba(var(--success-rgb), 0.30), rgba(var(--success-rgb), 0.30)),
        var(--bg-tertiary);
    border-left: 3px solid var(--success);
    color: var(--success);
}

.toast-warn {
    background:
        linear-gradient(rgba(var(--warning-rgb), 0.30), rgba(var(--warning-rgb), 0.30)),
        var(--bg-tertiary);
    border-left: 3px solid var(--warning);
    color: var(--warning);
}

.toast-error {
    background:
        linear-gradient(rgba(244, 67, 54, 0.30), rgba(244, 67, 54, 0.30)),
        var(--bg-tertiary);
    border-left: 3px solid var(--danger);
    color: var(--danger);
}

/* Sequence Styles */
.seq-progress-section {
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
}

.seq-progress-bar {
    height: 6px;
    background: var(--bg-primary);
    border-radius: 3px;
    overflow: hidden;
    margin-bottom: 8px;
}

.seq-progress-fill {
    height: 100%;
    background: var(--accent);
    border-radius: 3px;
    transition: width 0.5s ease;
}

.seq-progress-info {
    display: flex;
    gap: 16px;
    font-size: 12px;
    color: var(--text-secondary);
    flex-wrap: wrap;
}

.seq-error {
    margin-top: 6px;
    font-size: 12px;
    color: var(--danger);
    display: flex;
    align-items: flex-start;
    gap: 6px;
}

.seq-error-text {
    flex: 1;
    max-height: 4.5em;
    overflow-y: auto;
    overflow-wrap: anywhere;
    white-space: pre-wrap;
}

.seq-error-dismiss {
    flex: 0 0 auto;
    background: none;
    border: none;
    color: var(--danger);
    font-size: 16px;
    line-height: 1;
    padding: 0 2px;
    cursor: pointer;
    opacity: 0.8;
}

.seq-error-dismiss:hover {
    opacity: 1;
}

.sequence-empty {
    /* Override the earlier flex:1 + justify-content:center rule so
       the empty state collapses to its natural height instead of
       stretching to fill the autorun list column. The chip bar
       sits above it; cramping vertical space here gives the user
       a tight "no sequence yet — click + Add" line instead of a
       450 px void. */
    flex: 0 0 auto;
    display: block;
    padding: 18px 16px;
    text-align: center;
    color: var(--text-muted);
    font-size: 13px;
}
.sequence-empty p { margin: 0; }

/* Compact options bar (Meridian Flip / Dithering / End Events).
   Replaces three stacked accordions that used to consume ~120 px
   of fixed vertical space even when collapsed. Each chip shows
   status + one-line summary; clicking expands the matching
   inline settings panel below (single-open accordion). */
.autorun-options-bar {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    padding: 8px 12px 0;
}
.autorun-option-chip {
    /* Defeat the OS-default <button> look (gray gradient on Win,
       rounded blue on macOS, etc.) so the chips render the same
       across hosts. Without these resets the chips painted as
       white Windows-default buttons in early testing. */
    appearance: none;
    -webkit-appearance: none;
    font-family: inherit;
    line-height: 1.2;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-secondary);
    font-size: 11px;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}
.autorun-option-chip:hover {
    border-color: var(--text-secondary);
    color: var(--text-primary);
}
.autorun-option-chip.active {
    border-color: var(--accent);
    color: var(--text-primary);
    background: rgba(var(--accent-rgb), 0.12);
}
.autorun-option-chip strong { font-weight: 600; }
.autorun-option-chip .chip-state {
    font-size: 10px;
    text-transform: uppercase;
    color: var(--text-muted);
    letter-spacing: 0.3px;
}
.autorun-option-chip.chip-on .chip-state { color: #86efac; }
.autorun-option-chip .chip-detail { color: var(--text-muted); }
.autorun-option-chip .chip-dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--text-muted);
    flex-shrink: 0;
}
.autorun-option-chip .chip-dot-on { background: var(--success); }

/* Header strip inside an expanded option-panel body — same row
   that used to live in the chip's pre-refactor accordion header
   (enable toggle + status counters). */
.autorun-option-body-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
}

/* Dither settings panel */
.seq-dither-panel {
    /* Full width below the tab header: the .livestack-trig-tabs strip
       above has no horizontal margin, so match it (was 16px sides,
       which left the panel narrower than its own tab header). */
    margin: 8px 0 0;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}

/* Frame-type pill selector inside each Autorun item. Mirrors ASIAIR's
   Light/Bias/Flat/Dark group: ghost outlines until selected, then the
   active pill paints in the colour the convention assigns to that
   frame class (green light, blue bias, amber flat, red dark). */
.seq-type-pills {
    display: inline-flex;
    gap: 4px;
    flex-wrap: wrap;
    margin: 4px 0;
}
.seq-type-pill {
    padding: 3px 10px;
    font-size: 11px;
    font-weight: 600;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}
.seq-type-pill:hover:not(:disabled) {
    border-color: var(--text-secondary);
    color: var(--text-primary);
}
.seq-type-pill:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}
.seq-type-pill.seq-type-light {
    background: rgba(var(--success-rgb), 0.18);
    border-color: var(--success);
    color: #a5e1a8;
}
.seq-type-pill.seq-type-bias {
    background: rgba(var(--accent-rgb), 0.18);
    border-color: var(--accent);
    color: var(--accent-text);
}
.seq-type-pill.seq-type-flat {
    background: rgba(255, 167, 38, 0.18);
    border-color: #ffa726;
    color: #ffcc80;
}
.seq-type-pill.seq-type-dark {
    background: rgba(229, 57, 53, 0.18);
    border-color: #e53935;
    color: #ef9a9a;
}

/* Estimate strip at the foot of the Autorun panel, same role as the
   bottom strip on ASIAIR's shooting schedule. */
.seq-estimate {
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
    padding: 10px 16px;
    margin-top: 8px;
    border-top: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-secondary);
}
.seq-estimate strong {
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
    margin-left: 4px;
}

.seq-dither-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    cursor: pointer;
    user-select: none;
    background: rgba(255, 255, 255, 0.02);
    font-size: 13px;
}

.seq-dither-header:hover {
    background: rgba(255, 255, 255, 0.05);
}

.seq-dither-toggle {
    display: flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    font-size: 11px;
    margin-left: 8px;
}

.seq-dither-body {
    padding: 10px 12px 12px;
    border-top: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.seq-dither-row {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    align-items: flex-end;
}

.seq-dither-row label {
    display: flex;
    flex-direction: column;
    font-size: 11px;
    color: var(--text-secondary);
    gap: 3px;
}

.seq-dither-checkbox {
    flex-direction: row !important;
    align-items: center !important;
    padding-bottom: 6px;
    gap: 6px !important;
}

.seq-dither-hint {
    font-size: 11px;
    margin: 0;
}

/* Sequence items laid out as horizontal cards. CSS Grid auto-fit
   reflows them to 1, 2, 3, 4+ columns depending on viewport width
   instead of the old vertical stack that ate the same single
   ~600 px column on every screen size. The minmax 280 px floor
   keeps each card wide enough to land the name input + frame-type
   pills + Exp / count / gain inputs without horizontal scrolling. */
.sequence-list {
    padding: 8px;
    /* Single-column, top-down stack (the AUTORUN list column is now a
       narrow left rail beside the centre preview, so items read as a
       vertical schedule rather than a reflowing card grid). */
    display: flex;
    flex-direction: column;
    gap: 8px;
    overflow-y: auto;
    align-items: stretch;
}

.sequence-item {
    /* Relative so the celestial-thumbnail <img> (added for Sky-
       picked targets) can position absolutely in the bottom-right
       corner of the card without disturbing the existing flex
       column layout. padding-right reserved for the thumb so its
       overlap with the params row stays comfortable. */
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-primary);
    transition: border-color var(--transition), background var(--transition);
}
.sequence-item:has(.seq-card-thumb:not([style*="display: none"])) {
    /* When a thumb is present, reserve space on the right so the
       BIN/filter row doesn't run into the picture. :has() is
       widely supported in evergreen browsers; falls back to
       overlap on older ones, still readable. */
    padding-right: 80px;
}
.seq-card-thumb {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 64px;
    height: 64px;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--bg-secondary);
    pointer-events: none;     /* clicks fall through to the card */
}

.sequence-item.seq-active {
    border-color: var(--accent);
    background: rgba(var(--accent-rgb), 0.08);
}

.sequence-item.seq-done {
    opacity: 0.5;
}

/* Disabled item: kept in the list but visually dimmed + dashed so the
   operator can tell at a glance it will be skipped. */
.sequence-item.seq-disabled {
    opacity: 0.55;
    border-style: dashed;
    background: var(--bg-secondary);
}
.seq-enable-check {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    cursor: pointer;
    accent-color: var(--accent);
}

/* Top row inside every card: number badge + name input + remove
   button. The badge anchors the order; the name input is the only
   element that flexes, so it eats remaining width cleanly. */
.seq-card-head {
    display: flex;
    align-items: center;
    gap: 8px;
}
.seq-card-head .seq-input-name { flex: 1; min-width: 0; }
.seq-card-head .btn-icon-only  { flex: 0 0 28px; }

.seq-num {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-on-accent, #fff);
    background: var(--accent);
    min-width: 22px;
    height: 22px;
    border-radius: 11px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.sequence-item.seq-active .seq-num { background: var(--success); }
.sequence-item.seq-done   .seq-num { background: var(--text-muted); }

.seq-item-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.seq-input {
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 3px;
    color: var(--text-primary);
    /* Sequence rows are dense lists, so we keep them a touch shorter
       than .input-sm (~34 px vs 38 px) but still comfortable to tap.
       The seq-input-num below bumps width to fit "1000" + spinners. */
    font-size: 13px;
    padding: 7px 8px;
    min-height: 34px;
    box-sizing: border-box;
}

.seq-input:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.seq-input-name {
    width: 100%;
    font-weight: 600;
    font-size: 13px;
}
/* The LIGHT target name is auto-derived from the FOV, shown read-only in the
   place the per-item name input used to sit. Muted so it reads as a status,
   not an editable field. */
.seq-name-auto {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-weight: 600;
    font-size: 13px;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: default;
}

.seq-input-num {
    /* Bumped 60 → 80 px because the wider padding shrinks the inner
       text area; 80 keeps 4-digit values + spinner arrows visible. */
    width: 80px;
}

.seq-item-params {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    font-size: 11px;
    color: var(--text-secondary);
}

.seq-item-params label {
    display: flex;
    align-items: center;
    gap: 3px;
}

.seq-item-params select {
    width: 42px;
}

.seq-item-progress {
    font-size: 11px;
    color: var(--accent);
    font-weight: 600;
}

.seq-item-progress-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

/* Active card: live current-exposure frame number + countdown. */
.seq-item-frame-now {
    color: var(--text-muted);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Thin per-exposure progress bar under the frame counter, shown only
   on the card that is currently capturing. */
.seq-frame-bar {
    margin-top: 4px;
    height: 4px;
    border-radius: 2px;
    background: var(--border);
    overflow: hidden;
}

.seq-frame-bar-fill {
    height: 100%;
    background: var(--accent);
    border-radius: 2px;
    transition: width 0.12s linear;
}

.btn-icon-only {
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 16px;
    line-height: 1;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    flex-shrink: 0;
}

.btn-icon-only:hover {
    background: var(--danger);
    color: #fff;
    border-color: var(--danger);
}

.btn-icon-only:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

.btn-icon-only:disabled:hover {
    background: transparent;
    color: var(--text-muted);
    border-color: var(--border);
}

.status-indicator-sm.warn {
    background: rgba(var(--warning-rgb), 0.15);
    color: var(--warning);
}

/* Sky Explorer */
.sky-toolbar {
    display: flex;
    gap: 8px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
}

.search-input {
    flex: 1;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text-primary);
    padding: 8px 12px;
    font-size: 13px;
}

.search-input:focus {
    outline: none;
    border-color: var(--accent);
}

.sky-results {
    max-height: 240px;
    overflow-y: auto;
    border-bottom: 1px solid var(--border);
    background: var(--bg-secondary);
}

.sky-result-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 16px;
    cursor: pointer;
    font-size: 13px;
    border-bottom: 1px solid var(--border);
    transition: background var(--transition);
}

.sky-result-item:hover {
    background: rgba(var(--accent-rgb), 0.1);
}

/* Frente B: per-object DSO preview thumbnail (DSS2 cutout). Sits left of
   the text; the text block keeps the old single-row layout so the mag
   still right-aligns via its margin-left:auto. */
.sky-result-thumb {
    width: 46px;
    height: 46px;
    flex: 0 0 46px;
    object-fit: cover;
    border-radius: 4px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
}
.sky-result-text {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 0;
}

.sky-result-type {
    color: var(--text-muted);
    font-size: 11px;
}

.sky-result-common {
    color: var(--text-secondary);
    font-style: italic;
    font-size: 12px;
}

.sky-result-mag {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 11px;
}

.sky-result-size {
    color: var(--text-muted);
    font-size: 11px;
}

.sky-viewer {
    flex: 1;
    min-height: 250px;
    background: #0a0a1a;
    position: relative;
}

.sky-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
}

.sky-slew-progress {
    padding: 8px 16px;
    background: rgba(var(--accent-rgb), 0.08);
    border-bottom: 1px solid var(--accent);
}

/* Plate-solver console as a right-edge, collapsible overlay (instead of
   inline flow that pushed the SKY map down). Fixed to the screen so it
   floats over the map on both desktop and phone. */
.sky-solver-overlay {
    position: fixed;
    top: 64px;
    right: 12px;
    width: min(420px, 92vw);
    max-height: 92vh;
    /* Repositionable (drag the header) + resizable (drag the bottom-right
       grip). resize needs overflow != visible, which we already have. */
    resize: both;
    min-width: 260px;
    min-height: 140px;
    overflow: auto;
    z-index: 50;
    background: var(--bg-elev, #11161f);
    border: 1px solid var(--accent);
    border-radius: 8px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.55);
}
.sky-solver-overlay .sky-slew-progress {
    border-bottom: none;
    background: transparent;
}
.sky-solver-overlay-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 6px 10px;
    font-size: 12px;
    font-weight: 700;
    color: var(--accent);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    background: var(--bg-elev, #11161f);
    /* Drag handle for repositioning the panel. */
    cursor: move;
    user-select: none;
    touch-action: none;
}
/* Collapsed re-open pill, anchored to the right edge. */
.sky-solver-pill {
    position: fixed;
    top: 64px;
    right: 0;
    z-index: 50;
    border: 1px solid var(--accent);
    border-right: none;
    border-radius: 6px 0 0 6px;
    background: var(--bg-elev, #11161f);
    color: var(--accent);
    font-size: 12px;
    font-weight: 700;
    padding: 6px 10px;
    cursor: pointer;
}
@media (max-width: 640px) {
    .sky-solver-overlay { top: 56px; right: 6px; max-height: calc(100vh - 120px); }
    .sky-solver-pill { top: 56px; }
}

.slew-progress-row {
    display: flex;
    align-items: center;
    gap: 16px;
    font-size: 12px;
}

.slew-state {
    font-weight: 700;
    color: var(--accent);
}

.sky-info-panel {
    padding: 12px 16px;
    border-top: 1px solid var(--border);
}

.sky-target-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
}

.sky-target-type {
    color: var(--text-muted);
    font-size: 12px;
}

.sky-target-common {
    color: var(--accent);
    font-size: 13px;
    margin-bottom: 2px;
}

.sky-target-aliases {
    font-size: 11px;
    margin-bottom: 6px;
}

.sky-coords {
    display: flex;
    gap: 16px;
    font-size: 12px;
    color: var(--text-secondary);
    margin-bottom: 10px;
    flex-wrap: wrap;
}

.sky-empty {
    padding: 24px 16px;
    text-align: center;
}

.text-muted {
    color: var(--text-muted);
}

.tab-panel-sky {
    display: flex;
    flex-direction: column;
}

/* Image History */
.image-history {
    border-top: 1px solid var(--border);
}

.history-header {
    padding: 6px 16px;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    gap: 8px;
}

.history-thumbs {
    display: flex;
    gap: 6px;
    padding: 6px 12px;
    overflow-x: auto;
    background: var(--bg-primary);
    border-bottom: 1px solid var(--border);
}

.history-thumb {
    flex: 0 0 auto;
    width: 100px;
    background: #0a0a14;
    border: 1px solid var(--border);
    border-radius: 4px;
    overflow: hidden;
    cursor: pointer;
    transition: transform 0.15s ease, border-color 0.15s ease;
    position: relative;
}

.history-thumb:hover {
    transform: scale(1.04);
    border-color: var(--accent);
}

.history-thumb img {
    width: 100%;
    height: 70px;
    object-fit: cover;
    display: block;
    background: #000;
}

.history-thumb-meta {
    display: flex;
    justify-content: space-between;
    font-size: 9px;
    padding: 2px 4px;
    color: var(--text-muted);
    background: rgba(0, 0, 0, 0.4);
}

.history-header:hover {
    color: var(--text-secondary);
}

.history-list {
    max-height: 180px;
    overflow-y: auto;
    transition: max-height 0.3s ease;
}

.history-list.collapsed {
    max-height: 0;
    overflow: hidden;
}

.history-item {
    display: flex;
    gap: 10px;
    padding: 3px 16px;
    font-size: 11px;
    color: var(--text-secondary);
    border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}

.history-item:first-child {
    color: var(--text-primary);
}

.history-time {
    color: var(--text-muted);
    min-width: 60px;
}

/* ===== Equipment Management Panel ===== */

.tab-panel-equip {
    display: flex;
    flex-direction: column;
}

/* Framed panel wrapping the driver-host connection block (connected
   strip + INDI/Alpaca connect prompt) that sits above the RIGS
   sub-tabstrip. Gives the connection controls a distinct card surface. */
.equip-conn-panel {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin: 10px 0;
    overflow: hidden;
    /* The RIGS tab is a flex column; the Equipment sub-tab's card grid
       grabs flex:1 and would otherwise squeeze this wrapper, and with
       overflow:hidden the connect controls get clipped to an empty box.
       Pin the panel so it always keeps its natural height. */
    flex: 0 0 auto;
}

.equip-connect-prompt {
    /* Sits at the top of the panel, do NOT stretch to fill vertical
       space, otherwise the connect form ends up centered in the middle
       of the page while equipment cards render below it. */
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    padding: 16px 20px;
    color: var(--text-muted);
    font-size: 13px;
}

.equip-scroll {
    flex: 1;
    overflow-y: auto;
    padding: 8px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.equip-card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-secondary);
    overflow: hidden;
    transition: border-color var(--transition);
    /* Every RIGS card gets a 400 px floor so a Camera with cooler
       chart + cooler controls + sensor metadata + connect toggle has
       enough headroom without scrolling, and empty cards stay
       visually consistent. No max-height, taller devices grow as
       needed. */
    min-height: 400px;
    display: flex;
    flex-direction: column;
}

.equip-card:hover {
    border-color: rgba(255, 255, 255, 0.1);
}

.equip-card-placeholder {
    opacity: 0.5;
}

.equip-card-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    background: rgba(0, 0, 0, 0.15);
    border-bottom: 1px solid var(--border);
    font-size: 12px;
}

.equip-card-icon {
    font-size: 15px;
    line-height: 1;
}

.equip-card-title {
    font-weight: 600;
    flex: 1;
}

.equip-status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.equip-status-dot.connected {
    background: var(--success);
    box-shadow: 0 0 6px rgba(var(--success-rgb), 0.5);
}

.equip-status-dot.disconnected {
    background: var(--text-muted);
}

.equip-status-dot.warn {
    background: var(--warning, #ff9800);
    box-shadow: 0 0 6px rgba(var(--warning-rgb), 0.5);
}

.equip-card-body {
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    /* Cards live inside a responsive grid, so let the body grow to
       fill the card height set by min-height. */
    flex: 1;
    /* Smaller inner type so labels/values/buttons feel proportional
       to the role-card layout (user feedback: "diminuir a fonte dentro
       dos cards"). */
    font-size: 11px;
    /* Scroll inside the body when metadata overflows the card's
       max-height. The dropdown + Connect/Disconnect stay readable;
       the detail block scrolls beneath them. */
    overflow-y: auto;
    /* Slim, subtle scrollbar, matches the dark theme. */
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}
.equip-card-body::-webkit-scrollbar {
    width: 6px;
}
.equip-card-body::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 3px;
}
.equip-card-body::-webkit-scrollbar-track {
    background: transparent;
}

/* Mount safety-stop banner (anti cable-wrap / guide circuit breaker trip).
   Red, prominent, with a Dismiss button — shown in the Meridian Flip panel. */
.safety-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 10px 0;
    padding: 10px 12px;
    border: 1px solid var(--danger, #dc2626);
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--danger, #dc2626) 14%, transparent);
    color: var(--text-primary);
    font-size: 12px;
}
.safety-banner > span:first-child { flex: 1; }

/* Sub-group inside a role card (e.g. the Guiding System card split into
   Scope / Camera / Focus Motor). A thin top divider + an uppercase
   accent label, mirroring .blend-group-title. The first group skips the
   divider so it sits flush under the card header. */
.equip-group {
    border-top: 1px solid var(--border);
    padding-top: 8px;
    margin-top: 4px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.equip-group:first-of-type {
    border-top: 0;
    padding-top: 0;
    margin-top: 0;
}
.equip-group-title {
    font-weight: 600;
    font-size: 10px;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: .05em;
}

/* Pull a couple of inner text styles down a notch too so the visual
   hierarchy still reads with the smaller body font. */
.equip-card-body .equip-detail-name {
    font-size: 12px;
}
.equip-card-body .equip-detail-props {
    font-size: 11px;
}

.equip-select-row {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* iOS-style connection toggle. Replaces the previous Connect/
   Disconnect button pair on each equip-card. Red track when
   disconnected, green when connected, with a sliding thumb.
   The native checkbox is hidden behind the painted UI but stays
   focusable + keyboard-toggleable for accessibility. */
.equip-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
    cursor: pointer;
    width: 44px;
    height: 24px;
    flex: 0 0 auto;
    user-select: none;
}
.equip-toggle input {
    opacity: 0;
    width: 0;
    height: 0;
    position: absolute;
}
.equip-toggle-track {
    position: absolute;
    inset: 0;
    border-radius: 12px;
    background: #c53030;            /* red = disconnected */
    transition: background 0.18s ease;
}
.equip-toggle input:checked + .equip-toggle-track {
    background: #2f855a;            /* green = connected */
}
.equip-toggle-thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
    transition: transform 0.18s ease;
}
.equip-toggle input:checked + .equip-toggle-track + .equip-toggle-thumb,
.equip-toggle input:checked ~ .equip-toggle-thumb {
    transform: translateX(20px);
}
.equip-toggle-busy .equip-toggle-track {
    background: #b7791f;            /* amber = action in flight */
}
.equip-toggle[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Camera driver picker, shows above the device dropdown when more
   than one camera backend is available on this host (e.g. INDI plus
   Canon EDSDK on Windows). */
.equip-driver-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
}
.equip-driver-label {
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    min-width: 50px;
}
.equip-driver-select {
    flex: 1;
}
/* GUIDE side-panel groups (e.g. the Dithering section) pair a short
   uppercase caption with a value field on one line. Keep the caption
   on a single line — "SETTLE PX" / "TIMEOUT S" were wrapping to two
   rows (field report) — and let the input take the remaining width. */
.guide-group .equip-driver-label {
    white-space: nowrap;
    flex: 0 0 auto;
}
.guide-group .equip-driver-row > .input-sm {
    /* Cap the field at 60% of the row and push it to the right edge,
       so the captions stay left-aligned in their column and the value
       boxes line up on a consistent right margin (field request). */
    flex: 0 0 60%;
    min-width: 0;
    width: 60%;
    margin-left: auto;
}

/* Install-required banner: vendor SDK selected but the native DLLs
   aren't reachable. Tints amber and links to the matching install doc. */
.equip-install-banner {
    background: rgba(255, 160, 80, 0.12);
    border: 1px solid rgba(255, 160, 80, 0.4);
    border-radius: var(--radius);
    padding: 6px 10px;
    font-size: 11px;
    color: #ffb066;
    margin-bottom: 8px;
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}
.equip-install-banner a {
    color: #ffd0a0;
    text-decoration: underline;
}

.equip-select {
    flex: 1;
    min-width: 0;
    /* Match the .input-sm geometry used by the Main Telescope card
       (Brand / Model / Accessory dropdowns). Previously this was
       5px / 12px-font / no min-height — produced visibly shorter
       selects in Camera / Mount / Focuser / FilterWheel / Rotator /
       Flat Panel / Dome / Weather cards than in Telescope, which
       broke the uniform row rhythm. Now everything in RIGS uses
       the same 38 px tap-friendly height + 14 px font. */
    padding: 9px 10px;
    min-height: 38px;
    box-sizing: border-box;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text-primary);
    font-size: 13px;
    cursor: pointer;
    outline: none;
    appearance: auto;
}

.equip-select:focus {
    border-color: var(--accent);
}

.equip-detail {
    padding: 6px 0 2px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.equip-detail-name {
    font-weight: 600;
    font-size: 13px;
    color: var(--accent);
}

.equip-detail-props {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    font-size: 12px;
    color: var(--text-secondary);
}

.equip-detail-props strong {
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.equip-inline-controls {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-top: 2px;
}

/* Labeled control rows for cards with many buttons (mount card): a small
   uppercase group label over a wrapping button row, instead of one long
   flex-wrap line that broke unevenly. */
.equip-ctrl-grouplabel {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .5px;
    color: var(--text-secondary, #a0a0b0);
    margin: 8px 0 4px;
}
.equip-ctrl-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

/* FOCUSER-SPEC v2 + FILTERWHEEL-SPEC v2: collapsible driver
   settings block that lives below the standard equip-detail
   props. Stays out of the way (small button, body hidden by
   default) so the card looks unchanged when collapsed but
   exposes the optional INDI knobs (FOCUS_SYNC / REVERSE /
   BACKLASH, FILTER_NAME editor) when the user opens it. */
.equip-driver-settings {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--border, rgba(255, 255, 255, 0.08));
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.equip-driver-settings-toggle {
    align-self: flex-start;
    font-size: 11px;
    color: var(--text-secondary);
}
.equip-driver-settings-body {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 6px 0 2px;
}
.equip-driver-hint {
    font-size: 11px;
    color: var(--text-muted, var(--text-secondary));
    margin: 0 0 4px;
    line-height: 1.3;
}
.equip-driver-message {
    font-size: 11px;
    margin: 0;
}
/* Compact input variant for small inline forms (Sync position,
   Backlash steps, individual filter-name entries). Sits next to
   buttons in equip-driver-row without dominating the layout. */
.equip-input-sm {
    max-width: 110px;
    font-size: 12px;
    padding: 3px 6px;
}

.equip-temp-label {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--text-secondary);
}

.equip-temp-label .input-sm {
    width: 52px;
}

.equip-filter-buttons {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    margin-top: 2px;
}

.equip-footer {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-top: 1px solid var(--border);
    background: var(--bg-secondary);
}

/* Equipment panel mobile adjustments */
@media (max-width: 768px) {
    .equip-select-row {
        flex-wrap: wrap;
    }

    .equip-select {
        width: 100%;
    }

    .equip-detail-props {
        gap: 8px;
        font-size: 11px;
    }

    .equip-inline-controls {
        gap: 4px;
    }
}

/* ─── Weather forecast tab ──────────────────────────────────────────────
   3-day astronomy forecast pulled from 7Timer via /api/weather/forecast.
   Layout: callout strip → per-day cards (3 across on desktop, stacked on
   narrow) → legend. Each card carries a 8-slot grid (3-hour buckets)
   colour-coded by composite observation score. */

.weather-panel {
    padding: 16px;
    overflow-y: auto;
    gap: 16px;
    display: flex;
    flex-direction: column;
}
.weather-message {
    background: rgba(var(--warning-rgb), 0.12);
    border: 1px solid rgba(var(--warning-rgb), 0.35);
    color: #ffb74d;
    padding: 10px 14px;
    border-radius: 6px;
    font-size: 13px;
}
.weather-window-callout {
    background: rgba(var(--success-rgb), 0.10);
    border: 1px solid rgba(var(--success-rgb), 0.35);
    border-radius: 8px;
    padding: 12px 16px;
    box-shadow: 0 0 18px rgba(var(--success-rgb), 0.08) inset;
}
.weather-window-callout strong { color: #81c784; }
.weather-window-callout ul {
    list-style: none;
    padding: 0;
    margin: 8px 0 0;
    font-size: 13px;
    line-height: 1.7;
}
.weather-window-callout--meh {
    background: rgba(var(--warning-rgb), 0.08);
    border-color: rgba(var(--warning-rgb), 0.3);
    color: var(--text-secondary);
    font-size: 13px;
}
.weather-day-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: 16px;
}
.weather-day-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.weather-day-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
    flex-wrap: wrap;
}
.weather-day-name {
    font-weight: 700;
    font-size: 16px;
    color: var(--text, #e6ecf5);
}
.weather-day-sub {
    font-size: 11px;
    color: var(--text-muted);
    letter-spacing: 0.5px;
    text-transform: uppercase;
}
.weather-day-celestial {
    font-size: 11px;
    color: var(--text-secondary);
    text-align: right;
    line-height: 1.6;
}
.weather-icon {
    margin-right: 4px;
    font-size: 13px;
}
.weather-moon {
    margin-top: 2px;
    color: #ffd180;
}

/* 8-slot grid (24 h ÷ 3 h). On narrow viewports we let it wrap to two
   rows of 4 instead of squishing the slots flat. */
.weather-slot-grid {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 4px;
}
@media (max-width: 480px) {
    .weather-slot-grid { grid-template-columns: repeat(4, 1fr); }
}
.weather-slot {
    border-radius: 4px;
    padding: 8px 4px;
    text-align: center;
    cursor: default;
    transition: transform 0.08s ease;
    font-variant-numeric: tabular-nums;
}
.weather-slot:hover { transform: scale(1.04); }
.weather-slot-time {
    font-size: 10px;
    opacity: 0.7;
    letter-spacing: 0.5px;
}
.weather-slot-score {
    font-size: 18px;
    font-weight: 700;
    line-height: 1.1;
    margin: 2px 0;
}
.weather-slot-cloud {
    font-size: 10px;
    opacity: 0.7;
}
/* Per-slot weather glyph. Emoji sit awkwardly small in this dense grid
  , bump them up a notch so they're scannable. */
.weather-slot-icon {
    font-size: 16px;
    line-height: 1.1;
    margin: 1px 0 -1px;
}
/* Big day-summary glyph in the card header. Lets the user read the
   day at a glance without parsing the slot grid. */
.weather-day-title {
    display: flex;
    align-items: center;
    gap: 10px;
}
.weather-day-icon {
    font-size: 28px;
    line-height: 1;
    flex-shrink: 0;
}
.weather-slot--good {
    background: rgba(var(--success-rgb), 0.25);
    color: #c8e6c9;
}
.weather-slot--meh {
    background: rgba(var(--warning-rgb), 0.22);
    color: #ffe0b2;
}
.weather-slot--bad {
    background: rgba(244, 67, 54, 0.18);
    color: #ffcdd2;
}

.weather-legend {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    font-size: 11px;
    color: var(--text-secondary);
    padding: 8px 0 4px;
    border-top: 1px solid var(--border);
}
.weather-legend strong {
    color: var(--text, #e6ecf5);
    margin-right: 4px;
}
.weather-legend-chip {
    padding: 2px 8px;
    border-radius: 10px;
    font-weight: 600;
    font-size: 10px;
    letter-spacing: 0.4px;
}
.weather-legend a {
    color: var(--accent);
    text-decoration: none;
}
.weather-legend a:hover { text-decoration: underline; }

/* ─── Tonight's Best tab ────────────────────────────────────────────────
   ASIAIR-style ranked list. Each row is a card containing:
     thumb (left) │ name + coords + badges │ mini altitude chart │ compass │ Go to
*/
/* ───────────────────────── EDITOR (ED-4) ─────────────────────────
   Lightroom-style non-destructive image editor. Two-column workspace:
   preview + histogram on the left, collapsible slider sections on the
   right. Source picker uses three cards for the three load paths
   (dropzone / path / library).
*/
.editor-panel {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.editor-source-picker {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    padding: 24px;
}
.editor-source-card {
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 18px;
}
.editor-source-card h3 {
    margin: 0 0 8px 0;
    font-size: 13px;
    color: var(--text-primary);
}
.editor-source-card .hint {
    margin: 0 0 12px 0;
    color: var(--text-muted);
    font-size: 12px;
    line-height: 1.5;
}
.editor-source-dropzone {
    border: 2px dashed rgba(var(--accent-rgb), 0.4);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.editor-source-dropzone:hover {
    background: rgba(var(--accent-rgb), 0.08);
    border-color: rgba(var(--accent-rgb), 0.7);
}
.editor-path-row {
    display: flex;
    gap: 8px;
}
.editor-path-row > input { flex: 1; }
.editor-load-error {
    color: var(--warn);
    padding: 0 24px 16px;
    font-size: 12px;
}

.editor-workspace {
    flex: 1;
    display: grid;
    /* Middle "auto" column is the drag handle; the right column width is
       user-resizable via --editor-panel-w (set inline from Alpine, so the
       mobile media query below still wins by not using the var). */
    grid-template-columns: 1fr auto var(--editor-panel-w, 320px);
    gap: 0;
    overflow: hidden;
    min-height: 0;
}
@media (max-width: 960px) {
    .editor-workspace {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr auto;
    }
    /* No side-by-side resize on a stacked mobile layout. */
    .editor-resizer { display: none; }
}

/* Drag handle between the preview and the controls panel. A thin column
   the user grabs to widen/narrow the sliders panel. */
.editor-resizer {
    width: 6px;
    cursor: col-resize;
    background: var(--border);
    position: relative;
    flex: none;
    touch-action: none;        /* we handle the drag via pointer events */
    transition: background 0.12s ease;
}
.editor-resizer:hover,
.editor-resizer.dragging {
    background: var(--accent);
}
/* Fatter invisible hit area so it's easy to grab on touch without making
   the visible strip wide. */
.editor-resizer::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: -5px;
    right: -5px;
}
/* Grip dots so the handle reads as draggable. */
.editor-resizer::after {
    content: "⋮";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 13px;
    line-height: 1;
    color: var(--text-secondary);
    pointer-events: none;
}
.editor-preview-col {
    display: flex;
    flex-direction: column;
    min-height: 0;
    /* Grid items default to min-width:auto (min-content), which floors this
       1fr preview track at the canvas's intrinsic width. Without this the
       track can't shrink to make room, so widening the controls panel
       (--editor-panel-w) overflows the grid and .editor-workspace's
       overflow:hidden clips the panel off to the right instead of growing it
       leftward. min-width:0 lets the track shrink so the panel grows properly. */
    min-width: 0;
    background: #000;
}
.editor-preview-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    background: rgba(10, 14, 24, 0.85);
    border-bottom: 1px solid var(--border);
    font-size: 11px;
}
.editor-preview-wrap {
    flex: 1;
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    /* User-select off so click-drag pan doesn't pick a stray text
       selection on the surrounding panel. */
    user-select: none;
    -webkit-user-select: none;
    /* EDPAN: without this the browser claims a finger drag for scrolling
       and page zoom, and the pointermove events for the pan never arrive.
       Pointer handlers alone are not enough on a touchscreen. */
    touch-action: none;
}
/* Inner layer the CSS transform applies to. Sized 100%×100% so the
   contained <img>/<canvas> fills it at zoom=1; transform-origin centre
   makes zoom-with-wheel-anchor math straightforward. */
.editor-preview-stage {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    transform-origin: center center;
    will-change: transform;
    /* Snappy on zoom button clicks without slowing down a wheel drag. */
    transition: transform 0.12s ease-out;
}
/* Killed transition during pan (zoom unchanged, only translate moves),
   the .editor-preview-stage's transition above would smear the drag.
   We toggle by writing inline style from JS during pan; here we just
   guard against rendering glitches with the default. */
.editor-preview-stage img,
.editor-preview-stage canvas {
    -webkit-user-drag: none;
}
.editor-preview-img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    object-position: center;
}
.editor-preview-spinner {
    position: absolute;
    top: 8px;
    right: 12px;
    font-size: 18px;
    opacity: 0.6;
}
/* Histogram overlay. Pinned absolutely to the bottom-inside of the
   preview wrap so the image keeps the full vertical real estate
   when the user dismisses it. Translucent backdrop so the curve
   stays readable over a stretched master without completely
   blacking out the pixels behind it. Wide:90% / centered keeps a
   margin on the sides so the curve sits comfortably above the
   pan/zoom cursor region. The width attribute on the <canvas>
   (512) defines the backing pixel buffer for the histogram code
   to draw into; CSS width sizes the rendered box. */
/* Editor stretch histogram: the live .histo-panel docked as a translucent
   overlay strip at the bottom of the preview area. Interactive (handles +
   buttons), so unlike the old static strip it must capture pointer events. */
.editor-histo-panel {
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: 12px;
    z-index: 6;
    background: rgba(10, 15, 25, 0.78);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 6px 10px;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
/* The graph in the editor overlay is a touch shorter than the live panel
   so it doesn't cover too much of the image. */
.editor-histo-panel .histo-graph { height: 72px; }

/* Toolbar toggle states. .btn-toggle-on highlights the histogram
   button (and any future toggle the same way) so the user can
   tell at a glance whether the overlay is showing. */
.btn-toggle-on {
    background: rgba(var(--accent-rgb), 0.18);
    border-color: var(--accent);
    color: var(--text-primary);
}

.editor-controls-col {
    overflow-y: auto;
    padding: 12px;
    background: var(--bg-secondary);
    border-left: 1px solid var(--border);
}
/* AUTOED-2: Auto + Reset pair sitting above the sliders. Keeps the
   two starting-point buttons next to the controls they affect, the
   top toolbar still has its own Reset for muscle memory. */
.editor-controls-header {
    display: flex;
    flex-wrap: wrap;        /* the row used to hold Auto + Reset only;
                               with Crop + BGE + Denoise + Decon added
                               in subsequent passes, the buttons must
                               wrap to a second line on narrow columns
                               instead of all 6 squeezing to 36 px each. */
    gap: 6px;
    margin-bottom: 12px;
}

/* Path label inside the editor preview toolbar: middle-truncate
   via editorShortPath() in JS, and CSS-clip as a safety net so
   any path the helper missed still doesn't push the right-column
   sliders narrower. */
.editor-path-display {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.editor-controls-header .btn {
    /* Content-sized; the row wraps when it runs out of room. The old
       `flex: 1` made each button equal-width — fine for 2 buttons,
       broke entirely past 4. */
    flex: 0 0 auto;
}
.editor-section {
    margin-bottom: 8px;
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
}
.editor-section > summary {
    padding: 8px 12px;
    cursor: pointer;
    font-weight: 600;
    font-size: 12px;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    user-select: none;
}
.editor-section[open] > summary {
    border-bottom: 1px solid var(--border);
}
.editor-slider-row {
    display: grid;
    grid-template-columns: 80px 1fr 48px;
    align-items: center;
    gap: 8px;
    padding: 4px 12px;
    font-size: 11px;
}
.editor-slider-label { color: var(--text-secondary); }
.editor-slider-value {
    text-align: right;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}
.editor-slider-row input[type="range"] { width: 100%; }
/* A select occupying the slider column (e.g. the Stretch mode picker):
   fill the column like the range inputs do, no max-width cap. */
.editor-slider-select { width: 100%; max-width: none; }

/* ED-6 compute-mode pill, colour-codes which side is doing the math
   so the user knows at a glance whether slider drags will hit the
   network. Disabled state (when WASM not ready) is communicated by
   the standard :disabled styling. */
.editor-mode-pill { min-width: 78px; }
.editor-mode-server { border-color: rgba(148, 163, 184, 0.5); }
.editor-mode-wasm {
    border-color: rgba(74, 222, 128, 0.6);
    background: rgba(74, 222, 128, 0.12);
    color: #86efac;
}

/* GX-5: AI section button cluster inside the editor's right panel.
   Each button picks one pipeline (BGE / Denoise / Decon) and routes
   through editorRunAi(). Stacked vertically so labels stay readable
   in the narrow controls column. */
.editor-ai-buttons {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 6px 12px;
}
.editor-ai-buttons .btn { width: 100%; text-align: left; }

/* Editor export modal, backdrop covers the viewport, frame floats
   centred. z-index 1050 sits ABOVE the fullscreen editor overlay
   (.files-editor-modal, z-index 1000) so the Export dialog launched
   from inside the editor isn't painted behind it (same z-index +
   later DOM order would otherwise hide it), and still below toasts
   (z-index 1100) so the "Exported to ..." toast stays visible. */
.editor-export-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.78);
    z-index: 1050;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.editor-export-modal {
    min-width: 360px;
    max-width: 520px;
    width: 100%;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 22px;
    color: var(--text-primary);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}
.editor-export-modal h3 {
    margin: 0 0 14px 0;
    font-size: 16px;
}
.editor-export-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 18px;
}

/* GX-11: Before/After comparator. Full-screen overlay with a draggable
   vertical divider. Two layered <img> tags occupy the same box; the
   "AFTER" image (top layer) is clipped from the right edge by a CSS
   variable --split that the JS updates on drag, revealing the "BEFORE"
   image underneath the unclipped portion. */
.graxpert-compare-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(2, 6, 12, 0.85);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    /* GX-11b: match the .image-viewer-modal sizing (12 px outer
       padding, 96vw × 92vh shell) so before/after takes the same
       real estate as the regular OpenSeadragon FITS viewer.
       Previously the wider 24 px padding + un-stretched height
       left the image clamped to its natural aspect ratio in the
       middle of the screen, which made it look tiny on a 4k display
       when the source FITS was square. */
    padding: 12px;
}
.graxpert-compare-shell {
    display: flex;
    flex-direction: column;
    background: var(--bg-secondary, #0f1422);
    border: 1px solid var(--border, #2a3142);
    border-radius: var(--radius, 10px);
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
    width: 96vw;
    height: 92vh;
    max-width: 1600px;
    overflow: hidden;
}
.graxpert-compare-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border, #2a3142);
    background: rgba(255, 255, 255, 0.02);
    font-size: 13px;
}
.graxpert-compare-label {
    color: var(--text-muted, #94a3b8);
    font-family: var(--font-mono);
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 50%;
}
.graxpert-compare-nav {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.graxpert-compare-idx {
    font-variant-numeric: tabular-nums;
    color: var(--text-muted, #94a3b8);
    padding: 0 6px;
}
.graxpert-compare-wrap {
    position: relative;
    flex: 1 1 auto;
    min-height: 320px;
    background: #000;
    overflow: hidden;
    user-select: none;
    cursor: ew-resize;
}
/* Zoom/pan: each <img> lives in a full-size layer. The clip that makes
   the before/after divide lives on the LAYER (screen space) so it stays
   put while the <img> inside is transformed (zoom/pan). Both imgs share
   the same transform, so the two halves stay pixel-aligned. */
.graxpert-compare-wrap .gc-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;        /* let wheel/drag reach the wrap */
    overflow: hidden;
}
.graxpert-compare-wrap img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    /* Pixel-art rendering is wrong for star-field stretched JPEGs.
       The default bilinear interpolation is what we want, leave it. */
    pointer-events: none;
    transform-origin: 0 0;
    will-change: transform;
}
.gc-out-clip {
    /* GX-12f: AFTER (the processed image) sits on the RIGHT of the
       handle; clip masks the LEFT portion up to --split so SRC shows
       through on the left and OUT on the right. */
    clip-path: inset(0 0 0 var(--split));
}
/* When zoomed the wrap becomes a pan surface (grab cursor); at 1x it
   keeps the ew-resize hint for the divider drag. */
.graxpert-compare-wrap.gc-zoomed { cursor: grab; }
.graxpert-compare-wrap.gc-zoomed:active { cursor: grabbing; }
.graxpert-compare-zoom {
    position: absolute;
    left: 12px; bottom: 12px;
    padding: 2px 8px;
    border-radius: 6px;
    background: rgba(2, 6, 12, 0.7);
    color: var(--text-primary, #e5e7eb);
    font-variant-numeric: tabular-nums;
    font-size: 12px;
    pointer-events: none;
}
/* Toolbar zoom controls (+/- / Fit) for the compare modal. Trackpad
   wheel/pinch zoom is awkward (MacBook), so these give explicit steps. */
.gc-zoom-ctl {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.gc-zoom-ctl .gc-zoom-val {
    min-width: 3.2em;
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-size: 12px;
    color: var(--text-secondary, #9ca3af);
}
/* PA-6: TPPA target suggestion chips on the POLAR tab. Server
   ranks high-Dec objects currently above 30°; UI shows them as a
   pillsy chip grid. Click slews + plate-solves to that target via
   the existing skyTarget + slewAndCenter flow. */
/* POLAR refine layout: readout on the LEFT, ASIAIR bullseye on the RIGHT.
   The old inline `flex-wrap: wrap` keyed the stack purely off width, so a
   tablet in landscape could wrap the bullseye BELOW even with room to spare.
   Keep it a row everywhere and stack ONLY on a narrow portrait phone. */
.polar-refine-grid {
    display: flex;
    gap: 28px;
    align-items: flex-start;
}
@media (max-width: 700px) {
    .polar-refine-grid { flex-direction: column; }
}
.polar-targets {
    background: var(--bg-secondary, rgba(255, 255, 255, 0.02));
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 10px 12px;
}
.polar-targets-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 8px;
}
.polar-target-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.polar-target-chip {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    padding: 8px 12px;
    background: rgba(91, 141, 239, 0.10);
    border: 1px solid rgba(91, 141, 239, 0.35);
    border-radius: 8px;
    color: var(--text-primary);
    cursor: pointer;
    font-size: 12px;
    line-height: 1.3;
    text-align: left;
    transition: background 0.12s, border-color 0.12s, transform 0.08s;
    min-width: 140px;
}
.polar-target-chip:hover:not(:disabled) {
    background: rgba(91, 141, 239, 0.20);
    border-color: rgba(91, 141, 239, 0.6);
}
.polar-target-chip:active:not(:disabled) { transform: scale(0.98); }
.polar-target-chip:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
.polar-target-name {
    font-weight: 600;
    font-size: 13px;
}
.polar-target-meta {
    display: flex;
    gap: 10px;
    opacity: 0.78;
    font-variant-numeric: tabular-nums;
    font-size: 11px;
}
.polar-target-meta strong { color: var(--text-primary); font-weight: 600; }

/* GX-12q: Typography normalisation for the cert-install modal.
   Without these the global h3 / p / li / code styles render much
   larger than the rest of the app (h3 ≈ 22-28px, body ≈ 16px+),
   which made the modal feel like a different product. Scoped to
   .cert-modal-frame so it doesn't bleed into siril / other
   .siril-modal-frame instances that share the base class. */
.cert-modal-frame,
.cert-modal-frame p,
.cert-modal-frame li {
    font-size: 13px;
    line-height: 1.55;
}
.cert-modal-frame h2 { font-size: 16px; margin: 0 0 10px; }
.cert-modal-frame h3 { font-size: 13px; font-weight: 600;
    margin: 16px 0 6px; color: var(--text-primary); }
.cert-modal-frame p { margin: 6px 0; }
.cert-modal-frame ol,
.cert-modal-frame ul { margin: 6px 0; padding-left: 22px; }
.cert-modal-frame li { margin: 4px 0; }
.cert-modal-frame code {
    font-size: 12px;
    background: rgba(255, 255, 255, 0.06);
    padding: 1px 5px;
    border-radius: 3px;
}
.cert-modal-frame .hint { font-size: 12px; }

