:root {
  --bg: #f7f7f5;
  --panel: #ffffff;
  --text: #1f2328;
  --muted: #6b7280;
  --border: #e5e7eb;
  --accent: #2563eb;
  --low: #1d4ed8;
  --low-bg: #dbeafe;
  --high: #b45309;
  --high-bg: #fde7c7;
  --ok: #15803d;
  --ok-bg: #dcfce7;
  --warn: #92400e;
  --warn-bg: #fef3c7;
  --err: #b91c1c;
  --err-bg: #fee2e2;
  --mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Buttons, chips and inputs as tokens rather than literals, so dark mode is a palette swap and not a rewrite. */
  --btn-bg: #111827;
  --btn-text: #ffffff;
  --btn-border: #111827;
  --btn-hover: #1f2937;
  --chip: #f3f4f6;
  --input-border: #cbd5e1;
  --stripe: #f3f4f6;
}

/*
 * Dark mode follows the device. Every surface is a token above, so this is a palette and not a second stylesheet;
 * the header was already dark and keeps its colors. The rank and status backgrounds become deep tints rather than
 * pastels, because a pastel badge on a dark panel glows.
 */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1115;
    --panel: #16181d;
    --text: #e5e7eb;
    --muted: #9aa3af;
    --border: #272b33;
    --accent: #7aa7ff;
    --low: #93c5fd;
    --low-bg: #16273f;
    --high: #fcd34d;
    --high-bg: #33280f;
    --ok: #86efac;
    --ok-bg: #12301f;
    --warn: #fcd34d;
    --warn-bg: #33280f;
    --err: #fca5a5;
    --err-bg: #3a1a1a;

    /* Black on a dark page is invisible, so the button inverts: light face, dark label. */
    --btn-bg: #e5e7eb;
    --btn-text: #14161a;
    --btn-border: #e5e7eb;
    --btn-hover: #ffffff;
    --chip: #232830;
    --input-border: #39404b;
    --stripe: #1b1f26;
  }

  /* The header is dark in both modes, which leaves it nearly the same value as a dark page. */
  .site-header { border-bottom: 1px solid var(--border); }

  /* A white mark on the sign-in panel would disappear; it follows the text color like everything else. */
  .login-mark { color: var(--text); }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
}

a { color: var(--accent); }
code, pre { font-family: var(--mono); font-size: 0.85em; }
pre { margin: 0; white-space: pre-wrap; }

