/* ===================================================================
   FIGET — DESIGN SYSTEM
   "Paper & graphite" neutrals, one accent, mono for versions and ids.
   Ported from the sibling application so a theme pack written for one
   loads unchanged in the other: the token names below are the contract,
   and /themes/{name}.css overrides them.
   =================================================================== */

/* ── Tokens ─────────────────────────────────────────────────────── */
:root {
  --font-ui: ui-sans-serif, "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", Consolas, monospace;
  --font-display: var(--font-ui);

  --bg: #faf9f7;
  --surface: #ffffff;
  --inset: #f3f1ec;
  --hover: #edeae3;
  --border: #e6e3db;
  --border-strong: #cfcac0;

  --text: #26241f;
  --text-2: #6e6a60;
  --text-3: #9b968c;

  /* Topbar chrome — dark in both themes, so the page beneath it owns the light. */
  --chrome: #211e19;
  --chrome-text: #d9d5cb;
  --chrome-muted: #8f8a7e;
  --chrome-hover: rgb(255 255 255 / 0.08);
  --chrome-border: rgb(255 255 255 / 0.14);

  --accent: #2f5d8a;
  --accent-strong: #24486b;
  --accent-soft: #e6eef6;
  /* Text on an accent-filled surface. Dark themes lift the accent, so this flips with it. */
  --accent-contrast: #ffffff;

  --ok: #2f7d4f;      --ok-soft: #e3f1e8;
  --warn: #9a6200;    --warn-soft: #f7ecd6;
  --danger: #b3261e;  --danger-soft: #f9e5e3;
  --info: #3b6ea5;    --info-soft: #e5edf6;

  --r-1: 6px;
  --r-2: 10px;

  /* The widest the page's content grows, header and body alike. A theme pack sets it with layout.pageWidth. */
  --page-width: 1440px;
  --r-pill: 999px;
  --shadow-1: 0 1px 2px rgb(30 27 20 / 0.06);
  --shadow-2: 0 8px 28px rgb(30 27 20 / 0.14);

  color-scheme: light;
}

/*
  Dark is stated twice, on purpose. These pages are statically rendered and ship no script, so the
  media query is what answers a reader whose system asks for dark. The attribute selector is what an
  explicit choice sets, and it has to win over the system preference — which a single block cannot do.
  Theme packs emit the same pair, so a pack overriding dark overrides both.
*/
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #151412;
    --surface: #1d1b18;
    --inset: #262320;
    --hover: #2c2925;
    --border: #33302a;
    --border-strong: #45403a;

    --text: #e8e5dd;
    --text-2: #a39e93;
    --text-3: #7a756b;

    --chrome: #111009;

    --accent: #7fb0e0;
    --accent-strong: #a2c8ec;
    --accent-soft: #172634;
    --accent-contrast: #0e1a26;

    --ok: #5cb883;      --ok-soft: #1c2f24;
    --warn: #dba54b;    --warn-soft: #3a2f1a;
    --danger: #e5695f;  --danger-soft: #3a201d;
    --info: #7ba7d4;    --info-soft: #1e2a38;

    --shadow-1: 0 1px 2px rgb(0 0 0 / 0.4);
    --shadow-2: 0 8px 28px rgb(0 0 0 / 0.55);

    color-scheme: dark;
  }
}

[data-theme="dark"] {
  --bg: #151412;
  --surface: #1d1b18;
  --inset: #262320;
  --hover: #2c2925;
  --border: #33302a;
  --border-strong: #45403a;

  --text: #e8e5dd;
  --text-2: #a39e93;
  --text-3: #7a756b;

  --chrome: #111009;

  --accent: #7fb0e0;
  --accent-strong: #a2c8ec;
  --accent-soft: #172634;
  --accent-contrast: #0e1a26;

  --ok: #5cb883;      --ok-soft: #1c2f24;
  --warn: #dba54b;    --warn-soft: #3a2f1a;
  --danger: #e5695f;  --danger-soft: #3a201d;
  --info: #7ba7d4;    --info-soft: #1e2a38;

  --shadow-1: 0 1px 2px rgb(0 0 0 / 0.4);
  --shadow-2: 0 8px 28px rgb(0 0 0 / 0.55);

  color-scheme: dark;
}

/* ── Base ───────────────────────────────────────────────────────── */
* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
  background-color: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 0.875rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-strong); text-decoration: underline; }

h1, h2, h3, h4 { color: var(--text); font-family: var(--font-display); }
h1 { font-size: 1.35rem; margin: 0 0 0.9rem; font-weight: 700; }
h2 { font-size: 1.02rem; margin: 1.6rem 0 0.6rem; font-weight: 700; }
h3 { font-size: 0.92rem; margin: 1.2rem 0 0.5rem; font-weight: 700; }

code, .fg-mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

code {
  background: var(--inset);
  padding: 0.05em 0.35em;
  border-radius: var(--r-1);
  font-size: 0.9em;
  overflow-wrap: anywhere;
}

