/* ==========================================================================
   finally seo — application styles
   ==========================================================================

   Served statically from dist/public, so this can be edited and redeployed
   without rebuilding the binary. Every screen links it; no page carries its
   own <style> block.

   The palette is the brand's four colours and nothing else:

     #000A18  ink      the near-black navy everything sits on
     #FCFBFC  paper    text and anything that needs to read as white
     #12D082  green    the gradient's start; success, running work, focus
     #026AED  blue     the gradient's end; primary actions and links

   Surfaces are the ink lightened in steps rather than new hues, and the
   green→blue gradient from the logo is the one accent, used sparingly so it
   still means something where it appears.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Nunito Sans — self-hosted, so the type matches the logo's wordmark without
   a third-party request. One variable file per subset covers 400–800.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 100%;
  font-display: swap;
  src: url('/fonts/nunito-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 100%;
  font-display: swap;
  src: url('/fonts/nunito-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   Font Awesome 6.4.0 Free — the icon fonts
   --------------------------------------------------------------------------

   dist/public/css/all.min.css defines the icon classes and names the families,
   but ships with no @font-face rules and no reference to anything in
   webfonts/ — so on its own it renders every icon as a blank box. These are the
   declarations it is missing, pointing at the files that are already there.

   font-display: block, not swap. A swapped icon font shows the fallback glyph
   first, which for an icon is a visible wrong character rather than merely
   different type.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Font Awesome 6 Free';
  font-style: normal;
  font-weight: 900;
  font-display: block;
  src: url('/webfonts/fa-solid-900.woff2') format('woff2'),
    url('/webfonts/fa-solid-900.ttf') format('truetype');
}

@font-face {
  font-family: 'Font Awesome 6 Free';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('/webfonts/fa-regular-400.woff2') format('woff2'),
    url('/webfonts/fa-regular-400.ttf') format('truetype');
}

@font-face {
  font-family: 'Font Awesome 6 Brands';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('/webfonts/fa-brands-400.woff2') format('woff2'),
    url('/webfonts/fa-brands-400.ttf') format('truetype');
}

/* --------------------------------------------------------------------------
   Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Brand */
  --ink: #000a18;
  --paper: #fcfbfc;
  --green: #12d082;
  --blue: #026aed;
  --brand: linear-gradient(100deg, var(--green), var(--blue));

  /* Surfaces: the ink, lifted. Panels sit above the page, controls above
     panels, so depth reads without a single borrowed grey. */
  --bg: #000a18;
  --panel: #061530;
  --raised: #0c1e3d;
  --control: #0a1a35;

  /* Lines. Faint by default; the strong one is for anything interactive. */
  --line: rgba(252, 251, 252, 0.11);
  --line-strong: rgba(252, 251, 252, 0.2);

  /* Text. Full paper is reserved for headings and values so the hierarchy
     survives on a dark background, where everything otherwise glows equally. */
  --text: var(--paper);
  --muted: rgba(252, 251, 252, 0.62);
  --faint: rgba(252, 251, 252, 0.42);

  /* Status. Green and blue are the brand's; amber and red are the smallest
     departure that still reads as warning and error. */
  --warn: #f5a524;
  --danger: #ff5d5d;

  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 18px 40px rgba(0, 0, 0, 0.5);
  --focus: 0 0 0 3px rgba(18, 208, 130, 0.35);
}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */

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

body {
  margin: 0;
  padding: 2rem 1rem 4rem;
  background: var(--bg);
  color: var(--text);
  font-family: 'Nunito Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* A single wash of the brand behind the page. It is fixed and very low
   opacity — enough to keep a full-height dark screen from reading as flat,
   not enough to compete with anything on it. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(60rem 30rem at 12% -8%, rgba(18, 208, 130, 0.1), transparent 60%),
    radial-gradient(52rem 28rem at 92% 4%, rgba(2, 106, 237, 0.14), transparent 62%);
}

.wrap {
  max-width: 1040px;
  margin: 0 auto;
}

h1,
h2,
h3 {
  margin: 0;
  font-weight: 700;
  letter-spacing: -0.01em;
}
h1 { font-size: 1.3rem; }
h2 { font-size: 1.05rem; margin-bottom: 0.85rem; }
h3 {
  font-size: 0.8rem;
  margin: 1.35rem 0 0.5rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--faint);
  font-weight: 700;
}

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

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
  background: var(--control);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 0.1em 0.4em;
}

::selection {
  background: rgba(18, 208, 130, 0.3);
  color: var(--paper);
}

/* --------------------------------------------------------------------------
   Masthead and navigation

   One set of links, two layouts. Above the breakpoint the menu sits along the
   bar and the toggle is hidden; below it, the same markup becomes a dropdown
   anchored to the bar. Nothing is duplicated per breakpoint, so the two can
   never disagree and a screen reader is never offered the links twice.
   -------------------------------------------------------------------------- */

.masthead {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  margin-bottom: 0.6rem;
}

/* The line under the navigation: who is signed in, and what they have left.
   Neither is somewhere to go, so neither sits among the links — the bar above
   is for getting somewhere and this line is for knowing where you stand.

   The two are pushed to opposite ends so the balance lands under the
   navigation's right edge, where the eye already is after reading the bar. */
.identity {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--line);
}

/* In the bar the address was one item among several and needed the padding to
   sit with them. On its own line it is the first thing, so it lines up with the
   wordmark above it instead. */
.identity .nav-user {
  padding-left: 0;
}

.nav-menu {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
}

.nav-menu a,
.nav-user {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.7rem;
  border-radius: var(--radius-sm);
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.15s ease, background 0.15s ease;
}
.nav-menu a:hover {
  color: var(--text);
  background: var(--raised);
  text-decoration: none;
}
.nav-menu a:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}