.site-header {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  align-items: baseline;
  padding: 0.75rem 1.5rem;
  background: #111827;
  color: #f9fafb;
}
.brand { color: inherit; font-weight: 600; font-size: 1.1rem; text-decoration: none; }
.tagline { color: #9ca3af; font-size: 0.85rem; }
.site-nav a { color: #d1d5db; text-decoration: none; margin-right: 0.9rem; }
.site-nav a:hover { color: #ffffff; }

/* Every nav entry is an icon and a label on one line, in both shapes the nav takes. */
.site-nav a, .site-user button.link { display: inline-flex; align-items: center; gap: 0.5rem; white-space: nowrap; }
.nav-icon { width: 1.1rem; height: 1.1rem; flex: none; }

/* Three bars drawn from one element, so the summary stays a single hit target — the phone menu button and
   the rail's toggle are the same control in two places. */
.burger { width: 1.05rem; height: 2px; background: currentColor; box-shadow: 0 5px 0 currentColor, 0 -5px 0 currentColor; flex: none; }

/* A ticker is short, so the field is sized for one rather than stretching across the header. */
.site-search { display: flex; align-items: center; gap: 0.35rem; }
.site-search input {
  width: 100%;
  min-width: 0;
  padding: 0.3rem 0.6rem;
  background: #1f2937;
  border: 1px solid #374151;
  border-radius: 6px;
  color: #f9fafb;
  font: inherit;
}
.site-search input::placeholder { color: #9ca3af; }
.site-search input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.site-search button {
  display: inline-flex;
  align-items: center;
  padding: 0.3rem 0.45rem;
  background: none;
  border: 1px solid transparent;
  border-radius: 6px;
  color: #d1d5db;
  cursor: pointer;
}
.site-search button:hover { color: #ffffff; background: #1f2937; }

main { max-width: 1320px; margin: 0 auto; padding: 1rem 1.5rem 3rem; }
.nowrap { white-space: nowrap; }

/* Logging a trade: one row on a desktop, stacked on a phone, with the option-only fields able to vanish. */
.log-trade { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: flex-end; }
.log-trade label { display: flex; flex-direction: column; gap: 0.25rem; flex: 1 1 8rem; }
.log-trade label.wide { flex: 2 1 16rem; }
.log-trade label span { font-size: 0.8rem; color: var(--muted); }
.log-trade input, .log-trade select {
  padding: 0.4rem 0.6rem;
  background: var(--panel);
  border: 1px solid var(--input-border);
  border-radius: 6px;
  color: var(--text);
  font: inherit;
  min-width: 0;
}
.log-trade button { padding: 0.45rem 1.1rem; }
.log-trade label[hidden] { display: none; }

@media (max-width: 640px) {
  .log-trade label, .log-trade label.wide { flex-basis: 100%; }
  .log-trade button { width: 100%; }
}

/* Two links in one cell need a gap, or they read as one long sentence with an arrow in the middle. */
.external-links { display: flex; gap: 0.9rem; }

.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem 1.25rem;
  margin-bottom: 1rem;
}

h1 { margin: 0.25rem 0 1rem; font-size: 1.5rem; }
h2 { margin: 0 0 0.75rem; font-size: 1.05rem; }

.sub { color: var(--muted); font-size: 0.8rem; font-weight: normal; }
.muted { color: var(--muted); }
.stale { color: var(--warn); }
.legend { font-size: 0.8rem; margin: 0.5rem 0 0; }
.notes { white-space: pre-wrap; }

/*
 * A table scrolls inside its own card rather than running the page long, which is what lets the header stay
 * put: `position: sticky` resolves against the nearest scrolling ancestor, and with only `overflow-x` there
 * was nothing to stick to vertically. The max-height does nothing to a table that fits.
 */
.table-wrap { overflow: auto; max-height: 70vh; }

/* On paper there is no scrolling, so a height limit just drops the rows past it without saying so. */
@media print {
  /* Paper has no navigation, and the offset would shift every page in from the left margin. */
  .site-menu, .site-search { display: none; }
  body { padding-left: 0; }

  .table-wrap { overflow: visible; max-height: none; }
  table.data thead th { position: static; }
  .panel.collapsed > *:not(h2) { display: revert; }
}

table.data thead th {
  position: sticky;
  top: 0;
  z-index: 2;                      /* over the pinned first column, which is 1 */
  background: var(--panel);
  box-shadow: inset 0 -1px 0 var(--border);   /* a real border would scroll away with the cell */
}

/* The corner where the two sticky directions meet has to sit above both. */
table.data thead th:first-child,
table.data.stick-second thead th:nth-child(2) { z-index: 3; }

/*
 * The symbol stays put while the numbers scroll under it. Without this a narrow screen shows a column of
 * figures with no way to tell whose they are, which is the one thing a row has to keep saying.
 *
 * The background is required, not decoration: the cell would otherwise be transparent and the scrolling
 * columns would read straight through it. Nothing marks the edge, because nothing needs to — content being
 * clipped at the cell boundary is the cue, and a shadow drawn here would sit on every table on every page
 * whether or not anything was hidden behind it, which is what a first attempt did.
 */
table.data th:first-child,
table.data td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--panel);
}

/*
 * Some tables lead with a rank rather than the name, and pinning a column of numbers tells the reader
 * nothing — which is exactly what the scanner did. `stick-second` pins the name instead and lets the rank
 * scroll away beneath it.
 *
 * Pinning both was tried first and abandoned: the second column's `left` has to be a length, a table will not
 * honour a width on a cell exactly, and the mismatch left a sliver of scrolling content showing between the
 * two. A rank is worth less than a seam that looks like a rendering fault.
 */
/*
 * Only the body cell stops being sticky. Scoping this to `th` as well used to cancel the sticky header on
 * that one cell, so the "#" heading scrolled away while the rest of the row stayed — leaving a transparent
 * gap for the rank column to scroll through.
 */
table.data.stick-second tbody td:first-child { position: static; background: none; }

/*
 * The rank's heading keeps sticking to the top with the rest of the row, but stops sticking to the left —
 * `left: auto` rather than `position: static`, which would drop it out of the header row entirely. Leaving
 * it pinned at zero stacked it underneath the symbol heading, and the "#" simply vanished.
 */
table.data.stick-second thead th:first-child { left: auto; z-index: 2; }

table.data.stick-second th:nth-child(2),
table.data.stick-second td:nth-child(2) {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--panel);
}


table.data { width: 100%; border-collapse: collapse; }
table.data th,
table.data td { padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; }
table.data th { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); white-space: nowrap; }

