/* The CSP application shell.
 *
 * Two layouts and one vocabulary. Every server-rendered page is either an
 * *auth* page — signed out, one card, centred — or an *app* page — a top bar,
 * a bounded column, sections. Pages used to invent a third arrangement each
 * time, which is how a 24rem card ended up left-aligned inside a 64rem column
 * on six of them: nothing was wrong with any single page, and no two agreed.
 *
 * Same palette as the two islands, so the shell around a form and the form
 * itself read as one product rather than two.
 */

:root {
  color-scheme: light;

  /* The palette is duplicated, prefixed, inside renderer.css and builder.css:
     each island is a self-contained bundle with its own scope and cannot see
     these. Listed in the same order in all three so a diff lines up. */
  --page: #f4f6f9;
  --card: #ffffff;
  --sunk: #fbfcfd;
  --ink: #1a2430;
  --ink-soft: #33414f;
  --muted: #5d6b7a;
  --rule: #e3e8ee;
  --rule-strong: #c4cedb;
  --accent: #1b4f8a;
  --accent-hover: #16406f;
  --accent-wash: #eef4fb;
  --danger: #b02b21;
  --danger-wash: #fdf3f2;
  --danger-rule: #f0cfcc;
  --warn: #8a5a00;
  --warn-wash: #fdf6e7;
  --warn-rule: #ecd9a8;
  --ok: #2f7d5b;
  --ok-wash: #eef6f1;
  --ok-rule: #cfe4d8;

  --font: ui-sans-serif, -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --radius-sm: 4px;
  --radius: 6px;
  --radius-lg: 10px;

  --shadow-sm: 0 1px 2px rgb(26 36 48 / 6%);
  --shadow: 0 1px 2px rgb(26 36 48 / 5%), 0 8px 24px -18px rgb(26 36 48 / 25%);
  --shadow-lg: 0 1px 3px rgb(26 36 48 / 8%), 0 20px 44px -26px rgb(26 36 48 / 35%);

  /* One column width for the whole application, and one gutter that closes as
     the viewport does. Pages that set their own were the other half of the
     alignment problem. */
  --measure: 74rem;
  --measure-prose: 48rem;
  --measure-auth: 25rem;
  --gutter: clamp(1rem, 3vw, 2rem);
}

* { box-sizing: border-box; }

body.shell {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font);
  font-size: 0.9375rem;
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}
body.shell-flush { margin: 0; }

/* --- the mark -------------------------------------------------------------
 *
 * Three bars, no wordmark, no brand string: the product name is configuration
 * (SRS C-5), and a stylesheet is not allowed to know it. */
