/*
 * PyBEP website styles.
 *
 * The palette is taken from the project logo (LICEM/Logo3.png): the navy
 * of the wordmark, the two blues and the gold of the mark. Nothing else
 * is invented, so the page and the logo read as one thing.
 *
 * Layout rules of thumb, applied throughout: one column, generous
 * whitespace, labels at full strength with their explanations quieter
 * underneath, and flat surfaces rather than heavy effects.
 */

:root {
  /* --- straight from the logo --- */
  --ink: #00223f;        /* wordmark navy: headings and body text */
  --blue: #0c78b4;       /* the mark's deep blue: primary actions */
  --blue-dark: #095c8b;
  --sky: #54ccfc;        /* the mark's light blue: focus rings */
  --sky-wash: #eaf7fe;
  --gold: #fcc00c;       /* the mark's gold: highlights, active state */
  --gold-wash: #fff8e2;

  /* --- neutrals, tinted towards the navy so they belong to it --- */
  --bg: #f2f6fa;
  --surface: #ffffff;
  --surface-sunk: #f7fafc;
  --border: #d9e4ee;
  --border-strong: #b9cbdb;
  --muted: #55697d;

  --warning: #9a6700;
  --warning-bg: #fff8e2;
  --warning-border: #f0cd6b;
  --error: #b42318;
  --error-bg: #fef3f2;
  --error-border: #f2b8b5;

  --radius: 14px;
  --radius-sm: 10px;
  --shadow: 0 1px 2px rgba(0, 34, 63, .06), 0 8px 24px rgba(0, 34, 63, .06);
  --ring: 0 0 0 3px rgba(84, 204, 252, .45);
}

* { box-sizing: border-box; }

html {
  min-height: 100%;
  background: var(--bg);
}

body {
  font-family: "Inter", ui-sans-serif, system-ui, -apple-system, Segoe UI,
               Roboto, Helvetica, Arial, sans-serif;
  margin: 0;
  min-height: 100vh;
  padding: 28px 20px 20px;
  color: var(--ink);
  font-size: 15px;
  line-height: 1.5;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
  -webkit-font-smoothing: antialiased;
}

/* --- Header: logo, wordmark, section tabs --- */

.site-header {
  width: 100%;
  max-width: 900px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  color: inherit;
}

.brand-mark { width: 40px; height: auto; display: block; }

.brand-text { display: flex; flex-direction: column; line-height: 1.2; }

