/* ==========================================================================
   LogPose — control vocabulary.
   One shape language: 10px radius on rectangular controls, full pills on
   chips, 14px on floating surfaces. Every interactive element ships the whole
   state set (rest / hover / focus-visible / active / disabled / selected).
   ========================================================================== */

/* ------------------------------------------------------------------ buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 34px; padding: 0 14px;
  border: 1px solid transparent; border-radius: var(--r-md);
  background: none; color: inherit;
  font: inherit; font-size: var(--fs-sm); font-weight: 550; line-height: 1;
  letter-spacing: -0.006em; white-space: nowrap; cursor: pointer;
  transition: background-color var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out),
              box-shadow var(--t-fast) var(--ease-out),
              transform var(--t-instant) var(--ease-out);
}
.btn .lucide { width: 15px; height: 15px; }
.btn:active { transform: scale(0.975); }
.btn:disabled, .btn.is-disabled { opacity: 0.45; pointer-events: none; }

.btn-primary { background: var(--accent); color: var(--accent-fg); box-shadow: var(--shadow-1); }
.btn-primary:hover { background: var(--accent-hover); text-decoration: none; }

.btn-outline {
  background: var(--bg-surface); color: var(--fg);
  border-color: var(--line-strong); box-shadow: var(--shadow-1);
}
.btn-outline:hover { background: var(--bg-raised); border-color: var(--fg-faint); text-decoration: none; }

.btn-ghost { color: var(--fg-muted); }
.btn-ghost:hover { background: var(--bg-raised); color: var(--fg-strong); text-decoration: none; }

.btn-sm { height: 30px; padding: 0 11px; font-size: var(--fs-xs); }

/* Square icon-only button, same shape language as .btn. */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--bg-surface); color: var(--fg-muted); cursor: pointer;
  box-shadow: var(--shadow-1);
  transition: background-color var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out),
              transform var(--t-instant) var(--ease-out);
}
.icon-btn:hover { background: var(--bg-raised); color: var(--fg-strong); border-color: var(--fg-faint); }
.icon-btn:active { transform: scale(0.94); }
.icon-btn .lucide { width: 16px; height: 16px; }

.icon-btn-plain { border-color: transparent; background: transparent; box-shadow: none; }
.icon-btn-plain:hover { background: var(--bg-raised); border-color: var(--line); }
.icon-btn-sm { width: 28px; height: 28px; border-radius: var(--r-sm); }
.icon-btn-sm .lucide { width: 15px; height: 15px; }
.icon-btn-xs { width: 24px; height: 24px; border-radius: 7px; }
.icon-btn-xs .lucide { width: 13px; height: 13px; }
.icon-btn.is-ok { color: var(--ok); }

/* ------------------------------------------------------------------- inputs */
.input {
  height: 34px; padding: 0 11px;
  background: var(--bg-surface); color: var(--fg);
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  font: inherit; font-size: var(--fs-sm);
  box-shadow: var(--shadow-1);
  transition: border-color var(--t-fast) var(--ease-out),
              box-shadow var(--t-fast) var(--ease-out),
              background-color var(--t-fast) var(--ease-out);
}
.input::placeholder { color: var(--fg-faint); }
.input:hover { border-color: var(--fg-faint); }
.input:focus,
.input:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

/* Search field: icon inside the well, clear button appears once it has text. */
.searchbox { position: relative; display: inline-flex; align-items: center; flex: 1 1 260px; min-width: 180px; }
.searchbox .lucide-search {
  position: absolute; left: 11px; width: 15px; height: 15px;
  color: var(--fg-faint); pointer-events: none;
  transition: color var(--t-fast) var(--ease-out);
}
.searchbox .input { width: 100%; padding-left: 33px; padding-right: 30px; }
.searchbox:focus-within .lucide-search { color: var(--accent); }
.searchbox .search-clear {
  position: absolute; right: 5px;
  width: 24px; height: 24px; border-radius: 7px; border: none;
  background: transparent; color: var(--fg-faint); cursor: pointer;
  display: none; align-items: center; justify-content: center;
  transition: background-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.searchbox .search-clear .lucide { width: 13px; height: 13px; }
.searchbox .search-clear:hover { background: var(--bg-raised); color: var(--fg-strong); }
.searchbox.has-value .search-clear { display: inline-flex; }

/* Date input: native picker kept (standard affordance), chrome restyled. */
.input[type="date"] { padding-right: 8px; font-variant-numeric: tabular-nums; }
.input[type="date"]::-webkit-calendar-picker-indicator {
  opacity: 0.45; cursor: pointer;
  transition: opacity var(--t-fast) var(--ease-out);
}
.input[type="date"]:hover::-webkit-calendar-picker-indicator { opacity: 0.85; }
:root[data-theme="dark"] .input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1); }