/* The icons carry the meaning at a glance; the labels carry it exactly. */
.nav-menu i {
  font-size: 0.9em;
  opacity: 0.75;
}

.nav-user {
  color: var(--text);
  font-weight: 600;
  cursor: default;
}
.nav-user i {
  color: var(--green);
  opacity: 1;
}

/* The signed-out link is separated from the navigation proper: it leaves,
   rather than going somewhere. */
.nav-signout {
  margin-left: 0.15rem;
  border-left: 1px solid var(--line);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding-left: 0.85rem;
}

.nav-toggle {
  display: none;
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.7rem;
  font-size: 1rem;
  line-height: 1;
}
.nav-toggle:hover:not(:disabled) {
  border-color: var(--green);
  filter: none;
}

/* The glyph is the whole button — no label follows it — so the trailing space
   that separates an icon from its text would sit it left of centre. This holds
   for the close icon it swaps to as well. */
.nav-toggle i {
  margin-right: 0;
}

@media (max-width: 720px) {
  .nav-toggle {
    display: inline-flex;
    align-items: center;
  }

  .nav-menu {
    display: none;
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    z-index: 20;
    flex-direction: column;
    align-items: stretch;
    gap: 0.1rem;
    min-width: 12.5rem;
    padding: 0.45rem;
    background: var(--panel);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
  }
  .nav-menu.open {
    display: flex;
  }

  /* Stacked, the separator reads better as a rule above the item than as a
     border down its side. */
  .nav-signout {
    margin-left: 0;
    padding-left: 0.7rem;
    border-left: 0;
    border-top: 1px solid var(--line);
    border-radius: var(--radius-sm);
    margin-top: 0.15rem;
    padding-top: 0.6rem;
  }
}

/* A card's own header row is a heading and its controls, not the masthead. */
.top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}

/* The logo is inlined into the page rather than loaded through <img> so that
   the wordmark is drawn with the webfont above. In an <img> the SVG is an
   isolated document and would fall back to whatever the viewer happens to
   have installed. */
.brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
.brand:hover { text-decoration: none; }
.brand svg {
  display: block;
  height: 75px;
  width: auto;
  max-width: 100%;
}
.brand-sm svg { 
  height: 75px;
}

.session {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  font-size: 0.85rem;
  color: var(--muted);
  flex-wrap: wrap;
}
.session .who {
  color: var(--text);
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   Cards
   -------------------------------------------------------------------------- */

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
  margin-bottom: 1.25rem;
  box-shadow: var(--shadow);
}

/* The masthead is not a card, so its .top inside one needs no bottom gap. */
.card .top {
  margin-bottom: 0.85rem;
}

/* --------------------------------------------------------------------------
   Icons
   -------------------------------------------------------------------------- */

/* An icon inside a button or a heading sits with the text rather than on it. */
button i,
h2 i,
h3 i {
  margin-right: 0.45rem;
}
h3 i {
  color: var(--green);
  opacity: 0.85;
}

/* Status pills carry a shape as well as a colour, so the distinction survives
   a colour-blind reader and a greyscale print. */
.pill i {
  margin-right: 0.3rem;
  font-size: 0.85em;
}

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

label {
  display: block;
  font-size: 0.82rem;
  font-weight: 600;
  margin-bottom: 0.4rem;
  color: var(--text);
}

.hint {
  font-weight: 400;
  color: var(--faint);
}

input,
textarea,
select {
  width: 100%;
  padding: 0.65rem 0.75rem;
  font: inherit;
  font-size: 0.95rem;
  color: var(--text);
  background: var(--control);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
input::placeholder,
textarea::placeholder {
  color: var(--faint);
}
input:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--green);
  box-shadow: var(--focus);
}
textarea {
  min-height: 110px;
  resize: vertical;
}
select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 52%, calc(100% - 13px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 2rem;
}
input[type='checkbox'] {
  width: auto;
  accent-color: var(--green);
  vertical-align: middle;
}

/* A group of tickable options. The fieldset is there for the screen reader —
   the legend names what the group is asking — so its own borders are removed
   and the legend is styled to match every other field label. */
.choiceset {
  border: 0;
  padding: 0;
  margin: 1.25rem 0 0;
}
.choiceset legend {
  padding: 0;
  margin-bottom: 0.4rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
}

/* A set of tickable options, each with the sentence that says what choosing it
   means. The whole tile is the label, so the explanation is part of the click
   target rather than something to read and then aim past. */
.choices {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 0.5rem;
}
.choices label {
  flex: 1 1 220px;
  display: flex;
  gap: 0.55rem;
  align-items: flex-start;
  margin: 0;
  padding: 0.7rem 0.8rem;
  background: var(--control);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color 0.15s ease;
}
.choices label:hover {
  border-color: var(--muted);
}
.choices label:has(input:checked) {
  border-color: var(--green);
}
.choices input[type='checkbox'] {
  margin-top: 0.15rem;
}
.choices .hint {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.78rem;
  line-height: 1.35;
}

.row {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1rem;
}
.row > div {
  flex: 1 1 200px;
}

.foot {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.35rem;
  flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

button {
  font: inherit;
  font-size: 0.95rem;
  font-weight: 700;
  padding: 0.7rem 1.3rem;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--paper);
  background: var(--brand);
  transition: filter 0.15s ease, transform 0.05s ease;
}
button:hover:not(:disabled) { filter: brightness(1.12); }
button:active:not(:disabled) { transform: translateY(1px); }
button:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}
button:disabled {
  opacity: 0.45;
  cursor: default;
}

