/* =================================== */
/* MAIN CONTENT AREA STYLES */
/* =================================== */

.main-content {
    flex: 1;
    /* A flex item's `min-width` defaults to `auto`, meaning "never shrink
       below your content" — so one wide table anywhere on any page widened
       this element past the viewport, and the horizontal scrollbar that
       appeared dragged the top bar and the sidebar sideways with it. An
       `overflow-x: auto` container INSIDE such an item never engages, because
       the item simply grows instead.

       Setting it to 0 lets the item take the viewport width and makes inner
       scroll containers do their job. Found via the Pmetrics roster (12
       metric columns, 1949px intrinsic); it was never specific to that page. */
    min-width: 0;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background-color: rgb(var(--background));
    margin-left: 230px;
    transition: margin-left 0.3s ease;
}

.sidebar.collapsed + .main-content {
    margin-left: 60px;
}

.top-bar {
    background-color: rgb(var(--card));
    border-bottom: 1px solid rgb(var(--border));
    padding: 1rem 2.4rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    min-height: 72.8px;
    position: sticky;
    top: 0;
    /* Bootstrap sticky tier — must stay below modal-backdrop (1050) and
       modal (1055) so popups render above the top bar. Dropdowns inside
       the top bar use a higher z-index and still appear above it. */
    z-index: 1020;
}

.mobile-menu-toggle {
    border: none;
    background: none;
    color: rgb(var(--foreground));
    font-size: 1.2rem;
}

.page-title {
    font-size: 1.4rem;
    font-weight: 600;
    color: rgb(var(--foreground));
    /* The title is the only flexible thing in this row, and it did not flex:
       at 320px "Specifications" is 176px wide, which pushed the action cluster
       14px off the right edge and gave the whole page a horizontal scroll.
       Shorter titles fit, so the page that broke was whichever one happened to
       have the longest name — the layout was always this fragile.

       Clamped at DISPLAY time, which is the allowed form: the text is intact
       in the DOM and the `title` attribute, only the rendering is shortened. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.page-title i {
    padding-right: 4px;
}

.top-bar-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    /* Pinned: the theme toggle and the account menu are the same size on every
       page, so they should never be what gives way. */
    flex-shrink: 0;
}

/* WebSocket Status Indicator */
.ws-status-indicator {
    display: flex;
    align-items: center;
    padding: 0 0.5rem;
    cursor: help;
}

.ws-status-indicator .status-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background-color: rgb(var(--muted));
    transition: background-color 0.3s ease, box-shadow 0.3s ease;
    animation: pulse 2s ease-in-out infinite;
}

/* Connected state - green.
   The dot is the only thing reporting the connection, and it is 10px, so it
   uses the readable variant rather than the fill: --success on the top bar is
   2.28:1, under the 3:1 a graphical object needs to register at all. The ring
   below stays on the base colour — it is a halo, not the signal. */
.ws-status-indicator.connected .status-dot {
    background-color: rgb(var(--success-text));
    box-shadow: 0 0 0 2px rgb(var(--success) / 0.2);
    animation: pulse-connected 2s ease-in-out infinite;
}

/* Disconnected state - red */
.ws-status-indicator.disconnected .status-dot,
.ws-status-indicator.error .status-dot {
    background-color: rgb(var(--destructive-text));
    box-shadow: 0 0 0 2px rgb(var(--destructive) / 0.2);
    animation: pulse-error 2s ease-in-out infinite;
}

/* Warning state - yellow (no heartbeat) */
.ws-status-indicator.warning .status-dot {
    background-color: rgb(var(--warning-text));
    box-shadow: 0 0 0 2px rgb(var(--warning) / 0.2);
    animation: pulse-warning 2s ease-in-out infinite;
}

/* Pulse animations */
@keyframes pulse-connected {
    0%, 100% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.1);
        opacity: 0.8;
    }
}

@keyframes pulse-error {
    0%, 100% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.2);
        opacity: 0.9;
    }
}

@keyframes pulse-warning {
    0%, 100% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.15);
        opacity: 0.85;
    }
}

.content-container {
    flex: 1;
    padding: 1.5rem;
    overflow-y: auto;
}

/* =================================== */
/* TASKS HEADER STYLES */
/* =================================== */

.tasks-header {
    padding: 0.5rem 0;
}

.tasks-header .search-box {
    position: relative;
}

.tasks-header .search-icon {
    position: absolute;
    left: 0.75rem;
    top: 50%;
    transform: translateY(-50%);
    color: rgb(var(--muted-foreground));
    font-size: 0.8rem;
    pointer-events: none;
}

.tasks-header .form-select-sm {
    font-size: 0.8rem;
    padding: 0.35rem 2rem 0.35rem 0.75rem;
    border-radius: 6px;
    border-color: rgb(var(--border));
    background-color: rgb(var(--background));
    color: rgb(var(--foreground));
}

.tasks-header .form-control-sm {
    font-size: 0.8rem;
    border-radius: 6px;
    border-color: rgb(var(--border));
    background-color: rgb(var(--background));
    color: rgb(var(--foreground));
}