/* Native select fallback (used where a full listbox is overkill). */
.select-wrap { position: relative; display: inline-flex; }
.select {
  appearance: none; -webkit-appearance: none;
  height: 34px; padding: 0 30px 0 11px; min-width: 120px; cursor: pointer;
  background: var(--bg-surface); color: var(--fg);
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  font: inherit; font-size: var(--fs-sm);
  box-shadow: var(--shadow-1);
  transition: border-color var(--t-fast) var(--ease-out), box-shadow var(--t-fast) var(--ease-out);
}
.select:hover { border-color: var(--fg-faint); }
.select:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }
.select-wrap::after {
  content: ""; position: absolute; right: 10px; top: 50%;
  width: 13px; height: 13px; transform: translateY(-50%); pointer-events: none;
  background: var(--fg-faint);
  -webkit-mask: var(--icon-chevron) center / 13px no-repeat;
  mask: var(--icon-chevron) center / 13px no-repeat;
}
.select-sm { height: 28px; min-width: 0; padding: 0 26px 0 9px; font-size: var(--fs-xs); }
.select-wrap:has(.select-sm)::after { right: 8px; width: 11px; height: 11px; }

/* ------------------------------------------------------------------- switch */
.switch {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  user-select: none; font-size: var(--fs-xs); color: var(--fg-muted);
  transition: color var(--t-fast) var(--ease-out);
}
.switch:hover { color: var(--fg-strong); }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
  position: relative; width: 32px; height: 18px; border-radius: var(--r-pill);
  background: var(--bg-sunken); border: 1px solid var(--line-strong);
  transition: background-color var(--t-base) var(--ease-out), border-color var(--t-base) var(--ease-out);
}
.switch .track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--fg-faint); box-shadow: var(--shadow-1);
  transition: transform var(--t-base) var(--ease-out), background-color var(--t-base) var(--ease-out);
}
.switch input:checked + .track { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .track::after { transform: translateX(14px); background: var(--accent-fg); }
.switch input:focus-visible + .track { box-shadow: 0 0 0 3px var(--accent-wash); }
.switch input:checked ~ .switch-label { color: var(--fg-strong); }

/* Live indicator dot that breathes only while auto-refresh is armed. */
.live-dot {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--fg-faint);
  transition: background-color var(--t-base) var(--ease-out);
}
.is-live .live-dot { background: var(--ok); animation: live-pulse 2.2s var(--ease-both) infinite; }
@keyframes live-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklab, var(--ok) 55%, transparent); }
  60%      { box-shadow: 0 0 0 5px color-mix(in oklab, var(--ok) 0%, transparent); }
}

/* ================================================================== chips ==
   Service chips are the legend AND the filter, in one control.

   Semantics (deliberately chosen so the resting state needs no explanation):
     - nothing selected  -> no filter at all, every service streams through
     - clicking a chip   -> adds it to the filter set (narrowing)
     - clicking it again -> removes it; emptying the set returns to "all"
     - alt / meta click  -> solo: that service only
     - the "All" chip    -> clears the set

   The chip's own colour is the service's colour, so scanning the stream and
   scanning the filter use exactly the same visual key.
   ========================================================================== */
.chipbar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  min-width: 0;
}
.chip {
  --chip-color: var(--fg-faint);
  display: inline-flex; align-items: center; gap: 7px;
  height: 27px; padding: 0 11px 0 9px;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--bg-surface); color: var(--fg-muted);
  font: inherit; font-size: var(--fs-xs); font-weight: 550; letter-spacing: -0.004em;
  white-space: nowrap; cursor: pointer;
  transition: background-color var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out),
              opacity var(--t-fast) var(--ease-out),
              transform var(--t-instant) var(--ease-out);
}
.chip-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--chip-color);
  box-shadow: 0 0 0 0 color-mix(in oklab, var(--chip-color) 40%, transparent);
  transition: box-shadow var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out);
}
.chip:hover {
  background: var(--bg-raised); color: var(--fg-strong);
  border-color: color-mix(in oklab, var(--chip-color) 45%, var(--line));
}
.chip:hover .chip-dot { box-shadow: 0 0 0 3px color-mix(in oklab, var(--chip-color) 18%, transparent); }
.chip:active { transform: scale(0.96); }

.chip[aria-pressed="true"] {
  background: color-mix(in oklab, var(--chip-color) 14%, var(--bg-surface));
  border-color: color-mix(in oklab, var(--chip-color) 52%, transparent);
  color: color-mix(in oklab, var(--chip-color) 72%, var(--fg-strong));
  font-weight: 600;
}
.chip[aria-pressed="true"] .chip-dot { transform: scale(1.15); }