pre {
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: var(--r-1);
  padding: 0.7rem 0.9rem;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 0.8rem;
}
pre code { padding: 0; background: none; }

/* Mixed from the accent rather than its soft tint: on several themes the tint is within a few shades of the
   page, and a selection nobody can see is a selection nobody trusts. */
::selection { background: color-mix(in srgb, var(--accent) 40%, transparent); }

/* ── Utilities ──────────────────────────────────────────────────── */
.fg-row   { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.fg-stack { display: flex; flex-direction: column; gap: 0.75rem; }
.fg-grow  { flex: 1; }
.fg-right { margin-left: auto; }

.fg-muted { color: var(--text-2); }
.fg-faint { color: var(--text-3); }
.fg-small { font-size: 0.78rem; }

/* Uppercase mono label — the recurring pattern for anything that names a value. */
.fg-label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
}

.fg-empty { text-align: center; padding: 2rem; color: var(--text-3); font-style: italic; }

/* ── Shell and topbar ───────────────────────────────────────────── */
.fg-shell { display: flex; flex-direction: column; min-height: 100vh; }

.fg-navbar { background-color: var(--chrome); padding: 0 1.5rem; }

.fg-navbar-inner {
  max-width: var(--page-width);
  margin: 0 auto;
  min-height: 48px;
  padding: 0.45rem 0;
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

.fg-navbar-brand {
  color: var(--chrome-text);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.9rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  margin-right: auto;
}
.fg-navbar-brand:hover { color: #fff; text-decoration: none; }
.fg-navbar-brand { display: inline-flex; align-items: center; gap: 0.55rem; }
.fg-brand-logo { display: block; height: 26px; width: auto; max-width: 180px; }

.fg-nav-links { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }

.fg-navbar a:not(.fg-navbar-brand) {
  color: var(--chrome-muted);
  font-size: 0.86rem;
  padding: 0.3rem 0.6rem;
  border-radius: var(--r-1);
}
.fg-navbar a:not(.fg-navbar-brand):hover {
  color: var(--chrome-text);
  background: var(--chrome-hover);
  text-decoration: none;
}
.fg-navbar a.active { background: var(--chrome-hover); color: var(--chrome-text); }

.fg-navbar .fg-signed-in { display: flex; align-items: center; gap: 0.6rem; }
.fg-navbar .fg-signed-in .fg-who { color: var(--chrome-muted); font-size: 0.8rem; }

/* One glyph, no state: the server renders this page statically and cannot know which theme the
   reader chose, so the button says what it does rather than what is currently on. */
.fg-theme-btn {
  background: transparent;
  border: 1px solid var(--chrome-border);
  border-radius: var(--r-pill);
  color: var(--chrome-muted);
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1;
  padding: 0.28rem 0.55rem;
  transition: color 0.15s ease, background 0.15s ease;
}
.fg-theme-btn:hover { color: var(--chrome-text); background: var(--chrome-hover); }

/* A button that has to read as a link: sign-out posts, because a GET sign-out is reachable
   from any page a reader visits. */
.fg-linklike {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.fg-navbar .fg-linklike { color: var(--chrome-muted); font-size: 0.86rem; }
.fg-navbar .fg-linklike:hover { color: var(--chrome-text); }

.fg-main {
  flex: 1;
  width: 100%;
  /* Its padding inside the width, so its content lines up with the header's, which has its padding outside. */
  max-width: calc(var(--page-width) + 3rem);
  margin: 1.6rem auto;
  padding: 0 1.5rem;
}

/* ── Page heading and breadcrumbs ───────────────────────────────── */
.fg-crumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  margin: 0 0 0.5rem;
  color: var(--text-3);
}
.fg-crumbs a { color: var(--text-2); }
.fg-crumbs a:hover { color: var(--accent); }
.fg-crumb-sep { color: var(--text-3); margin: 0 0.35rem; }

.fg-page-head { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.fg-page-head h1 { margin-bottom: 0; }

/* The publisher's icon. Removed by app.js when it fails to load, which an air-gapped install
   guarantees, so nothing here has to style a broken image. */
/* A package's own icon, as its author drew it: no frame or backdrop, which made every icon look like a tile. */
.fg-icon {
  width: 34px;
  height: 34px;
  object-fit: contain;
  border-radius: var(--r-1);
  flex: 0 0 auto;
}
.fg-version {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
  font-size: 0.95rem;
  font-weight: 400;
}
.fg-lede { color: var(--text-2); max-width: 48rem; margin: 0.4rem 0 1rem; }

/* ── Cards and panels ───────────────────────────────────────────── */
.fg-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-2);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.fg-card-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.8rem;
  border-bottom: 1px solid var(--border);
  background: var(--inset);
  font-weight: 700;
  font-size: 0.85rem;
}
.fg-card-body { padding: 0.8rem; }

.fg-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-2);
  box-shadow: var(--shadow-1);
  padding: 0.9rem 1.1rem;
}
.fg-panel > h2:first-child { margin-top: 0; }
.fg-panel-danger { border-color: var(--danger); }