.brand-name {
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.brand-tag { font-size: 0.78rem; color: var(--muted); }

.site-nav {
  display: flex;
  gap: 4px;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
}

.site-nav a {
  padding: 7px 16px;
  border-radius: 999px;
  text-decoration: none;
  font-size: 0.87rem;
  font-weight: 600;
  color: var(--muted);
  transition: background .15s ease, color .15s ease;
}
.site-nav a:hover { background: var(--sky-wash); color: var(--blue); }
.site-nav a.is-active { background: var(--ink); color: #fff; }

/* --- The paper, under the header on every page ---
   Spans the header's width so it reads as part of the page's frame
   rather than as one more card. Neutral surface: it is a reference, not
   a notice, and a coloured strip would be read as a warning. */

.paper-note {
  width: 100%;
  max-width: 900px;
  box-sizing: border-box;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 12px;
  padding: 10px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  color: var(--muted);
  /* Pulls up closer to the header than the body's gap would, so the two
     read as one band above the content. */
  margin-top: -14px;
}

.paper-label {
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--sky-wash);
  color: var(--blue);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* Takes the spare width, so "How to cite" sits at the right-hand end
   and drops to its own line on a phone instead of squeezing the title. */
.paper-ref { flex: 1 1 320px; }
.paper-ref a { color: var(--ink); font-weight: 600; }
.paper-ref cite { font-style: normal; }

.paper-cite {
  font-weight: 600;
  color: var(--blue);
  white-space: nowrap;
}

.cite-block {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: 0.8rem;
  padding: 12px 14px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

/* --- Layout --- */

.container {
  display: flex;
  gap: 32px;
  align-items: flex-start;
  justify-content: center;
  flex-wrap: wrap;
  width: 100%;
}

/* A hairline of the logo's own gradient across the top of each surface —
   the one place the mark's gold appears in the chrome. Painted as a
   background layer rather than a child element so the card's rounded
   corners clip it for free. */
.card, .results-card {
  background:
    linear-gradient(90deg, var(--sky), var(--blue) 45%, var(--gold))
      top / 100% 3px no-repeat,
    var(--surface);
}

.card {
  width: min(560px, 100%);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 30px 32px;
  box-shadow: var(--shadow);
}

h1 {
  margin: 0 0 6px;
  font-size: 1.4rem;
  font-weight: 750;
  letter-spacing: -0.015em;
  color: var(--ink);
}

p { margin: 0 0 24px; color: var(--muted); }

.card-wide { width: min(820px, 100%); }

/* Heading row with the "?" shortcut into the instructions. */
.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
}

.help-dot {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 700;
  text-decoration: none;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.help-dot:hover {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}
.help-dot:focus-visible { outline: none; box-shadow: var(--ring); }

/* Beside a field name rather than beside the heading of the card, so it
   has to be small enough not to be the thing read first. */
.help-dot-sm {
  width: 17px;
  height: 17px;
  font-size: 0.68rem;
  border-color: var(--border);
}

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

.toc {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 14px;
  margin-bottom: 30px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.toc a {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--blue);
  text-decoration: none;
  padding: 4px 11px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--border);
}
.toc a:hover { background: var(--sky-wash); border-color: var(--blue); }

.prose h2 {
  margin: 34px 0 12px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 1.05rem;
  font-weight: 750;
  color: var(--ink);
  /* Clears the heading when jumped to from the "?" deep links. */
  scroll-margin-top: 20px;
}

.prose p { margin: 0 0 16px; line-height: 1.65; }
.prose ul, .prose ol { margin: 0 0 16px; padding-left: 22px; color: var(--muted); }
.prose li { margin-bottom: 9px; line-height: 1.6; }
.prose strong { color: var(--ink); font-weight: 650; }
/* Not :not() for neatness — ".prose a" is specificity (0,1,1) and would
   otherwise beat .primary/.secondary (0,1,0) and repaint button text. */
.prose a:not(.link-button) { color: var(--blue); }

.prose code, .field-dialog code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.86em;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 1px 5px;
  color: var(--ink);
}

.prose dl { margin: 0 0 16px; }
.prose dt {
  font-weight: 650;
  color: var(--ink);
  font-size: 0.9rem;
  margin-top: 12px;
}
.prose dd {
  margin: 4px 0 0;
  padding-left: 14px;
  border-left: 2px solid var(--border);
  color: var(--muted);
  line-height: 1.6;
}

.prose .note {
  background: var(--sky-wash);
  border-left: 3px solid var(--sky);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 12px 16px;
  color: var(--muted);
  font-size: 0.88rem;
}

.prose .actions { margin-top: 30px; }

.fields { display: grid; gap: 22px; margin-bottom: 26px; }

/* Which curves to compare is one question; how to fit them is another,
   and the form ran the two together as six labels of the same weight. A
   rule and a name in a register no field label uses — small, spaced,
   upper case — say where the first ends without adding another box to a
   column that has boxes enough. */
.form-section {
  margin-top: 6px;
  padding-top: 22px;
  border-top: 1px solid var(--border);
}

.form-section-label {
  margin: 0 0 14px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The section keeps the rhythm of the form it sits in, being one item of
   it now rather than three. */
.form-section-body { display: grid; gap: 22px; }

/* --- Form controls --- */

.file-group, .slider-group { text-align: left; }

/* The control's own name reads at full strength; its explanation stays
   quieter underneath. Grey-on-white labels are the classic way to make a
   form unreadable. */
.file-group label, .slider-group label {
  display: block;
  margin-bottom: 7px;
  font-size: 0.87rem;
  font-weight: 650;
  color: var(--ink);
}

/* A field's name and the ? that explains it, on one line. The margin
   under the name moves to the row: left on the label, it would push the
   name up off the middle of the dot. */
.field-head {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 7px;
}
.field-head label, .field-head .group-label { margin-bottom: 0; }
/* Except in the candidate groups, where the grid gap already spaces it. */
.candidate-group > .field-head { margin-bottom: 0; }

.file-hint {
  display: block;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--muted);
  margin-top: 6px;
}

.input-file, .select-control, .text-control {
  width: 100%;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.9rem;
  font-family: inherit;
  cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input-file:hover, .select-control:hover, .text-control:hover {
  border-color: var(--blue);
}
.input-file:focus-visible, .select-control:focus-visible,
.text-control:focus, .text-control:focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: var(--ring);
}
/* A text box is typed in, not clicked open. */
.text-control { cursor: text; }
.text-control::placeholder { color: var(--muted); opacity: .7; }

.input-file::file-selector-button {
  margin: -2px 10px -2px -4px;
  padding: 6px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  background: var(--surface-sunk);
  color: var(--ink);
  font: inherit;
  font-size: 0.84rem;
  font-weight: 600;
  cursor: pointer;
}
.input-file::file-selector-button:hover { background: var(--sky-wash); }

/* Sliders: styled explicitly, since the native look varies wildly. */
input[type=range] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 20px;
  background: transparent;
  cursor: pointer;
}
input[type=range]::-webkit-slider-runnable-track {
  height: 5px;
  border-radius: 999px;
  background: var(--border);
}
input[type=range]::-moz-range-track {
  height: 5px;
  border-radius: 999px;
  background: var(--border);
}
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 17px;
  height: 17px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--blue);
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0, 34, 63, .3);
}
input[type=range]::-moz-range-thumb {
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--blue);
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0, 34, 63, .3);
}
input[type=range]:focus-visible { outline: none; }
input[type=range]:focus-visible::-webkit-slider-thumb { box-shadow: var(--ring); }
input[type=range]:focus-visible::-moz-range-thumb { box-shadow: var(--ring); }

