/* Tusua Panel.
 *
 * One stylesheet, no framework, no build step. The panel is a handful of
 * pages; a bundler would be more machinery than the thing it builds.
 * Light and dark follow the operating system: this is a tool people open at
 * three in the morning during an incident.
 */

:root {
  --bg: #fbfaf8;
  --bg-raised: #ffffff;
  --bg-sunken: #f3f1ec;
  --text: #1a1815;
  --text-muted: #5f5a52;
  --line: #e2ddd4;
  --accent: #0f6b5c;
  --accent-text: #ffffff;
  --accent-soft: #e6f1ee;
  --danger: #b3261e;
  --danger-soft: #fdeceb;
  --warn: #8a5a00;
  --warn-soft: #fdf3e2;

  --radius: 12px;
  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #12110f;
    --bg-raised: #1b1a17;
    --bg-sunken: #191816;
    --text: #f2efe9;
    --text-muted: #a8a29a;
    --line: #302e2a;
    --accent: #4fd1b5;
    --accent-text: #08201c;
    --accent-soft: #16302b;
    --danger: #ff8a80;
    --danger-soft: #331a18;
    --warn: #f0b429;
    --warn-soft: #302614;
  }
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; line-height: 1.2; letter-spacing: -0.015em; }
h1 { font-size: 1.6rem; font-weight: 640; }
h2 { font-size: 1.15rem; font-weight: 620; }
h3 { font-size: 0.95rem; font-weight: 600; }
p { margin: 0; }
a { color: var(--accent); }

.wrap { width: min(100% - 2rem, 1180px); margin-inline: auto; }

.skip { position: absolute; left: -9999px; background: var(--accent);
        color: var(--accent-text); padding: 0.6rem 1rem; }
.skip:focus { left: 0; top: 0; z-index: 20; }
:focus-visible { outline: 2.5px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* ------------------------------------------------------------------ chrome */

.topbar { border-bottom: 1px solid var(--line); background: var(--bg-raised); }
.topbar .wrap { display: flex; align-items: center; gap: 1.5rem; min-height: 3.5rem;
                flex-wrap: wrap; }

/* On a narrow screen the brand keeps its own line and the navigation takes the
   next one, full width. Letting them share a wrapped row left the brand
   floating beside a ragged block of links. */
@media (max-width: 34rem) {
  .topbar .wrap { gap: 0.5rem; padding-block: 0.7rem; }
  .topbar nav { margin-inline-start: 0; width: 100%; }
}
.brand { font-weight: 640; text-decoration: none; color: var(--text);
         display: inline-flex; align-items: center; gap: 0.5rem; }
.brand .dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; background: var(--accent); }
.topbar nav { display: flex; gap: 0.3rem; margin-inline-start: auto; flex-wrap: wrap; }
.topbar nav a {
  color: var(--text-muted); text-decoration: none; padding: 0.35rem 0.75rem;
  border-radius: 999px; font-weight: 520;
}
.topbar nav a:hover { color: var(--text); background: var(--bg-sunken); }
.topbar nav a[aria-current="page"] { color: var(--accent); background: var(--accent-soft); }

main { padding-block: 2rem 4rem; }
.page-head { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap;
             margin-bottom: 1.5rem; }
.page-head .filters { margin-inline-start: auto; display: flex; gap: 0.4rem; flex-wrap: wrap; }

/* The period buttons and the dates they resolve to, as one block: the caption
   has to sit under the buttons and move with them when they wrap, not float
   at the left margin of a row it no longer belongs to. */
