/* Stream + toolbar styles shared by the home-page bundle and the ws-scrcpy library bundle.
 *
 * This file is also loaded standalone by embed.html (which does NOT link app.css),
 * so it must self-contain every theme variable the stream/toolbar styling depends on.
 * Home-page callers (app.css) re-define the same variables — identical values, so
 * the cascade harmless. Light-mode auto-detection uses prefers-color-scheme, but
 * an explicit data-theme="light"/"dark" attribute on <html> wins.
 */

:root,
[data-theme="dark"] {
    --control-buttons-bg-color: #2d3741;
    --svg-button-fill: #ffffff;
    --svg-checkbox-bg-color: #10b981;
}

@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) {
        --control-buttons-bg-color: #f8f9fa;
        --svg-button-fill: #495057;
        --svg-checkbox-bg-color: #198754;
    }
}

[data-theme="light"] {
    --control-buttons-bg-color: #f8f9fa;
    --svg-button-fill: #495057;
    --svg-checkbox-bg-color: #198754;
}

/* Stream layout: device-view is a flex row (video | toolbar). Video cell uses
 * CSS grid so the video-layer and touch-layer stack in the same cell — this
 * replaces legacy position:absolute layers which broke parent auto-sizing.
 * DOM order is toolbar-first; the toolbar uses `order: 1` to appear right.
 *
 * Defaults deliberately DO NOT use flex:1 — that would force the container to
 * stretch to max height in fit-content parents (like the ConnectModal frame),
 * leaving dead space below a smaller canvas. Full-body embed contexts that
 * need the stream to fill the viewport opt in via body[data-embed-entry]
 * below. */
.device-view {
    display: flex;
    flex-direction: row;
    min-width: 0;
    min-height: 0;
}

.video {
    display: grid;
    min-width: 0;
    min-height: 0;
    place-items: center;
    background-color: #000;
    /* Grid layout + the canvas max-width/max-height caps control the displayed
     * cell size with correct aspect; `.video` itself stays auto. The device
     * resolution is exposed as the --video-width / --video-height custom props
     * (set by the player's setScreenInfo) instead of an inline width/height, so
     * no `!important` is needed to keep the cell auto-sized. (#106) */
    width: auto;
    height: auto;
}

/* Viewport-based max caps on the canvas layers. Canvas elements preserve
 * intrinsic aspect ratio when both max-width and max-height are set, so these
 * make the canvas shrink-to-fit the viewport while preserving the device
 * aspect ratio. (The player's `calculateScreenInfoForBounds` never runs
 * because `resizeVideoToBounds` is false on every subclass — so CSS alone
 * has to do the sizing.) Modal context overrides these with narrower caps. */
.video-layer {
    grid-area: 1 / 1;
    max-width: calc(100vw - 3.715rem);
    max-height: 100vh;
}

.control-wrapper > input[type="checkbox"] {
    display: none;
}

.control-wrapper > label.control-button {
    display: block;
    margin: 0.571rem auto;
}

.control-wrapper > input[type="checkbox"].two-images:checked + label > svg.image-on {
    display: block;
}

.control-wrapper > input[type="checkbox"].two-images:not(:checked) + label > svg.image-on {
    display: none;
}

.control-wrapper > input[type="checkbox"].two-images:checked + label > svg.image-off {
    display: none;
}

.control-wrapper > input[type="checkbox"].two-images:not(:checked) + label > svg.image-off {
    display: block;
}

.control-wrapper > input[type="checkbox"]:checked + label > svg {
    fill: var(--svg-checkbox-bg-color);
}

.touch-layer {
    grid-area: 1 / 1;
    max-width: calc(100vw - 3.715rem);
    max-height: 100vh;
}

.control-buttons-list {
    flex-shrink: 0;
    width: 3.715rem;
    background-color: var(--control-buttons-bg-color);
    order: 1; /* DOM order is toolbar-first, but we want video-left toolbar-right */
}

/* Embed page (embed.html) marks its body with data-embed-entry. The stream
 * container fills the viewport, but the video cell itself stays content-sized
 * so canvas + toolbar hug together as a pair — matches the legacy float:right
 * look where the toolbar is flush against the mirror's right edge. Without
 * justify-end the pair sits left and portrait devices leave the toolbar
 * marooned at the far right. Modal contexts (fit-content frame) skip this. */
body[data-embed-entry] .device-view {
    flex: 1;
    justify-content: flex-end;
}

.control-button {
    margin: 0.571rem auto;
    padding: 0;
    display: block;
    width: 2.143rem;
    height: 2.143rem;
    border: none;
    opacity: 0.75;
    background-color: var(--control-buttons-bg-color);
    overflow: hidden;
}

.control-button:hover {
    opacity: 1;
}

