/* FundingAlt Hub and FundingAlt Analytics: Funding Alternative brand (spec section 12).
   One file, no inline styles anywhere: the CSP forbids them. */

:root {
  --orange: #ea4628;       /* bars, rules, buttons, large headings */
  --orange-text: #c4381d;  /* small orange text (contrast) */
  --cream: #f7ead2;        /* panels */
  --charcoal: #404040;     /* text and top bar */
  --grey: #efeff0;         /* page background */
  --steel: #70a0b4;
  --steel-text: #3f6f82;   /* small steel text (contrast) */
  --sky: #9adbf5;
  --red: #d2232a;          /* errors, failed runs */
  --white: #ffffff;
  --line: #d4d4d7;
  --muted: #666668;

  --font: "Barlow", "Helvetica Neue", Arial, sans-serif;
  --radius: 4px;
}

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

html { font-size: 16px; }

body {
  margin: 0;
  background: var(--grey);
  color: var(--charcoal);
  font-family: var(--font);
  font-weight: 400;
  line-height: 1.45;
}

a { color: var(--orange-text); }
a:hover { text-decoration-thickness: 2px; }

:focus-visible {
  outline: 3px solid var(--charcoal);
  outline-offset: 2px;
}

/* ---- Headings: Black 900 italic caps for the page title, Medium below ---- */

h1, .page-title {
  margin: 0 0 1rem;
  color: var(--orange);
  font-size: 2rem;
  font-weight: 900;
  font-style: italic;
  text-transform: uppercase;
  line-height: 1.1;
  letter-spacing: 0.01em;
}

h2, h3 { font-weight: 500; line-height: 1.2; margin: 1.5rem 0 0.5rem; }
h2 { font-size: 1.4rem; }
h3 { font-size: 1.15rem; }

/* ---- Top bar ---- */

.topbar {
  position: relative;  /* where the menu row scrolls sideways, the Admin menu hangs from the bar (Small screens) */
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: 3.75rem;
  padding: 0.5rem 1.5rem;
  background: var(--charcoal);
  border-bottom: 3px solid var(--orange);
  color: var(--white);
}

