/* ==========================================================================
   LogPose — the log stream.

   Not a "table card". The stream is the page: no outer box, no vertical rules,
   no border between it and the application. Only horizontal hairlines separate
   one line from the next, exactly as a terminal separates them with nothing at
   all. Rows are CSS-grid based rather than <table> based so the column header
   can live outside the scroller and stay perfectly aligned with the body.

   Reading order is oldest -> newest, top -> bottom, and the scroller opens
   pinned to the bottom: the newest line is the one under your eyes, like
   `tail -f`.
   ========================================================================== */

.stream {
  min-height: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
}

/* -------------------------------------------------------------- column head */
.stream-head {
  border-bottom: 1px solid var(--line);
  background: var(--bg-base);
}
.stream-head-inner,
.stream-inner {
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: 0 var(--shell-pad);
}
/* Padded rather than fixed-height: uppercase labels at 11px have ascenders
   that a 30px box with baseline alignment clips at the top. */
.stream-head .row-grid {
  align-items: center;
  padding: 13px 0 9px;
  line-height: 1.2;
  color: var(--fg-faint);
  font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: 0.07em; text-transform: uppercase;
}
.stream-head .head-msg {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  min-width: 0;
}
.stream-head .head-meta {
  text-transform: none; letter-spacing: 0; white-space: nowrap;
  font-size: var(--fs-2xs); font-variant-numeric: tabular-nums;
  color: var(--fg-faint); font-weight: 550;
}

/* --------------------------------------------------------------- scroller */
.stream-scroll {
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  /* Anchor growth to the bottom edge so appended lines don't shove the
     viewport, the same guarantee a terminal gives. */
  overflow-anchor: auto;
}

/* --------------------------------------------------------------- row grid */
/* One template, shared by the header strip, every log line and every rail. */
.row-grid {
  display: grid;
  grid-template-columns:
    var(--col-time, 104px)
    var(--col-level, 108px)
    var(--col-service, 170px)
    minmax(0, 1fr);
  align-items: baseline;
  gap: 0 16px;
}

.log-row {
  position: relative;
  /* Bled 10px past the content edge so the hover and selection surfaces have
     breathing room without shifting the columns. */
  width: calc(100% + 20px);
  margin: 0 -10px;
  padding: 7px 10px;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color var(--t-instant) var(--ease-out),
              box-shadow var(--t-fast) var(--ease-out);
}
/* The hairline lives on a pseudo-element inset to the content width, so the
   selected row's rounded highlight never collides with a divider. */
.log-row::after {
  content: ""; position: absolute; left: 10px; right: 10px; bottom: 0;
  height: 1px; background: var(--line);
}
.log-row:last-of-type::after { background: transparent; }
.log-row:hover { background: var(--bg-raised); }
.log-row:hover .svc-dot { box-shadow: 0 0 0 3px color-mix(in oklab, var(--svc-color) 20%, transparent); }
.log-row.is-active {
  background: var(--accent-wash);
  box-shadow: inset 0 0 0 1px var(--accent-edge);
}
.log-row.is-active::after { background: transparent; }
.log-row:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }

/* ------------------------------------------------------------------ cells */
.cell-time {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--fg-faint);
  white-space: nowrap;
  transition: color var(--t-fast) var(--ease-out);
}
.log-row:hover .cell-time { color: var(--fg-muted); }
.cell-time .ms { opacity: 0.55; }

.cell-level {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--tone, var(--fg-muted));
  font-size: var(--fs-2xs); font-weight: 650;
  letter-spacing: 0.03em; text-transform: uppercase;
  white-space: nowrap;
}
.cell-level .lucide { width: 13px; height: 13px; }