/* biome-ignore lint/style/noDescendingSpecificity: intentional source order; reordering would change the cascade */
.control-button > svg {
    fill: var(--svg-button-fill);
    width: 100%;
    height: 100%;
    display: block;
}

/* ── Native <dialog> modal system ── */

/* The <dialog> itself is an invisible full-screen positioning layer.
   padding:0 is critical: clicks on ::backdrop bubble to the <dialog> element,
   and we detect backdrop clicks by checking event.target === dialog.
   All visible content lives inside .modal-frame. */
dialog.modal {
    padding: 0;
    border: none;
    background: transparent;
    max-width: 100vw;
    max-height: 100vh;
    overflow: visible;
}

/* ── Backdrop ── */
dialog.modal::backdrop {
    background: rgba(0, 0, 0, 0.45);
}

/* ── Glassmorphism frame ── */
dialog.modal .modal-frame {
    font-family: var(--font-mono);
    color: var(--text-color, #ddd);
    width: clamp(400px, 50vw, 700px);
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    background: rgba(42, 42, 42, 0.85);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
    overflow: hidden;
}

[data-theme="light"] dialog.modal .modal-frame {
    background: rgba(245, 248, 252, 0.87);
    border: 1px solid rgba(0, 0, 0, 0.1);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}

/* Nested-modal readability: when a second modal opens on top, the top one uses
   a fully opaque frame so its content is crisp over the lower modal's glass. */
body:has(dialog.modal[open] ~ dialog.modal[open]) dialog.modal[open]:last-of-type .modal-frame {
    background: rgb(42, 42, 42);
}

[data-theme="light"] body:has(dialog.modal[open] ~ dialog.modal[open]) dialog.modal[open]:last-of-type .modal-frame {
    background: rgb(245, 248, 252);
}

dialog.modal .modal-frame button,
dialog.modal .modal-frame select,
dialog.modal .modal-frame input {
    font-family: var(--font-mono);
    font-size: var(--font-size);
}

dialog.modal .modal-frame select {
    text-overflow: ellipsis;
}

/* ── Open/close transitions via @starting-style ──
   How this works:
   - Base state (no [open]) = exit-end values (invisible)
   - [open] state = visible values
   - @starting-style = entry-start values (invisible, same as base)
   - allow-discrete on display/overlay keeps dialog rendered during exit transition
*/

/* Dialog element: transparent container, stays rendered during exit */
dialog.modal {
    opacity: 0;
    transition:
        opacity 0.2s ease-out,
        display 0.2s ease-out allow-discrete,
        overlay 0.2s ease-out allow-discrete;
}

dialog.modal[open] {
    opacity: 1;
}

/* Frame: exit-end state (invisible, scaled down) */
dialog.modal .modal-frame {
    opacity: 0;
    transform: scale(0.96) translateY(8px);
    transition:
        opacity 0.2s ease-out,
        transform 0.2s ease-out;
}

/* Frame: visible state when open */
dialog.modal[open] .modal-frame {
    opacity: 1;
    transform: scale(1) translateY(0);
}

/* Backdrop: exit-end state */
dialog.modal::backdrop {
    background: rgba(0, 0, 0, 0.45);
    opacity: 0;
    transition:
        opacity 0.2s ease-out,
        display 0.2s ease-out allow-discrete;
}

/* Backdrop: visible state when open */
dialog.modal[open]::backdrop {
    opacity: 1;
}

/* Entry animation: these are the "from" values when [open] is first applied */
@starting-style {
    dialog.modal[open] {
        opacity: 0;
    }
    dialog.modal[open] .modal-frame {
        opacity: 0;
        transform: scale(0.96) translateY(8px);
    }
    dialog.modal[open]::backdrop {
        opacity: 0;
    }
}

/* ── Header ── */
dialog.modal .modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--modal-divider);
    flex-shrink: 0;
}

dialog.modal .modal-title {
    font-size: 15px;
    font-weight: 600;
}

dialog.modal .modal-header-controls {
    display: flex;
    align-items: center;
    gap: 40px;
}

/* Reset home-page theme-toggle fixed positioning when it's reused inside a modal */
dialog.modal .modal-header-controls .theme-toggle {
    position: static;
    top: auto;
    right: auto;
    z-index: auto;
    width: auto;
    height: auto;
    background: transparent;
    border: none;
    border-radius: 0;
}

dialog.modal .modal-close {
    background: transparent;
    border: none;
    color: var(--text-color-light, #888);
    cursor: pointer;
    padding: 4px;
    line-height: 1;
    font-size: 18px;
}

dialog.modal .modal-close:hover {
    color: var(--text-color, #ddd);
}

/* ── Body (scrollable) ── */
dialog.modal .modal-body {
    padding: 1rem;
    overflow-y: auto;
    flex: 1;
    min-height: 0;
}

/* ── Footer ── */
dialog.modal .modal-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--modal-divider);
    flex-shrink: 0;
}