/* Analytics pages: the way back to the hub, first in the bar. On a phone it reads "← Hub". */
.hub-back {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  flex: 0 0 auto;
  padding: 0.3rem 0.75rem;
  border: 1px solid rgba(255, 255, 255, 0.65);
  border-radius: var(--radius);
  color: var(--white);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.hub-back:hover { background: rgba(255, 255, 255, 0.12); border-color: var(--white); }
.hub-back-short { display: none; }

.brand {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  color: var(--white);
  text-decoration: none;
}

.brand-logo { display: block; height: 2.25rem; width: auto; }

.brand-name {
  padding-left: 0.9rem;
  border-left: 1px solid rgba(255, 255, 255, 0.35);
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.nav { display: flex; align-items: stretch; gap: 0.25rem; align-self: stretch; }

.nav > a {
  display: flex;
  align-items: center;
  padding: 0 0.85rem;
  color: rgba(255, 255, 255, 0.88);
  font-weight: 500;
  text-decoration: none;
}
.nav > a:hover { background: rgba(255, 255, 255, 0.1); color: var(--white); }
.nav > a[aria-current="page"] {
  color: var(--white);
  box-shadow: inset 0 -4px 0 var(--orange);
}
.topbar :focus-visible { outline-color: var(--white); }

/* ---- User menu (a <details>: works without scripts) ---- */

.user-menu { position: relative; margin-left: auto; }

.user-menu summary {
  padding: 0.45rem 0.8rem;
  white-space: nowrap;
  border-radius: var(--radius);
  cursor: pointer;
  font-weight: 500;
  list-style: none;
}
.user-menu summary::-webkit-details-marker { display: none; }
.user-menu summary::after { content: " \25BE"; }
.user-menu summary:hover,
.user-menu[open] summary { background: rgba(255, 255, 255, 0.12); }

.user-menu-panel {
  position: absolute;
  right: 0;
  z-index: 20;
  min-width: 13rem;
  margin-top: 0.4rem;
  padding: 0.5rem 0;
  background: var(--white);
  border: 1px solid var(--line);
  border-top: 3px solid var(--orange);
  border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  color: var(--charcoal);
}
.user-menu-panel a,
.user-menu-panel .link-button {
  display: block;
  width: 100%;
  padding: 0.5rem 1rem;
  color: var(--charcoal);
  text-align: left;
  text-decoration: none;
}
.user-menu-panel a:hover,
.user-menu-panel .link-button:hover { background: var(--grey); }
.user-menu-who { margin: 0; padding: 0.25rem 1rem 0.6rem; border-bottom: 1px solid var(--line); color: var(--muted); }
.user-menu-role { text-transform: capitalize; }
.user-menu-panel form { margin: 0; }

/* ---- Admin menu (R96): the admin pages in groups, a <details> like the user menu. Its
   button looks like the other menu items; the panel starts on the bar's orange rule. ---- */

.admin-menu { position: relative; }

.admin-menu summary {
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 0.85rem;
  color: rgba(255, 255, 255, 0.88);
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}
.admin-menu summary::-webkit-details-marker { display: none; }
.menu-arrow { margin-left: 0.3rem; }
.admin-menu summary:hover,
.admin-menu[open] summary { background: rgba(255, 255, 255, 0.1); color: var(--white); }
.admin-menu summary[aria-current="true"] {
  color: var(--white);
  box-shadow: inset 0 -4px 0 var(--orange);
}

.admin-menu-panel {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-width: 13rem;
  margin-top: 0.5rem;  /* the bar's padding below the menu */
  padding: 0.4rem 0;
  background: var(--white);
  border: 1px solid var(--line);
  border-top: 3px solid var(--orange);
  border-radius: 0 0 var(--radius) var(--radius);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  color: var(--charcoal);
}
/* Analytics: four groups, two by two. */
.admin-menu-panel.admin-menu-columns { width: 30rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.admin-menu-group { padding: 0.2rem 0 0.4rem; }
.admin-menu-heading {
  margin: 0;
  padding: 0.3rem 1rem 0.15rem;
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.admin-menu-panel ul { margin: 0; padding: 0; list-style: none; }
.admin-menu-panel a {
  display: block;
  padding: 0.4rem 1rem;
  color: var(--charcoal);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.admin-menu-panel a:hover { background: var(--grey); }
.admin-menu-panel a[aria-current="page"] { font-weight: 600; box-shadow: inset 4px 0 0 var(--orange); }
/* The top bar's focus ring is white: on the menus' white panels it is charcoal. */
.admin-menu-panel :focus-visible,
.user-menu-panel :focus-visible { outline-color: var(--charcoal); outline-offset: -3px; }

/* Under the ADMIN heading, which admin page this is: "Connections › VPN". */
.admin-crumb { margin: -0.5rem 0 1rem; color: var(--muted); font-weight: 500; }
.admin-crumb-sep { margin: 0 0.2rem; color: var(--orange-text); }
.admin-crumb-page { color: var(--charcoal); }

/* The hidden attribute wins over any display a class gives (faga.js shows and hides with it). */
[hidden] { display: none !important; }

/* ---- Page ---- */

.page { max-width: 80rem; margin: 0 auto; padding: 1.75rem 1.5rem 3rem; }
.page-wide { max-width: none; padding: 0; }   /* the output viewer */

.muted { color: var(--muted); }

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ---- Panels and messages ---- */

.panel {
  padding: 1rem 1.25rem;
  background: var(--cream);
  border-radius: var(--radius);
  border-left: 5px solid var(--orange);
}

.card {
  padding: 1rem 1.25rem;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.flash {
  margin: 0 0 1rem;
  padding: 0.7rem 1rem;
  background: var(--white);
  border: 1px solid var(--line);
  border-left: 5px solid var(--steel);
  border-radius: var(--radius);
  overflow-wrap: anywhere;   /* a long email address on a phone */
}
.flash.error { border-left-color: var(--red); color: var(--red); font-weight: 500; }
.flash.warning { border-left-color: var(--orange); background: var(--cream); }

/* ---- Buttons ----
   White on #ea4628 is 3.9:1, which passes only for large bold text, hence the
   18px bold label. */

.btn, button.btn {
  display: inline-block;
  padding: 0.45rem 1.1rem;
  background: var(--orange);
  border: 2px solid var(--orange);
  border-radius: var(--radius);
  color: var(--white);
  cursor: pointer;
  font-family: var(--font);
  font-size: 1.17rem;
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
}
.btn:hover { background: var(--orange-text); border-color: var(--orange-text); }
/* Two classes, so they outrank `button.btn` (an element and a class) on a <button>
   as well as on a link (tests/test_css.py). */
.btn.btn-secondary { background: var(--white); border-color: var(--charcoal); color: var(--charcoal); }
.btn.btn-secondary:hover { background: var(--charcoal); border-color: var(--charcoal); color: var(--white); }
.btn.btn-danger { background: var(--red); border-color: var(--red); }
.btn.btn-danger:hover { background: #a91b21; border-color: #a91b21; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }

.link-button {
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  font: inherit;
}

/* ---- Forms ---- */

label { display: block; margin: 0 0 0.25rem; font-weight: 500; }

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="search"], input[type="date"], input[type="time"], select, textarea {
  width: 100%;
  max-width: 28rem;
  padding: 0.5rem 0.65rem;
  background: var(--white);
  border: 1px solid #a9a9ad;
  border-radius: var(--radius);
  color: var(--charcoal);
  font: inherit;
}
input:focus, select:focus, textarea:focus { border-color: var(--charcoal); }
.field { margin: 0 0 1rem; }
input[type="file"] { display: block; width: 100%; max-width: 28rem; padding: 0.5rem 0; font: inherit; }
.hint { margin: 0.25rem 0 0; color: var(--muted); }
.field-error { margin: 0.25rem 0 0; color: var(--red); font-weight: 500; }

/* ---- Sign-in, 2FA and account pages ---- */

.auth { max-width: 32rem; margin: 1.5rem auto; }
.auth h1 { font-size: 1.8rem; }
.auth form { margin: 1rem 0; }

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.95em;
}
.secret { word-break: break-all; }

.steps { padding-left: 1.25rem; }
.steps li { margin: 0 0 0.75rem; }
.qr-box { margin: 0.75rem 0; }
svg.qr { width: 14rem; height: auto; }   /* the QR has a viewBox, no fixed size */

.recovery { margin: 1rem 0; }
.recovery-codes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, max-content));
  gap: 0.35rem 2.5rem;
  margin: 0.75rem 0 0;
  padding-left: 1.75rem;
  font-size: 1.05rem;
}

.account { display: grid; gap: 1.25rem; max-width: 40rem; }
.account h2 { margin-top: 0; }

/* ---- Status badges (run states) ---- */

.badge {
  display: inline-block;
  padding: 0.1rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--white);
  font-size: 0.9rem;
  font-weight: 600;
  white-space: nowrap;
}
.badge.succeeded { border-color: var(--steel); color: var(--steel-text); }
.badge.failed { background: var(--red); border-color: var(--red); color: var(--white); }
.badge.timed_out { border-color: var(--red); color: var(--red); }
.badge.running { background: var(--sky); border-color: var(--sky); }
.badge.queued { background: var(--cream); border-color: var(--cream); }
.badge.cancelled { background: #dcdcde; border-color: #dcdcde; }

/* ---- Tables: charcoal header band with an orange underline, figures centred ---- */

/* position: relative: the grids' visually hidden labels (.sr-only, absolutely positioned)
   belong to this box, so they scroll with the table instead of widening the page on a phone. */
.table-wrap { overflow-x: auto; position: relative; }

table.data {
  width: 100%;
  border-collapse: collapse;
  background: var(--white);
}
table.data th, table.data td { padding: 0.6rem 0.85rem; text-align: left; vertical-align: top; }
table.data thead th {
  background: var(--charcoal);
  border-bottom: 3px solid var(--orange);
  color: var(--white);
  font-weight: 600;
}
table.data tbody td { border-bottom: 1px solid var(--line); }
table.data tbody tr:nth-child(even) { background: #f8f8f9; }
table.data tbody tr:hover { background: var(--cream); }
table.data tfoot td { border-top: 2px solid var(--charcoal); font-weight: 600; }
table.data .num { text-align: center; font-variant-numeric: tabular-nums; }

/* Sortable columns (faga.js adds aria-sort and the arrows). Opt a column out
   with data-nosort. */
table.sortable thead th:not([data-nosort]) { cursor: pointer; user-select: none; white-space: nowrap; }
table.sortable thead th:not([data-nosort]):hover { background: #505050; }
table.sortable thead th[aria-sort="ascending"]::after { content: " \25B2"; font-size: 0.7em; }
table.sortable thead th[aria-sort="descending"]::after { content: " \25BC"; font-size: 0.7em; }
table.sortable thead th:focus-visible { outline-color: var(--white); outline-offset: -3px; }

/* ---- Row actions always in view (Serge, 19 Oct 2026: the tabs table's Edit and Delete were
   off-screen behind a sideways scroll he did not see) ----
   Long names with no spaces wrap (.breakable, and the `breakable` filter's <wbr> after _ . - /),
   and the buttons of a row stack in a narrow column, so at 1,024 px and wider the admin tables
   fit their box. Narrower, a table scrolls sideways in its box (.table-wrap) and its actions
   column stays pinned to the box's right edge, on a background of its own (the stripes and
   the hover follow it). A collapsed border stays where the cell was, so the cell also draws its
   rules as shadows just outside its box, on top of the border (the same place: no thicker line
   when the table fits), plus a thin rule on its left. */
.breakable { overflow-wrap: anywhere; }
/* Not a ribbon a few letters wide below 1,024 px, where the table scrolls anyway. */
@media (max-width: 1023px) {
  table.data td.breakable { min-width: 8rem; }
}
table.data .sticky-actions {
  position: sticky;
  right: 0;
  z-index: 1;
  background: var(--white);
  box-shadow: inset 1px 0 0 var(--line), 0 1px 0 var(--line);
}
table.data thead th.sticky-actions { background: var(--charcoal); box-shadow: 0 3px 0 var(--orange); }
table.data tbody tr:nth-child(even) td.sticky-actions { background: #f8f8f9; }
table.data tbody tr:hover td.sticky-actions { background: var(--cream); }
/* One button under the other, all as wide as the widest (beats .inline-form and td.actions form). */
table.data td.sticky-actions > .btn, table.data td.sticky-actions > form { display: block; margin: 0 0 0.35rem; }
table.data td.sticky-actions > :last-child { margin-bottom: 0; }
table.data td.sticky-actions .btn { width: 100%; text-align: center; }

/* ---- Scripts: the "Code from GitHub" header, search, groups, one script ---- */

:root {
  --amber: #b0812f;
  --amber-bg: #fbf1d9;
  --amber-text: #6e4f12;   /* text on amber: the warning badges and the tab list's warnings */
}

.sync-banner {
  margin: 0 0 1.25rem;
  padding: 0.7rem 1rem;
  background: var(--white);
  border: 1px solid var(--line);
  border-left: 5px solid var(--steel);
  border-radius: var(--radius);
}
.sync-banner p { margin: 0; }
.sync-banner .sync-line { font-weight: 500; }
.sync-banner .sync-note,
.sync-banner .sync-error { margin-top: 0.25rem; }
.sync-banner.sync-stale,
.sync-banner.sync-none { background: var(--amber-bg); border-left-color: var(--amber); }
.sync-banner.sync-failed { border-left-color: var(--red); }
.sync-banner.sync-failed .sync-error { color: var(--red); font-weight: 500; }

.search-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin: 0 0 1.25rem; }
.search-bar input[type="search"] { flex: 1 1 16rem; max-width: 28rem; }
.search-bar .btn { font-size: 1rem; padding: 0.4rem 1rem; }

.script-group h2 { margin-top: 1.75rem; }
.script-group .count { font-size: 1rem; font-weight: 400; }
.file-name { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.85rem; }

.badge.rstudio { background: var(--amber-bg); border-color: var(--amber); color: var(--amber-text); }
.rstudio-panel { margin: 0 0 1.25rem; }
.rstudio-panel p { margin: 0.5rem 0 0; }

.crumb { margin: 0 0 0.5rem; }
.script-meta { margin: -0.5rem 0 1rem; }
.header-card, .run-form { margin: 0 0 1.25rem; }
.header-card h2, .run-form h2 { margin-top: 0; }
.run-form { max-width: 40rem; }
.req { color: var(--orange-text); font-weight: 400; }
pre.header-text {
  margin: 0;
  max-height: 28rem;
  overflow: auto;
  white-space: pre-wrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9rem;
  line-height: 1.4;
}

/* ---- Runs: the run page, the list, the queue ---- */

.btn-small { font-size: 1rem; padding: 0.3rem 0.85rem; }
.inline-form { display: inline; margin: 0; }
.actions { white-space: nowrap; }

.run-status, .run-inputs, .run-outputs { margin: 0 0 1.25rem; }
.run-inputs h2, .run-outputs h2, .run-log-section h2 { margin-top: 0; }
.run-log-section h2 { margin-top: 1.5rem; }
.run-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin: 0 0 0.9rem; }
.run-head .badge { padding: 0.2rem 0.85rem; font-size: 1.05rem; }

.facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.35rem 1.5rem;
  margin: 0;
}
.facts dt { color: var(--muted); font-weight: 500; }
.facts dd { margin: 0; overflow-wrap: anywhere; }
.run-error { margin: 0.9rem 0 0; }

pre.run-log {
  min-height: 5rem;
  max-height: 34rem;
  margin: 0 0 1.25rem;
  padding: 0.75rem 1rem;
  overflow: auto;
  background: var(--charcoal);
  border-radius: var(--radius);
  color: var(--white);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
  line-height: 1.4;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem 1rem; margin: 0 0 1.25rem; }
.filter label { font-size: 0.9rem; }
.filters select, .filters input[type="date"] { width: auto; min-width: 9rem; max-width: 16rem; }
.filter-buttons { display: flex; align-items: center; gap: 0.9rem; }
.pager { display: flex; gap: 1.5rem; margin: 1rem 0 0; }

.queue-section h2 { margin-top: 1.5rem; }
.queue-section .count { font-size: 1rem; font-weight: 400; }

/* Run all scripts (/runs and its confirmation page): the title with the button on the right
   (below it on a phone), and the batch's box: progress and Cancel the rest, then the summary. */
.page-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0 1rem; }
.page-head .btn { margin: 0 0 1rem; }
.run-all-box { margin: 0 0 1.25rem; padding: 0.6rem 1rem; background: var(--cream); border-left: 5px solid var(--orange); border-radius: var(--radius); }
.run-all-box p { margin: 0; }
.run-all-box p + p, .run-all-box div + p { margin-top: 0.25rem; }
.run-all-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.4rem 1rem; }
.run-all-status { font-weight: 500; }
.run-all-failed { margin: 0.15rem 0 0; padding-left: 1.25rem; overflow-wrap: anywhere; }
.run-all-estimate { margin: 0 0 0.25rem; }
.run-all-confirm { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.25rem; margin: 1rem 0 0; }
.run-all-warning { margin: 1rem 0 0; }
.run-all-warning p { margin: 0; }
.run-all-warning p + p { margin-top: 0.4rem; }
td.run-all-time { white-space: nowrap; }
#run-all-queue .file-name, #run-all-skipped .file-name { overflow-wrap: anywhere; }