.slider-value { font-size: 0.82rem; color: var(--muted); margin-top: 2px; }
.slider-value span { font-weight: 700; color: var(--blue); }

/* --- Buttons --- */

.actions { display: flex; gap: 12px; flex-wrap: wrap; }

button {
  flex: 1;
  min-width: 110px;
  border: 1px solid transparent;
  padding: 11px 18px;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-weight: 650;
  font-size: 0.9rem;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, transform .06s ease;
}
button:active { transform: translateY(1px); }
button:focus-visible { outline: none; box-shadow: var(--ring); }

.primary { background: var(--blue); color: #fff; border-color: var(--blue); }
.primary:hover { background: var(--blue-dark); border-color: var(--blue-dark); }

.secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--border-strong);
}
.secondary:hover { background: var(--surface-sunk); border-color: var(--blue); }

.link-button {
  flex: 1;
  min-width: 110px;
  /* Anchors have no border-style of their own, so .secondary's
     border-color would have nothing to colour. Width and style only —
     the shorthand would reset the colour that .primary/.secondary set,
     since this rule comes after both. */
  border-style: solid;
  border-width: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 11px 18px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  font-weight: 650;
  font-size: 0.9rem;
}

/* A class rather than button.mini: one of these is a link to another
   page, and an anchor has no border style of its own to colour, so the
   shorthand is written out here in full. */