.mark {
  flex: none;
  width: 1.625rem;
  height: 1.625rem;
  border-radius: 7px;
  background: linear-gradient(155deg, #2f6fb3, #16406f);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 22%), 0 1px 2px rgb(26 36 48 / 22%);
  display: grid;
  place-items: center;
}
.mark::after {
  content: "";
  width: 0.75rem;
  height: 0.5625rem;
  background:
    linear-gradient(#fff, #fff) 50% 0 / 100% 2px no-repeat,
    linear-gradient(#fff, #fff) 50% 50% / 68% 2px no-repeat,
    linear-gradient(#fff, #fff) 50% 100% / 38% 2px no-repeat;
}
.mark-lg { width: 2.375rem; height: 2.375rem; border-radius: 10px; }
.mark-lg::after { width: 1.0625rem; height: 0.8125rem; background-size: 100% 2.5px, 68% 2.5px, 38% 2.5px; }

/* --- app chrome ------------------------------------------------------------
 *
 * One set of sections, in one order, on every page. Each page used to carry
 * its own handful of buttons in its heading — Cases offered Conflicts,
 * Conflicts offered Cases — so where you could go depended on where you
 * happened to be, and nothing told you where you were.
 *
 * Sections sit on the left with the workspace; the account sits on the right,
 * behind a rule. Which workspace you are in and who you are signed in as are
 * not navigation, and looked exactly like it when everything was one flat row
 * of identical links. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--card);
  border-bottom: 1px solid var(--rule);
}
.topbar-inner {
  /* Full width, unlike the page under it. Capping the bar at the page measure
     looks tidy and then hides the last section behind the account on a 13-inch
     laptop, which is the machine most of this is used on. Chrome that spans
     the window and content that does not is the ordinary arrangement. */
  padding: 0.5rem var(--gutter);
  min-height: 3.375rem;
  display: flex;
  align-items: center;
  gap: 0.75rem 1.25rem;
  /* Deliberately not wrapping. The bar is capped at the page measure, and a
     long workspace name plus seven sections plus the account exceeds it on an
     ordinary laptop -- which dropped the account onto a second row and made
     the chrome taller than the page heading under it. The sections scroll
     instead, and below 56rem they get a row of their own. */
  flex-wrap: nowrap;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  text-decoration: none;
  color: var(--ink);
  font-weight: 600;
  letter-spacing: -0.01em;
  min-width: 0;
  max-width: 14rem;
}
.brand { flex: none; }
.brand:hover { color: var(--accent); }
.brand-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.nav {
  display: flex;
  gap: 0.125rem;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav::-webkit-scrollbar { display: none; }
.nav-link {
  display: inline-block;
  padding: 0.4375rem 0.6875rem;
  border-radius: var(--radius);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
}
.nav-link:hover { background: var(--page); color: var(--ink); }
/* Marked with aria-current rather than a class, so the page you are on is
   announced as well as shaded. */
.nav-link[aria-current="page"] {
  background: var(--accent-wash);
  color: var(--accent);
  font-weight: 600;
}

.account {
  margin-left: auto;
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}
.account-rule { width: 1px; height: 1.5rem; background: var(--rule); flex: none; }
.account-who {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  font-size: 0.875rem;
  color: var(--ink-soft);
}
.account-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 11rem; }
.avatar {
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent);
  border: 1px solid #d8e5f4;
  display: grid;
  place-items: center;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
}

/* Sitting between the bar and the page, full width, so it is read before the
   page rather than as part of it. */
.banner { background: var(--warn-wash); border-bottom: 1px solid var(--warn-rule); }
.banner-inner {
  padding: 0.625rem var(--gutter);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  font-size: 0.875rem;
  color: var(--warn);
}
.banner .link { color: var(--warn); text-decoration: underline; }

/* --- page ------------------------------------------------------------------ */

.page { max-width: var(--measure); margin: 0 auto; padding: 1.75rem var(--gutter) 5rem; }
/* Settings, security, a list of workspaces: forms and prose, which are
   unreadable at 74rem and were being set at it anyway. */
.page-prose { max-width: var(--measure-prose); }

.crumbs {
  font-size: 0.8125rem;
  color: var(--muted);
  margin: 0 0 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.375rem;
  flex-wrap: wrap;
}
.crumbs .link { font-weight: 400; }

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}
.page-headings { min-width: 0; }
.page-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

.h1 { font-size: 1.5rem; font-weight: 600; margin: 0; letter-spacing: -0.015em; }
/* The sentence under a page title. Bounded by its measure rather than by the
   column, because a line of prose 74rem wide cannot be read. */
.lede { color: var(--muted); margin: 0.375rem 0 0; max-width: 62ch; }

/* --- section ---------------------------------------------------------------
 *
 * The rhythm of every app page: a titled band, its content, and an optional
 * note underneath. Headings used to be .h on some pages and .h2 on others,
 * with whatever margin each happened to inherit. */
.section { margin-top: 2.5rem; }
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.875rem;
}
.h2 { font-size: 1rem; font-weight: 600; margin: 0; letter-spacing: -0.005em; }
.note { color: var(--muted); font-size: 0.8125rem; margin: 0.75rem 0 0; max-width: 74ch; }

.muted { color: var(--muted); }
.mono { font-family: var(--mono); font-size: 0.8125rem; }
.hint { color: var(--muted); font-size: 0.8125rem; margin: 0.375rem 0 0; }
.link { color: var(--accent); text-decoration: none; font-weight: 500; }
.link:hover { text-decoration: underline; }

.empty {
  color: var(--muted);
  padding: 2.5rem 1.5rem;
  text-align: center;
  background: var(--card);
  border: 1px dashed var(--rule-strong);
  border-radius: var(--radius-lg);
  margin: 0;
}

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

.card {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  box-shadow: var(--shadow-sm);
}
.card-lg { padding: clamp(1.5rem, 4vw, 2rem); box-shadow: var(--shadow); }
.card-narrow { width: 100%; max-width: 24rem; }

/* A stacked form. No top margin of its own: what it sits under is the caller's
   business, and a built-in one is invisible until it is wrong. */
.stack { display: flex; flex-direction: column; gap: 1rem; }
.stack-tight { gap: 0.625rem; }
/* A button in a stacked form sizes to its label. Stretching it to the width of
   the longest text field reads as a page-wide action rather than the end of
   the form it belongs to. */
.stack > .button { align-self: flex-start; }

/* A form that flows inside a table cell or the top bar: one control, no layout
   of its own. */