/* ── Controls grid (used by ConfigureScrcpy) ── */
dialog.modal .modal-controls {
    display: grid;
    grid-template-columns: [labels] 35% [controls] 1fr;
    gap: 0.5rem 0.75rem;
    align-items: center;
    padding-right: 0.5rem;
    min-width: 0;
}

dialog.modal .modal-controls select,
dialog.modal .modal-controls input {
    max-width: 100%;
    box-sizing: border-box;
    min-width: 0;
}

dialog.modal .modal-controls .label {
    grid-column: labels;
    color: var(--text-color-light, #888);
    font-size: 13px;
}

dialog.modal .modal-controls .input,
dialog.modal .modal-controls select,
dialog.modal .modal-controls input:not([type="checkbox"]) {
    grid-column: controls;
    box-sizing: border-box;
    background: var(--stream-bg-color, #1a1a2e);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    color: var(--text-color, #ddd);
    padding: 4px 8px;
}

[data-theme="light"] dialog.modal .modal-controls select,
[data-theme="light"] dialog.modal .modal-controls input:not([type="checkbox"]) {
    background: #fff;
    border-color: rgba(0, 0, 0, 0.15);
    color: #333;
}

dialog.modal .modal-controls select:focus,
dialog.modal .modal-controls input:focus {
    outline: none;
    border-color: var(--accent-color);
}

dialog.modal .modal-controls input:disabled,
dialog.modal .modal-controls select:disabled,
dialog.modal .modal-advanced input:disabled,
dialog.modal .modal-advanced select:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    background: rgba(255, 255, 255, 0.03);
}

[data-theme="light"] dialog.modal .modal-controls input:disabled,
[data-theme="light"] dialog.modal .modal-controls select:disabled,
[data-theme="light"] dialog.modal .modal-advanced input:disabled,
[data-theme="light"] dialog.modal .modal-advanced select:disabled {
    background: #eee;
}

/* ── Slider ── */
dialog.modal .modal-controls input[type="range"] {
    grid-column: controls;
    width: 100%;
    cursor: pointer;
    accent-color: var(--accent-color);
}

/* ── Advanced toggle ── */
dialog.modal .modal-advanced-separator {
    grid-column: 1 / -1;
    border-top: 1px solid var(--modal-divider);
    margin-top: 0.5rem;
    padding-top: 0.5rem;
}

dialog.modal .modal-advanced-toggle {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    padding: 0.25rem 0;
    color: var(--text-color-light, #888);
    font-size: 13px;
    user-select: none;
    background: transparent;
    border: none;
    width: 100%;
    font-family: var(--font-mono);
}

dialog.modal .modal-advanced-toggle:hover {
    color: var(--text-color, #ddd);
}

dialog.modal .modal-advanced-toggle .chevron {
    transition: transform 0.3s ease;
    font-size: 12px;
}

dialog.modal .modal-advanced-toggle .chevron.expanded {
    transform: rotate(180deg);
}

/* ── Advanced section (animated reveal) ── */
dialog.modal .modal-advanced {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: [labels] 35% [controls] 1fr;
    gap: 0.5rem 0.75rem;
    align-items: center;
    padding-right: 0.5rem;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    transition:
        max-height 0.3s ease,
        opacity 0.3s ease,
        margin 0.3s ease;
    margin-top: 0;
}

dialog.modal .modal-advanced.expanded {
    max-height: 300px;
    opacity: 1;
    margin-top: 0.5rem;
}

dialog.modal .modal-advanced .label {
    grid-column: labels;
    color: var(--text-color-light, #888);
    font-size: 13px;
}

dialog.modal .modal-advanced .input,
dialog.modal .modal-advanced select,
dialog.modal .modal-advanced input:not([type="checkbox"]) {
    grid-column: controls;
    box-sizing: border-box;
    width: 100%;
    background: var(--stream-bg-color, #1a1a2e);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    color: var(--text-color, #ddd);
    padding: 4px 8px;
}

dialog.modal .modal-controls input[type="checkbox"],
dialog.modal .modal-advanced input[type="checkbox"] {
    grid-column: controls;
    justify-self: start;
    width: 16px;
    height: 16px;
    cursor: pointer;
    accent-color: var(--accent-color);
}

[data-theme="light"] dialog.modal .modal-advanced select,
[data-theme="light"] dialog.modal .modal-advanced input:not([type="checkbox"]) {
    background: #fff;
    border-color: rgba(0, 0, 0, 0.15);
    color: #333;
}

/* ── Settings buttons ── */
dialog.modal .modal-settings {
    display: flex;
    gap: 8px;
    justify-content: center;
    margin-top: 1rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--modal-divider);
}

dialog.modal .modal-settings button {
    border: 0.5px solid var(--text-color, #ddd);
    border-radius: 6px;
    background: transparent;
    color: var(--accent-color);
    padding: 6px 16px;
    cursor: pointer;
    white-space: nowrap;
}

dialog.modal .modal-settings button:hover {
    background: var(--device-list-hover-color, hsl(218, 17%, 18%));
}

/* Shared "got it" / confirm button for the bookmark (PortChangeModal) +
   service-first-run (ServiceFirstRunModal) modals. Matches the welcome modal's
   outline buttons — white outline + white text, transparent, mono font —
   replacing the browser-default look (the class previously had no CSS). */
.modal-button {
    border: 0.5px solid var(--text-color, #ddd);
    border-radius: 6px;
    background: transparent;
    color: var(--text-color, #ddd);
    padding: 8px 16px;
    cursor: pointer;
    font: inherit;
}

.modal-button:hover {
    background: var(--device-list-hover-color, hsl(218, 17%, 18%));
}

/* ── Status text (footer) ── */
dialog.modal .status-text {
    font-size: 13px;
}

dialog.modal .status-text.status-probing {
    color: var(--danger-color);
}

dialog.modal .status-text.status-ready {
    color: var(--success-color);
}

dialog.modal .status-text.status-error {
    color: var(--danger-color);
}

/* ── Connect button (footer) ── */
dialog.modal .connect-btn {
    border: 0.5px solid var(--text-color, #ddd);
    border-radius: 6px;
    background: transparent;
    color: var(--accent-color);
    padding: 6px 20px;
    cursor: pointer;
    white-space: nowrap;
}

dialog.modal .connect-btn:hover:not(:disabled) {
    background: var(--device-list-hover-color, hsl(218, 17%, 18%));
}

dialog.modal .connect-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ── Shell modal overrides ── */
dialog.modal.shell-modal .modal-frame {
    width: clamp(500px, 90vw, 1600px);
    max-height: 90vh;
}

dialog.modal .shell-warning {
    padding: 4px 1rem;
    font-size: 11px;
    color: var(--danger-color);
    border-bottom: 1px solid var(--modal-divider);
    flex-shrink: 0;
}

dialog.modal.shell-modal .modal-body {
    padding: 0;
    background: #000;
    min-height: 600px;
    position: relative;
    overflow: hidden;
}

dialog.modal .terminal-container {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

/* ── Connect modal overrides ── */
dialog.connect-modal .modal-frame {
    max-height: 90vh;
    width: fit-content;
    max-width: 95vw;
}

dialog.connect-modal .modal-body {
    padding: 0;
    display: flex;
    flex-direction: row;
    overflow: hidden;
    background: #000;
}

/* Stream layout (.device-view, .video, .control-buttons-list) lives in
 * ws-scrcpy.css and is shared with embed.html. The modal-body above is the
 * flex parent that gives .device-view its box.
 *
 * The modal still needs to cap the canvas layers: StreamClientScrcpy.getMaxSize()
 * bounds the canvas to window.innerWidth/Height, but the modal-frame is
 * constrained to max 95vw × 90vh. Without these caps, the canvas overflows the
 * frame and gets clipped by the modal-body's `overflow: hidden`. */
dialog.connect-modal .video-layer,
dialog.connect-modal .touch-layer {
    max-width: calc(95vw - 3.715rem);
    max-height: calc(90vh - 2.5rem);
}

/* ── List files modal overrides ── */
dialog.list-files-modal .modal-frame {
    width: clamp(500px, 70vw, 900px);
    max-height: 85vh;
}

dialog.list-files-modal .modal-body {
    padding: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* ── Settings modal ── */
/* Wider than the base modal: the Settings modal has accreted enough sections
   (service scope, updates, App-section) that long info/error text
   wrapped into many lines and pushed the content past max-height -> a vertical
   scrollbar. The extra horizontal room lets that text breathe (fewer wrapped
   lines -> shorter content). Scoped to .settings-modal so the small confirm
   modals (AdminConfirmModal etc.) stay narrow. */
dialog.settings-modal .modal-frame {
    width: clamp(460px, 66vw, 820px);
    /* 95vh (not the base 80vh / prior 85vh): the App + Updates + Service
       sections together are tall enough that 85vh forced an inner scrollbar on
       smaller viewports. 95vh leaves a ~2.5% margin top/bottom so the frame
       doesn't kiss the screen edges; long sections still scroll inside. */
    max-height: 95vh;
}

dialog.settings-modal .settings-section {
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--modal-divider);
}

dialog.settings-modal .settings-section:last-child {
    border-bottom: none;
}

dialog.settings-modal .settings-section-heading {
    font-size: 14px;
    font-weight: 600;
    margin: 0 0 0.5rem;
}

/*
 * v0.1.23-stable Settings layout: two-column CSS grid mirroring
 * .modal-controls in ConfigureScrcpy. Labels live on the left, every
 * control sits on the right at the same x-coordinate across all rows
 * and all sections. Section action buttons (save / check now / install)
 * live in .settings-section-footer which spans both columns.
 */
dialog.settings-modal .settings-section-body {
    display: grid;
    /* Labels fixed at 20rem so the controls boundary doesn't shift when
       dynamic label text changes (status messages, version strings) or
       when button text changes (install/uninstall states). Controls
       take 1fr (was 16rem + [end] 1fr buffer pre-2026-05-28). The fixed
       16rem controls column was sized against Windows font rendering
       ("not installed — install?" ≈ 231px at 13px monospace, fit) but
       wrapped on Linux 1920x1080 unscaled where the same monospace
       string renders ~10% wider. Flexing controls to 1fr gives Linux
       the breathing room without affecting Windows (controls hug left
       via .settings-control justify-content: flex-start, so the extra
       width on Windows is just empty space to the right of each control). */
    grid-template-columns: [labels] 20rem [controls] 1fr;
    align-items: center;
    gap: 0.5rem 1rem;
}

dialog.settings-modal .settings-row {
    display: contents;
}

/*
 * Error rows stretch out. The error message is rendered on the row's label,
 * which normally lives in the fixed 20rem [labels] column — too narrow for the
 * longer service/update errors. When a row's label carries .settings-status-error,
 * lift the whole row out of `display: contents` into a full-width block spanning
 * both columns so the error wraps across the full width; the retry/save button
 * drops beneath it. (Status notes already span via grid-column:1/-1 on a <p>.)
 */
dialog.settings-modal .settings-row:has(.settings-status-error) {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
    grid-column: 1 / -1;
}

dialog.settings-modal .settings-label {
    grid-column: labels;
    color: var(--text-color-light, #888);
    font-size: 13px;
    /* Equal row heights across the whole modal — sets the visual rhythm.
       Long labels (status messages, descriptive notes) wrap within the
       40% column rather than pushing the modal wide. */
    min-height: 32px;
    display: flex;
    align-items: center;
    line-height: 1.4;
    word-break: break-word;
    padding-right: 0.75rem;
}

/*
 * Right-column wrapper. Multi-control rows (radios, checkbox+text)
 * use this to lay them out in a flex row inside the grid cell.
 * min-height matches .settings-label so rows stay flush at 32px.
 * justify-content stays at flex-start (default) so controls hug the
 * left edge of the right column — buttons align with inputs above
 * them across all rows.
 */
dialog.settings-modal .settings-control {
    grid-column: controls;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
    min-height: 32px;
}

dialog.settings-modal .settings-control .settings-radio-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--text-color, #ddd);
    font-size: 13px;
    cursor: pointer;
    margin: 0;
}

/* Bright accent so the radio's selected dot stays visible even when the label
 * is muted (locked scope). Matches the app's checkboxes/range controls. The
 * radio is never `disabled` (Chromium desaturates accent-color on :disabled),
 * so this dot renders at full saturation in both the active and locked state. */
dialog.settings-modal .settings-control .settings-radio-label input[type="radio"] {
    accent-color: var(--accent-color);
}

/*
 * Locked radio (service scope while a service is installed): mute the whole
 * label so the read-only state reads at a glance, and block interaction with
 * pointer-events. We do NOT use the `disabled` attribute — Chromium
 * desaturates accent-color on :disabled controls, which hid the selected dot
 * (item 42). The radio stays enabled (accent-color renders) and is locked via
 * this class (set by lockScopeRadioControl) plus tabindex=-1 on the input.
 */
dialog.settings-modal .settings-control .settings-radio-label.settings-radio-locked {
    opacity: 0.65;
    cursor: not-allowed;
    pointer-events: none;
}

/*
 * Apply-update button styling — green outline + green text — mirroring
 * the home-page UpdateButton chip's "ready" state. Higher specificity
 * than .settings-btn-primary so it overrides when both classes are
 * present (used in the Updates section's dual-purpose button).
 */
dialog.modal .settings-btn.settings-btn-ready {
    color: var(--success-color);
    border-color: var(--success-color);
}

dialog.modal .settings-btn.settings-btn-ready:hover {
    background: rgba(var(--success-rgb), 0.08);
}

dialog.settings-modal .settings-input {
    box-sizing: border-box;
    background: var(--stream-bg-color, #1a1a2e);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    color: var(--text-color, #ddd);
    padding: 4px 8px;
    width: 100%;
    max-width: 240px;
    font: inherit;
}

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

[data-theme="light"] dialog.settings-modal .settings-input {
    background: #fff;
    border-color: rgba(0, 0, 0, 0.15);
}

dialog.modal .settings-btn {
    border: 0.5px solid var(--text-color, #ddd);
    border-radius: 6px;
    background: transparent;
    color: var(--text-color, #ddd);
    padding: 6px 14px;
    cursor: pointer;
    font: inherit;
}

dialog.modal .settings-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

dialog.modal .settings-btn-primary {
    color: var(--accent-color);
    border-color: var(--accent-color);
}

dialog.modal .settings-btn-danger {
    color: var(--danger-color);
    border-color: var(--danger-color);
}

dialog.modal .settings-btn-danger-outline {
    color: var(--danger-color);
    border-color: var(--danger-color);
    background: transparent;
}

dialog.settings-modal .settings-status {
    margin: 0.25rem 0 0;
    /* Notes + status read as sub-text of the setting they annotate: indented +
       bold-italic to set them apart. Errors add red via .settings-status-error
       (layered on top, so they inherit this indent/weight/style). */
    padding-left: 1.25rem;
    color: var(--text-color-light, #888);
    font-size: 13px;
    min-height: 1em;
    font-style: italic;
    font-weight: 600;
}

dialog.settings-modal .settings-status-error {
    color: var(--danger-color);
}

/*
 * Status text turns green when an update is ready to apply, mirroring
 * the .settings-btn-ready button color (var(--success-color)) so the description
 * and action visually pair — "update: vX.Y.Z" + green "apply update"
 * button reinforce one another.
 */
dialog.settings-modal .settings-status-ready {
    color: var(--success-color);
}

dialog.settings-modal .settings-stub-note {
    margin: 0;
    padding-left: 1.25rem;
    color: var(--text-color-light, #888);
    font-size: 13px;
    font-style: italic;
    font-weight: 600;
}

/*
 * v0.1.23-stable: per-section legacy layout overrides removed.
 * All sections now use the unified .settings-section-body grid above.
 * Section-specific input widths handled inline (max-width on the
 * .settings-input itself).
 */

/* --- Service operation modal (Phase 3) --- */

dialog.service-operation-modal .modal-header-controls {
    display: none;
}

dialog.service-operation-modal .modal-body {
    text-align: center;
    padding: 1rem 0;
}

.service-operation-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--border-muted);
    border-top-color: var(--accent-color);
    border-radius: 50%;
    margin: 0 auto 1rem;
    animation: service-op-spin 0.8s linear infinite;
}

@keyframes service-op-spin {
    to {
        transform: rotate(360deg);
    }
}

/* Dark theme (default) */
:root,
[data-theme="dark"] {
    --device-border-color: #444444;
    --device-list-default-color: #252525;
    --device-list-hover-color: hsl(218, 17%, 18%);
    --section-border-color: #5a5a5a;
}

/* Light theme */
[data-theme="light"] {
    --device-border-color: #c0c6cc;
    --device-list-default-color: #ffffff;
    --device-list-hover-color: hsl(218, 67%, 95%);
    --section-border-color: #8f959d;
}

body.list {
    position: static;
    height: auto;
    width: auto;
    overflow: auto;
}

#devices {
    padding: 20px;
    width: 100%;
    box-sizing: border-box;
}

body.list #devices {
    border: 1.5px solid var(--section-border-color, #5a5a5a);
    border-radius: 8px;
    margin-top: 35px;
    margin-bottom: 35px;
}

body.stream #devices {
    background-color: var(--device-list-default-color);
    opacity: 0.8;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 3;
    height: 100%;
    width: 100%;
}

body.list #device_list_menu {
    display: none;
}

/* biome-ignore lint/style/noDescendingSpecificity: intentional source order; reordering would change the cascade */
#device_list_menu {
    display: block;
    position: absolute;
    bottom: 0;
    left: 0;
    z-index: 4;
}

/* Device list container */
#devices .device-list {
    font-family: var(--font-mono);
    font-size: var(--font-size);
}

#devices .device-list button {
    font-family: var(--font-mono);
    font-size: var(--font-size);
    color: var(--button-text-color);
}

/* Device card */
#devices .device-list div.device {
    border: 1px solid var(--device-border-color);
    border-radius: 8px;
    /* Symmetric horizontal padding so the dark-grey content area sits
       equidistant from the left and right card borders. (The active
       device's 3px green / not-active 3px red left border is outside
       this padding and replaces the 1px default left border — that
       asymmetry is intentional, not part of the content padding.) */
    padding: 8px 14px;
    background: var(--device-list-default-color);
    transition:
        border-color 0.15s,
        box-shadow 0.15s;
}

#devices .device-list div.device:hover {
    border-color: var(--device-list-hover-color);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

#devices .device-list div.device.active {
    border-left: 3px solid var(--success-color);
}

#devices .device-list div.device.not-active {
    border-left: 3px solid var(--danger-color);
    opacity: 0.7;
}

/* Device info table */
#devices table.device-info {
    margin-bottom: 0;
    border-collapse: collapse;
    font-size: 13px;
    width: 100%;
}

#devices .device-info td {
    padding: 2px 0;
    vertical-align: top;
}

#devices .device-info td.device-label {
    color: var(--text-color-light, #888);
    padding-right: 24px;
    white-space: nowrap;
    width: 1%;
}

#devices .device-info tr:first-child td:last-child {
    font-size: 15px;
    font-weight: 600;
}