.mini {
  flex: 0 0 auto;
  min-width: 0;
  padding: 4px 12px;
  font-size: 0.75rem;
  font-weight: 650;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font-family: inherit;
  cursor: pointer;
}
a.mini { display: inline-block; text-decoration: none; line-height: 1.5; }
.mini:hover { color: var(--blue); border-color: var(--blue); background: var(--sky-wash); }
.mini:focus-visible { outline: none; box-shadow: var(--ring); }

/* --- Built-in candidate curves --- */

.candidate-group { text-align: left; display: grid; gap: 8px; }

/* The same name-above-control shape as the battery dropdown, on a panel
   that has no control to be a real <label> for. The grid gap above
   supplies the space that label's own margin would. */
.candidate-group .group-label {
  font-size: 0.87rem;
  font-weight: 650;
  color: var(--ink);
}

.candidate-panel {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.candidate-panel[open] { background: var(--surface-sunk); }

.candidate-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 14px;
  cursor: pointer;
  font-size: 0.87rem;
  font-weight: 500;
  color: var(--ink);
  border-radius: var(--radius-sm);
  /* Suppress the browser's own marker; .candidate-title draws one that
     can be animated. */
  list-style: none;
}
.candidate-summary::-webkit-details-marker { display: none; }
.candidate-summary:hover { background: var(--sky-wash); }

.candidate-title { display: flex; align-items: center; gap: 10px; }
.candidate-title::before {
  content: "";
  border: solid var(--blue);
  border-width: 0 2px 2px 0;
  padding: 2.5px;
  transform: rotate(-45deg);
  transition: transform .15s ease;
}
.candidate-panel[open] .candidate-title::before { transform: rotate(45deg); }

.candidate-badge {
  font-size: 0.75rem;
  font-weight: 650;
  color: var(--blue);
  background: var(--sky-wash);
  border-radius: 999px;
  padding: 2px 10px;
  white-space: nowrap;
}
.candidate-badge-empty {
  color: var(--warning);
  background: var(--warning-bg);
}
/* Nothing selected is a run that will be refused; nothing uploaded is
   simply the usual case. Same pill, without the wash that says read me. */
.candidate-badge-quiet {
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--border);
  padding: 1px 9px;
}

.candidate-actions {
  display: flex;
  gap: 6px;
  padding: 2px 14px 10px;
}

/* The panel holding the unchecked curves is drawn like the two beside it.
   Tinting the whole thing amber was tried and read as an alarm going off
   in the middle of the form: the heading on it says "not verified" and
   there is a banner inside saying what that means, which is the warning.
   The one rule left here just sets that banner in from the panel edge,
   the way the buttons and the hint are. */
.unverified-panel > .banner { margin: 0 14px 10px; }

/* The file box sits in from the panel edge the way the All/None buttons
   in the other panel do, so the two panels open onto the same margin. */
.candidate-upload { padding: 2px 14px 10px; }

.candidate-panel > .file-hint { padding: 0 14px 12px; margin-top: 0; }

/* The way out of a panel and on to another page sits at its foot, hard
   right, where a next step belongs — not in the reading column with the
   text that explains what is above it. */
.panel-footer {
  display: flex;
  justify-content: flex-end;
  padding: 2px 14px 12px;
}

.candidate-list {
  padding: 0 8px;
  max-height: 300px;
  overflow-y: auto;
}

.candidate-item + .candidate-item { border-top: 1px solid var(--border); }

.candidate-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  margin: 0;
  cursor: pointer;
  border-radius: 8px;
}
.candidate-row:hover { background: var(--sky-wash); }
.candidate-row input {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  accent-color: var(--blue);
  cursor: pointer;
}

.candidate-name { font-size: 0.83rem; color: var(--ink); word-break: break-word; }

.curve-peek { padding: 0 8px 8px 34px; }
.curve-peek summary {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--blue);
}
.curve-peek img {
  width: 100%;
  max-width: 420px;
  margin-top: 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: #fff;
}