.inline { display: inline; margin: 0; }
/* Except when it wraps a checkbox and its words, which inline leaves sitting
   on different baselines. */
label.inline { display: inline-flex; align-items: center; gap: 0.4375rem; }

/* Controls that belong together on one line — a select and the button that
   acts on it, a search box and its filter.
 *
 * Not .inline. That is `display: inline`, so a full-width .input inside it
 * takes a line to itself and drops whatever follows onto the next one; on a
 * .card it collapses the card entirely and the contents overlap the heading
 * above. Both happened. */
.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
}
/* Sized to the row rather than to the container: .input is width:100% by
   default, which is right in a stacked form and wrong in a line of them. */
.row > .input { width: auto; flex: 1 1 12rem; min-width: 0; }
.row > select.input { flex: 0 1 16rem; }
.row > .button, .row > .muted, .row > .pill { flex: 0 0 auto; }

/* A control that acts on the list it follows. */
.table-wrap + .row, .empty + .row, .table + .row { margin-top: 1rem; }

.actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* A .row in a table cell is working with a column, not a card: the select that
   sizes comfortably in a form pushes its own button onto a second line here. */
.table .row { flex-wrap: nowrap; }
.table .row > select.input { flex: 0 1 10rem; }

/* Search and filters above a list. */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.toolbar .input { width: auto; flex: 1 1 18rem; min-width: 0; }
/* The search box takes the slack; a status filter with three options does not
   need to be as wide as it. */
.toolbar select.input { flex: 0 1 12rem; }

/* --- table ----------------------------------------------------------------- */

/* A table is the one thing here with an irreducible minimum width. Rather than
   letting it push the whole page sideways on a phone, it scrolls inside its
   own box. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-wrap > .table { min-width: 40rem; }

.table {
  width: 100%;
  border-collapse: collapse;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.table th {
  text-align: left;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--sunk);
  padding: 0.625rem 0.875rem;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
.table td { padding: 0.6875rem 0.875rem; border-bottom: 1px solid var(--rule); vertical-align: middle; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--sunk); }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
.table .shrink { width: 1%; white-space: nowrap; }
.row-actions { display: flex; gap: 0.375rem; justify-content: flex-end; align-items: center; }

/* Facts about one thing, rather than rows of many. Was a two-column table with
   a class that did not exist. */
.kv { display: grid; grid-template-columns: minmax(7rem, max-content) minmax(0, 1fr); gap: 0.625rem 1.5rem; margin: 0; }
.kv dt { color: var(--muted); font-size: 0.8125rem; font-weight: 600; }
.kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* --- status ---------------------------------------------------------------- */