/* The benchmark row of a grading table, set apart because the rows above it mean nothing without it. */
table.data tr.control td { border-top: 2px solid var(--border); background: var(--chip); font-weight: 600; }
table.data .num { text-align: right; font-variant-numeric: tabular-nums; }
th.sort-header { cursor: pointer; user-select: none; }
th[aria-sort="ascending"]::after { content: " ▲"; }
th[aria-sort="descending"]::after { content: " ▼"; }
tr.inactive td { opacity: 0.6; }
tr.missed td {
  color: var(--muted);
  font-style: italic;
  background: repeating-linear-gradient(45deg, transparent 0 6px, var(--stripe) 6px 12px);
}
td.errors { max-width: 28rem; }
td.actions { white-space: nowrap; }
td.actions form { display: inline; margin-right: 0.5rem; }

.symbol { font-weight: 600; text-decoration: none; }

.badge {
  display: inline-block;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
  background: var(--chip);
  color: var(--text);
}
.badge-ok { background: var(--ok-bg); color: var(--ok); }
.badge-warn { background: var(--warn-bg); color: var(--warn); }
.badge-err { background: var(--err-bg); color: var(--err); }
.badge-muted { background: var(--chip); color: var(--muted); }
.rank-low { background: var(--low-bg); color: var(--low); }
.rank-mid { background: var(--chip); color: var(--text); }
.rank-high { background: var(--high-bg); color: var(--high); }
.withheld { color: var(--muted); font-size: 0.8rem; font-weight: normal; }

.alert { padding: 0.6rem 0.8rem; border-radius: 6px; margin-bottom: 0.6rem; }
.alert-error { background: var(--err-bg); color: var(--err); }
.alert-warn { background: var(--warn-bg); color: var(--warn); }
.alert .detail { font-family: var(--mono); font-size: 0.8rem; margin-top: 0.25rem; }

.notice { padding: 0.6rem 0.8rem; border-radius: 6px; margin-bottom: 1rem; }
.notice-ok { background: var(--ok-bg); color: var(--ok); }
.notice-error { background: var(--err-bg); color: var(--err); }

.facts { display: grid; grid-template-columns: max-content 1fr; gap: 0.3rem 1rem; margin: 0 0 0.75rem; }
.facts dt { color: var(--muted); }
.facts dd { margin: 0; }

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 0.75rem; margin: 0 0 1rem; }
.cards > div { border: 1px solid var(--border); border-radius: 6px; padding: 0.6rem 0.75rem; }
.cards dt { color: var(--muted); font-size: 0.75rem; text-transform: uppercase; }
.cards dd { margin: 0.2rem 0 0; font-size: 1.2rem; font-weight: 600; }
.cards dd.rank-low, .cards dd.rank-mid, .cards dd.rank-high { display: inline-block; padding: 0 0.4rem; border-radius: 4px; }
.cards dd.withheld { font-size: 0.9rem; }

.chart { position: relative; height: 360px; }
.chart-short { height: 240px; }
.up { color: var(--ok); }
.down { color: var(--err); }