/* Sign-in is the one page that is a single narrow column rather than a full-width view. */
.fg-signin { max-width: 26rem; margin: 3rem auto; }

/* One full-width button per sign-in provider, and the way to the local form underneath. */
.fg-signin-providers { display: grid; gap: 0.5rem; }
.fg-btn.fg-btn-block { width: 100%; justify-content: center; }
.fg-signin-local { margin: 0.9rem 0 0; text-align: center; font-size: 0.9em; }

.fg-panel-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr));
  align-items: start;
}

/* ── Tables: the record list ────────────────────────────────────── */
.fg-table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-2);
  box-shadow: var(--shadow-1);
  overflow-x: auto;
}

.fg-table { width: 100%; border-collapse: collapse; }

.fg-table th {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  text-align: left;
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--border);
  background: var(--inset);
  white-space: nowrap;
}

.fg-table td {
  padding: 0.45rem 0.7rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.82rem;
  vertical-align: middle;
  white-space: nowrap;
}
.fg-table td:last-child, .fg-table th:last-child { white-space: normal; }
.fg-table tr:last-child td { border-bottom: none; }
.fg-table tbody tr:hover { background: var(--inset); }

/* An unlisted or revoked row is still readable, just plainly secondary. */
.fg-table tr.is-inactive td { color: var(--text-3); }

/* Versions, sizes and counts line up when they are tabular. */
.fg-num, .fg-table td.fg-num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* A table cell, not a flex container: `display: flex` here overrides `display: table-cell`, which
   takes the cell out of the row and leaves the column a wide empty gutter with borders that stop
   short of it. Width 1% is the usual trick for "shrink to content" in a full-width table. */
.fg-table td.fg-cell-actions {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}
.fg-cell-actions form { display: inline; margin: 0; }
.fg-cell-actions > * + * { margin-left: 0.4rem; }

/* A form opened under the row it changes (the upstreams of a feed): a row of its own, set in from the table by an accent
   rule so it reads as belonging to the row above rather than as the next row. */
.fg-table tr.fg-row-panel > td { padding: 0 0.5rem 0.75rem 1.5rem; background: var(--surface); }
.fg-inline-panel {
  border-left: 3px solid var(--accent);
  background: var(--inset);
  border-radius: var(--r-1);
  padding: 0.75rem 1rem;
  margin-block: 0.5rem;
  white-space: normal;
}
.fg-inline-panel > h3 { margin: 0 0 0.5rem; font-size: 0.95rem; }

/* ── Toolbar above a list ───────────────────────────────────────── */
.fg-toolbar {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 0.7rem;
}

.fg-result-count { color: var(--text-2); font-size: 0.8rem; margin: 0.6rem 0; }

/* ── Pager ──────────────────────────────────────────────────────── */
.fg-pager {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0;
  flex-wrap: wrap;
}
.fg-pager-info { font-size: 0.78rem; color: var(--text-2); white-space: nowrap; }

/* How many rows per page. Quiet by default: it is a preference, not an action, so it should not
   compete with the Newer/Older buttons beside it. */
.fg-pager-sizes { display: inline-flex; gap: 0.4rem; align-items: baseline; font-size: 0.78rem; }
.fg-pager-sizes a { color: var(--text-2); text-decoration: none; }
.fg-pager-sizes a:hover { color: var(--accent); text-decoration: underline; }
.fg-pager-sizes strong { color: var(--text); font-weight: 600; }

/* No rules for the grid's own paginator: it is not used. Styling it meant fighting scoped selectors
   that outrank plain ones, and the background this set wiped out the arrows it draws — which is what
   turned the pager into blank circles. The grid now uses the same fg-btn pager as everything else. */

/* ── Buttons ────────────────────────────────────────────────────── */
.fg-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  color: var(--text);
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.32rem 0.85rem;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  white-space: nowrap;
}
.fg-btn:hover { background: var(--hover); text-decoration: none; color: var(--text); }
.fg-btn:disabled { opacity: 0.45; cursor: not-allowed; }

.fg-btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); }
.fg-btn-primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); color: var(--accent-contrast); }

.fg-btn-ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.fg-btn-ghost:hover { background: var(--hover); color: var(--text); }

.fg-btn-danger { background: transparent; border-color: var(--danger); color: var(--danger); }
.fg-btn-danger:hover { background: var(--danger-soft); color: var(--danger); }

.fg-btn-sm { font-size: 0.72rem; padding: 0.18rem 0.6rem; }

/* ── Pills (filters that are links, because these pages do not post to filter) ── */
.fg-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--text-2);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  padding: 0.2rem 0.65rem;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.fg-pill:hover { background: var(--hover); color: var(--text); text-decoration: none; }
.fg-pill.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-strong); }

