/* manage.astrallabs.uk — the operator console.
 *
 * Plain CSS, no build step. The same reasoning as the CMS: this deploys to
 * shared hosting by uploading a zip, and a stylesheet that needs compiling is
 * a stylesheet that needs a toolchain on a server that has none.
 *
 * Everything is driven by the custom properties below, so the whole console
 * recolours from one block. */

:root {
  --brand: #12a06d;
  --brand-dark: #0d7e57;
  --brand-tint: #eefbf4;
  --ink: #161a26;
  --ink-2: #4c5468;
  --ink-3: #8b93a7;
  --line: #e4e7ee;
  --surface: #ffffff;
  --surface-2: #f7f8fa;
  --danger: #c0392b;
  --radius: 12px;
  --sidebar: 232px;
  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans Bengali", sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0; font-family: var(--font); color: var(--ink);
  background: var(--surface-2); line-height: 1.6; font-size: 15px;
}

h1, h2, h3 { margin: 0; line-height: 1.25; letter-spacing: -0.015em; }
a { color: inherit; }

/* ---- the frame ---- */
.console { display: flex; min-height: 100vh; }

.side {
  width: var(--sidebar); flex: none; background: var(--surface);
  border-right: 1px solid var(--line); display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh;
}

.side__brand {
  display: flex; align-items: center; gap: 10px; padding: 18px 16px;
  text-decoration: none;
}
.side__mark {
  width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  background: var(--brand); color: #fff; font-weight: 700; font-size: .875rem; flex: none;
}
.side__name { font-weight: 650; font-size: .875rem; line-height: 1.2; }
.side__host { display: block; font-size: .6875rem; font-weight: 400; color: var(--ink-3); }

.side__nav { flex: 1; overflow-y: auto; padding: 4px 10px 20px; }
.side__group {
  font-size: .6875rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); padding: 16px 8px 6px;
}
.side__link {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border-radius: 8px; text-decoration: none; color: var(--ink-2);
  font-size: .9375rem;
}
.side__link:hover { background: var(--surface-2); color: var(--ink); }
.side__link.is-here { background: var(--brand-tint); color: var(--brand-dark); font-weight: 600; }
.side__link.is-open { color: var(--ink); font-weight: 600; }
.side__link svg { width: 17px; height: 17px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* Sub-entries. Shown only while their section is open, so the sidebar does not
   grow a second list of everything the moment somebody has permission for it.
   The rule down the left is what makes them read as belonging to the line
   above rather than as three more top-level screens. */
.side__sub {
  display: grid; gap: 2px;
  margin: 2px 0 6px 26px; padding-left: 10px;
  border-left: 1px solid var(--line);
}
.side__sublink {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 10px; border-radius: 7px;
  text-decoration: none; color: var(--ink-2); font-size: .875rem;
}
/* A size down from the entries above them. Same weight of mark at the same
   size would read as a fourth top-level list rather than as three parts of
   the line it hangs from. */
.side__sublink svg {
  width: 15px; height: 15px; flex: none; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.side__sublink:hover { background: var(--surface-2); color: var(--ink); }
.side__sublink.is-here { background: var(--brand-tint); color: var(--brand-dark); font-weight: 600; }

.side__who {
  border-top: 1px solid var(--line); padding: 14px 16px;
  font-size: .8125rem;
}
.side__who b { display: block; font-weight: 600; }
.side__who span { color: var(--ink-3); font-size: .75rem; }
.side__out {
  margin-top: 10px; width: 100%; font: inherit; font-size: .8125rem;
  padding: 7px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); cursor: pointer; color: var(--ink-2);
}
.side__out:hover { background: var(--surface-2); color: var(--ink); }

.main { flex: 1; min-width: 0; padding: 28px 32px 64px; }

/* ---- pieces ---- */
.page-head { margin-bottom: 22px; }
.page-head h1 { font-size: 1.5rem; }
.page-head p { margin: 4px 0 0; color: var(--ink-3); font-size: .9375rem; }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 20px;
}
.card h2 { font-size: 1rem; font-weight: 600; }
.card__note { margin: 6px 0 0; max-width: 62ch; font-size: .875rem; color: var(--ink-2); }