/* ---- Outputs: the list, by date, the viewer ---- */

.subnav { display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; margin: 0 0 1rem; }   /* wraps on a phone */
.subnav [aria-current="page"] { font-weight: 600; box-shadow: inset 0 -3px 0 var(--orange); }
.list-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem 1rem; margin: 0 0 0.75rem; }
.file-cell { overflow-wrap: anywhere; }
.sha { overflow-wrap: anywhere; font-size: 0.85rem; }

/* The viewer: a slim bar, then a frame that fills the rest of the window. */
.viewer-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem 1.25rem;
  padding: 0.4rem 1rem;
  background: var(--white);
  border-bottom: 1px solid var(--line);
}
.viewer-name { flex: 1 1 14rem; min-width: 0; overflow-wrap: anywhere; }
.viewer-links { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1rem; }
.viewer-frame {
  display: block;
  width: 100%;
  height: calc(100vh - 7.3rem);
  min-height: 28rem;
  border: 0;
  background: var(--white);
}
.download-card { max-width: 40rem; margin: 1.5rem; }
.download-card .card-title {
  margin: 0 0 1rem;
  color: var(--charcoal);
  font-size: 1.4rem;
  font-style: normal;
  font-weight: 600;
  text-transform: none;
  overflow-wrap: anywhere;
}