.fg-pill-count {
  font-size: 0.62rem;
  background: var(--inset);
  border-radius: var(--r-pill);
  padding: 0 0.35rem;
  color: var(--text-2);
}
.fg-pill.active .fg-pill-count { background: var(--surface); }

/* ── Badges ─────────────────────────────────────────────────────── */
.fg-badge {
  display: inline-block;
  padding: 0.1rem 0.42rem;
  border-radius: var(--r-1);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.7rem;
  font-weight: 500;
  white-space: nowrap;
  line-height: 1.5;
  /* Reserved, and left transparent by every variant: it keeps a badge the same size whether or not
     something paints it, so a bordered one does not sit a pixel taller than its neighbours. */
  border: 1px solid transparent;
  background: var(--inset);
  color: var(--text-2);
}
.fg-badge-accent { background: var(--accent-soft); color: var(--accent-strong); }
.fg-badge-ok     { background: var(--ok-soft);     color: var(--ok); }
.fg-badge-warn   { background: var(--warn-soft);   color: var(--warn); }
.fg-badge-danger { background: var(--danger-soft); color: var(--danger); }
.fg-badge-info   { background: var(--info-soft);   color: var(--info); }
.fg-badge-plain  { background: var(--inset); color: var(--text-2); }

/* ── Chips (tags) ───────────────────────────────────────────────── */
.fg-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.25rem 0; }
.fg-chip {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-2);
  font-size: 0.75rem;
  white-space: nowrap;
}

details.fg-more { margin-top: 0.6rem; }
details.fg-more > summary { cursor: pointer; color: var(--accent); font-size: 0.8rem; }
details.fg-more[open] > summary { margin-bottom: 0.5rem; }

/* ── Forms ──────────────────────────────────────────────────────── */
.fg-field { display: flex; flex-direction: column; gap: 0.25rem; margin-bottom: 0.7rem; }
.fg-field > label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* Blazor's InputText renders a bare <input> with no type attribute, so it has to be matched too. */
.fg-input,
input:not([type]), input[type=text], input[type=password], input[type=search],
input[type=number], input[type=email], input[type=url], input[type=date], select, textarea {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-1);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  padding: 0.4rem 0.6rem;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
input:focus, select:focus, textarea:focus, .fg-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input::placeholder, textarea::placeholder { color: var(--text-3); }

input[type=checkbox] { width: auto; margin: 0; accent-color: var(--accent); }

.fg-check { display: flex; gap: 0.5rem; align-items: center; font-size: 0.85rem; }

fieldset {
  border: 1px solid var(--border);
  border-radius: var(--r-1);
  padding: 0.6rem 0.8rem;
  display: grid;
  gap: 0.35rem;
  margin: 0 0 0.7rem;
}
legend {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  padding: 0 0.25rem;
}

.fg-form { display: grid; gap: 0.5rem; max-width: 34rem; }
.fg-form button { justify-self: start; }

/* One bordered bar holding the query, the filter and the button, so the filter reads as part of
   the search rather than as a separate control sitting under it. */
.fg-search-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }

.fg-search-bar {
  display: flex;
  flex: 1 1 28rem;
  min-width: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-2);
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow-1);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.fg-search-bar:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

.fg-search-bar input[type=search] {
  flex: 1;
  min-width: 0;
  border: none;
  border-radius: 0;
  background: transparent;
  padding: 0.5rem 0.8rem;
  font-size: 0.9rem;
}
.fg-search-bar input[type=search]:focus { outline: none; box-shadow: none; }

/* The one control a stylesheet cannot fully own: the popup list is drawn by the operating system.
   Everything around it is styled and it sits inside the bar, so the seam does not show. */
.fg-search-bar select {
  width: auto;
  flex: 0 0 auto;
  border: none;
  border-left: 1px solid var(--border);
  border-radius: 0;
  background: var(--inset);
  color: var(--text);
  padding: 0 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  cursor: pointer;
}
.fg-search-bar select:focus { outline: none; box-shadow: none; }

.fg-search-bar button {
  flex: 0 0 auto;
  border: none;
  border-radius: 0;
  background: var(--accent);
  color: var(--accent-contrast);
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 0.82rem;
  padding: 0 1.1rem;
  cursor: pointer;
}
.fg-search-bar button:hover { background: var(--accent-strong); }

/* ── Alerts and validation ──────────────────────────────────────── */
.fg-alert {
  border: 1px solid var(--border);
  border-radius: var(--r-1);
  padding: 0.6rem 0.9rem;
  font-size: 0.82rem;
  background: var(--inset);
  color: var(--text);
  margin: 0 0 0.9rem;
}
.fg-alert-info   { background: var(--info-soft);   border-color: var(--info); }
.fg-alert-warn   { background: var(--warn-soft);   border-color: var(--warn); }
.fg-alert-danger { background: var(--danger-soft); border-color: var(--danger); }
.fg-alert-ok     { background: var(--ok-soft);     border-color: var(--ok); }