#devices .device-serial {
    font-family: var(--font-mono);
    font-size: 12px;
}

/* Device action buttons (disconnect + sleep/wake) — sibling row to the
   device-info table. 2-column grid with outer borders on all four sides;
   border-top is the row divider shared with the services grid above.
   Inner vertical divider is drawn as a border-right on the first
   .action-cell (an element edge → integer pixel → crisp rendering, no
   sub-pixel AA artifacts). */
#devices .device-list div.device .device-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto;
    gap: 0;
    border-top: 1px solid var(--device-border-color);
    border-left: 1px solid var(--device-border-color);
    border-right: 1px solid var(--device-border-color);
    border-bottom: 1px solid var(--device-border-color);
    padding: 0;
}

/* Each action button lives inside an .action-cell that stretches to
   fill its grid cell. The cell's right border (first cell only) is
   the inner vertical divider. */
#devices .device-list div.device .device-actions > .action-cell {
    justify-self: stretch;
    align-self: stretch;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding: 0 8px;
    height: 36px;
    box-sizing: border-box;
}

#devices .device-list div.device .device-actions > .action-cell:nth-child(1) {
    border-right: 1px solid var(--device-border-color);
}

#devices .device-list div.device .device-actions:empty {
    display: none;
}

#devices .device-list .disconnect-btn {
    border: 0.5px solid var(--danger-color);
    border-radius: 6px;
    background: transparent;
    color: var(--danger-color);
    padding: 2px 8px;
    font-size: var(--font-size);
    cursor: pointer;
    white-space: nowrap;
    min-width: 95px;
    text-align: center;
}