/* ---- Admin: users, CIMS login, toolkit sync, system, audit ---- */

.admin-sections { display: grid; gap: 1.25rem; max-width: 40rem; }
.admin-sections h2, .admin-section h2, .invite-result h2, .sync-result h2 { margin-top: 0; }
.upload-card { max-width: 40rem; }
.upload-card h2 { margin-top: 0; }
.flash-title { margin: 0 0 0.25rem; font-weight: 700; }
.problems { margin: 0; padding-left: 1.25rem; }
.admin-section { margin: 0 0 1.5rem; }
.admin-section .count { font-size: 1rem; font-weight: 400; }
.inline-fields { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0 1rem; }
.inline-fields .field { margin-bottom: 0.5rem; }
.inline-fields input[type="email"] { min-width: 18rem; }
.invite-result { margin: 0 0 1.25rem; border-left: 5px solid var(--orange); }
.invite-link { padding: 0.6rem 0.85rem; background: var(--cream); border-radius: var(--radius); overflow-wrap: anywhere; }
.invite-link code { user-select: all; }
.invite-result h2 { overflow-wrap: anywhere; }   /* a long address in "Invitation emailed to ..." */
.invite-copy { margin: 0.75rem 0 0; }
.invite-copy > summary { cursor: pointer; font-weight: 600; }
.invite-copy .invite-link { margin-bottom: 0; }
select.select-small { width: auto; max-width: 9rem; padding: 0.25rem 0.5rem; }
button.btn.btn-compact { font-size: 1rem; padding: 0.3rem 0.85rem; }   /* beats button.btn's 18px */
td.actions form { margin: 0 0.35rem 0.25rem 0; }
.nowrap { white-space: nowrap; }
.stored-line { overflow-wrap: anywhere; }
/* Admin → VPN (R95): this server's public key with its Copy button, and Turn the VPN off below the settings. */
.vpn-key { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; }
.vpn-key code { padding: 0.35rem 0.6rem; background: var(--cream); border-radius: var(--radius); }
.vpn-off { margin: 1.25rem 0 0; padding-top: 1rem; border-top: 1px solid var(--line); }
.name-list { margin: 0 0 0.75rem; padding-left: 1.25rem; }
.sync-result { margin: 0 0 1.25rem; }
.sync-result h3 { margin-top: 1rem; }