.validation-message, .fg-error { color: var(--danger); font-size: 0.8rem; margin: 0; }
.fg-ok { color: var(--ok); margin: 0; }
.valid.modified:not([type=checkbox]) { outline: 1px solid var(--ok); }
.invalid { outline: 1px solid var(--danger); }

/* A secret is shown once, so it gets the loudest surface on the page. */
.fg-secret {
  border: 1px solid var(--ok);
  background: var(--ok-soft);
  border-radius: var(--r-2);
  padding: 0.8rem 1rem;
  margin-bottom: 1rem;
}
.fg-secret pre { background: var(--surface); }

/* ── Tabs ───────────────────────────────────────────────────────── */
.fg-tabs {
  display: flex;
  gap: 0;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--border);
  margin: 0 0 1rem;
}
.fg-tab {
  background: none;
  border: none;
  color: var(--text-2);
  padding: 0.45rem 0.9rem;
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 600;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color 0.15s ease;
}
.fg-tab:hover { color: var(--text); text-decoration: none; }
.fg-tab.active { color: var(--accent-strong); border-bottom-color: var(--accent); }

/* ── Definition lists ───────────────────────────────────────────── */
.fg-facts { display: grid; grid-template-columns: max-content 1fr; gap: 0.3rem 1.1rem; margin: 0; }
.fg-facts dt {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  padding-top: 0.15rem;
}
.fg-facts dd { margin: 0; overflow-wrap: anywhere; font-size: 0.85rem; }

/* On a phone a URL beside its label gets a third of the width and breaks every twenty characters; under its
   label it gets all of it. Only lists that hold a URL: a short fact reads better on one line. */
@media (max-width: 600px) {
  .fg-facts:has(.fg-url) { grid-template-columns: minmax(0, 1fr); gap: 0.15rem; }
  .fg-facts:has(.fg-url) dd + dt { margin-top: 0.5rem; }
}

/* ── Copyable URL ───────────────────────────────────────────────── */
.fg-url { display: flex; align-items: center; gap: 0.4rem; min-width: 0; }
.fg-url code { overflow-wrap: anywhere; font-size: 0.78rem; }

/* The same floor, for the same reason, where a URL sits in a table cell: it breaks at any character, so
   without one the cell collapses to one character on a phone and stretches its row down the page. Scoped
   to tables, because a URL shown in a narrow panel elsewhere has no such problem and no room to spare. */
.fg-table .fg-url { min-width: 14rem; }

/* A copy button is an icon: a word beside every URL and command was noise, and read as part of what gets
   copied. The icons are Bootstrap Icons' "copy" and "check2" (MIT licence), drawn as masks filled with
   currentColor so every theme colours them. The word stays in the markup and in aria-label for screen readers;
   it is shown only when copying failed and the reader has to press Ctrl+C. */
.fg-copy {
  --fg-copy-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z'/%3E%3C/svg%3E");
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  background: transparent;
  color: var(--text-2);
  border: 1px solid transparent;
  border-radius: var(--r-1);
  font-size: 0;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.fg-copy::before {
  content: "";
  width: 1rem;
  height: 1rem;
  background-color: currentColor;
  -webkit-mask: var(--fg-copy-icon) center / contain no-repeat;
  mask: var(--fg-copy-icon) center / contain no-repeat;
}
.fg-copy:hover { background: var(--hover); color: var(--text); border-color: var(--border); }
.fg-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.fg-copy.copied {
  --fg-copy-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0'/%3E%3C/svg%3E");
  color: var(--ok);
  background: var(--ok-soft);
  border-color: var(--ok);
}
.fg-copy.copy-failed {
  width: auto;
  padding: 0 0.5rem;
  font-size: 0.72rem;
  color: var(--warn);
  border-color: var(--warn);
  white-space: nowrap;
}
.fg-copy.copy-failed::before { display: none; }

/* A command with its copy button inside the box, top right, where a reader looks for it. The command keeps
   room on its right so a long one never runs under the button. */
.fg-snippet { position: relative; margin: 0 0 0.5rem; }
/* Wrapped, not scrolled. A scrolling box slides its text under the button, and on a phone every command scrolls: the
   reserved right padding only keeps text clear of the button when the text wraps inside it. */
.fg-snippet pre { margin: 0; padding-right: 2.9rem; white-space: pre-wrap; overflow-wrap: anywhere; overflow-x: visible; }
.fg-snippet .fg-copy { position: absolute; top: 0.4rem; right: 0.4rem; }

/* ── The signed-in menu ─────────────────────────────────────────── */
/* A <details>, not a scripted popover: this bar is rendered by static SSR on every page, including the
   anonymous ones that load no framework, so opening has to work with no script at all. */
.fg-nav-dropdown { position: relative; }

.fg-nav-dropdown > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.55rem;
  border-radius: var(--r-1);
  color: var(--chrome-muted);
  font-size: 0.86rem;
}
.fg-nav-dropdown > summary::-webkit-details-marker { display: none; }
.fg-nav-dropdown > summary:hover,
.fg-nav-dropdown[open] > summary { color: var(--chrome-text); background: var(--chrome-hover); }
.fg-nav-dropdown-caret { font-size: 0.62rem; }