/* "view curve" gets the same chevron as the candidate panels; the
   browser's default triangle next to a custom one looks like a bug.

   The upload disclosure was styled here too, as a small blue link. It is a
   candidate panel now and takes its summary from those rules — which these
   would outrank, being a tag inside a class: blue text at 0.8rem, and a
   second chevron beside the one .candidate-title draws. */
.curve-peek summary {
  display: flex;
  align-items: center;
  gap: 7px;
  width: max-content;
  cursor: pointer;
  list-style: none;
}
.curve-peek summary::-webkit-details-marker { display: none; }

.curve-peek summary::before {
  content: "";
  border: solid currentColor;
  border-width: 0 1.5px 1.5px 0;
  padding: 2px;
  transform: rotate(-45deg);
  transition: transform .15s ease;
}
.curve-peek[open] summary::before { transform: rotate(45deg); }

.curve-peek summary:hover { color: var(--blue-dark); }

/* --- Column confirmation --- */

.confirm-list {
  display: grid;
  gap: 14px;
  margin-bottom: 24px;
  text-align: left;
  max-height: 55vh;
  overflow-y: auto;
  padding-right: 4px;
}

.confirm-card {
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 16px;
}

.confirm-card h3 {
  margin: 0 0 6px;
  font-size: 0.92rem;
  font-weight: 650;
  color: var(--ink);
  word-break: break-word;
}

.confirm-card .curve-type-tag {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--blue);
  background: var(--sky-wash);
  border-radius: 999px;
  padding: 2px 9px;
  margin-bottom: 8px;
}

.preview-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.78rem;
  margin-bottom: 12px;
  color: var(--muted);
  background: var(--surface);
}
.preview-table th, .preview-table td {
  border: 1px solid var(--border);
  padding: 5px 8px;
  text-align: right;
}
.preview-table th {
  color: var(--ink);
  font-weight: 650;
  background: var(--surface-sunk);
}

/* Picking a file opens the question here rather than sending the page
   away, so everything else already chosen on the form stays chosen and
   visible behind it. Holding focus inside, and closing on Escape, come
   from <dialog> itself and are not written anywhere below. */