/* "What each role can do" (hub/_role_help.html): a <details>, open at the top of Access,
   closed above the invite form. Each role's name beside its lines; on a phone, above them.
   On a wide screen the first app (Analytics, the longest) takes the left column and the
   others (the tracker, Market Monitoring, then hub admin) stack on the right, so the grid
   below is not pushed a long way down. */
.role-help {
  margin: 0 0 1.25rem;
  background: var(--cream);
  border-left: 5px solid var(--orange);
  border-radius: var(--radius);
}
.role-help > summary { padding: 0.65rem 1rem; cursor: pointer; font-weight: 600; }
.role-help-body { padding: 0 1rem 0.75rem; }
.role-help-app h3 { margin: 0.75rem 0 0.15rem; }
.role-help-app .hint { margin: 0 0 0.4rem; }
.role-list { margin: 0; }
.role-list > div {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr);
  gap: 0 1rem;
  padding: 0.45rem 0;
  border-top: 1px solid var(--line);
}
.role-list dt { font-weight: 700; }
.role-list dd { margin: 0; overflow-wrap: anywhere; }
.role-list ul { margin: 0; padding-left: 1.1rem; }
.role-list ul ul { margin: 0.15rem 0 0.1rem; }
@media (min-width: 1100px) {
  .role-help-body {
    display: grid;
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
    grid-template-rows: auto auto 1fr;
    column-gap: 2rem;
    align-items: start;
  }
  .role-help-app:first-child { grid-row: span 3; }
}
@media (max-width: 720px) {
  .role-help-body { padding: 0 0.75rem 0.6rem; }
  .role-help > summary { padding: 0.6rem 0.75rem; }
  .role-list > div { grid-template-columns: minmax(0, 1fr); }
}
.sync-result .count { font-size: 1rem; font-weight: 400; }