.tasks-header .form-control-sm:focus,
.tasks-header .form-select-sm:focus {
    border-color: rgb(var(--primary));
    box-shadow: 0 0 0 2px rgb(var(--primary) / 0.1);
}

/* ============================================
   EXECUTION NAME CELL
   ============================================ */

/* An execution name is built from user input — a DataKit question, an issue
   title — and is never cut short in the database (executions.name is TEXT).
   The clamp belongs here, at display time: the row stays two lines tall
   whatever the length, and the full text is on the title attribute, so
   nothing is lost to see it. */
.execution-name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------------
   Chart containers
   ---------------------------------------------------------------------------
   Chart.js is used site-wide with `maintainAspectRatio: false`, which makes it
   size its canvas to the parent's height. A parent whose height is `auto` is
   therefore sized BY the canvas it is sizing — a loop with no fixed point that
   adds pixels on every re-layout. On /workers that took the Queue Distribution
   doughnut from 11,585px to 47,534px in 70 seconds and made the page unusable.

   `position: relative` is the other half of Chart.js's contract: it is what the
   library measures against, and what an empty-state overlay anchors to.

   A `height="200"` attribute on the canvas does NOT do this. It sizes the
   drawing buffer, which Chart.js overwrites on its first resize — which is why
   three pages carried the bug while appearing to have handled it.

   Gate: tests/test_chart_containers.py
   --------------------------------------------------------------------------- */
.chart-box {
    position: relative;
    width: 100%;
    height: 240px;
}
.chart-box-sm { height: 180px; }
.chart-box-lg { height: 320px; }

/* Belt and braces: if a future option change re-enables aspect-ratio sizing,
   the canvas still cannot exceed the box it was given. */
.chart-box > canvas {
    max-height: 100%;
}

/* An empty-state message for a chart is an overlay, never a sibling — as a
   sibling it adds to the very height the chart measures itself against. */
.chart-empty {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    color: rgb(var(--muted-foreground));
    pointer-events: none;
}

/* ---------------------------------------------------------------------------
   Segmented control  (project UI convention: tabs are pv-segmented, not
   Bootstrap nav-tabs)
   ---------------------------------------------------------------------------
   A recessed track with a raised chip on the selected segment, rather than a
   row of tabs with one underlined. The visual treatment is the corrected one
   from the engineering page: the chip's ring is a TOKEN, not a black shadow.
   The older copies of this control (builds_notarize.html, admin_bitrise.css,
   evalkit-page.js) use `0 1px 3px rgb(0 0 0 / 0.08)`, which is invisible on a
   near-black card — dark mode got a chip with no edge at all.

   The chip is --card on a --muted track: 1.10:1 light, 1.36:1 dark, nowhere
   near enough on its own. Selection is carried by the TEXT stepping from
   --muted-foreground to --foreground (5.5:1 -> 17.9:1), with the ring merely
   outlining the chip.

   Lit pages must still restate this inside their shadow root — a stylesheet
   cannot cross that boundary, though the tokens it is built from can.
   --------------------------------------------------------------------------- */
.pv-segmented {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    background: rgb(var(--muted));
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius, 8px);
    flex-wrap: wrap;
}

.pv-segmented .seg-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    background: transparent;
    border: none;
    color: rgb(var(--muted-foreground));
    font-size: 0.8rem;
    font-weight: 500;
    padding: 0.36rem 0.95rem;
    border-radius: calc(var(--radius, 8px) - 2px);
    cursor: pointer;
    white-space: nowrap;
    text-decoration: none;
    transition: background 0.15s, color 0.15s;
}
.pv-segmented .seg-btn:hover:not(.active) { color: rgb(var(--foreground)); }
.pv-segmented .seg-btn.active {
    background: rgb(var(--card));
    color: rgb(var(--foreground));
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.10), 0 0 0 1px rgb(var(--foreground) / 0.10);
}
.pv-segmented .seg-btn:focus-visible {
    outline: 2px solid rgb(var(--primary));
    outline-offset: 1px;
}

/* Bootstrap's Tab plugin needs `.nav` on the track and `.nav-link` on each
   toggle to find its siblings, so both stay in the markup on pages that keep
   the plugin. Its tab chrome has to be undone here or it fights the chip. */
.pv-segmented.nav { flex-wrap: wrap; }
.pv-segmented .nav-link {
    border: none;
    margin: 0;
    background: transparent;
    isolation: auto;
}
.pv-segmented .nav-link.active,
.pv-segmented .nav-link:focus,
.pv-segmented .nav-link:hover { border: none; }

/* A count that belongs to one segment. Sits inside the chip, so it takes its
   contrast from the chip rather than the track. */
.seg-btn-count {
    font-size: 0.68rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background: rgb(var(--foreground) / 0.10);
    color: inherit;
}
.seg-btn.active .seg-btn-count { background: rgb(var(--foreground) / 0.09); }