.form { display: grid; gap: 14px; max-width: 640px; margin-top: 18px; }
.field-row { display: grid; gap: 5px; align-content: start; }

/* Fields side by side.
 *
 * align-items:start is the whole point. Without it each field stretches to the
 * height of the tallest one in the row, and because a field is itself a grid,
 * that extra height is shared out between its label, input and hint — so three
 * boxes that should line up end up at three different heights, and it reads as
 * a broken page rather than a spacing choice. */
.field-group {
  display: grid; gap: 12px; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

/* A checkbox and its words on one line, which a grid will not do however many
   flex properties are set on it. */
.field-check { display: flex; align-items: center; gap: 9px; font-size: .9375rem; }
.field-check input { width: 16px; height: 16px; flex: none; margin: 0; }
.field-row > span:first-child { font-size: .875rem; color: var(--ink-2); }
.field {
  width: 100%; padding: 9px 12px; border: 1px solid var(--line); border-radius: 9px;
  font: inherit; font-size: .9375rem; background: var(--surface); color: var(--ink);
}
.field:focus {
  outline: 0; border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 25%, transparent);
}
textarea.field { resize: vertical; min-height: 62px; }
.hint { font-size: .75rem; color: var(--ink-3); }
.error { font-size: .8125rem; color: var(--danger); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 9px 18px; border-radius: 8px; font: inherit; font-size: .9375rem;
  font-weight: 600; border: 1px solid transparent; cursor: pointer; text-decoration: none;
}
.btn--primary { background: var(--brand); color: #fff; }
.btn--primary:hover { background: var(--brand-dark); }
.btn--quiet { background: var(--surface); border-color: var(--line); color: var(--ink); }
.btn--quiet:hover { background: var(--surface-2); }

.flash {
  padding: 11px 15px; border-radius: 9px; margin-bottom: 20px; font-size: .9375rem;
  background: var(--brand-tint); color: var(--brand-dark);
  border: 1px solid color-mix(in srgb, var(--brand) 25%, transparent);
}
.flash--bad {
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
  color: var(--danger); border-color: color-mix(in srgb, var(--danger) 25%, transparent);
}

/* ---- signed out ---- */
.gate { display: grid; place-items: center; min-height: 100vh; padding: 20px; }
.gate .card { width: 100%; max-width: 360px; }

/* ---- the installer ----
   Its own frame: no sidebar, because there is nowhere else to go until this is
   finished.

   Each step is meant to sit in one screen without scrolling — somebody setting
   this up is following instructions, and a step whose button is below the fold
   is a step people believe they have finished. So the frame centres itself
   vertically while the content is short, and gives that up rather than clip
   anything when it is not. */
.install {
  max-width: 620px; margin: 0 auto; padding: 28px 20px 40px;
  min-height: 100vh; display: flex; flex-direction: column; justify-content: center;
}
@media (max-height: 700px) { .install { justify-content: flex-start; } }

.install__actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; margin-top: 20px;
}

/* The full list, for anybody who would rather not take the summary on trust. */
.install__more { font-size: .8125rem; color: var(--ink-3); }
.install__more summary { cursor: pointer; }
.install__more .checklist { margin-top: 10px; }