/* System status: colour is never the only signal, each state also has a word. */
.system-table th.check-label { background: none; border-bottom: 1px solid var(--line); color: var(--charcoal); font-weight: 600; white-space: nowrap; }
.badge.status-ok { border-color: var(--steel); color: var(--steel-text); }
.badge.status-warn { background: var(--amber-bg); border-color: var(--amber); color: var(--amber-text); }
.badge.status-bad { background: var(--red); border-color: var(--red); color: var(--white); }
.badge.status-unknown { background: #dcdcde; border-color: #dcdcde; }

/* ---- Dashboard: tabs, the bar above the report, the frame; the admin grid ---- */

.dash-head { padding: 1.25rem 1.5rem 0; }
.dash-head h1 { margin-bottom: 0.75rem; }
/* The title, with Refresh all (or the line in its place) on the right; below it on a phone. */
.dash-title { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0 1rem; }
.dash-title form, .refresh-all-status { margin: 0 0 0.75rem; }
.refresh-all-status { font-weight: 500; }
.refresh-all-summary { margin: 0 0 0.75rem; padding: 0.5rem 0.85rem; background: var(--cream); border-left: 5px solid var(--orange); }
.refresh-all-summary p { margin: 0; }
.refresh-all-summary p + p, .refresh-all-summary div + p { margin-top: 0.25rem; }
.refresh-all-summary-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0 1rem; }
.refresh-all-skipped { margin: 0.15rem 0 0; padding-left: 1.25rem; }

.tabs { display: flex; flex-wrap: wrap; gap: 0.25rem; border-bottom: 3px solid var(--orange); }
.tabs a {
  padding: 0.55rem 1.1rem;
  background: var(--white);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  color: var(--charcoal);
  font-weight: 500;
  text-decoration: none;
}
.tabs a:hover { background: var(--cream); }
.tabs a[aria-current="page"] { background: var(--charcoal); border-color: var(--charcoal); color: var(--white); }

.dash-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1.25rem;
  padding: 0.6rem 1.5rem;
  background: var(--white);
  border-bottom: 1px solid var(--line);
}
.dash-info { flex: 1 1 18rem; min-width: 0; }
.dash-description { margin: 0 0 0.15rem; }
.dash-updated { margin: 0; }
.dash-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; }
/* Beside a greyed Refresh: why it is off (and, for an admin, the way to switch it on). */
.refresh-off-note { color: var(--muted); }
.dash-flash { margin: 0.75rem 1.5rem 0; }
.refresh-status {
  margin: 0;
  padding: 0.55rem 1.5rem;
  background: var(--cream);
  border-left: 5px solid var(--orange);
  font-weight: 500;
}
.dash-empty { margin: 1.5rem; }
.dash-frame {
  display: block;
  width: 100%;
  height: calc(100vh - 13rem);
  min-height: 28rem;
  border: 0;
  background: var(--white);
}

/* The order arrows: a white button (button.btn would otherwise paint them orange). */
button.btn.btn-move {
  padding: 0.25rem 0.6rem;
  background: var(--white);
  border-color: var(--charcoal);
  color: var(--charcoal);
  font-size: 1rem;
}
button.btn.btn-move:hover:not([disabled]) { background: var(--charcoal); border-color: var(--charcoal); color: var(--white); }

input[type="checkbox"] { width: 1.1rem; height: 1.1rem; accent-color: var(--orange); }
.check { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.5rem; font-weight: 400; }
textarea { min-height: 4rem; resize: vertical; }
table.access-grid thead th.num { text-align: center; }
/* The tab list: a "File to show" that no report of the tab's script matches. */
.tab-warning { margin: 0.35rem 0 0; max-width: 26rem; color: var(--amber-text); font-weight: 500; }

/* "File to show" (admin tab form, R81): a long file name stays inside the field on a phone
   (the browser shows the start, the title has the whole name); "Other" shows the text field. */
select.file-select { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-other { display: none; margin: 0.6rem 0 0; }
.file-other.is-open { display: block; }
.file-choice:has(option[value="__other__"]:checked) .file-other { display: block; }

/* "Files it makes" (a script's page, R81) */
.file-list { margin: 0.4rem 0 0.6rem; padding-left: 1.1rem; }
.file-list li { margin: 0 0 0.3rem; overflow-wrap: anywhere; }
.files-card h3 { margin: 0.9rem 0 0.2rem; font-size: 1rem; }
/* File names have no spaces to break at: let them wrap on a phone. */
.files-card .hint, .script-meta { overflow-wrap: anywhere; }

/* ---- The Excel and CSV preview: a page of its own, framed by the dashboard and the viewer.
   The bar, the sheet tabs and the note sit on top; the table scrolls in the box below them
   (both ways), with its header row kept in view. ---- */

/* The page is a column as tall as the window (the frame's, when framed): the top bar (on its
   own), the bar, the sheet tabs and the note at their own height, the table's box takes the rest. */
body.preview-page { display: flex; flex-direction: column; height: 100vh; height: 100dvh; background: var(--white); }
body.preview-page > .topbar { flex: 0 0 auto; }
/* .page's auto side margins would stop a flex item stretching (it would grow to the table's
   width and the page would scroll sideways): no margins, the window's width. */
main.preview-main { flex: 1 1 auto; min-height: 0; min-width: 0; width: 100%; margin: 0; display: flex; flex-direction: column; }
.preview-embedded .preview-sheets { padding-top: 0.4rem; }
.preview-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem 1rem;
  padding: 0.5rem 1rem;
  border-bottom: 1px solid var(--line);
}
.preview-name { flex: 1 1 14rem; min-width: 0; margin: 0; overflow-wrap: anywhere; }
.preview-sheets { flex: 0 0 auto; flex-wrap: nowrap; overflow-x: auto; padding: 0.5rem 1rem 0; }   /* one row: it scrolls on a phone */
.preview-sheets a { white-space: nowrap; }
.preview-note { flex: 0 0 auto; margin: 0; padding: 0.45rem 1rem; background: var(--cream); border-left: 5px solid var(--orange); font-weight: 500; }
.preview-message { margin: 1rem; }
.preview-wrap { flex: 1 1 auto; min-height: 0; overflow: auto; }
/* As wide as its content (it scrolls in its box): cells stay on one line, and a long text
   wraps in a box of its own (.wrap), narrower in the header. */