button.ghost {
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--line-strong);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 0.42rem 0.8rem;
}
button.ghost:hover:not(:disabled) {
  border-color: var(--green);
  filter: none;
}

button.danger {
  background: transparent;
  color: var(--danger);
  border: 1px solid rgba(255, 93, 93, 0.4);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 0.42rem 0.8rem;
}
button.danger:hover:not(:disabled) {
  background: rgba(255, 93, 93, 0.12);
  filter: none;
}

/* --------------------------------------------------------------------------
   Messages
   -------------------------------------------------------------------------- */

.error,
.warn,
.ok {
  padding: 0.8rem 1rem;
  border-radius: var(--radius-sm);
  font-size: 0.88rem;
  border: 1px solid;
}
.error {
  margin-top: 1rem;
  background: rgba(255, 93, 93, 0.1);
  border-color: rgba(255, 93, 93, 0.35);
  color: #ffb3b3;
}
.warn {
  margin-bottom: 1rem;
  background: rgba(245, 165, 36, 0.1);
  border-color: rgba(245, 165, 36, 0.35);
  color: #ffd79a;
}
.ok {
  margin-bottom: 1rem;
  background: rgba(18, 208, 130, 0.1);
  border-color: rgba(18, 208, 130, 0.4);
  color: #8ff0c6;
}
.error ul,
.warn ul,
.ok ul {
  margin: 0.4rem 0 0;
  padding-left: 1.1rem;
}
.ok code {
  display: inline-block;
  margin: 0.3rem 0;
  background: rgba(0, 10, 24, 0.6);
  border-color: rgba(18, 208, 130, 0.35);
  color: var(--green);
  letter-spacing: 0.03em;
}

.status {
  font-size: 0.85rem;
  color: var(--muted);
}
.note {
  font-size: 0.8rem;
  color: var(--faint);
  margin: 0.75rem 0 0;
  line-height: 1.5;
}
.muted { color: var(--muted); }
.small { font-size: 0.8rem; }
.hidden { display: none; }
.stack {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.chips {
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   Tabs
   -------------------------------------------------------------------------- */

.tabs {
  display: flex;
  gap: 0.15rem;
  border-bottom: 1px solid var(--line);
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.tabs button {
  background: none;
  color: var(--muted);
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  padding: 0.6rem 0.85rem;
  font-size: 0.87rem;
  font-weight: 600;
}
.tabs button:hover:not(:disabled) {
  color: var(--text);
  filter: none;
}
.tabs button[aria-selected='true'] {
  color: var(--green);
  border-bottom-color: var(--green);
}

/* --------------------------------------------------------------------------
   Meta rows
   -------------------------------------------------------------------------- */

.meta {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
  font-size: 0.83rem;
  color: var(--muted);
  margin-bottom: 1rem;
}
.meta strong {
  color: var(--text);
  font-weight: 700;
}

/* --------------------------------------------------------------------------
   Tables
   -------------------------------------------------------------------------- */

.tablewrap {
  overflow-x: auto;
  border-radius: var(--radius-sm);
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}
th,
td {
  text-align: left;
  padding: 0.65rem 0.7rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
th {
  color: var(--faint);
  font-weight: 700;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  white-space: nowrap;
}
tbody tr:last-child td {
  border-bottom: 0;
}
td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
td.actions {
  white-space: nowrap;
  text-align: right;
}
td.actions button {
  margin-left: 0.35rem;
}

/* Under a table read a page at a time. The two steps sit at the edges with the
   position between them, so where you are stays put as the buttons enable and
   disable at the ends of the history. */
.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.85rem;
  flex-wrap: wrap;
}
/* .hidden is a bare class and is declared above this, so a display set here
   would outrank it and the pager could never be put away. Every other partner
   of .hidden leaves display alone; this one cannot, so it says so instead. */
.pager.hidden {
  display: none;
}
.pager button {
  padding: 0.4rem 0.75rem;
  font-size: 0.8rem;
}
/* The forward step puts its chevron after the label, where the shared rule's
   margin would sit on the wrong side of it. */
.pager button.next i {
  margin-right: 0;
  margin-left: 0.45rem;
}

/* The row's actions, collapsed to one control. The glyph is the whole button,
   so the trailing space that separates an icon from a label would sit it left
   of centre — the same reason .nav-toggle needs this. */
button.kebab {
  padding: 0.4rem 0.6rem;
  line-height: 1;
}
button.kebab i {
  margin-right: 0;
}

/* Fixed, and attached to the body rather than the cell: tables sit inside
   .tablewrap, which scrolls horizontally and would clip a menu positioned
   within it. */
.rowmenu {
  position: fixed;
  z-index: 60;
  min-width: 11.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.3rem;
  background: var(--raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
}
.rowmenu button {
  display: flex;
  align-items: center;
  width: 100%;
  margin: 0;
  padding: 0.5rem 0.6rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 600;
  text-align: left;
}
/* A fixed width on the glyph so the labels line up whatever it is. */
.rowmenu button i {
  width: 1.05rem;
  margin-right: 0.55rem;
  text-align: center;
  color: var(--muted);
}
.rowmenu button:hover:not(:disabled) {
  background: var(--control);
  filter: none;
}
.rowmenu button.danger {
  color: var(--danger);
}
.rowmenu button.danger i {
  color: var(--danger);
}
.rowmenu button.danger:hover:not(:disabled) {
  background: rgba(255, 93, 93, 0.12);
}
tr.clickable {
  cursor: pointer;
  transition: background 0.12s ease;
}
tr.clickable:hover {
  background: var(--raised);
}

/* --------------------------------------------------------------------------
   Status pills
   -------------------------------------------------------------------------- */

.pill {
  display: inline-block;
  padding: 0.16rem 0.6rem;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  border: 1px solid transparent;
}
.pill-queued {
  background: rgba(252, 251, 252, 0.07);
  border-color: var(--line-strong);
  color: var(--muted);
}
.pill-running {
  background: rgba(2, 106, 237, 0.16);
  border-color: rgba(2, 106, 237, 0.5);
  color: #8ebcff;
}
.pill-complete {
  background: rgba(18, 208, 130, 0.14);
  border-color: rgba(18, 208, 130, 0.45);
  color: var(--green);
}
.pill-failed,
.pill-canceled {
  background: rgba(255, 93, 93, 0.12);
  border-color: rgba(255, 93, 93, 0.4);
  color: #ffb3b3;
}
.pill-needs_info {
  background: rgba(245, 165, 36, 0.13);
  border-color: rgba(245, 165, 36, 0.4);
  color: #ffd79a;
}
.pill-admin {
  background: rgba(2, 106, 237, 0.16);
  border-color: rgba(2, 106, 237, 0.5);
  color: #8ebcff;
}
.pill-plain {
  background: rgba(252, 251, 252, 0.06);
  border-color: var(--line);
  color: var(--muted);
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   Progress
   -------------------------------------------------------------------------- */

.bar {
  height: 8px;
  background: var(--control);
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
  margin: 0.9rem 0 0.6rem;
}
.bar > div {
  height: 100%;
  background: var(--brand);
  border-radius: 999px;
  transition: width 0.4s ease;
}

/* A slow shimmer over the filled portion, so a stage that takes minutes still
   looks alive between percentage changes. */
.bar > div::after {
  content: '';
  display: block;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(252, 251, 252, 0.35),
    transparent
  );
  animation: sweep 1.8s ease-in-out infinite;
}
@keyframes sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .bar > div::after { animation: none; }
  * { transition: none !important; }
}

/* --------------------------------------------------------------------------
   Panels and disclosure
   -------------------------------------------------------------------------- */

details {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 0.8rem 1rem;
  margin-top: 1rem;
  background: var(--raised);
}
details[open] {
  padding-bottom: 1.25rem;
}
summary {
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: 700;
}
summary::marker {
  color: var(--green);
}

pre {
  background: #030d1e;
  color: #cfe3ff;
  border: 1px solid var(--line);
  padding: 1rem;
  border-radius: var(--radius-sm);
  overflow: auto;
  max-height: 60vh;
  font-size: 0.8rem;
  line-height: 1.55;
  margin: 0;
}

/* Markup being edited. It reads as the code it is — same palette as the <pre>
   above, which is what the same content looked like before it could be typed
   into — and gets more room than an ordinary field, since a page body is the
   longest thing anyone enters anywhere in this application. */
textarea.code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8rem;
  line-height: 1.55;
  background: #030d1e;
  color: #cfe3ff;
  min-height: 24rem;
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}

/* A post being edited. Unlike markup this is prose, so it wraps and is set in
   the reading font — it is pasted into a network's own box exactly as it looks
   here. */
#drafts textarea {
  min-height: 0;
  margin-bottom: 0.6rem;
}

/* The generated page's preview, on the job screen.
 *
 * Scoped to that one frame rather than written as a bare `iframe` selector.
 * As a bare selector it also claimed every frame the application does not
 * create itself — which meant Square's card fields, drawn into a frame of
 * their own by its SDK, came out 62vh tall on a white background. An element
 * selector carrying a fixed height is a rule about one thing, so it says
 * which one. */
#preview {
  width: 100%;
  height: 62vh;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
}