#devices .device-list .disconnect-btn:hover:not(:disabled) {
    background: var(--danger-hover-bg);
}

#devices .device-list .disconnect-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Sleep/wake button */
#devices .device-list .sleep-wake-btn {
    border-radius: 6px;
    background: transparent;
    padding: 2px 8px;
    font-size: var(--font-size);
    cursor: pointer;
    white-space: nowrap;
    min-width: 70px;
    text-align: center;
}

#devices .device-list .sleep-wake-btn.state-off {
    border: 0.5px solid var(--success-color);
    color: var(--success-color);
}

#devices .device-list .sleep-wake-btn.state-on {
    border: 0.5px solid var(--danger-color);
    color: var(--danger-color);
}

#devices .device-list .sleep-wake-btn.state-unknown {
    border: 0.5px solid var(--text-color-light, #888);
    color: var(--text-color-light, #888);
}

#devices .device-list .sleep-wake-btn.state-off:hover:not(:disabled) {
    background: var(--success-hover-bg);
}

#devices .device-list .sleep-wake-btn.state-on:hover:not(:disabled) {
    background: var(--danger-hover-bg);
}

#devices .device-list .sleep-wake-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Modal-launch buttons — 2x2 grid filled column-first:
   left col = [shell][list files], right col = [connect][configure stream].
   Outer borders on left/right; border-top is the row divider shared with
   the device-info table above. Inner cross dividers are drawn as
   border-right / border-bottom on the cells (the .desc-block items
   themselves stretch to fill their cell). Borders sit at element edges
   (integer pixels) so they render crisply, unlike 1px grid-track
   dividers which can land on sub-pixel positions and anti-alias. */