.pill {
  display: inline-block;
  font-size: 0.6875rem;
  font-weight: 600;
  padding: 0.1875rem 0.5rem;
  border-radius: 999px;
  background: var(--rule);
  color: var(--ink-soft);
  white-space: nowrap;
  vertical-align: baseline;
}
.pill-breaking { background: var(--danger-wash); color: var(--danger); }
.pill-additive, .pill-initial { background: var(--accent-wash); color: var(--accent); }
.pill-live { background: var(--accent); color: #fff; margin-left: 0.375rem; }
.pill-accepted, .pill-valid, .pill-ok { background: var(--ok-wash); color: var(--ok); }
.pill-quarantined, .pill-invalid { background: var(--danger-wash); color: var(--danger); }
/* Due, or overdue: a thing to act on rather than a thing that went wrong. */
.pill-warn { background: var(--warn-wash); color: var(--warn); }

.alert {
  padding: 0.75rem 0.875rem;
  border: 1px solid;
  border-left-width: 3px;
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.875rem;
  margin: 0 0 1rem;
}
.alert:last-child { margin-bottom: 0; }
.alert p { margin: 0.5rem 0 0; }
.alert p:first-child { margin-top: 0; }
.alert-error { background: var(--danger-wash); border-color: var(--danger-rule); border-left-color: var(--danger); color: var(--danger); }
.alert-warn { background: var(--warn-wash); border-color: var(--warn-rule); border-left-color: var(--warn); color: var(--warn); }
.alert-ok { background: var(--ok-wash); border-color: var(--ok-rule); border-left-color: var(--ok); color: var(--ok); }
.alert-info { background: var(--accent-wash); border-color: #d8e5f4; border-left-color: var(--accent); color: var(--ink-soft); }

/* Something shown once and never again — an invitation link, a token, ten
   recovery codes. It is meant to be selected, so it gets a surface of its own
   rather than being a paragraph in a monospace font. */
.code {
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 1.7;
  color: var(--ink-soft);
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0.75rem 0.875rem;
  margin: 0;
  overflow-x: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.code-columns { columns: 12rem auto; column-gap: 1.5rem; }

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

.field { display: block; min-width: 0; }
.label { display: block; font-size: 0.8125rem; font-weight: 600; color: var(--ink-soft); margin-bottom: 0.375rem; }

.input {
  width: 100%;
  font: inherit;
  padding: 0.5625rem 0.75rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.input::placeholder { color: var(--muted); }
.input:hover:not(:disabled):not(:focus) { border-color: var(--muted); }
.input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
textarea.input { min-height: 8rem; resize: vertical; font-family: var(--mono); font-size: 0.8125rem; }
select.input {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 2.25rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%235d6b7a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.6875rem center;
  background-size: 0.6875rem;
}
input[type="checkbox"] { width: 1rem; height: 1rem; accent-color: var(--accent); }

.button {
  font: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  min-height: 36px;
  padding: 0.4375rem 0.875rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink-soft);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  white-space: nowrap;
  transition: border-color 0.12s ease, background-color 0.12s ease, color 0.12s ease;
}
.button:hover { border-color: var(--muted); background: var(--sunk); }
.button-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.button-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; }
.button-quiet { border-color: transparent; color: var(--muted); background: transparent; }
.button-quiet:hover { border-color: var(--rule-strong); background: var(--sunk); color: var(--ink-soft); }
.button-danger { color: var(--danger); border-color: var(--danger-rule); }
.button-danger:hover { background: var(--danger-wash); border-color: var(--danger); color: var(--danger); }
.button-sm { min-height: 30px; padding: 0.25rem 0.5625rem; font-size: 0.8125rem; }
.button-block { width: 100%; }
.button:disabled { opacity: 0.45; cursor: not-allowed; }

.button:focus-visible, .input:focus-visible, .link:focus-visible,
.nav-link:focus-visible, .brand:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* --- records ---------------------------------------------------------------
 *
 * The caseload: a list of households rather than a table, because each row
 * carries a summary and an action and reads badly as columns on a phone.
 * Built by the island, so these names are a contract with caseload.js. */
.record-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.75rem; }
.record {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 1rem 1.125rem;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
.record:hover { border-color: var(--rule-strong); }
.record-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.record-actions { margin-top: 0.625rem; display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }

/* --- auth ------------------------------------------------------------------
 *
 * Signed out: one card, centred on both axes, under the mark. `safe center`
 * rather than `center` because a card taller than the viewport centred the
 * ordinary way has its top cut off with no way to scroll to it — which the
 * signup form, the longest of them, would do on a laptop. */
body.shell-auth {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  justify-items: center;
  align-content: safe center;
  padding: clamp(1.5rem, 5vh, 3.5rem) 1rem;
}
.auth { width: 100%; max-width: var(--measure-auth); display: flex; flex-direction: column; gap: 1.25rem; }
.auth-wide { max-width: 30rem; }
.auth-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
}
.auth-head { margin-bottom: 1.25rem; }
.auth-title { font-size: 1.25rem; font-weight: 600; margin: 0; letter-spacing: -0.01em; }
.auth-sub { color: var(--muted); font-size: 0.875rem; margin: 0.375rem 0 0; }
/* The install offer on a page with no top bar to hold it. Fixed, out of the
   way, and visible only once the browser has said the application is
   installable -- so it never appears in Firefox, which has no such thing. */
.install-chip {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 30;
  box-shadow: var(--shadow-lg);
}

.auth-alt { margin: 1.125rem 0 0; font-size: 0.8125rem; }
.auth-foot { text-align: center; font-size: 0.8125rem; color: var(--muted); margin: 0; }
.auth-foot .link { font-weight: 500; }
.auth .stack > .button-primary { align-self: stretch; }

/* --- narrow ---------------------------------------------------------------- */

@media (max-width: 56rem) {
  /* Sections drop to their own line and scroll sideways, so the account and
     the workspace stay where they are instead of reflowing into the nav. */
  .topbar-inner { flex-wrap: wrap; row-gap: 0.5rem; }
  .nav {
    order: 3;
    flex: 1 1 100%;
    margin: 0 calc(var(--gutter) * -1);
    padding: 0 var(--gutter);
  }
  .account-name { display: none; }
}
@media (max-width: 34rem) {
  .page { padding-top: 1.25rem; }
  .h1 { font-size: 1.3125rem; }
  .card { padding: 1rem; }
}