/* --------------------------------------------------------------------------
   Downloads
   -------------------------------------------------------------------------- */

.dl {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}

/* --------------------------------------------------------------------------
   Photo gallery
   -------------------------------------------------------------------------- */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1rem;
}
.shot {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--raised);
}
/* The photo the user picked. A set of three all look equally plausible, so the
   choice has to be visible from across the grid rather than read off a label. */
.shot-chosen {
  border-color: var(--green);
  box-shadow: var(--focus);
}
.shot img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  background: var(--control);
}
.shot .body {
  padding: 0.8rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  flex: 1;
}
.shot p {
  margin: 0;
  font-size: 0.79rem;
  color: var(--muted);
  line-height: 1.45;
  flex: 1;
}
.shot .failed {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  background: rgba(255, 93, 93, 0.08);
  color: #ffb3b3;
  font-size: 0.79rem;
  padding: 1rem;
  text-align: center;
}

/* --------------------------------------------------------------------------
   Sign-in
   -------------------------------------------------------------------------- */

.signin {
  max-width: 400px;
  margin: 10vh auto 0;
}
.signin .brand {
  justify-content: center;
  display: flex;
  margin-bottom: 1.75rem;
}
.signin .field {
  margin-bottom: 1rem;
}
.signin button {
  width: 100%;
  margin-top: 0.35rem;
}
.signin .card {
  padding: 1.85rem;
}

/* --------------------------------------------------------------------------
   Narrow screens
   -------------------------------------------------------------------------- */

@media (max-width: 620px) {
  body { padding: 1.25rem 0.85rem 3rem; }
  .card { padding: 1.15rem; }
  td.actions { text-align: left; }
  td.actions button { margin: 0.2rem 0.35rem 0.2rem 0; }
}

/* --------------------------------------------------------------------------
   Tool cards — the landing screen
   -------------------------------------------------------------------------- */

.tools {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1rem;
  margin-bottom: 1.75rem;
}