#devices .device-list div.device .services {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto;
    grid-auto-flow: column;
    gap: 0;
    border-top: 1px solid var(--device-border-color);
    border-left: 1px solid var(--device-border-color);
    border-right: 1px solid var(--device-border-color);
    padding: 0;
}

/* Each .desc-block (modal-launch cell) stretches to fill its grid cell.
   Padding gives breathing room around the inner button (which carries
   the blue border — moved off .desc-block so .desc-block can act as
   the gray-bordered cell). */
#devices .device-list div.device .services > .desc-block {
    justify-self: stretch;
    align-self: stretch;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding: 0 8px;
    margin: 0;
    border: none;
    border-radius: 0;
    overflow: visible;
    height: 36px;
    box-sizing: border-box;
}

/* Cross dividers via cell borders. With grid-auto-flow: column over 2
   rows: nth-child(1)=top-left, (2)=bottom-left, (3)=top-right,
   (4)=bottom-right. Top-left gets right + bottom; bottom-left gets
   right; top-right gets bottom; bottom-right gets none. For inactive
   devices (3 buttons, no `connect`), nth-child(3) is `config stream`
   sitting at top-right with border-bottom — that's fine; the line
   ends at the cell boundary regardless of whether anything below
   occupies the empty bottom-right cell. */