.period-picker { margin-bottom: 1.5rem; }
.period-picker .filters { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.range-note { color: var(--text-muted); font-size: 0.85rem; margin-top: 0.5rem;
              font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------- cards */

.card {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.2rem 1.3rem;
  min-width: 0;
}
.grid { display: grid; gap: 1rem; }

/* A grid item defaults to min-width:auto, which means it refuses to shrink
   below the widest unbreakable thing inside it. One <pre> holding a script tag
   is enough to blow the track out, push a neighbouring column underneath it,
   and make the whole page scroll sideways on a phone — which is exactly what
   the sites page was doing. This one line is what lets overflow-x on the
   children actually engage. */
.grid > *, .stack > * { min-width: 0; }

.grid-metrics { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }

/* Content plus a sidebar, not two equal halves. auto-fit gave both children an
   equal share, which is wrong when one is a list of cards and the other is a
   short form. */
.grid-aside { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 62rem) {
  .grid-aside { grid-template-columns: minmax(0, 1fr) minmax(17rem, 21rem); }
}

.grid-halves { grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); }

.metric .label { color: var(--text-muted); font-size: 0.8rem; font-weight: 560;
                 text-transform: uppercase; letter-spacing: 0.04em; }
.metric .value { font-size: 1.9rem; font-weight: 640; line-height: 1.1;
                 margin-top: 0.35rem; font-variant-numeric: tabular-nums; }
.metric .delta { font-size: 0.82rem; margin-top: 0.2rem; color: var(--text-muted); }
.metric .delta.up { color: var(--accent); }
.metric .delta.down { color: var(--danger); }

/* -------------------------------------------------------------- breakdowns */

.bars { list-style: none; margin: 0.8rem 0 0; padding: 0; display: grid; gap: 0.35rem; }
.bars li { position: relative; display: flex; justify-content: space-between;
           gap: 1rem; padding: 0.35rem 0.6rem; border-radius: 7px;
           font-size: 0.9rem; overflow: hidden; }
.bars li::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: var(--share, 0%);
  background: var(--accent-soft); border-radius: 7px; z-index: 0;
}
.bars li > * { position: relative; z-index: 1; }
.bars .label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bars .count { font-variant-numeric: tabular-nums; color: var(--text-muted); flex: none; }

/* ------------------------------------------------------------------ tables */

table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
th, td { text-align: start; padding: 0.55rem 0.6rem; border-bottom: 1px solid var(--line); }
th { color: var(--text-muted); font-weight: 560; font-size: 0.8rem;
     text-transform: uppercase; letter-spacing: 0.04em; }
tbody tr:last-child td { border-bottom: none; }
td.num { text-align: end; font-variant-numeric: tabular-nums; }
.table-scroll { overflow-x: auto; }

/* ------------------------------------------------------------------- forms */

label.field { display: block; margin-bottom: 1rem; }
label.field > span { display: block; font-size: 0.85rem; font-weight: 560;
                     margin-bottom: 0.3rem; }
/* On its own line. Inline, a label and its help read as one run-on sentence —
   "Telegram chat The numeric chat id" — and the eye cannot find where the name
   of the field ends. */
label.field .help { display: block; font-weight: 400; color: var(--text-muted);
                    font-size: 0.8rem; margin-top: 0.1rem; }
input[type="text"], input[type="email"], input[type="password"],
input[type="number"], select, textarea {
  font: inherit; width: 100%; padding: 0.55rem 0.7rem; color: var(--text);
  background: var(--bg); border: 1px solid var(--line); border-radius: 8px;
}
textarea { min-height: 5rem; resize: vertical; font-family: var(--mono); font-size: 0.85rem; }
input:focus, select:focus, textarea:focus { border-color: var(--accent); outline: none; }

.btn {
  font: inherit; font-weight: 560; cursor: pointer; border: 1px solid transparent;
  padding: 0.5rem 1rem; border-radius: 999px; text-decoration: none;
  display: inline-block; background: var(--accent); color: var(--accent-text);
}
.btn:hover { opacity: 0.9; }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--line); }
.btn-ghost:hover { border-color: var(--text-muted); }
.btn-ghost[aria-current="true"] { border-color: var(--accent); color: var(--accent);
                                  background: var(--accent-soft); }
.btn-danger { background: transparent; color: var(--danger); border-color: var(--danger); }
.btn-small { padding: 0.28rem 0.7rem; font-size: 0.85rem; }

/* ------------------------------------------------------------------ notices */

.notice { border-radius: var(--radius); padding: 0.85rem 1rem; margin-bottom: 1.2rem;
          border: 1px solid var(--line); font-size: 0.92rem; }