table.preview-table { width: max-content; min-width: 100%; }
table.preview-table th, table.preview-table td { padding: 0.35rem 0.75rem; white-space: nowrap; }
table.preview-table .wrap { min-width: 8rem; max-width: 26rem; white-space: pre-line; }
table.preview-table th .wrap { min-width: 5rem; max-width: 10rem; }
/* The orange rule is a shadow: a sticky cell's own border does not stay with it. */
table.preview-table thead th { position: sticky; top: 0; z-index: 1; border-bottom: 0; box-shadow: inset 0 -3px 0 var(--orange); }

/* ---- The hub: a card per app (they stack on a phone) ---- */

.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 1.25rem; }
.tile { display: flex; flex-direction: column; border-top: 5px solid var(--orange); }
.tile h2 { margin-top: 0; }
.tile p { margin: 0 0 0.6rem; }
/* The foot (role, last update, Open) sits at the bottom of every card, so the cards of a row
   (the same height: grid items stretch) have their role lines and buttons at the same place. */
.tile-foot { margin-top: auto; }
.tile-updated { min-height: 1.45em; }   /* kept, empty, on a card that has no date line */
.tile .tile-open { margin: 0; padding-top: 0.4rem; }
.tile-button { display: inline-block; min-width: 8rem; text-align: center; }   /* one size for every Open */
/* "On your phone" (R90), after the tiles: the steps, then the address and its QR code beside
   them, or under them on a phone (each column at least 16rem, never wider than the panel). */
.phone-panel { margin-top: 1.75rem; border-top: 5px solid var(--steel); }
.phone-panel h2 { margin-top: 0; }
.phone-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); gap: 0.5rem 2rem; }
.phone-steps h3 { margin: 0.5rem 0 0.35rem; font-size: 1.05rem; }
.phone-steps .steps li { margin: 0 0 0.4rem; }
.phone-menu { font-weight: 700; }
.phone-url { overflow-wrap: anywhere; font-weight: 600; }
.phone-address .qr-box { margin: 0.5rem 0; }
.phone-email { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line); }
table.access-grid select.select-small { max-width: 8.5rem; }
/* People (hub): the grid beside each person's account and actions. The app headers wrap
   (a sortable header otherwise stays on one line), so a column is as wide as its select;
   the last sign-in sits under the email; hub admin is a badge. */
table.people-table thead th.app-col { white-space: normal; min-width: 7rem; }
table.people-table .account-cell { min-width: 9rem; }
.person-seen { font-size: 0.9rem; }
.badge.hub-admin { background: var(--cream); border-color: var(--orange); color: var(--orange-text); }
.access-save { margin: 0.75rem 0 0; }
/* Long names and emails wrap, so the choices next to them come into view on a phone. */
table.access-grid .person-cell { min-width: 9rem; overflow-wrap: anywhere; }

/* ---- htmx (its own indicator styles are switched off for the CSP) ---- */

.htmx-indicator { opacity: 0; transition: opacity 150ms ease-in; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }

/* ---- Small screens ---- */

/* Below this the button back to the hub reads "← Hub", to leave the menu its room. */
@media (max-width: 1380px) {
  .hub-back-long { display: none; }
  .hub-back-short { display: inline; }
}

/* A narrower window: the analytics menu takes a row of its own under the logo, so the bar
   never makes the page scroll sideways (an admin's menu is the widest: at exactly 1,280 px it
   was 4 px too wide, so the menu moves down below 1,320 px). */
@media (max-width: 1319px) {
  .topbar { flex-wrap: wrap; row-gap: 0.25rem; }
  .hub-back ~ .nav { order: 3; width: 100%; overflow-x: auto; align-self: auto; }
  .hub-back ~ .nav > a, .hub-back ~ .nav .admin-menu summary { padding: 0.6rem 0.85rem; }
  /* The menu's own box (it scrolls sideways) would clip a panel hanging from the Admin item:
     the panel hangs from the bar instead, across it, its groups side by side. */
  .hub-back ~ .nav .admin-menu { position: static; }
  .hub-back ~ .nav .admin-menu-panel {
    left: 1.5rem;
    right: 1.5rem;
    width: auto;
    margin-top: 0;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  }
}