.tool {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 1.35rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  text-decoration: none;
  color: inherit;
  position: relative;
  overflow: hidden;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

/* The brand gradient sits as a hairline along the top of a card and is only
   revealed on hover, so a grid of cards reads calm until one is chosen. */
.tool::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--brand);
  opacity: 0;
  transition: opacity 0.15s ease;
}
a.tool:hover {
  border-color: var(--line-strong);
  transform: translateY(-2px);
  text-decoration: none;
}
a.tool:hover::before { opacity: 1; }
a.tool:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}

.tool-icon {
  font-size: 1.4rem;
  line-height: 1;
  margin-bottom: 0.35rem;
}
/* The glyph carries the brand gradient rather than a flat accent, which is the
   one place per card the palette shows without competing with the text. */
.tool-icon i {
  background: var(--brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.tool-name {
  font-weight: 700;
  font-size: 1rem;
}
.tool-tagline {
  font-size: 0.84rem;
  color: var(--muted);
  line-height: 1.45;
}

/* A tool that is announced but not built yet stays visible and obviously
   inert — more useful than a gap or a dead link. */
.tool-soon {
  opacity: 0.55;
  cursor: default;
}
.tool .pill {
  align-self: flex-start;
  margin-top: 0.35rem;
}

/* The landing screen is grouped: the review first, then what writes, then
   the measurements it runs, each available on its own. */
.tools-heading {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 0.75rem;
}
.tools-intro {
  margin: -0.35rem 0 0.9rem;
  font-size: 0.86rem;
  color: var(--muted);
}
.tools-featured { grid-template-columns: 1fr; }
/* The one card that runs everything reads as the way in: wider, with the
   brand hairline showing before it is hovered. */
.tool-featured {
  padding: 1.6rem 1.75rem;
  border-color: var(--line-strong);
}
.tool-featured::before { opacity: 1; height: 3px; }
.tool-featured .tool-icon { font-size: 1.8rem; }
.tool-featured .tool-name { font-size: 1.2rem; }
.tool-featured .tool-tagline { font-size: 0.92rem; max-width: 60rem; }

/* --------------------------------------------------------------------------
   The token meter

   What is left to spend, in the masthead of every screen. It is a link to the
   billing screen, so the number and the thing you do about it are one step
   apart rather than two.

   Three states, and the colour is never the only difference between them: the
   glyph changes with the level, so an empty balance is distinguishable in
   greyscale and to a colour-blind reader.
   -------------------------------------------------------------------------- */

.meter {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--raised);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.meter:hover {
  text-decoration: none;
  border-color: var(--green);
}
.meter i {
  color: var(--green);
  font-size: 0.85em;
}
.meter-unit {
  font-weight: 400;
  color: var(--muted);
  font-size: 0.8em;
}

/* The plan the balance belongs to. A number with no plan beside it invites the
   wrong question — "a hundred of what, and for how long" — and there is room
   for the answer now that this is not competing with the navigation. */
.meter-plan {
  padding-left: 0.55rem;
  margin-left: 0.15rem;
  border-left: 1px solid var(--line-strong);
  font-weight: 600;
  font-size: 0.8em;
  color: var(--muted);
}

/* Running low, and run out. Both keep the pill shape and change the colour of
   the glyph and the border rather than shouting in the bar. */
.meter-low {
  border-color: rgba(245, 165, 36, 0.5);
}
.meter-low i { color: var(--warn); }
.meter-empty {
  border-color: rgba(255, 93, 93, 0.5);
  background: rgba(255, 93, 93, 0.1);
}
.meter-empty i { color: var(--danger); }
.meter-unlimited i { color: var(--green); }

/* On a narrow screen the line stays one line: the address truncates rather
   than wrapping under the balance, since a long address is the part somebody
   already knows and the balance is the part they are looking for. */
@media (max-width: 720px) {
  .identity {
    flex-wrap: nowrap;
  }
  /* Both the flex item and the text inside it have to be allowed to shrink
     below their content, or the address simply pushes the balance off the
     edge instead of truncating. */
  .identity .nav-user {
    min-width: 0;
  }
  .identity .who {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .meter {
    flex: none;
    padding: 0.3rem 0.55rem;
    font-size: 0.8rem;
  }
  .meter-unit,
  .meter-plan { display: none; }
}

/* A tool a plan does not include. It is not an error state — the tool is still
   there, on another tier — so it reads as information rather than a warning. */
.pill-plan {
  background: rgba(2, 106, 237, 0.16);
  border: 1px solid rgba(2, 106, 237, 0.45);
  color: #9dc4ff;
}

/* --------------------------------------------------------------------------
   Billing
   -------------------------------------------------------------------------- */

/* The balance, as the one number this screen is really about. */
.balance {
  display: flex;
  align-items: center;
  gap: 2rem;
  flex-wrap: wrap;
}
.balance-figure {
  display: flex;
  flex-direction: column;
}
.balance-number {
  font-size: 2.6rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  background: var(--brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.balance-label {
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 0.3rem;
}

/* The two buckets, side by side. They are shown apart because they behave
   differently, and because which of them a cancellation touches is the
   question somebody is on this screen to answer. */
.balance-split {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.bucket {
  display: flex;
  flex-direction: column;
  padding-left: 1.5rem;
  border-left: 1px solid var(--line);
}
.bucket-figure {
  font-size: 1.2rem;
  font-weight: 700;
}
.bucket-label {
  font-size: 0.8rem;
  color: var(--muted);
}
.bucket-note {
  font-size: 0.75rem;
  color: var(--faint);
  margin-top: 0.15rem;
}

/* The tiers. */
.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1rem;
  margin-top: 1.25rem;
}
.plan {
  display: flex;
  flex-direction: column;
  padding: 1.35rem;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* The plan somebody is already on is marked by the brand rather than by a
   larger card: this is a statement of where they are, not an advertisement. */
.plan-current {
  border-color: rgba(18, 208, 130, 0.45);
}
.plan-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
.plan-name {
  margin: 0;
  font-size: 0.95rem;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text);
}
.plan-price {
  margin: 0;
  font-size: 1.8rem;
  font-weight: 800;
  line-height: 1;
}
.plan-cadence {
  font-size: 0.78rem;
  color: var(--faint);
}
.plan-blurb {
  margin: 0.75rem 0 0;
  font-size: 0.84rem;
  color: var(--muted);
  line-height: 1.5;
}
.plan-features {
  margin: 0.9rem 0 1.25rem;
  padding: 0;
  list-style: none;
  font-size: 0.83rem;
  color: var(--muted);
}
.plan-features li {
  position: relative;
  padding-left: 1.3rem;
  margin-top: 0.45rem;
  line-height: 1.45;
}
.plan-features li::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: var(--green);
  font-weight: 700;
}
/* The action sits at the bottom of the card whatever the feature list does, so
   a row of cards has its buttons on one line. */
.plan-foot {
  margin-top: auto;
}
.plan-foot button {
  width: 100%;
}

/* Square draws the card fields into this element, inside a frame of its own.
   What is inside that frame is the SDK's business and is not reachable from
   here — see CARD_STYLE in scripts.js, which is the only way in.

   The panel is light because the frame's own background is white and cannot be
   set from this document. Rather than leave a white block sitting on a dark
   card looking like a failed load, it is given a light surface of its own with
   room around it, so the payment fields read as a deliberate inset — which is
   also how most people have seen a card form before.

   The frame sizes itself to its content, so nothing here sets a height.
   min-height reserves roughly one row of fields, so the button below does not
   jump down the page as the form loads; on a narrow screen the fields stack and
   the panel simply grows. */
#card-container {
  margin-bottom: 1rem;
  min-height: 56px;
  padding: 0.75rem;
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
}

/* The token packs. Three buttons rather than a quantity box, each stating what
   it gives and what it costs, so a purchase is one decision and one click. */
.packs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.75rem;
  margin-top: 1.25rem;
}
.pack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  padding: 1rem 0.75rem;
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--line-strong);
}
.pack:hover:not(:disabled) {
  border-color: var(--green);
  filter: none;
}
.pack-tokens {
  font-size: 0.95rem;
  font-weight: 700;
}
/* The price carries the brand gradient: it is the number being agreed to, and
   the one place on this card worth the accent. */