.fg-nav-dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 1090;
  min-width: 210px;
  padding: 0.35rem;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-2);
  box-shadow: var(--shadow-2);
}

/* display:contents lifts the button out of the form, so it stays a direct child of the flex column
   and lines up with the anchors beside it. */
.fg-nav-dropdown-menu form { display: contents; }

.fg-nav-dropdown-menu a,
.fg-nav-dropdown-menu button {
  /* Beats `.fg-navbar a:not(.fg-navbar-brand)`, which paints for the dark chrome this menu sits in
     front of rather than for the surface it is drawn on. */
  color: var(--text) !important;
  background: none;
  border: 0;
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  width: 100%;
  padding: 0.4rem 0.6rem;
  border-radius: var(--r-1);
  cursor: pointer;
}
.fg-nav-dropdown-menu a:hover,
.fg-nav-dropdown-menu button:hover { background: var(--hover); text-decoration: none; }

.fg-nav-dropdown-menu hr { border: none; border-top: 1px solid var(--border); margin: 0.25rem 0; }

.fg-nav-dropdown-version {
  padding: 0.4rem 0.6rem;
  font-size: 0.7rem;
  color: var(--text-3);
  font-family: var(--font-mono);
}

/* ── Admin shell (the side navigation of the admin area) ────────── */
.fg-admin-shell {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: 1.8rem;
  align-items: start;
}

.fg-admin-nav {
  position: sticky;
  top: 1rem;
  display: flex;
  flex-direction: column;
  gap: 1.2rem;
  padding-right: 1rem;
  border-right: 1px solid var(--border);
}

.fg-admin-nav-group { display: flex; flex-direction: column; gap: 0.1rem; }

.fg-admin-nav-title {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: 0.3rem;
}

.fg-admin-nav-link {
  padding: 0.3rem 0.6rem;
  font-size: 0.84rem;
  color: var(--text-2);
  border-radius: var(--r-1);
  border-left: 3px solid transparent;
  transition: color 0.15s, background-color 0.15s, border-color 0.15s;
}
.fg-admin-nav-link:hover { color: var(--text); background: var(--hover); text-decoration: none; }
.fg-admin-nav-link.active { color: var(--accent); border-left-color: var(--accent); font-weight: 600; }

/* Panels stacked in the admin body, one per section: without a gap their borders touched and read as one
   box with a seam. The facts under a settings form get a rule, so the Save button ends the form instead of
   sitting on top of the first fact. */
:not(.fg-panel-grid) > .fg-panel + .fg-panel { margin-top: 1rem; }
h2 + .fg-panel { margin-top: 0.5rem; }
.fg-panel .fg-form + .fg-facts {
  margin-top: 1rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--border);
}

/* The phone menu: hidden on a wide screen, where the column above does the job. */
.fg-admin-menu { display: none; }

/* Narrow screens have no room for a column. The strip that replaced it ran to two rows of two groups side by
   side, a third of the screen before the page began; a bar that opens into the same links costs one line. */
@media (max-width: 900px) {
  .fg-admin-shell { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .fg-admin-nav { display: none; }
  .fg-panel-grid { grid-template-columns: minmax(0, 1fr); }

  .fg-admin-menu {
    display: block;
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-2);
  }
  .fg-admin-menu > summary {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.6rem 0.9rem;
    cursor: pointer;
    list-style: none;
    user-select: none;
    min-height: 2.75rem; /* a thumb, not a cursor */
  }
  .fg-admin-menu > summary::-webkit-details-marker { display: none; }
  .fg-admin-menu-icon { font-size: 1.05rem; line-height: 1; color: var(--text-2); }
  .fg-admin-menu-label {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-3);
  }
  .fg-admin-menu-current {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
    color: var(--accent);
  }
  .fg-admin-menu-caret { margin-left: auto; font-size: 0.7rem; color: var(--text-3); transition: transform 0.15s ease; }
  .fg-admin-menu[open] > summary { border-bottom: 1px solid var(--border); }
  .fg-admin-menu[open] .fg-admin-menu-caret { transform: rotate(180deg); }

  .fg-admin-menu-panel {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: 0.8rem 0.6rem 0.9rem;
  }
  .fg-admin-menu-panel .fg-admin-nav-title { padding-left: 0.6rem; }
  .fg-admin-menu-panel .fg-admin-nav-link { padding: 0.6rem 0.6rem; font-size: 0.92rem; }
}