.cell-service {
  display: inline-flex; align-items: center; gap: 7px; min-width: 0;
  font-size: var(--fs-xs); font-weight: 550;
  color: var(--fg-muted);
}
.svc-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--svc-color, var(--fg-faint));
  transition: box-shadow var(--t-base) var(--ease-out);
}
.svc-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cell-msg {
  min-width: 0;
  font-size: var(--fs-sm);
  color: var(--fg);
  word-break: break-word;
}
/* Two lines in the stream; the full text always lives in the detail panel. */
.msg-clamp {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
.log-row[data-level="error"] .cell-msg,
.log-row[data-level="critical"] .cell-msg { color: var(--fg-strong); }

/* Errors carry a faint severity wash so a failure is findable while scrolling
   fast — a tint on the row, never a coloured stripe on its edge. */
.log-row[data-level="error"]   { background: color-mix(in oklab, var(--lv-error) 4.5%, transparent); }
.log-row[data-level="critical"]{ background: color-mix(in oklab, var(--lv-critical) 7%, transparent); }
.log-row[data-level="error"]:hover    { background: color-mix(in oklab, var(--lv-error) 9%, transparent); }
.log-row[data-level="critical"]:hover { background: color-mix(in oklab, var(--lv-critical) 12%, transparent); }

/* ----------------------------------------------------------- day separator */
/* Inserted by stream.js whenever the calendar day changes between two lines.
   Sticky, so while you scroll through yesterday it says so. */
.day-sep {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; gap: 12px;
  padding: 9px 0 8px;
  background: linear-gradient(var(--bg-base) 72%, transparent);
}
.day-sep::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.day-sep-label {
  padding: 2px 10px; border-radius: var(--r-pill);
  background: var(--bg-surface); border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  font-size: var(--fs-2xs); font-weight: 600; letter-spacing: 0.03em;
  color: var(--fg-muted); text-transform: uppercase; white-space: nowrap;
}

/* ================================================================== rails ==
   Pagination without page numbers. Scrolling up hits a rail that pulls in the
   previous (older) page; scrolling down hits the mirror rail back toward now.
   Direction is encoded in the arrow, the copy and the hover motion, so "older"
   always means "further up".
   ========================================================================== */
.rail {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; height: var(--rail-h);
  margin: 4px 0;
  padding: 0 14px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg);
  background: transparent;
  color: var(--fg-muted);
  font: inherit; font-size: var(--fs-xs); font-weight: 550;
  cursor: pointer;
  transition: background-color var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out),
              color var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out);
}
.rail:hover {
  color: var(--fg-strong);
  border-style: solid;
  border-color: var(--line-glass);
  background: var(--bg-glass);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  box-shadow: var(--shadow-2), var(--inner-lip);
  text-decoration: none;
}
.rail:active { transform: scale(0.995); }
.rail .lucide { width: 15px; height: 15px; color: var(--accent); }
.rail-meta { color: var(--fg-faint); font-variant-numeric: tabular-nums; }
.rail-meta::before { content: "·"; margin-right: 8px; color: var(--line-strong); }

/* The arrow drifts in the direction it will take you, continuously but
   quietly, and lengthens its stride on hover. */
.rail--older .lucide  { animation: drift-up 2.6s var(--ease-both) infinite; }
.rail--newer .lucide  { animation: drift-down 2.6s var(--ease-both) infinite; }
.rail:hover .lucide   { animation-duration: 1.1s; }
@keyframes drift-up   { 0%,100% { transform: translateY(1.5px); } 50% { transform: translateY(-2.5px); } }
@keyframes drift-down { 0%,100% { transform: translateY(-1.5px); } 50% { transform: translateY(2.5px); } }

/* Terminal markers: no more history above, live head below. */
.rail-static {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  height: 38px; margin: 2px 0;
  color: var(--fg-faint); font-size: var(--fs-2xs);
  letter-spacing: 0.06em; text-transform: uppercase; font-weight: 600;
}
.rail-static::before,
.rail-static::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.rail-static .lucide { width: 13px; height: 13px; }
.rail-static.is-head { color: var(--ok); }
.rail-static.is-head .live-dot { background: var(--ok); animation: live-pulse 2.2s var(--ease-both) infinite; }

.stream-inner { padding-top: 4px; padding-bottom: 22px; }

