/* ============================================================================
   zhuzhbox — page layouts
   ========================================================================= */

.stage {
  max-width: 1280px;
  margin: 0 auto;
  padding: calc(var(--cell) * 1.6) var(--cell) calc(var(--cell) * 2.5);
  min-height: 60vh;
}
.view[hidden] { display: none; }

.head { margin-bottom: calc(var(--cell) * 1.4); }
.head .label { display: block; margin-bottom: 0.5rem; }
.head h1 {
  font-size: clamp(1.25rem, 3vw, 1.75rem);
  letter-spacing: -0.04em;
  color: var(--hi);
  margin-bottom: 0.4rem;
}
.head p { color: var(--dim); max-width: 74ch; }

/* ============================ drop portal ================================= */

/* Two panes tiling the space, the way a split terminal does: the log column
   is exactly as tall as the work column beside it. */
.portal {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: var(--cell);
  align-items: stretch;
}
.portal-side {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: var(--cell);
}
.pane-log { display: flex; flex-direction: column; }

/* Drop target. The dashed rule is the one place a dashed border appears, so it
   reads as "this edge is provisional — put something through it". */
.dz {
  border: 1px dashed var(--rule-2);
  padding: calc(var(--cell) * 2) var(--cell);
  text-align: center;
  cursor: pointer;
  display: block;
  width: 100%;
  font: inherit;
  color: inherit;
  background: transparent;
}
.dz:hover { border-color: var(--fg); }
.dz.hot { border-color: var(--hi); background: var(--panel); }
.dz-line { color: var(--hi); font-size: 1rem; margin-bottom: 0.35rem; }
.dz-sub { font-size: 0.75rem; color: var(--dim); }
.dz-sub kbd { margin: 0 0.15rem; }

/* Whole-window drag veil, so a file dropped anywhere lands. */
.veil {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  pointer-events: none;
}
.veil[hidden] { display: none; }
.veil-box {
  border: 1px dashed var(--hi);
  padding: var(--cell) calc(var(--cell) * 2);
  color: var(--hi);
  background: var(--bg);
}

.mode-row {
  display: flex;
  gap: var(--half);
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  margin-top: var(--cell);
}

.bundle-fields[hidden] { display: none; }

/* ---- queue --------------------------------------------------------------- */

.q { list-style: none; margin: 0; padding: 0; max-width: none; }
.qi {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.15rem 1rem;
  padding: 0.5rem 0;
  border-top: 1px solid var(--rule);
  align-items: baseline;
  font-size: 0.8125rem;
}
.qi-name { color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qi-right { display: flex; gap: 0.75rem; align-items: center; color: var(--dim); font-size: 0.75rem; }
.qi-meter { grid-column: 1 / -1; }
.qi[data-state="done"] .qi-name { color: var(--hi); }
.qi[data-state="failed"] .qi-name { text-decoration: line-through; color: var(--dim); }
.qi-drop {
  font: inherit;
  background: transparent;
  border: 0;
  color: var(--dim);
  cursor: pointer;
  padding: 0 0.2rem;
  line-height: 1;
}
.qi-drop:hover { color: var(--hi); }

.q-foot {
  display: flex;
  gap: var(--half);
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  margin-top: var(--cell);
  padding-top: var(--cell);
  border-top: 1px solid var(--rule);
}

/* ---- the transfer log ---------------------------------------------------- */

/* The signature element: the upload protocol, printed as it happens. */
.log {
  font-size: 0.75rem;
  line-height: 1.75;
  flex: 1 1 auto;
  min-height: calc(var(--cell) * 8);
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--rule-2) transparent;
  margin: 0;
  padding: 0;
  list-style: none;
  max-width: none;
}
.log::-webkit-scrollbar { width: 6px; }
.log::-webkit-scrollbar-thumb { background: var(--rule-2); }
.log-line {
  display: grid;
  grid-template-columns: 5.5ch 7ch minmax(0, 1fr);
  gap: 0.9rem;
  animation: log-in 140ms ease-out;
}
@keyframes log-in { from { opacity: 0; transform: translateY(-2px); } }
.log-line .t { color: var(--dim); font-variant-numeric: tabular-nums; }
.log-line .v { color: var(--dim); text-transform: lowercase; }
.log-line .m { color: var(--fg); overflow-wrap: anywhere; }
.log-line[data-kind="ok"] .v { color: var(--hi); }
.log-line[data-kind="err"] .v { background: var(--hi); color: var(--bg); padding: 0 0.25rem; }
.log-line[data-kind="err"] .m { color: var(--hi); }
.log-empty { color: var(--dim); }