.checklist { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 8px; }
.checklist li { font-size: .875rem; }
.checklist b { display: block; font-weight: 600; color: var(--ink); font-size: .8125rem; }
.checklist span { color: var(--ink-2); font-size: .8125rem; }
.checklist .is-bad b { color: var(--danger); }
.checklist .is-warn b { color: #8a5a08; }

.install__head { display: flex; align-items: center; gap: 11px; margin-bottom: 22px; }
.install__head b { display: block; font-size: 1rem; line-height: 1.2; }
.install__head span { font-size: .75rem; color: var(--ink-3); }

.steps {
  display: flex; flex-wrap: wrap; gap: 6px 18px; list-style: none;
  margin: 0 0 22px; padding: 0; font-size: .8125rem; color: var(--ink-3);
}
.steps li { display: flex; align-items: center; gap: 7px; }
.steps span {
  width: 21px; height: 21px; border-radius: 50%; display: grid; place-items: center;
  border: 1px solid var(--line); font-size: .6875rem; font-weight: 600; background: var(--surface);
}
.steps .is-here { color: var(--ink); font-weight: 600; }
.steps .is-here span { background: var(--brand); border-color: var(--brand); color: #fff; }
.steps .is-done { color: var(--brand-dark); }
.steps .is-done span { background: var(--brand-tint); border-color: color-mix(in srgb, var(--brand) 30%, transparent); color: var(--brand-dark); }

/* ---- narrow ----
   The sidebar becomes a strip across the top. This console is used at a desk,
   but a licence gets suspended from wherever somebody happens to be. */
@media (max-width: 820px) {
  .console { flex-direction: column; }
  .side { width: auto; height: auto; position: static; border-right: 0; border-bottom: 1px solid var(--line); }
  .side__nav { display: flex; gap: 4px; overflow-x: auto; padding: 6px 10px; }
  .side__group { display: none; }
  .side__link { white-space: nowrap; }
  /* The strip is a single row, so the sub-entries join it rather than hanging
     under a line that is now beside them. display:contents drops the wrapper
     and lets its links become items of the same row. */
  .side__sub { display: contents; }
  .side__sublink { white-space: nowrap; padding: 8px 10px; }
  .side__who { display: flex; align-items: center; gap: 12px; }
  .side__out { margin: 0 0 0 auto; width: auto; }
  .main { padding: 22px 18px 48px; }
}

/* ---- statistics ---- */
.stats { display: grid; gap: 12px; grid-template-columns: repeat(2, 1fr); margin-bottom: 26px; }
@media (min-width: 900px) { .stats { grid-template-columns: repeat(4, 1fr); } }

.stat {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 18px;
}
.stat__label {
  font-size: .6875rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-3);
}
.stat__value { font-size: 1.75rem; font-weight: 650; line-height: 1.15; margin-top: 6px; letter-spacing: -.02em; }
.stat__hint { font-size: .75rem; color: var(--ink-3); margin-top: 2px; }
.stat--positive .stat__value { color: var(--brand-dark); }
.stat--warning .stat__value { color: #8a5a08; }
.stat--danger .stat__value { color: var(--danger); }

/* ---- tables ---- */
.panel-title { font-size: .875rem; font-weight: 600; color: var(--ink-2); margin: 0 0 8px; }

.table-wrap {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow-x: auto;
}
table.list { width: 100%; border-collapse: collapse; font-size: .875rem; }
table.list th {
  text-align: left; font-size: .6875rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3);
  padding: 10px 14px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
table.list td { padding: 10px 14px; border-bottom: 1px solid var(--line); }
table.list tr:last-child td { border-bottom: 0; }
td.mono { font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace; font-size: .8125rem; }

.badge {
  display: inline-block; font-size: .6875rem; font-weight: 600;
  padding: 3px 9px; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-2);
}
.badge--good { background: var(--brand-tint); border-color: color-mix(in srgb, var(--brand) 25%, transparent); color: var(--brand-dark); }
.badge--warn { background: #fdf3e2; border-color: #f0dcb4; color: #8a5a08; }
.badge--bad { background: color-mix(in srgb, var(--danger) 8%, var(--surface)); border-color: color-mix(in srgb, var(--danger) 25%, transparent); color: var(--danger); }

.empty {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 40px 20px; text-align: center;
}
.empty b { display: block; font-size: 1rem; }
.empty span { display: block; margin-top: 5px; font-size: .875rem; color: var(--ink-3); }

.note {
  margin: -14px 0 24px; padding: 9px 13px; border-radius: 9px; font-size: .75rem;
  background: #fdf3e2; border: 1px solid #f0dcb4; color: #8a5a08;
}

/* ---- uploads ----
   Empty until something is being sent, so the control does not carry a bar
   sitting at zero as though it had failed. */
.upload-bar {
  height: 5px; border-radius: 999px; background: var(--surface-2);
  overflow: hidden; margin-top: 12px;
}
.upload-bar i {
  display: block; height: 100%; width: 0;
  background: var(--brand); transition: width .18s ease-out;
}

.split { display: grid; gap: 18px; }
@media (min-width: 1000px) { .split { grid-template-columns: 2fr 1fr; } }