/* ------------------------------------------------------------ empty state */
/* Teaches the control set rather than announcing emptiness. */
.stream-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; min-height: 46vh; padding: 40px 24px; text-align: center;
}
.stream-empty .empty-mark {
  display: grid; place-items: center;
  width: 46px; height: 46px; border-radius: var(--r-lg);
  background: var(--bg-sunken); border: 1px solid var(--line);
  color: var(--fg-faint);
}
.stream-empty .empty-mark .lucide { width: 20px; height: 20px; }
.stream-empty h3 { font-size: var(--fs-md); color: var(--fg-strong); }
.stream-empty p { margin: 0; max-width: 44ch; font-size: var(--fs-sm); color: var(--fg-muted); }
.stream-empty .empty-actions { display: flex; gap: 8px; margin-top: 6px; }

/* ------------------------------------------------------------- responsive */
@media (max-width: 1180px) {
  :root { --col-service: 130px; }
}
@media (max-width: 860px) {
  /* Two-line layout: metadata above, message below, full width. */
  .stream-head { display: none; }
  .row-grid { grid-template-columns: auto auto minmax(0, 1fr); gap: 0 10px; }
  .log-row { padding: 9px 10px; }
  .log-row .cell-msg {
    grid-column: 1 / -1;
    margin-top: 5px;
  }
  .cell-service { justify-self: start; }
  .rail-meta { display: none; }
}

/* ------------------------------------------------------- jump-to-now pill */
/* Appears only once you have left the head of the stream, floating clear of
   the content so it never covers a line you are reading. */
.jump-head {
  position: absolute; left: 50%; bottom: 18px; z-index: 20;
  display: inline-flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 14px;
  border: 1px solid var(--line-glass); border-radius: var(--r-pill);
  background: var(--bg-glass);
  backdrop-filter: blur(20px) saturate(190%);
  -webkit-backdrop-filter: blur(20px) saturate(190%);
  box-shadow: var(--shadow-2), var(--inner-lip);
  color: var(--fg-strong);
  font: inherit; font-size: var(--fs-xs); font-weight: 550;
  cursor: pointer;
  opacity: 0; transform: translate(-50%, 10px) scale(0.96); pointer-events: none;
  transition: opacity var(--t-base) var(--ease-out),
              transform var(--t-base) var(--ease-out);
}
.jump-head.is-visible { opacity: 1; transform: translate(-50%, 0) scale(1); pointer-events: auto; }
.jump-head:hover { border-color: var(--accent-edge); }
.jump-head:active { transform: translate(-50%, 0) scale(0.96); }
.jump-head .lucide { width: 14px; height: 14px; color: var(--accent); }

/* The stream needs a positioning context for the floating pill. */
.stream { position: relative; }

/* --------------------------------------------------------- flows variant */
/* Same stream, one extra trailing column for duration. */
.row-grid--flows {
  grid-template-columns:
    var(--col-time, 104px)
    var(--col-level, 118px)
    var(--col-service, 170px)
    minmax(0, 1fr)
    var(--col-duration, 82px);
}
.cell-right { justify-self: end; text-align: right; }
.cell-duration {
  font-size: var(--fs-xs); color: var(--fg-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
a.log-row:hover { text-decoration: none; }
.log-row[data-status="failed"] { background: color-mix(in oklab, var(--st-failed) 4.5%, transparent); }
.log-row[data-status="failed"]:hover { background: color-mix(in oklab, var(--st-failed) 9%, transparent); }
/* A run still in flight breathes rather than spins: a spinning clock reads as
   a loading state for the page, which it is not. */
.log-row[data-status="running"] .cell-level .lucide { animation: breathe 2.4s var(--ease-both) infinite; }
@keyframes breathe { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }

@media (max-width: 860px) {
  .row-grid--flows { grid-template-columns: auto auto minmax(0, 1fr) auto; }
  .row-grid--flows .cell-msg { grid-column: 1 / -1; }
}