/* ---- results ------------------------------------------------------------- */

.sent { list-style: none; margin: 0; padding: 0; max-width: none; }
.sent > li + li { margin-top: var(--cell); padding-top: var(--cell); border-top: 1px solid var(--rule); }
.sent-name { color: var(--hi); display: flex; gap: 0.6rem; align-items: baseline; justify-content: space-between; }
.sent-name b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link-row { display: flex; gap: 0; margin: var(--half) 0; }
.link-row .input { border-right: 0; }
.link-row .btn { border-radius: 0; }
.sent-meta {
  display: grid;
  /* Wide enough for "delete token" once the tracking is applied — it must not
     wrap onto two lines beside a one-line value. */
  grid-template-columns: 15ch minmax(0, 1fr);
  gap: 0.15rem 1rem;
  font-size: 0.75rem;
  color: var(--dim);
}
.sent-meta dt { text-transform: uppercase; letter-spacing: 0.14em; font-size: 0.6875rem; }
.sent-meta dd { margin: 0; color: var(--fg); overflow-wrap: anywhere; }
.secret {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
}
.secret code { color: var(--hi); }

/* ---- session pane -------------------------------------------------------- */

.kv {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.35rem 1.1rem;
  font-size: 0.8125rem;
  align-items: baseline;
}
.kv dt {
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
}
.kv dd { margin: 0; color: var(--fg); }

/* ============================ apps ======================================== */

/* A CSS grid with auto-fit rows locks every card in a row to the height of
   the tallest one, so a short card sits above a canyon of white space before
   the next row starts. Multi-column flow doesn't have rows: each card just
   stacks under the previous one in whichever column is shortest, which is
   what "panels take up the free space" actually means here. It also collapses
   to one column on its own once 26rem doesn't fit twice — no media query. */
.cols-2 {
  columns: 2 26rem;
  column-gap: var(--cell);
}
.cols-2 > .pane {
  break-inside: avoid;
  margin: 0 0 var(--cell);
}

.dl-row { display: flex; gap: var(--half); flex-wrap: wrap; margin-top: var(--cell); }

/* ============================ api ========================================= */

.api-grid {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: calc(var(--cell) * 1.5);
  align-items: start;
}
.api-index {
  position: sticky;
  top: calc(var(--cell) * 2 + 1px + var(--cell));
  font-size: 0.8125rem;
}
.api-index ol { list-style: none; margin: 0; padding: 0; }
.api-index a {
  display: block;
  padding: 0.1rem 0;
  color: var(--dim);
  border: 0;
  /* Long paths wrap; the continuation reads as subordinate rather than as a
     second entry. */
  text-indent: -1.5ch;
  padding-left: 1.5ch;
}
.api-index a:hover { color: var(--hi); border: 0; }
.api-index .label { display: block; margin-bottom: 0.5rem; }
.api-index li + .idx-gap { margin-top: var(--half); }

.ep { scroll-margin-top: calc(var(--cell) * 4); }
.ep + .ep { margin-top: calc(var(--cell) * 1.6); }
.ep-sig { display: flex; gap: 0.6rem; align-items: baseline; flex-wrap: wrap; margin-bottom: 0.5rem; }
.verb {
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  border: 1px solid var(--rule-2);
  padding: 0 0.4rem;
  color: var(--hi);
}
.ep-path { color: var(--hi); font-size: 0.9375rem; overflow-wrap: anywhere; }

/* ============================ stats ======================================= */

.figs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.fig { background: var(--bg); padding: var(--cell); }
.fig-n {
  font-size: clamp(1.5rem, 4vw, 2.125rem);
  color: var(--hi);
  letter-spacing: -0.05em;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  display: block;
}
.fig-n small { font-size: 0.4em; letter-spacing: 0.1em; color: var(--dim); margin-left: 0.35em; }
.fig .label { display: block; margin-top: 0.4rem; }