/* When a filter set exists, unselected services recede — they are the ones
   currently being held back. */
.chipbar.is-filtering .chip[aria-pressed="false"] { opacity: 0.5; }
.chipbar.is-filtering .chip[aria-pressed="false"]:hover { opacity: 1; }

.chip-count {
  padding: 0 5px; border-radius: var(--r-pill);
  background: var(--bg-sunken); color: var(--fg-faint);
  font-size: var(--fs-2xs); font-weight: 600; font-variant-numeric: tabular-nums;
  line-height: 15px;
}
.chip[aria-pressed="true"] .chip-count {
  background: color-mix(in oklab, var(--chip-color) 22%, transparent);
  color: inherit;
}

/* The reset chip: active exactly when the filter set is empty. */
.chip-all { --chip-color: var(--accent); padding-left: 10px; }
.chip-all .lucide { width: 13px; height: 13px; }

/* ============================================================== listbox ===
   Custom single-select used for Log level. A native <select> cannot render an
   icon + a per-option colour, and this control's whole job is to make severity
   readable at a glance. The hidden <input> keeps it a normal form field, so
   the server contract is unchanged.
   ========================================================================== */
.listbox { position: relative; display: inline-flex; }

.listbox-trigger {
  display: inline-flex; align-items: center; gap: 8px;
  height: 34px; padding: 0 9px 0 11px; min-width: 148px;
  background: var(--bg-surface); color: var(--fg);
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
  font: inherit; font-size: var(--fs-sm); font-weight: 550;
  cursor: pointer; text-align: left;
  transition: border-color var(--t-fast) var(--ease-out),
              box-shadow var(--t-fast) var(--ease-out),
              background-color var(--t-fast) var(--ease-out);
}
.listbox-trigger:hover { border-color: var(--fg-faint); background: var(--bg-raised); }
.listbox-trigger:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }
.listbox-trigger[aria-expanded="true"] { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }
.listbox-value { flex: 1; }
.listbox-caret {
  width: 14px; height: 14px; color: var(--fg-faint); flex: none;
  transition: transform var(--t-base) var(--ease-out);
}
.listbox-trigger[aria-expanded="true"] .listbox-caret { transform: rotate(180deg); }

/* Severity tinting flows from a single data attribute on trigger and options. */
[data-tone="debug"]    { --tone: var(--lv-debug); }
[data-tone="info"]     { --tone: var(--lv-info); }
[data-tone="warning"]  { --tone: var(--lv-warning); }
[data-tone="error"]    { --tone: var(--lv-error); }
[data-tone="critical"] { --tone: var(--lv-critical); }
[data-tone="running"]   { --tone: var(--st-running); }
[data-tone="completed"] { --tone: var(--st-completed); }
[data-tone="failed"]    { --tone: var(--st-failed); }
[data-tone="cancelled"] { --tone: var(--st-cancelled); }

.listbox-trigger[data-tone] .listbox-icon,
.listbox-trigger[data-tone] .listbox-value { color: var(--tone, var(--fg)); }
.listbox-icon { display: inline-flex; color: var(--fg-faint); }
.listbox-icon .lucide { width: 15px; height: 15px; }

/* The popover is real glass: blurred, saturated, with a bright top lip and a
   deep tinted shadow. It floats above content, so the blur is bounded and
   cheap — the one place this material is warranted. */
.listbox-popover {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  min-width: 100%; width: max-content; max-width: 280px;
  padding: 5px;
  background: var(--bg-glass);
  backdrop-filter: blur(28px) saturate(200%);
  -webkit-backdrop-filter: blur(28px) saturate(200%);
  border: 1px solid var(--line-glass);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3), var(--inner-lip);
  transform-origin: top left;
  opacity: 0; transform: translateY(-6px) scale(0.96); filter: blur(3px);
  pointer-events: none;
  transition: opacity var(--t-fast) var(--ease-in),
              transform var(--t-fast) var(--ease-in),
              filter var(--t-fast) var(--ease-in);
}
.listbox.is-open .listbox-popover {
  opacity: 1; transform: translateY(0) scale(1); filter: blur(0);
  pointer-events: auto;
  transition: opacity var(--t-base) var(--ease-expo),
              transform var(--t-base) var(--ease-expo),
              filter var(--t-base) var(--ease-expo);
}
/* Right-aligned variant for triggers near the viewport edge. */
.listbox-popover.align-end { left: auto; right: 0; transform-origin: top right; }