/* The first column is always a name or an id, and some are very long — a fully qualified module id, or
   a version with a prerelease label. Every other cell stays on one line, but forcing this one to as well
   pushed the table past its container, which turned into a horizontal scrollbar and clipped the buttons
   in the last column. Only this column is allowed to wrap.

   It needs a floor, though. `overflow-wrap: anywhere` does not only let text wrap: it shrinks the cell's
   minimum width to a single character, and the table's automatic layout takes that literally. On a phone
   the columns that never wrap claimed the whole width and this one collapsed - a feed name broke into
   "galle / ry" and each row grew roughly a thousand pixels tall around it, with the name floating in the
   middle and the next row far off-screen. With a minimum, long ids still wrap as intended but the column
   never falls below a readable width, and a table wider than a phone scrolls sideways inside its wrapper,
   which is what a table of records should do there. */
.fg-table td:first-child { white-space: normal; overflow-wrap: anywhere; min-width: 8rem; }

/* ── Uploading into an asset directory ──────────────────────────── */
/* A dashed target that says what dropping does. Visible without script, where the file picker inside it
   still works as a plain control; the drag highlight and the progress rows only exist once app.js runs. */
.fg-dropzone {
  border: 2px dashed var(--border-strong);
  border-radius: var(--r-2);
  background: var(--surface);
  padding: 1rem 1.2rem;
  margin: 0.8rem 0;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.fg-dropzone-over { border-color: var(--accent); background: var(--accent-soft); }
.fg-dropzone.fg-uploading { border-style: solid; }
.fg-dropzone-prompt { margin: 0 0 0.3rem; }
.fg-dropzone-pick { color: var(--accent); text-decoration: underline; cursor: pointer; }
.fg-dropzone-pick input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.fg-dropzone-pick:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

.fg-upload-list { list-style: none; margin: 0.5rem 0 0; padding: 0; display: grid; gap: 0.3rem; }
.fg-upload-list:empty { display: none; }
.fg-upload-list li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(6rem, 12rem) minmax(0, max-content);
  gap: 0.6rem;
  align-items: center;
  font-size: 0.82rem;
}
.fg-upload-name { overflow-wrap: anywhere; font-family: var(--font-mono); }
.fg-upload-list progress { width: 100%; accent-color: var(--accent); }
.fg-upload-ok [data-status] { color: var(--ok); }
.fg-upload-failed [data-status] { color: var(--danger); }

@media (max-width: 600px) {
  .fg-upload-list li { grid-template-columns: minmax(0, 1fr); gap: 0.15rem; }
}
.fg-check.fg-inline { display: inline-flex; margin-left: 0.6rem; }

/* An upstream's place in the priority order, with the two buttons that change it kept on the number's line. */
.fg-priority { flex-wrap: nowrap; gap: 0.3rem; }
.fg-priority span { min-width: 1.2rem; }
.fg-priority form { display: contents; }
.fg-priority .fg-btn[disabled] { opacity: 0.35; cursor: default; }

/* ── Error pages ────────────────────────────────────────────────── */
/* The code as a large, faint watermark the title sits on, as in CustomsHive: recognisable at a glance, and
   out of the way of the words that say what happened. */
.fg-error-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 3rem 0 2rem;
  max-width: 36rem;
  margin: 0 auto;
}
.fg-error-code {
  font-family: var(--font-mono);
  font-size: clamp(5.5rem, 22vw, 12rem);
  font-weight: 700;
  line-height: 1;
  color: var(--accent);
  opacity: 0.14;
  user-select: none;
  margin-bottom: -1.2rem;
}
.fg-error-page h1 { margin: 0 0 0.5rem; }
.fg-error-page p { margin: 0.25rem 0; }
.fg-error-user {
  margin: 1.2rem 0 0.6rem;
  padding: 0.75rem 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-2);
  text-align: left;
  width: 100%;
  max-width: 22rem;
  font-size: 0.85rem;
}
.fg-error-user-label { font-weight: 600; margin-bottom: 0.2rem; }
.fg-error-actions { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; margin-top: 1.2rem !important; }

/* ── Tooltips ───────────────────────────────────────────────────── */
/* Drawn by app.js from each element's title. Dark in both themes, like the top bar, so it reads as chrome
   laid over the page rather than as part of it. */
.fg-tooltip {
  position: absolute;
  z-index: 1100;
  max-width: 18rem;
  padding: 0.3rem 0.55rem;
  background: var(--chrome);
  color: var(--chrome-text);
  border: 1px solid var(--chrome-border);
  border-radius: var(--r-1);
  box-shadow: var(--shadow-2);
  font-family: var(--font-ui);
  font-size: 0.75rem;
  line-height: 1.35;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 0.12s ease, transform 0.12s ease;
}
.fg-tooltip.fg-tooltip-on { opacity: 1; transform: none; }
.fg-tooltip::after {
  content: "";
  position: absolute;
  left: var(--fg-tooltip-arrow, 50%);
  top: 100%;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--chrome);
  border-right: 1px solid var(--chrome-border);
  border-bottom: 1px solid var(--chrome-border);
  transform: translate(-50%, -50%) rotate(45deg);
}
.fg-tooltip-below::after { top: 0; transform: translate(-50%, -50%) rotate(225deg); }
@media (prefers-reduced-motion: reduce) {
  .fg-tooltip { transition: none; transform: none; }
}