button {
  font: inherit;
  font-weight: 500;
  /* Named explicitly, never left to the browser: iOS Safari picks its own color for a button that doesn't name one,
     and against a pale face it chose white — an invisible label on a visible border. */
  color: var(--btn-text);
  background: var(--btn-bg);
  border: 1px solid var(--btn-border);
  padding: 0.35rem 0.8rem;
  border-radius: 6px;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
button:hover:not(:disabled) { background: var(--btn-hover); border-color: var(--btn-hover); }
button:disabled { opacity: 0.5; cursor: not-allowed; }
button.link { border: none; background: none; padding: 0; color: var(--accent); }
button.link.danger { color: var(--err); }

.inline { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }
.add-symbol { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: 1.25rem; align-items: center; }
.add-symbol .field { flex: 1 1 11rem; }
.add-symbol .field.wide { flex: 2 1 16rem; }

/*
 * Material 3 outlined text field. Two details are load-bearing: the input PRECEDES its label, so
 * the label can float via the adjacent-sibling combinator, and placeholder="&#160;" (a non-breaking
 * space — a plain space is trimmed to empty) is the pure-CSS "is this field empty" signal. The
 * label's background must match the field's fill so the floated label notches the outline cleanly.
 */
.field { position: relative; }
.field input {
  width: 100%;
  height: 2.75rem;
  padding: 0 0.8rem;
  font: inherit;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--input-border);
  border-radius: 6px;
  outline: none;
  transition: border-color 0.13s ease;
}
.field input:focus { border-color: var(--accent); }
.field label {
  position: absolute;
  left: 0.55rem;
  top: 50%;
  transform: translateY(-50%);
  padding: 0 0.3rem;
  pointer-events: none;
  background: var(--panel);
  color: var(--muted);
  transition:
    top 0.13s cubic-bezier(0.2, 0, 0, 1),
    font-size 0.13s cubic-bezier(0.2, 0, 0, 1),
    font-weight 0.13s linear,
    color 0.13s linear;
}
.field input:focus + label,
.field input:not(:placeholder-shown) + label {
  top: 0;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.field input:focus + label { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .field input, .field label { transition: none; }
}

.lookup { flex-basis: 100%; margin: 0; min-height: 1.2em; }
.lookup[data-state="ok"] { color: var(--ok); }
.lookup[data-state="warn"] { color: var(--high); }
.lookup[data-state="error"] { color: var(--err); }
.lookup[data-state="pending"] { color: var(--muted); }

.nav-badge {
  display: inline-block;
  min-width: 1.25rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: #ef4444;
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  text-align: center;
}
tr.unread td { font-weight: 600; }
tr.unread td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

.panel-narrow { max-width: 30rem; margin-left: auto; margin-right: auto; }
.site-user { margin-left: auto; display: flex; align-items: center; gap: 0.9rem; font-size: 0.85rem; }
.site-user a { color: #d1d5db; text-decoration: none; }
.site-user a:hover { color: #ffffff; }
.site-user .who { color: #9ca3af; }
.site-user form { display: inline; }
.site-user button.link { color: #d1d5db; font-size: 0.85rem; }
.checkbox { display: inline-flex; align-items: center; gap: 0.35rem; }

/*
 * The header nav is a <details> so it needs no JavaScript and works on pages that load none. Wide screens hide the
 * summary and lay the links out in a row; the disclosure only becomes a menu below the breakpoint.
 */
.site-menu { position: relative; }
.site-menu > summary { display: none; }
.site-menu .site-nav { display: flex; align-items: center; gap: 1.1rem; }
.site-menu .site-nav a { margin-right: 0; }

/*
 * Wide screens show the links whether or not the disclosure is open, which needs saying twice.
 *
 * A closed <details> used to hide its contents in a way a display rule on the child could override, and the
 * line above was that override. Chrome 131, Safari 18.4 and Firefox 139 moved the hiding to
 * content-visibility on the ::details-content pseudo-element, which display cannot reach — so the summary was
 * hidden here, the nav was hidden by the browser, and a desktop had no navigation at all. Phones were
 * unaffected because the summary is visible below the breakpoint.
 *
 * Both declarations stay: engines old enough to lack ::details-content are exactly the ones the display
 * override still works on. Scoped to a min-width query so the menu behaves normally on a phone.
 */
@media (min-width: 821px) {
  .site-menu::details-content { content-visibility: visible; }

  /*
   * The desktop rail.
   *
   * It is fixed to the left edge and always visible: `open` widens it, it does not reveal it, which is why
   * the content-visibility override above matters as much here as it did when the nav was a row.
   *
   * Peeking and pinning are deliberately different. Hovering slides the labels out *over* the page, because
   * every page here is a wide table and reflowing the columns each time a mouse crosses the left edge would
   * be intolerable. Pinning it open with the button does move the page across — that is a choice someone
   * made, and a rail that permanently covered the first inch of the content would be worse than the reflow.
   *
   * The push rides on :has(), so where that is unsupported a pinned rail overlays like a peek: narrower than
   * intended, still entirely usable.
   */
  :root { --rail: 3.5rem; --rail-open: 13.5rem; }

  /* Only where there is a rail: signed out there is no navigation at all, and the sign-in page should not
     be indented past a sidebar that is not there. */
  body:has(.site-menu) { padding-left: var(--rail); transition: padding-left 0.18s ease; }
  body:has(.site-menu[open]) { padding-left: var(--rail-open); }

  .site-menu {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 70;
    display: flex;
    flex-direction: column;
    width: var(--rail);
    padding: 0.5rem 0;
    background: #111827;
    border-right: 1px solid #374151;
    overflow-x: hidden;     /* what makes the labels a slide rather than a wrap */
    overflow-y: auto;       /* a short window with every admin link still reaches the bottom */
    transition: width 0.18s ease;
  }

  /* Hovering or tabbing into the rail peeks at the labels; clicking the button pins them open. */
  .site-menu[open],
  .site-menu:hover,
  .site-menu:focus-within { width: var(--rail-open); box-shadow: 4px 0 24px rgb(0 0 0 / 0.35); }

  /* Pinned, the rail sits beside the page rather than on it, so it stops casting a shadow over it. */
  .site-menu[open]:not(:hover):not(:focus-within) { box-shadow: none; }

  /* Restoring the remembered state on load must not animate, or every page starts with the rail sliding. */
  .site-menu.no-transition { transition: none; }

  .site-menu > summary {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-height: 2.25rem;
    padding: 0 1.15rem;
    margin-bottom: 0.5rem;
    color: #9ca3af;
    cursor: pointer;
    list-style: none;
  }
  .site-menu > summary::-webkit-details-marker { display: none; }
  .site-menu > summary:hover { color: #f9fafb; }

  /*
   * The nav is not a child of the rail — a <details> wraps its contents in ::details-content — so height has
   * to be handed down through that box for the account block to reach the foot of the rail. Where the
   * pseudo-element is not supported the rule is simply ignored and the account block sits under the links,
   * which is untidy rather than broken.
   */
  .site-menu::details-content { flex: 1; display: flex; flex-direction: column; min-height: 0; }

  .site-menu .site-nav {
    flex: 1;
    flex-direction: column;
    align-items: stretch;
    gap: 0.1rem;
  }

  /*
   * Collapsed, the labels are hidden rather than merely clipped: 56px of rail leaves a few pixels past the
   * icon, and a sliver of the first letter of every label reads as a rendering fault.
   */
  .site-menu .label,
  .site-menu .site-user .who { opacity: 0; transition: opacity 0.12s ease; }
  .site-menu[open] .label,
  .site-menu:hover .label,
  .site-menu:focus-within .label,
  .site-menu[open] .site-user .who,
  .site-menu:hover .site-user .who,
  .site-menu:focus-within .site-user .who { opacity: 1; }
  .site-menu.no-transition .label,
  .site-menu.no-transition .site-user .who { transition: none; }

  /* The rail still opens and closes; it just stops sliding for anyone who asked for less movement. */
  @media (prefers-reduced-motion: reduce) {
    body, .site-menu, .site-menu .label, .site-menu .site-user .who { transition: none; }
  }

  .site-menu .site-nav a,
  .site-menu .site-user button.link {
    gap: 0.75rem;
    padding: 0.5rem 1.15rem;
    border-radius: 0;
  }
  .site-menu .site-nav a:hover,
  .site-menu .site-user button.link:hover { background: #1f2937; color: #ffffff; }

  /* Pushed to the foot of the rail, where an account block belongs. */
  .site-menu .site-user {
    margin: auto 0 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0.2rem;
    padding-top: 0.5rem;
    border-top: 1px solid #374151;
  }
  .site-menu .site-user .who { padding: 0 1.15rem; overflow: hidden; text-overflow: ellipsis; }

  /* The dot only ever stood in for a hidden menu; on the rail the Alerts link is right there. */
  .site-menu > summary .nav-badge { display: none; }

  /* The header keeps the brand at its left, and the search sits on the center of the window. */
  .site-header { position: relative; }
  .site-search {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    width: min(26rem, 40vw);
  }
}

@media (max-width: 640px) {
  /*
   * Use the width. A phone has none to spare, and a rounded card inset on both sides spends about 10% of the
   * screen drawing a border around a table that then has to scroll sideways. The panels reach the edges; the
   * gutter stays on `main` so loose text outside a panel is not flush against the glass.
   */
  main { padding-left: 1rem; padding-right: 1rem; }

  .panel {
    margin-left: -1rem;
    margin-right: -1rem;
    border-left: 0;
    border-right: 0;
    border-radius: 0;
    padding-left: 1rem;
    padding-right: 1rem;
  }
}

@media (max-width: 820px) {
  .site-header { gap: 0.5rem 0.75rem; padding: 0.6rem 1rem; }
  .site-header .tagline { display: none; }

  .site-menu { margin-left: auto; }

  /*
   * The search sits on the one header row, between the mark and the menu button, and the wordmark gives up
   * its place for it: on a 390px screen "SlowTrades" is about 130px of a name the reader already knows,
   * against a field they came to use. The word is hidden from the eye, not from the accessibility tree —
   * display: none would leave the home link with no name at all, since the mark beside it is decorative.
   */
  .brand span {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* The mark is now the whole of the home link, so the link keeps a thumb-sized box around it. */
  .brand { min-height: 2.25rem; padding-right: 0.15rem; }
  .brand .mark { height: 1.15rem; }

  .site-search { flex: 1; min-width: 0; }
  .site-search button { padding: 0.3rem 0.2rem; }

  .site-menu > summary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    /* Comfortably tappable without a label to pad it out. */
    min-width: 2.75rem;
    min-height: 2.25rem;
    padding: 0.35rem 0.6rem;
    border: 1px solid #374151;
    border-radius: 6px;
    color: #f9fafb;
    cursor: pointer;
    list-style: none;          /* Firefox */
    user-select: none;
  }
  .site-menu > summary::-webkit-details-marker { display: none; }  /* Safari's triangle */

  .site-menu[open] > summary { background: #1f2937; }

  /*
     Tapping away from an open <details> doesn't close it, which reads as broken. This backdrop belongs to the
     summary, so a tap anywhere outside the dropdown lands on the toggle itself and closes it — no script, and the
     dropdown stays clickable because it sits above.
  */
  .site-menu[open] > summary::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 55;
  }

  .site-menu .site-nav {
    position: absolute;
    right: 0;
    top: calc(100% + 0.5rem);
    z-index: 60;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    min-width: 12rem;
    padding: 0.35rem 0;
    background: #111827;
    border: 1px solid #374151;
    border-radius: 8px;
    box-shadow: 0 10px 30px rgb(0 0 0 / 0.35);
  }
  .site-menu .site-nav > * { width: 100%; padding: 0.55rem 0.9rem; }
  .site-menu .site-nav a { width: 100%; }
  .site-menu .site-nav a:hover { background: #1f2937; }

  .site-user { margin-left: 0; width: 100%; flex-direction: column; align-items: flex-start; gap: 0.35rem; border-top: 1px solid #374151; padding-top: 0.6rem; }

  main { padding: 1rem 1rem 3rem; }
}

/* The mark takes the color of the header text through currentColor, so it needs no separate light/dark file. */
.brand { display: inline-flex; align-items: center; gap: 0.5rem; }
/* Matched to the wordmark's cap height, so mark and text read as one lockup. */
.brand .mark { height: 0.78em; width: auto; aspect-ratio: 183 / 77; flex: none; }
.login-mark { display: block; margin: 0 auto 1rem; height: 1.6rem; width: auto; aspect-ratio: 183 / 77; color: var(--text); }

/* Journal venue tabs. Real, simulated and sandbox results are never mixed, so the switch between them is a
   navigation control rather than a filter: each one is its own record with its own statistics. */
.venue-tabs {
    display: flex;
    gap: 0.25rem;
    margin: 0 0 1rem;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}

.venue-tabs a {
    padding: 0.5rem 0.9rem;
    text-decoration: none;
    color: var(--muted);
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    font-weight: 500;
}

.venue-tabs a:hover {
    color: var(--text);
}

.venue-tabs a.current {
    color: var(--text);
    border-bottom-color: var(--text);
}

/* The moving-average pickers above the price chart. */
.ma-toggles { display: flex; flex-wrap: wrap; gap: 0.35rem 0.75rem; margin: 0 0 0.75rem; }
.ma-toggle { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.85rem; cursor: pointer; }
.ma-toggle input { margin: 0; }

/* Picking the two averages to compare. */
.ma-picker { display: flex; flex-wrap: wrap; align-items: end; gap: 0.6rem; margin: 0 0 0.85rem; }
.ma-picker label { display: inline-flex; flex-direction: column; gap: 0.2rem; font-size: 0.8rem; color: var(--muted); }
.ma-picker select { font: inherit; padding: 0.3rem 0.4rem; border: 1px solid var(--input-border); border-radius: 6px; background: var(--panel); color: var(--text); }

/*
 * Folding panels. The heading becomes the control, and everything else in the panel hides — so a long page
 * can be cut down to the two sections someone actually wants. Added by script, so a page without it keeps
 * panels permanently open rather than permanently shut.
 */
.panel > h2:has(.panel-toggle) { margin-bottom: 0; }
.panel:not(.collapsed) > h2:has(.panel-toggle) { margin-bottom: 0.75rem; }
.panel.collapsed > *:not(h2) { display: none; }

.panel-toggle {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.panel-toggle:hover { color: var(--accent); }

.panel-caret {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.12s ease;
}
.panel.collapsed .panel-caret { transform: rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .panel-caret { transition: none; }
}

/* While a deferred section is fetching. Says what is happening rather than leaving an empty card. */
.loading { display: flex; align-items: center; gap: 0.5rem; color: var(--muted); margin: 0.6rem 0; }

.spinner {
  flex: none;
  width: 0.9rem;
  height: 0.9rem;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* A spinner that never stops is worse than none for anyone who finds motion difficult. */
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; border-top-color: var(--border); }
}

/* Server-side sorting and paging for a table too large to send whole. */
.macd-controls { display: flex; flex-wrap: wrap; align-items: end; gap: 0.6rem; margin: 0 0 0.85rem; }

.sort-link {
  padding: 0; border: 0; background: none; color: inherit;
  font: inherit; text-transform: inherit; letter-spacing: inherit;
  cursor: pointer;
}
.sort-link:hover { color: var(--text); }
/* The arrow comes from th[aria-sort] further up, which already styles the click-to-sort tables. Adding one
   here too drew it twice. */

.pager { display: flex; align-items: center; gap: 0.9rem; margin-top: 0.7rem; }
.pager button[disabled] { opacity: 0.4; cursor: default; }

/* Links to what this tool does not hold — the share structure above all. */
.external { display: flex; flex-wrap: wrap; gap: 0.9rem; margin: 0.6rem 0 0.9rem; font-size: 0.85rem; }
.external a { text-decoration: none; }
.external a:hover { text-decoration: underline; }