.notice-error { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.notice-warn { background: var(--warn-soft); border-color: var(--warn); color: var(--warn); }
.notice-ok { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

.empty { color: var(--text-muted); text-align: center; padding: 2.5rem 1rem; }
.empty h3 { color: var(--text); margin-bottom: 0.4rem; }

code, pre, .mono { font-family: var(--mono); font-size: 0.85rem; }
pre { background: var(--bg-sunken); border: 1px solid var(--line); border-radius: 8px;
      padding: 0.85rem 1rem; overflow-x: auto; margin: 0; max-width: 100%; }

/* The snippet and the ping URL are single long lines that exist to be copied.
   Wrapping beats scrolling for those: you can see the whole thing, and select
   it in one gesture instead of dragging a scrollbar first. */
pre.snippet { white-space: pre-wrap; word-break: break-all; }

/* Long values — a site name somebody typed, a list of six domains — must wrap
   rather than widen their card. */
.wrap-anywhere { overflow-wrap: anywhere; }

.pill { display: inline-block; padding: 0.12rem 0.6rem; border-radius: 999px;
        font-size: 0.78rem; font-weight: 560; white-space: nowrap; }

/* Title on the left, status on the right. The title is allowed to shrink and
   wrap; the pill never is, so a long name pushes the text down rather than
   pushing the status out of the card. */
.card-head { display: flex; gap: 0.5rem 0.75rem; align-items: baseline;
             justify-content: space-between; flex-wrap: wrap; }
/* break-word, not anywhere: a name is made of words, and "deliberadament / e"
   reads worse than a slightly ragged line. `anywhere` is right for the keys
   and domain lists below, which have no word boundaries to break on. */
/* The title asks for 14rem before the status is allowed to sit beside it.
   Below that the pill wraps to its own line, which is better than squeezing
   the title into a column so narrow that a word like "deliberadamente" has to
   be broken in half. break-word is the last resort for when even a full line
   is not enough. */
.card-head h2 { min-width: 0; flex: 1 1 14rem; overflow-wrap: break-word; }
.card-head .pill { flex: none; }
.pill-on { background: var(--accent-soft); color: var(--accent); }
.pill-off { background: var(--danger-soft); color: var(--danger); }
.muted { color: var(--text-muted); }
.stack { display: grid; gap: 1rem; }
.row { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }

/* ------------------------------------------------------------------- chart */

.chart { display: flex; align-items: flex-end; gap: 2px; height: 120px;
         margin-top: 1rem; }
/* Gaps do not shrink. Ninety of them at 2px each eat two thirds of a phone
   screen, so past sixty bars the gap halves and the bars keep the width. */
.chart.dense { gap: 1px; }
/* height:100% is load-bearing, not decoration. The fill inside is sized as a
   percentage, and a percentage height resolves against the containing block —
   so without a height here the column was as tall as its content, which is
   nothing, and every bar in the chart came out two pixels tall whatever the
   numbers said. */
.chart .bar { flex: 1; height: 100%; background: var(--accent-soft);
              border-radius: 3px 3px 0 0; position: relative; }
.chart .bar > span { position: absolute; inset: auto 0 0 0; background: var(--accent);
                     border-radius: 3px 3px 0 0; }

/* One entry in a list inside a card. A hairline rather than a box: nesting
   cards inside cards makes a page of frames, and what matters here is where
   one entry ends. */
.entry { border-top: 1px solid var(--line); padding-top: 0.9rem; }
.entry:first-child { border-top: 0; padding-top: 0; }
.entry h3 { font-size: 0.95rem; font-weight: 600; margin: 0; min-width: 0;
            flex: 1 1 10rem; overflow-wrap: break-word; }

/* ------------------------------------------------------------------- auth  */

.auth-page { display: grid; place-items: center; min-height: 100vh; padding: 2rem 1rem; }
.auth-card { width: min(100%, 26rem); }
.auth-card h1 { margin-bottom: 0.4rem; }
.auth-card .lead { color: var(--text-muted); margin-bottom: 1.5rem; font-size: 0.95rem; }
.auth-card .below { margin-top: 1.2rem; font-size: 0.9rem; text-align: center; }
.auth-card .brand { justify-content: center; margin-bottom: 1.5rem; font-size: 1.2rem; }
.auth-card form > .btn { width: 100%; }

.qr { display: grid; place-items: center; padding: 1rem; background: #ffffff;
      border-radius: var(--radius); margin-bottom: 1rem; }
.qr svg { width: 100%; max-width: 15rem; height: auto; }

.codes { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.4rem;
         font-family: var(--mono); margin: 1.2rem 0; }
.codes li { list-style: none; background: var(--bg-sunken); border-radius: 6px;
            padding: 0.4rem 0.6rem; text-align: center; letter-spacing: 0.05em; }