/* ── Sortable headers ───────────────────────────────────────────── */
/* A header link that reads as a header: the column's own label, with an arrow only on the column in use. */
.fg-sort { color: inherit; display: inline-flex; align-items: center; gap: 0.3rem; white-space: nowrap; }
.fg-sort:hover { color: var(--text); text-decoration: none; }
.fg-sort::after { content: ""; font-size: 0.7em; color: var(--accent); }
.fg-sort-asc::after { content: "\25B2"; }
.fg-sort-desc::after { content: "\25BC"; }

/* ── Icons on buttons ───────────────────────────────────────────── */
/* Bootstrap Icons "download" and "cloud-download" (MIT), drawn as masks in the button's own colour, the same way
   the copy icon is. A button in a table row is the icon alone - its title and aria-label say what it does - and a
   page's main action keeps its words beside the icon. */
.fg-icon-download { --fg-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5'/%3E%3Cpath d='M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708z'/%3E%3C/svg%3E"); }
.fg-icon-pull { --fg-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.406 1.342A5.53 5.53 0 0 1 8 0c2.69 0 4.923 2 5.166 4.579C14.758 4.804 16 6.137 16 7.773 16 9.569 14.502 11 12.687 11H10a.5.5 0 0 1 0-1h2.688C13.979 10 15 8.988 15 7.773c0-1.216-1.02-2.228-2.313-2.228h-.5v-.5C12.188 2.825 10.328 1 8 1a4.53 4.53 0 0 0-2.941 1.1c-.757.652-1.153 1.438-1.153 2.055v.448l-.445.049C2.064 4.805 1 5.952 1 7.318 1 8.785 2.23 10 3.781 10H6a.5.5 0 0 1 0 1H3.781C1.708 11 0 9.366 0 7.318c0-1.763 1.266-3.223 2.942-3.593.143-.863.698-1.723 1.464-2.383'/%3E%3Cpath d='M7.646 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V5.5a.5.5 0 0 0-1 0v8.793l-2.146-2.147a.5.5 0 0 0-.708.708z'/%3E%3C/svg%3E"); }
.fg-icon-download::before,
.fg-icon-pull::before {
  content: "";
  flex: none;
  width: 1em;
  height: 1em;
  background-color: currentColor;
  -webkit-mask: var(--fg-icon) center / contain no-repeat;
  mask: var(--fg-icon) center / contain no-repeat;
}
/* A rounded square like the copy button, not the pill a worded button is: an icon in a circle beside one in a
   square read as two different kinds of control. */
.fg-btn.fg-btn-icon { padding: 0; width: 1.9rem; height: 1.9rem; justify-content: center; font-size: 0.95rem; border-radius: var(--r-1); }
.fg-btn.fg-btn-icon.fg-btn-sm { width: 1.75rem; height: 1.75rem; font-size: 0.9rem; }

/* A button waiting for the server: its icon, or the space before its words, turns into a spinning ring. */
.fg-btn.is-busy { cursor: progress; opacity: 1; }
.fg-btn.is-busy::before {
  content: "";
  flex: none;
  width: 1em;
  height: 1em;
  -webkit-mask: none;
  mask: none;
  background: none;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: fg-spin 0.8s linear infinite;
}
@keyframes fg-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .fg-btn.is-busy::before { animation-duration: 2.4s; }
}

/* A form that adds to the table above it - a grant, a member - starts below it, not against its border. */
.fg-table-wrap + .fg-form { margin-top: 1rem; }

/* The audit page: filters in a row that wraps, and long details that wrap rather than widen the table. */
.fg-audit-filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 0 1rem; align-items: end; margin-bottom: 1rem; }
.fg-audit-filters .fg-field { margin-bottom: 0.9rem; }
.fg-audit-filter-actions { display: flex; gap: 0.5rem; padding-bottom: 0.9rem; }
.fg-table td.fg-audit-detail { min-width: 12rem; max-width: 26rem; white-space: normal; overflow-wrap: anywhere; }
input[type=date] { color-scheme: inherit; }
.fg-nowrap { white-space: nowrap; }

/* Instructions: a caption above the commands it introduces, and the disclosure that holds them on the feed page. */
.fg-instruction-caption { margin: 0.8rem 0 0.25rem; font-size: 0.78rem; color: var(--text-2); font-weight: 600; }
.fg-connect { margin: 0.25rem 0 1rem; }
textarea.fg-mono { font-family: var(--font-mono); font-size: 0.8rem; }

/* The prerelease switch sits in the search bar beside the button. */
.fg-search-toggle { margin: 0 0.25rem 0 0.5rem; white-space: nowrap; font-size: 0.84rem; }

/* A badge after a version number in a table cell keeps a gap from it. */
.fg-num .fg-badge { margin-left: 0.4rem; }

/* The upstream a package comes from, beside its source badge. */
.fg-source-upstream { margin-left: 0.4rem; white-space: nowrap; }