/* Day columns, drawn as stacked cells. */
.bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: calc(var(--cell) * 8);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 1px;
}
.bar-col {
  flex: 1 1 0;
  min-width: 3px;
  height: 100%;
  display: flex;
  align-items: flex-end;
  position: relative;
}
.bar-col i {
  display: block;
  width: 100%;
  height: var(--h, 0%);
  /* A day with nothing in it draws nothing — a minimum height here would lay a
     second line over the axis and read as a floor of activity that isn't there. */
  background: repeating-linear-gradient(to top, var(--fg) 0 5px, transparent 5px 7px);
  transition: height var(--t-med) ease-out;
}
.bar-col:hover i { background: repeating-linear-gradient(to top, var(--hi) 0 5px, transparent 5px 7px); }
.bars-axis {
  display: flex;
  justify-content: space-between;
  font-size: 0.6875rem;
  color: var(--dim);
  margin-top: 0.4rem;
}

/* ============================ faq ========================================= */

/* Two independent columns, each its own vertical flow — not a grid (which
   locks every row to its tallest cell) and not multicol balancing (whose
   heuristic doesn't handle one card as dominant as "questions" well: it
   tends to pair it with two short cards rather than isolating it, leaving
   the other column mostly empty). Deciding the split by hand once, here,
   beats leaving it to either algorithm's guess. */
.split-2 {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--cell);
  align-items: start;
}
/* Retention ladder: each step is a size bracket and a .meter sized to its
   share of the longest retention, so "bigger files are kept for less time"
   reads as a shrinking bar instead of a table you have to parse row by row. */
.ladder { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--half); max-width: none; }
.ladder-step {
  display: grid;
  grid-template-columns: auto minmax(3rem, 1fr) auto;
  gap: 0.85rem;
  align-items: center;
  font-size: 0.8125rem;
  /* .ladder's own gap already spaces the steps; cancel the global li margin
     so it isn't doubled. */
  margin-bottom: 0;
}
.ladder-size { color: var(--fg); white-space: nowrap; }
.ladder-days { color: var(--hi); white-space: nowrap; text-align: right; }

.qa + .qa { margin-top: var(--cell); padding-top: var(--cell); border-top: 1px solid var(--rule); }
.qa h3 { color: var(--hi); font-size: 0.9375rem; margin-bottom: 0.4rem; }
.qa p { color: var(--fg); margin-bottom: 0.5rem; }
.qa p:last-child { margin-bottom: 0; }

/* ============================ foot ======================================== */

.foot {
  border-top: 1px solid var(--rule);
  margin-top: calc(var(--cell) * 2);
  padding-top: var(--cell);
  display: flex;
  gap: var(--cell);
  flex-wrap: wrap;
  justify-content: space-between;
  font-size: 0.75rem;
  color: var(--dim);
}
.foot a { color: var(--dim); border-bottom-color: var(--rule); }
.foot a:hover { color: var(--hi); }

/* ============================ responsive ================================== */

@media (max-width: 1020px) {
  .portal { grid-template-columns: minmax(0, 1fr); }
  .portal-side { grid-template-rows: none; }
  .log { min-height: calc(var(--cell) * 6); max-height: calc(var(--cell) * 12); }
  .api-grid { grid-template-columns: minmax(0, 1fr); }
  .api-index { position: static; }
  .api-index ol { columns: 2; column-gap: var(--cell); }
}

/* .split-2's content (tables, the ladder, short Q&A paragraphs) tolerates a
   narrower column than the code-heavy layouts above, so it keeps its two
   columns well past 1020px and only drops to one when a column would
   actually get cramped. */
@media (max-width: 760px) {
  .split-2 { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 560px) {
  .sent-meta { grid-template-columns: minmax(0, 1fr); gap: 0 0; }
  .sent-meta dd { margin-bottom: 0.4rem; }
  .kv { grid-template-columns: minmax(0, 1fr); gap: 0 0; }
  .kv dd { margin-bottom: 0.5rem; }
  .link-row { flex-wrap: wrap; }
  .link-row .input { border-right: 1px solid var(--rule); }
  /* Two columns of endpoint paths (some 35+ characters) don't fit a phone-width
     column without wrapping every other line — one column reads cleanly. */
  .api-index ol { columns: 1; }
}