#devices .device-list div.device .services > .desc-block:nth-child(1) {
    border-right: 1px solid var(--device-border-color);
    border-bottom: 1px solid var(--device-border-color);
}
#devices .device-list div.device .services > .desc-block:nth-child(2) {
    border-right: 1px solid var(--device-border-color);
}
#devices .device-list div.device .services > .desc-block:nth-child(3) {
    border-bottom: 1px solid var(--device-border-color);
}

#devices .device-list div.device .services:last-child {
    padding-bottom: 0;
}

#devices .device-list div.device .services:first-of-type {
    margin-top: 8px;
}

/* .desc-block default styles for places OUTSIDE the services grid
   (kept for any other consumer). Inside the services grid, .desc-block
   acts as a cell; the blue border is moved to the inner button/link. */
#devices .device-list div.desc-block {
    margin: 0;
    display: inline-flex;
    align-items: center;
    border-radius: 6px;
    overflow: hidden;
    white-space: nowrap;
}

#devices .device-list div.desc-block.hidden {
    display: none;
}

/* Inactive device styling */
#devices .device-list .device.not-active div.desc-block button > svg > path {
    fill: var(--text-color-light);
}

#devices .device-list .device.not-active {
    color: var(--text-color-light);
}

#devices .device-list .device.not-active a {
    color: var(--link-color-light);
}