.column-dialog {
  width: min(720px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.column-dialog::backdrop {
  background: rgba(0, 34, 63, .45);
  backdrop-filter: blur(2px);
}
.column-dialog h2 { margin: 0 0 6px; font-size: 1.05rem; }
.column-dialog-lead { margin: 0 0 16px; color: var(--muted); font-size: 0.85rem; }
/* While the file is still being read there are no cards yet, and the
   gap the list leaves would read as something failing to appear. */
.column-dialog .confirm-list:empty { margin-bottom: 0; }

/* What a ? beside a field name opens. Narrower than the column dialog:
   this one holds prose, and prose across 720px is a tiring line. */
.field-dialog {
  width: min(460px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.field-dialog::backdrop {
  background: rgba(0, 34, 63, .45);
  backdrop-filter: blur(2px);
}
.field-dialog h2 { margin: 0 0 12px; font-size: 1.05rem; }
.field-dialog p {
  margin: 0 0 12px;
  font-size: 0.88rem;
  line-height: 1.6;
  text-align: left;
}
.field-dialog p:last-of-type { margin-bottom: 20px; }
.field-dialog a { color: var(--blue); font-weight: 600; }
/* One button, which has no business spanning the dialog. */
.field-dialog .actions { justify-content: flex-end; }
.field-dialog .actions button { flex: 0 0 auto; }

.column-select-row { display: flex; gap: 12px; flex-wrap: wrap; }
.column-select-row > div { flex: 1; min-width: 140px; }
.column-select-row label {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 5px;
}

/* --- Results --- */

.graph-results {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 1;
  min-width: min(420px, 100%);
  max-width: 760px;
  gap: 20px;
}

.graph { width: 100%; text-align: center; }
.graph img {
  max-width: 760px;
  width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  box-shadow: var(--shadow);
}

.results-card {
  width: 100%;
  max-width: 760px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px 24px;
  box-shadow: var(--shadow);
}

.results-card h2, .placeholder h2 {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 750;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.results-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 8px;
}

.download-form { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }

.download-label {
  font-size: 0.8rem;
  color: var(--muted);
  white-space: nowrap;
  cursor: pointer;
}

/* Scoped to the form so it beats .select-control's width:100%, which is
   meant for the stacked selects on the confirm page. Left unscoped this
   loses on source order and the select eats the whole row. */
.download-form .download-format {
  width: auto;
  padding: 7px 10px;
  font-size: 0.83rem;
}

.download-btn {
  flex: 0 0 auto;
  background: var(--blue);
  color: #fff;
  border: 1px solid transparent;
  padding: 8px 14px;
  border-radius: 8px;
  font-family: inherit;
  font-size: 0.83rem;
  font-weight: 650;
  cursor: pointer;
  transition: background .15s ease;
}
.download-btn:hover { background: var(--blue-dark); }
.download-btn:focus-visible { outline: none; box-shadow: var(--ring); }

.result-item {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border);
}
.result-item:last-child { border-bottom: none; padding-bottom: 0; }
.result-item .label { color: var(--muted); font-size: 0.85rem; white-space: nowrap; }
.result-item .value {
  font-weight: 650;
  color: var(--ink);
  font-size: 0.9rem;
  text-align: right;
  word-break: break-word;
}

/* The right-hand column before there is a run to put in it. Same
   footprint as .graph-results, so the form does not shift sideways the
   moment the first results arrive. */
.placeholder {
  flex: 1;
  min-width: min(420px, 100%);
  max-width: 760px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-sunk);
  padding: 38px 34px;
}

.placeholder p { margin: 0 0 14px; }
.placeholder p:last-child { margin-bottom: 0; }

/* The file box, drawn rather than left to the browser — but only once
   the script that stages files has said it is running.

   A file input writes its own filename beside its own Browse button and
   there is no way to reach that text. The moment a file goes to the
   server the box is emptied, so the browser writes "No files selected."
   next to a file that is very much selected. With the script running the
   input is hidden and this row says what the server holds; without it the
   browser's own box is left exactly as it was, because then nothing is
   ever staged and its own text is right. */
.file-picker.is-drawn {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  /* The clipped input below is positioned against this, so a validation
     bubble comes up at the box rather than at the corner of the page. */
  position: relative;
  padding: 8px 10px;
  border: 1px solid var(--picker-border, var(--border-strong));
  border-radius: var(--radius-sm);
  background: var(--surface);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.file-picker.is-drawn:hover { border-color: var(--blue); }
/* The input is what takes focus, so the ring belongs to the row. */
.file-picker.is-drawn:focus-within {
  border-color: var(--blue);
  box-shadow: var(--ring);
}

/* Inside a panel the frame is already drawn, and a second strong line
   14px in from the first reads as a box in a box. A variable rather than
   a border-color, because a descendant selector would outrank the hover
   and focus rules above and the row would stop answering to either. */
.candidate-panel .file-picker.is-drawn { --picker-border: var(--border); }

/* Clipped rather than display:none, which would take it out of the tab
   order and stop the label opening it. */
.file-picker.is-drawn .input-file {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}

/* Stands in for the button inside the input, and is only there when that
   one is not. Same look, since it is the same button as far as anyone
   using it is concerned. */
.file-picker-browse { display: none; }
.file-picker.is-drawn .file-picker-browse {
  display: inline-block;
  flex: none;
  padding: 6px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  background: var(--surface-sunk);
  color: var(--ink);
  font-size: 0.84rem;
  font-weight: 600;
  cursor: pointer;
}
.file-picker.is-drawn .file-picker-browse:hover { background: var(--sky-wash); }

/* Names the files the server is holding for the next run, in the place
   the browser would have named them. Nothing is staged without the script
   running, so with the browser's own box on show this would only repeat
   what that box already says. */
.file-picker:not(.is-drawn) .staged-files { display: none; }

/* The names belong in the row, not in a box inside it. Held together as
   one box they are one thing to fit: the moment a third name stops fitting
   beside Browse, all three drop below it — including the two that fitted.
   As items of the row itself they wrap one at a time, which is what a row
   of names is expected to do.

   The flex box below is what a browser that has never heard of `contents`
   falls back to, and what the row is before the script draws it. */
.file-picker.is-drawn .staged-files { display: contents; }

.staged-files {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
}

.staged-empty { color: var(--muted); font-size: 0.85rem; }

/* Quiet on purpose. In the blue wash the badges wear, five filenames
   spoke as loudly as "7 of 7 selected" did, and the box holding them
   outweighed the list of built-in curves it is an alternative to. A name
   is a label, not a status. */
.staged-file {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  padding: 4px 4px 4px 11px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-sunk);
  font-size: 0.79rem;
}

/* File names run long and have no spaces to break at. */
.staged-name { overflow-wrap: anywhere; }

/* A round target a little larger than the cross in it, which is what
   makes it clickable at all — but only just larger. The cross is drawn
   rather than typed because the x glyph is a maths symbol and brings the
   spacing of one with it, on both sides, whatever the padding says. */
.staged-drop {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Every button on the page starts from one shared rule, and that rule
     reserves 110px for the wide ones. A width of 14px does not override a
     min-width of 110px: without this line the cross sits in the middle of
     a 110px button, with some fifty pixels of nothing either side of it,
     and no amount of shrinking the width changes that. */
  min-width: 0;
  width: 14px;
  height: 14px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.staged-drop svg {
  width: 9px;
  height: 9px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
}
.staged-drop:hover { background: var(--surface); color: var(--error); }
.staged-drop:focus-visible { outline: none; box-shadow: var(--ring); }

/* --- Submitted curves --- */

/* A long DOI has nowhere to break, so the table scrolls inside its own
   box rather than pushing the card wider than the window. */
.table-scroll { overflow-x: auto; }

.submissions-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.83rem;
  text-align: left;
  margin-bottom: 8px;
}
.submissions-table th, .submissions-table td {
  border-bottom: 1px solid var(--border);
  padding: 8px 10px;
  vertical-align: top;
}
.submissions-table th {
  color: var(--muted);
  font-weight: 650;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  white-space: nowrap;
}
.submissions-table a { color: var(--blue); }

/* The organization is context for the name above it, not a second name. */
.submission-org {
  display: block;
  color: var(--muted);
  font-size: 0.78rem;
}

/* --- Banners --- */

.banner {
  border-radius: var(--radius-sm);
  padding: 12px 16px;
  margin-bottom: 18px;
  text-align: left;
  font-size: 0.85rem;
  line-height: 1.5;
}
.banner-warning {
  background: var(--warning-bg);
  border: 1px solid var(--warning-border);
  color: var(--warning);
}
.banner-success {
  background: var(--sky-wash);
  border: 1px solid var(--border-strong);
  color: var(--ink);
}

.banner-error {
  background: var(--error-bg);
  border: 1px solid var(--error-border);
  color: var(--error);
}
.banner ul { margin: 6px 0 0; padding-left: 18px; }
.banner li { margin-bottom: 2px; }

/* One row per converted file, each with its own download. */
.download-list {
  list-style: none;
  margin: 0 0 20px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.download-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 10px 14px;
  background: var(--surface);
}
.download-list li + li { border-top: 1px solid var(--border); }
.download-list li:nth-child(even) { background: var(--surface-sunk); }

.download-name {
  font-size: 0.84rem;
  color: var(--ink);
  word-break: break-word;
}

/* The per-file buttons are anchors, so they need their own box. */
a.download-btn {
  flex: 0 0 auto;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}

/* --- While a run is going --- */

/* A form POST leaves the browser on the old page with nothing to show for
   it, sometimes for most of a minute. The buttons stand down when a run
   starts and the battery takes their place, so the sign that something is
   happening is in the spot that was just clicked. The page that answers
   the POST clears it away with the rest of the document.

   In the button's place rather than over the page: the last graph and the
   numbers beside it stay readable for the whole of that minute, which is
   the only thing worth doing while waiting. */
.run-status, .run-stopped-note { display: none; }

.js-long-run.is-running .actions,
.js-long-run.is-running .run-hint { display: none; }
.js-long-run.is-running .run-status { display: block; }
.js-long-run.was-stopped .run-stopped-note { display: block; }

/* The same row, keeping its shape: the battery takes the width the button
   that was pressed had, and Stop stands where its neighbour was. */
.run-status-row {
  display: flex;
  align-items: stretch;
  flex-wrap: wrap;
  gap: 12px;
}

/* Shaped like the button it stands in for — same height, same corners —
   so the swap reads as one control changing rather than the form jumping. */
.run-status-bar {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 9px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--sky-wash);
}

/* Buttons are flex: 1, which would hand Stop half the row. */
.run-stop { flex: 0 0 auto; }

/* And the pair being replaced splits the row the same way, or pressing Run
   changes the shape of the row it is in: Reset was half the card wide and
   Stop, standing in its place, a fifth of it. Reset and Stop come out the
   same width now — both are shorter than the 110px every button reserves.
   Only the forms that raise a battery: elsewhere a row of two is two
   equally weighted ways on, and reads better halved. */
.js-long-run .actions > .primary { flex: 1 1 auto; }
.js-long-run .actions > .secondary { flex: 0 0 auto; }

.run-status-title {
  font-size: 0.9rem;
  font-weight: 650;
  color: var(--ink);
}

.run-status-note { display: block; }

.run-status-note, .run-stopped-note {
  margin-top: 8px;
  font-size: 0.8rem;
  line-height: 1.45;
  text-align: center;
  color: var(--muted);
}

/* A battery charging: the body fills from the left in the logo's own
   gradient, and the bolt says what the filling means. Drawn here rather
   than shipped as a GIF so it takes its colours from the palette and
   stays sharp at any size. */
.battery { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }

.battery-body {
  position: relative;
  width: 56px;
  height: 24px;
  border: 2px solid var(--ink);
  border-radius: 7px;
  background: var(--surface);
  overflow: hidden;
}

.battery-cap {
  width: 4px;
  height: 10px;
  border-radius: 0 3px 3px 0;
  background: var(--ink);
}

.battery-fill {
  position: absolute;
  inset: 3px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--sky), var(--blue) 55%, var(--gold));
  transform-origin: left center;
  animation: battery-charge 2.4s ease-in-out infinite;
}

/* Navy rather than white: it has to stay legible over the empty end of
   the sweep as well as over the gradient. */
.battery-bolt {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 14px;
  height: 14px;
  color: var(--ink);
  animation: battery-bolt 2.4s ease-in-out infinite;
}

@keyframes battery-charge {
  0%   { transform: scaleX(0.03); opacity: 1; }
  75%  { transform: scaleX(1); opacity: 1; }
  100% { transform: scaleX(1); opacity: 0; }
}

@keyframes battery-bolt {
  0%, 100% { opacity: .18; }
  60%      { opacity: 1; }
}

/* A looping animation is the thing this setting exists to stop. The card
   still says what is happening, and the battery still reads as full. */
@media (prefers-reduced-motion: reduce) {
  .battery-fill, .battery-bolt { animation: none; }
  .battery-fill { transform: scaleX(1); }
  .battery-bolt { opacity: 1; }
}

/* --- Footer --- */

.site-footer {
  color: var(--muted);
  font-size: 0.78rem;
  padding-bottom: 8px;
  /* Sinks to the bottom of the viewport on short pages, and simply
     follows the content on long ones. */
  margin-top: auto;
}