@media (max-width: 720px) {
  .topbar { flex-wrap: wrap; gap: 0.5rem 1rem; padding: 0.5rem 1rem; }
  .hub-back { padding: 0.25rem 0.6rem; }
  /* Analytics on a phone: "← Hub" and the person's menu share the first row, the logo
     and the app's name the second, the menu the third. */
  .hub-back ~ .user-menu { order: 1; }
  .hub-back ~ .brand { order: 2; flex-basis: 100%; }
  .nav { order: 3; width: 100%; overflow-x: auto; align-self: auto; }
  .nav > a, .admin-menu summary { padding: 0.6rem 0.85rem; }
  /* The Admin menu's panel, on either host: from the bar, 16 px in from each side, one column. */
  .admin-menu { position: static; }
  .nav .admin-menu-panel, .hub-back ~ .nav .admin-menu-panel {
    left: 1rem;
    right: 1rem;
    width: auto;
    margin-top: 0;
    grid-template-columns: minmax(0, 1fr);
  }
  .page { padding: 1.25rem 1rem 2.5rem; }
  h1, .page-title { font-size: 1.6rem; }
  .dash-head { padding: 1rem 1rem 0; }
  .dash-bar { padding: 0.6rem 1rem; }
  .refresh-status { padding: 0.55rem 1rem; }
  .dash-flash { margin: 0.75rem 1rem 0; }
  .dash-empty { margin: 1rem; }
}

/* The full-width pages (the dashboard, the viewer) keep no page padding on a phone either:
   the frame fills the screen and their own bars carry the side padding. Two classes, so it
   wins over the .page padding above whatever the order. */
.page.page-wide { padding: 0; }

/* A phone (375 px): the logo and the app's name share one line with room to spare. */
@media (max-width: 420px) {
  .brand { gap: 0.6rem; }
  .brand-logo { height: 1.8rem; }
  .brand-name { padding-left: 0.6rem; font-size: 1.05rem; }
}

/* ---- Reports by email (R93): the send form, its confirmation, Sent reports, schedules ---- */

.report-card { max-width: 52rem; }
.report-card h2 { margin: 1.25rem 0 0.35rem; font-size: 1.15rem; }
.report-card .facts { margin: 0 0 1rem; }
.form-buttons { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1rem; margin: 1rem 0 0; }
.form-buttons .inline-form { display: inline-flex; }
fieldset.field { min-width: 0; margin: 0 0 1rem; padding: 0; border: 0; }
fieldset.field legend { margin: 0 0 0.35rem; padding: 0; font-weight: 500; }

/* The recipients: check boxes in a box of their own that scrolls, with a search above it
   (faga.js unhides it and filters the list; without script every name is listed). */
.picker-search { margin: 0 0 0.5rem; }
.pick-list {
  max-width: 36rem;
  max-height: 20rem;
  overflow-y: auto;
  padding: 0.5rem 0.75rem;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.pick-group { margin: 0.35rem 0 0.4rem; color: var(--muted); font-size: 0.9rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
.pick { flex-wrap: wrap; align-items: baseline; column-gap: 0.4rem; row-gap: 0; }
.pick input[type="checkbox"] { flex: none; align-self: center; }
.pick-address { overflow-wrap: anywhere; }
.pick[hidden], .pick-none[hidden] { display: none; }

.confirm-list { margin: 0 0 0.75rem; padding-left: 1.25rem; }
.confirm-list li { margin: 0 0 0.3rem; overflow-wrap: anywhere; }
.message-preview { white-space: pre-wrap; }
.error-text { color: var(--red); }

/* Sent reports */
.sent-files, .sent-recipients { margin: 0.25rem 0 0; padding-left: 1.1rem; }
.sent-recipients li, .sent-files li { margin: 0 0 0.2rem; overflow-wrap: anywhere; }
table.sent-table td { vertical-align: top; }
.sent-copy { margin: 0.35rem 0 0; }
/* View the email: the email's HTML, framed (a sandbox with no permissions), then its text. */
.email-frame {
  display: block;
  width: 100%;
  height: 32rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--white);
}
.email-text { margin: 0.75rem 0 0; }
.email-text pre { margin: 0.5rem 0 0; overflow-wrap: anywhere; font-family: inherit; }
.recipient-sent { color: var(--steel-text); font-weight: 600; }
.recipient-failed, .recipient-not_sent { color: var(--red); font-weight: 600; }
.recipient-queued { color: var(--amber-text); font-weight: 600; }
.badge.send-sent { border-color: var(--steel); color: var(--steel-text); }
.badge.send-sending { background: var(--sky); border-color: var(--sky); }
.badge.send-partly_sent, .badge.send-skipped { background: var(--amber-bg); border-color: var(--amber); color: var(--amber-text); }
.badge.send-failed, .badge.send-not_sent { background: var(--red); border-color: var(--red); color: var(--white); }

/* A schedule's timing, and the recipients' notes */
.weekday-list { display: flex; flex-wrap: wrap; gap: 0 1rem; margin: 0 0 0.5rem 1.6rem; }
.schedule-when select.select-small { width: auto; max-width: 100%; margin: 0 0 0.25rem 1.6rem; }
.schedule-when label[for="rs-time"] { margin-top: 0.75rem; }
input.input-small { max-width: 8rem; }
td.note-cell { max-width: 18rem; white-space: pre-wrap; overflow-wrap: anywhere; }

@media (max-width: 720px) {
  .pick-list { max-height: 16rem; }
  .weekday-list, .schedule-when select.select-small { margin-left: 0; }
}