#devices .device-list .device.not-active a:visited {
    color: var(--link-color-visited-light);
}

/* Action buttons and links styled as buttons. Blue border moved here
   from .desc-block so the parent .desc-block can act as a grid cell
   (with gray cell borders) without an outer blue border around it. */
/* biome-ignore-start lint/style/noDescendingSpecificity: intentional source order; reordering would change the cascade */
#devices .device-list div.desc-block .action-button,
#devices .device-list div.desc-block a {
    border: 0.5px solid var(--accent-color);
    border-radius: 6px;
    background-color: transparent;
    color: var(--accent-color);
    text-decoration: none;
    padding: 0.15rem 0.5rem;
    font-family: var(--font-mono);
    font-size: var(--font-size);
    cursor: pointer;
    display: inline-block;
}
/* biome-ignore-end lint/style/noDescendingSpecificity: end desc-block action selectors */

#devices .device-list div.desc-block .action-button:hover,
#devices .device-list div.desc-block a:hover {
    background-color: var(--device-list-hover-color);
}

/* Tracker block — inherits grid layout so device cards flow correctly */
#devices .device-list > div {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap: 12px;
}

/* Tracker name header */
#devices .tracker-name {
    padding: 0 0 8px 0;
    font-size: 18px;
    font-weight: 600;
    color: var(--text-color, #eee);
    grid-column: 1 / -1;
}

/* Empty-state card inside the tracker block spans the full row instead of
 * sitting in a single grid cell — matches the look of the scan-network
 * info box below Available Network Devices. */
#devices .empty-state-card {
    grid-column: 1 / -1;
}

/* Device name row — inline label editor */
#devices .device-name-cell {
    position: relative;
    /* biome-ignore lint/complexity/noImportantStyles: targeted override of the row's base padding for the inline name-editor affordance */
    padding-right: 28px !important;
}

#devices .device-name-text {
    font-size: 15px;
    font-weight: 600;
}

#devices .device-name-text.unnamed {
    font-style: italic;
    opacity: 0.5;
    font-weight: 400;
}

#devices .device-name-edit-btn {
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    background: transparent;
    border: none;
    cursor: pointer;
    color: var(--text-color-light, #888);
    padding: 2px;
    line-height: 1;
    opacity: 0.6;
}

#devices .device-name-edit-btn:hover {
    opacity: 1;
    color: var(--text-color, #ddd);
}

#devices .device-name-input {
    width: calc(100% - 24px);
    background: var(--stream-bg-color, #333);
    border: 1px solid var(--text-color-light, #888);
    border-radius: 4px;
    color: var(--text-color, #ddd);
    font-size: 14px;
    padding: 2px 6px;
}

#devices .device-name-input:focus {
    outline: none;
    border-color: var(--accent-color);
}

a.link-stream .kind-icon {
    margin-left: 6px;
    width: 14px;
    height: 14px;
    vertical-align: middle;
}