.pack-price {
  font-size: 1.35rem;
  font-weight: 800;
  background: var(--brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* The ledger's own columns. */
.ledger-kind {
  display: block;
  font-weight: 600;
}
.ledger-detail {
  display: block;
  font-size: 0.8rem;
  color: var(--faint);
}
.ledger-in {
  color: var(--green);
  font-weight: 700;
  white-space: nowrap;
}
.ledger-out {
  color: var(--muted);
  font-weight: 700;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   The manual

   A document rather than a screen: no controls, no tables of live data, just
   long-form text that has to stay readable. It borrows the cards, headings and
   pills every other screen uses, and adds only what prose needs — a contents
   list, a lead paragraph, and the definition lists the per-tool sections are
   built from.
   -------------------------------------------------------------------------- */

/* Scoped to .manual so it outranks the prose list rules below, which are
   element-qualified and would otherwise indent the contents. */
.manual .manual-toc {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 0.4rem 1.25rem;
  margin: 0.5rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.85rem;
}
.manual-toc i {
  width: 1.1em;
  margin-right: 0.5rem;
  color: var(--green);
  opacity: 0.85;
}

/* Each section is its own anchor target. The margin keeps a jumped-to heading
   off the very top of the viewport, where it reads as cut off. */
.manual-card {
  scroll-margin-top: 1rem;
}

/* The sentence under a tool's heading that says what it is for. Larger than a
   .note, because it is the line most readers will stop at. */
.manual-lead {
  margin: 0;
  font-size: 0.9rem;
  color: var(--muted);
  line-height: 1.6;
}

/* A field and what it is for. The term carries the weight so the list can be
   scanned for one input without reading the descriptions. */
.fields {
  margin: 0.25rem 0 0;
}
.fields dt {
  margin-top: 0.9rem;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text);
}
.fields dd {
  margin: 0.2rem 0 0;
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.55;
}

.manual ul,
.manual-steps {
  margin: 0.4rem 0 0;
  padding-left: 1.15rem;
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.55;
}
.manual li + li {
  margin-top: 0.45rem;
}
.manual li strong {
  color: var(--text);
}

/* The contents is the one list on the page that is navigation rather than
   prose, so it keeps its own spacing. */
.manual .manual-toc li + li {
  margin-top: 0;
}

/* --------------------------------------------------------------------------
   Dialogs and toasts

   Gooey Toolbox draws the alert, the confirm and the toasts; everything below
   restyles them into this design system. Its own stylesheet loads first (see
   _shell.html), so these are overrides rather than a fork — the library keeps
   its behaviour and animation timing, and takes this file's surfaces, type and
   colours.

   The library's defaults are a light grey card in Arial with a red and a green
   button, which on this dark page reads as something a different application
   put there.
   -------------------------------------------------------------------------- */

.gtb-background {
  background: rgba(0, 10, 24, 0.72);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  padding: 1rem;
}

.gtb-alert-frame,
.gtb-confirm-frame {
  font-family: inherit;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.5rem;
  /* The header is positioned absolutely by the library and would otherwise sit
     on top of the first line of the message. */
  padding-top: 3.25rem;
  width: min(30rem, 100%);
}

/* The header is the title, so it reads as one: left-aligned like every other
   heading here, with the close button held to the right of it. */
.gtb-alert-header,
.gtb-confirm-header {
  gap: 0.75rem;
  padding: 1.1rem 1.5rem 0;
  width: 100%;
  color: var(--text);
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  text-align: left;
  justify-content: space-between;
}

.gtb-alert-content,
.gtb-confirm-content {
  padding-top: 0;
  color: var(--muted);
  line-height: 1.55;
}

.gtb-close-button {
  background: transparent;
  color: var(--faint);
  border-radius: var(--radius-sm);
  box-shadow: none;
  padding: 0.35rem 0.6rem;
  font-weight: 400;
  transition: color 0.15s ease, background 0.15s ease;
}
.gtb-close-button:hover { background: var(--raised); color: var(--text); }
/* The library scales its buttons on hover. On the close button that reads as a
   wobble rather than as feedback, so only the colour moves. */
.gtb-close-button:hover,
.gtb-close-button:active { transform: none; }

.gtb-confirm-button-bar {
  justify-content: flex-end;
  gap: 0.6rem;
  margin-top: 1.5rem;
}

/* Both buttons take the shape of every other button in the application. The
   confirming one is the destructive one — these dialogs exist for deletions —
   so it carries the danger colour, and the way out of the dialog is the quiet
   one beside it. */
.gtb-yes-button,
.gtb-no-button {
  font: inherit;
  font-size: 0.95rem;
  font-weight: 700;
  text-transform: none;
  border: 0;
  border-radius: var(--radius-sm);
  padding: 0.7rem 1.3rem;
  margin: 0;
  box-shadow: none;
  color: var(--paper);
  transition: filter 0.15s ease, transform 0.05s ease;
}
.gtb-yes-button { background: var(--danger); }
.gtb-no-button { background: var(--raised); color: var(--text); }

/* Scaling a button that sits next to another button moves the pair. The rest
   of the application nudges instead, so these do too. */
.gtb-yes-button:hover,
.gtb-no-button:hover {
  filter: brightness(1.12);
  transform: none;
}
.gtb-yes-button:active,
.gtb-no-button:active { transform: translateY(1px); }
.gtb-yes-button:focus-visible,
.gtb-no-button:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}

/* Toasts rise above the bottom edge rather than sitting on it, and clear the
   safe area on a phone with a home indicator. */
.gtb-toast-container {
  width: auto;
  left: 0;
  right: 0;
  bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  padding: 0 1rem;
  pointer-events: none;
}

.gtb-toast {
  pointer-events: auto;
  background: var(--raised);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--green);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  color: var(--text);
  font-size: 0.92rem;
  line-height: 1.5;
  padding: 0.8rem 1.1rem;
  max-width: min(34rem, calc(100vw - 2rem));
}