.listbox-option {
  display: flex; align-items: center; gap: 9px; width: 100%;
  height: 32px; padding: 0 9px;
  border: none; border-radius: var(--r-sm);
  background: transparent; color: var(--fg);
  font: inherit; font-size: var(--fs-sm); font-weight: 550;
  text-align: left; cursor: pointer;
  transition: background-color var(--t-instant) var(--ease-out);
}
.listbox-option .lucide { width: 15px; height: 15px; flex: none; }
.listbox-option[data-tone] { color: var(--tone); }
.listbox-option:hover,
.listbox-option.is-cursor { background: color-mix(in oklab, var(--tone, var(--fg)) 12%, transparent); }
.listbox-option .opt-label { flex: 1; }
.listbox-option .opt-check { width: 14px; height: 14px; opacity: 0; color: currentColor; }
.listbox-option[aria-selected="true"] .opt-check { opacity: 1; }
.listbox-option[aria-selected="true"] { background: color-mix(in oklab, var(--tone, var(--accent)) 14%, transparent); }
.listbox-sep { height: 1px; margin: 4px 6px; background: var(--line); }

/* =============================================================== filterbar ==
   The sticky control surface directly under the topbar. It replaces the old
   page header entirely: on a tool this dense, a title that repeats the nav item
   and a sentence explaining what "Logs" means are pure overhead.
   ========================================================================== */
.filterbar {
  position: relative; z-index: 30;
  background: var(--bg-chrome);
  border-bottom: 1px solid var(--line);
}
.filterbar-inner {
  max-width: var(--shell-max); margin: 0 auto;
  padding: 10px var(--shell-pad);
  display: flex; flex-direction: column; gap: 9px;
}
.filterbar-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.filterbar-row .spacer { flex: 1 1 auto; min-width: 0; }
.filterbar-tools { display: inline-flex; align-items: center; gap: 8px; flex: none; }

/* On desktop the field group dissolves entirely into the surrounding row, so
   every control is a direct flex item and the row can lay them out as one line;
   on mobile the same markup becomes the collapsible panel. One DOM, two
   behaviours, no duplication. */
.filterbar-fields,
.filterbar-fields > .filterbar-row { display: contents; }

/* Second row: the service chips, with a leading key so the colour coding is
   self-explanatory the first time someone sees it. */
.filterbar-chips { display: flex; align-items: center; gap: 10px; min-width: 0; }
.filterbar-chips .eyebrow { flex: none; }

.filter-divider { width: 1px; height: 20px; background: var(--line); flex: none; margin: 0 2px; }

.stamp {
  font-size: var(--fs-2xs); color: var(--fg-faint);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* ------------------------------------------------------------- responsive */
/* Below 860px the filter surface folds into a single toggle so the stream
   keeps the screen. Filters stay applied while folded — only their controls
   are out of sight, and the toggle carries a count so that is never a
   surprise. */
.filters-toggle { display: none; }

@media (max-width: 860px) {
  .filterbar-inner { padding: 8px var(--shell-pad); gap: 8px; }

  .filters-toggle {
    display: inline-flex; align-items: center; gap: 7px;
    height: 32px; padding: 0 12px;
    border: 1px solid var(--line-strong); border-radius: var(--r-md);
    background: var(--bg-surface); color: var(--fg); box-shadow: var(--shadow-1);
    font: inherit; font-size: var(--fs-sm); font-weight: 550; cursor: pointer;
  }
  .filters-toggle .lucide { width: 15px; height: 15px; color: var(--fg-faint); }
  .filters-toggle .filters-chevron { transition: transform var(--t-base) var(--ease-out); }
  .filters-toggle[aria-expanded="true"] .filters-chevron { transform: rotate(180deg); }
  .filters-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 17px; height: 17px; padding: 0 5px; border-radius: var(--r-pill);
    background: var(--accent); color: var(--accent-fg);
    font-size: var(--fs-2xs); font-weight: 650; line-height: 1;
  }

  /* grid-template-rows 0fr -> 1fr animates an auto height without touching
     the height property itself. */
  .filterbar-fields > .filterbar-row {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  }
  .filterbar-fields,
  .filterbar-chips-wrap {
    display: grid; grid-template-rows: 0fr; flex: 1 0 100%;
    opacity: 0;
    transition: grid-template-rows var(--t-base) var(--ease-out),
                opacity var(--t-fast) var(--ease-out);
  }
  .filterbar-fields > *,
  .filterbar-chips-wrap > * { overflow: hidden; min-height: 0; }
  .filterbar-inner.is-open .filterbar-fields,
  .filterbar-inner.is-open .filterbar-chips-wrap { grid-template-rows: 1fr; opacity: 1; }

  .filterbar-fields .filterbar-row { padding-top: 8px; }
  .searchbox { flex: 1 1 100%; }
  .listbox, .listbox-trigger { width: 100%; }
  .listbox-popover { width: 100%; max-width: none; }
}