/* A failure is the toast worth noticing. The text carries the colour rather
   than the border alone, so the distinction does not rest on :has() being
   supported — an error that looks like a confirmation is the one mistake a
   toast must not make. It matches the inline .error boxes elsewhere. */
.gtb-toast-error { color: #ffb3b3; }
.gtb-toast:has(.gtb-toast-error) {
  border-left-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, var(--raised));
}

/* The library's entry animation overshoots and springs back, which suits a
   notification and not a dialog carrying a question about deleting something. */
.gtb-alert-frame,
.gtb-confirm-frame {
  transition: transform 0.18s ease, opacity 0.18s ease;
}

@media (prefers-reduced-motion: reduce) {
  .gtb-fade-in,
  .gtb-fade-out { animation: none; opacity: 1; }
}

@media (max-width: 620px) {
  .gtb-confirm-button-bar { flex-direction: column-reverse; }
  .gtb-yes-button,
  .gtb-no-button { width: 100%; }
}

/* ---------------------------------------------------------------------------
   Progress

   One site's measured history: a row per metric, each with its current value,
   how far it has moved, and the readings it moved through.

   The charts are inline SVG drawn by the screen rather than by a library. There
   is no build step here, so a charting dependency would be a file every page
   fetches to draw what a polyline already draws.
   --------------------------------------------------------------------------- */

.metric {
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--line);
}
.metric:last-child { border-bottom: 0; }

.metric-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.metric-label {
  font-weight: 600;
  min-width: 11rem;
}

/* The current reading, which is the figure somebody came to see. */
.metric-now {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.metric-move { font-size: 0.82rem; }

/* Better and worse are named by the metric, not by the arrow: rank falls as it
   improves, a score rises, and both are drawn green when they are going the
   right way. */
.metric-better { color: var(--green); }
.metric-worse { color: var(--danger); }

/* The trail of a state over time — one dot per reading, oldest first. */
.metric-trail {
  display: flex;
  gap: 4px;
  margin-top: 0.5rem;
  flex-wrap: wrap;
}
.trail-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--line-strong);
  display: inline-block;
}
.trail-dot.pill-complete { background: var(--green); }
.trail-dot.pill-failed { background: var(--danger); }
.trail-dot.pill-plain { background: var(--faint); }

.spark {
  width: 100%;
  max-width: 22rem;
  height: 48px;
  margin-top: 0.4rem;
  display: block;
  overflow: visible;
}
.spark-line {
  fill: none;
  stroke: var(--blue);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.spark-dot { fill: var(--blue); }
.spark-last { fill: var(--green); }

@media (max-width: 620px) {
  .metric-label { min-width: 0; }
  .spark { max-width: 100%; }
}

/* The full text of an assistant's answer, revealed under its row.
   It is prose written by a model and can be a screenful, so it is given a
   readable measure and its line breaks are kept — the paragraphing is part of
   how the answer reads, and collapsing it would turn a recommendation into a
   wall. Often the most useful thing on the screen: reading what ChatGPT tells
   a customer about your trade is its own briefing. */
.answer-text {
  white-space: pre-wrap;
  max-width: 68ch;
  padding: 0.85rem 1rem;
  background: var(--control);
  border-left: 3px solid var(--line-strong);
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.6;
}

/* ---- What to do next ------------------------------------------------------
   The plan, above the charts on the site's progress screen. Deliberately not
   a table: these are things to work through, and a row of cells reads as a
   report. Note that .actions is already the row-button cell elsewhere, which
   is why nothing here is called that. */
.plan-band {
  margin: 1.4rem 0 0.6rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--faint);
}
.plan-band:first-child { margin-top: 0.75rem; }

.plan-item {
  padding: 0.85rem 0 0.95rem;
  border-top: 1px solid var(--line);
}
.plan-item:first-of-type { border-top: 0; }

/* A reading old enough that it may already have been acted on. Dimmed rather
   than hidden: it is still the best information there is, and dropping it
   would make the plan lie by omission. */
.plan-stale { opacity: 0.72; }

.plan-head {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  flex-wrap: wrap;
}
.plan-n {
  min-width: 1.4rem;
  font-variant-numeric: tabular-nums;
  color: var(--faint);
  font-size: 0.85rem;
}
.plan-title {
  font-weight: 600;
  flex: 1 1 18rem;
}
.plan-detail {
  margin: 0.4rem 0 0 1.95rem;
  max-width: 62ch;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.55;
}
.plan-why {
  margin: 0.45rem 0 0 1.95rem;
  font-size: 0.82rem;
  color: var(--faint);
}
.plan-why-label { font-weight: 600; }
.plan-item > a.small { margin-left: 1.95rem; }

@media (max-width: 640px) {
  .plan-detail, .plan-why, .plan-item > a.small { margin-left: 0; }
}

/* --------------------------------------------------------------------------
   Strategy review

   The one report that joins the others up. It borrows the plan's rows so an
   action reads the same here as on the progress screen, and adds the pieces
   only a review has: the scorecard, the roadmap, and the checklist of what
   was measured.
   -------------------------------------------------------------------------- */

/* What this account's review covers, on the form. */
.scope-box {
  margin: 1rem 0 0;
  padding: 0.8rem 1rem;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 0.88rem;
}
.scope-box p { margin: 0; }
.scope-box p + p { margin-top: 0.4rem; color: var(--muted); }
.scope-lite { border-color: rgba(2, 106, 237, 0.45); }

/* Seven networks at the width of a feature tile would be a wall of boxes. */
#networks label { flex: 0 1 auto; padding: 0.5rem 0.8rem; }

/* The measurements, while they run and once they are in. */
.parts {
  list-style: none;
  margin: 0.9rem 0 0;
  padding: 0;
}
.part {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.6rem;
  padding: 0.45rem 0;
  border-top: 1px solid var(--line);
}
.part:first-child { border-top: 0; }
.part i { width: 1.1rem; text-align: center; color: var(--faint); }
.part-done i, .part-reused i { color: var(--green); }
.part-failed i { color: var(--warn); }
.part-label { font-weight: 600; min-width: 10rem; }
.part-note { flex: 1 1 100%; margin-left: 1.7rem; color: var(--muted); }
.part a { margin-left: auto; }

/* Eight figures, one per thing the review measured. */
.scorecard {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 0.75rem;
  margin: 0.75rem 0 0.5rem;
}
.score {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.85rem 0.95rem;
  background: var(--raised);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--radius-sm);
}
.score-good { border-left-color: var(--green); }
.score-bad { border-left-color: var(--danger); }
.score-neutral { border-left-color: var(--blue); }
.score-unmeasured { opacity: 0.7; }
.score-label {
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--faint);
}
.score-value { font-size: 1.2rem; font-weight: 700; }
.score-detail { font-size: 0.8rem; color: var(--muted); }

.strategy-summary {
  max-width: 70ch;
  font-size: 1rem;
  line-height: 1.6;
  margin: 0.5rem 0 0;
}

/* This week, this month, this quarter, side by side where there is room. */
.roadmap {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.9rem;
  margin: 0.5rem 0 0.5rem;
}
.roadmap-col {
  padding: 0.85rem 1rem;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.roadmap-col h4 {
  margin: 0 0 0.5rem;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--green);
}
.roadmap-col ol { margin: 0; padding-left: 1.2rem; font-size: 0.88rem; }
.roadmap-col li + li { margin-top: 0.35rem; }

.strategy-channel { margin-top: 1.75rem; }
.strategy-channel > h3 { margin-bottom: 0.2rem; }
.strategy-help { margin-top: 0; max-width: 70ch; }

/* A review's items lead with their priority rather than a number, so their
   working is not indented under one. */
.strategy-item .plan-detail,
.strategy-item .plan-why { margin-left: 0; }
.strategy-item:target { background: rgba(18, 208, 130, 0.06); }
.strategy-priority-high { border-color: rgba(255, 93, 93, 0.45); color: #ffb3b3; }
.strategy-priority-medium { border-color: rgba(245, 165, 36, 0.45); color: #ffd79a; }

.strategy-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin-top: 0.65rem;
}
/* A link that does something reads as a button, in the button's own two
   weights. */
a.button {
  display: inline-block;
  font-size: 0.85rem;
  font-weight: 700;
  padding: 0.45rem 0.9rem;
  border-radius: var(--radius-sm);
  color: var(--paper);
  background: var(--brand);
  text-decoration: none;
}
a.button:hover { filter: brightness(1.12); text-decoration: none; }
a.button:focus-visible { outline: none; box-shadow: var(--focus); }
a.button.ghost {
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--line-strong);
  font-weight: 600;
}
a.button.ghost:hover { border-color: var(--green); filter: none; }

