/* Duke's Picks AI — chat UI.

   Structure only. Every colour, typeface and radius comes from theme.css, which
   is swapped per build so the internal LineFigures deployment can look like a
   different product off the same source. Do not hardcode a colour in this file:
   if it is not a var, it will not follow the brand. */

:root {
  /* Derived from the theme's two base colours, so a brand sets two values and
     the greys and hairlines follow. */
  --muted:     rgba(var(--ink-rgb), 0.6);
  --muted-2:   rgba(var(--ink-rgb), 0.4);
  --line:      rgba(var(--accent-rgb), 0.2);
  --line-soft: rgba(var(--accent-rgb), 0.1);

  --sidebar-w: 288px;
  /* Wide enough for a 40px target with air around it, narrow enough that the
     page gets the room back. */
  --rail-w: 62px;
  /* The assistant's second bar. Narrower than the main one: it holds
     conversation titles, which wrap, not tool names, which must not. */
  --assistant-w: 248px;
  --measure:   760px;
  /* Real viewport height on mobile: 100vh sits under Safari's chrome and cuts
     off the composer. Overridden with the visual viewport in app.js. */
  --vh: 100dvh;
}

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

html, body {
  height: 100%;
  margin: 0;
  /* The page itself never scrolls; only the message list does. Without this,
     iOS rubber-banding drags the whole app and hides the composer. */
  overflow: hidden;
  overscroll-behavior: none;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

[hidden] { display: none !important; }

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

.eyebrow {
  font-size: 11px;
  letter-spacing: var(--eyebrow-tracking);
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 8px;
  font-weight: 500;
}

.fine { font-size: 12px; color: var(--muted-2); }

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

/* ── Buttons ─────────────────────────────────────────────────────────────── */

.btn {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  padding: 11px 20px;
  border-radius: var(--btn-radius);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}
/* --accent is the readable-on-dark colour and does all the text and border
   work. A solid fill needs its own token, because a brand whose real colour is
   dark (the LineFigures brick red) has to lighten it for text and cannot fill with
   the lightened one. */
.btn--accent { background: var(--accent-fill); color: var(--accent-ink); }
.btn--accent:hover:not(:disabled) { background: var(--accent-fill-hover); }
.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn--sm { padding: 8px 14px; font-size: 12.5px; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.linkish {
  background: none; border: none; padding: 0;
  color: var(--muted); font-family: var(--sans); font-size: 12px;
  cursor: pointer; text-decoration: underline;
}
.linkish:hover { color: var(--accent); }

/* Square tap target, comfortably past the 44px minimum on touch. */
.icon-btn {
  background: none;
  border: 1px solid transparent;
  color: var(--muted);
  cursor: pointer;
  border-radius: var(--radius-sm);
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  display: none;
  place-items: center;
  padding: 0;
  transition: background 0.18s ease, color 0.18s ease;
}
.icon-btn:hover { background: rgba(var(--accent-rgb), 0.08); color: var(--accent); }
.icon-btn svg { width: 19px; height: 19px; display: block; }

/* ── Sign in ─────────────────────────────────────────────────────────────── */

.login {
  height: 100%;
  overflow-y: auto;
  display: grid;
  place-items: center;
  padding: 32px 20px;
  background:
    radial-gradient(1000px 500px at 50% -10%, rgba(var(--accent-rgb), 0.07), transparent 70%),
    var(--bg-deep);
}

.login-card {
  width: 100%;
  max-width: 420px;
  background: var(--bg-raised);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 40px 36px;
}

.login-card h1 {
  font-family: var(--display);
  font-size: 34px;
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  text-transform: var(--display-transform);
  margin: 0 0 12px;
}

.lead { color: var(--muted); margin: 0 0 28px; }

.login-card label {
  display: block;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
}

.login-card input {
  width: 100%;
  padding: 13px 16px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-family: var(--sans);
  /* 16px stops iOS zooming the page when the field is focused. */
  font-size: 16px;
  margin-bottom: 16px;
}
.login-card input:focus { outline: none; border-color: var(--accent); }

.login-card .btn { width: 100%; }

.msg { font-size: 13.5px; margin: 16px 0 0; min-height: 20px; }
.msg.ok { color: var(--accent-soft); }
.msg.err { color: #e8836f; }

.login-card .fine { margin: 24px 0 0; text-align: center; }
/* The line under the button: why there is no password. */
.login-card .login-note { margin: 12px 0 0; line-height: 1.5; }

/* ── App shell ───────────────────────────────────────────────────────────── */

.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  height: var(--vh);
  /* One easing and one duration for the track and the bar, because they move
     together and any difference between them reads as a stutter. */
  transition: grid-template-columns 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  /* The collapsed bar lifts out of the flow when it opens on hover, and it is
     positioned against this. */
  position: relative;
}
/* PINNED, so the page cannot slide under the bar when the bar leaves the grid.
   Without this, hovering the rail moved main into the empty first track and
   squeezed the whole app into 62 pixels. */
.app > .sidebar { grid-column: 1; }
.app > main,
.app > .tool-view { grid-column: 2; }

/* ALL ON ONE ROW.
   Pinning the column is only half of it: with three things asking for column 2
   the grid gave them a row each, and the tool view landed underneath the
   assistant bar with 427 pixels of nothing above it. A column without a row is
   only half an instruction. */
.app > .sidebar,
.app > main,
.app > .tool-view { grid-row: 1; }


/* Desktop collapse: the panel becomes a RAIL, not a hole.
   Carson, 10 September 2026: "That menu bar should minimize to a smaller bar
   that shows icons instead of written out names. when hovering it comes back
   out for someone to select something."
   It used to collapse to nothing, which is how somebody ended up with no menu
   and no way back to it. A rail is always there and always has the handle on
   it. */
.app.collapsed { grid-template-columns: var(--rail-w) 1fr; }

/* THE TRACK MOVES WITH THE BAR, on the same curve and the same duration.
   The bar is absolutely positioned, so the grid's first column is a spacer that
   decides where the page starts. Animating only one of the two is what made
   this feel cheap: the bar glided and the page jumped. */
.app {
  transition: grid-template-columns 0.22s cubic-bezier(0.4, 0, 0.2, 1);
}

/* OUT OF THE GRID, ALWAYS, so the only thing that ever changes is `width`.
   Carson: "it needs to be smoother. not clunky and fast." Width animates
   cleanly; the positioning scheme it used to switch between mid-click cannot
   animate at all, which is what made the old version snap. */
.sidebar {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--sidebar-w);
  z-index: 30;
  background: var(--bg-deep);
  border-right: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  transition: width 0.22s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Collapsed: a strip of icons, and it STAYS a strip of icons.
   Carson, 10 September 2026: "take the hover out and make sure the minimize and
   open code is simple and moves smooth."

   There is no hover state here any more, and that is the whole fix. The handle
   that collapses the bar sits INSIDE the bar, so every hover rule written here
   was fighting the click that made it: the bar collapsed, the pointer was now
   over a collapsed bar, `:hover` fired, and it flew straight back to full
   width. Three rounds of trying to time that away produced three overlapping
   hover blocks, a `.peek-off` class and a `pointerleave` listener, and it still
   read as broken, because the bar was being asked to be two things at once.

   One class, one property, one transition. `.app.collapsed` sets the width and
   nothing else moves. Open and shut are the same animation in reverse and the
   handle is on the bar in both states.

   The labels stay in the DOM, for screen readers and so opening costs no
   rebuild. Each icon keeps its name as a tooltip while it is shut. */
.app.collapsed .sidebar { width: var(--rail-w); }

.app.collapsed .tool-link-name,
.app.collapsed .tool-soon,
.app.collapsed .sidebar-brand .eyebrow,
.app.collapsed .sidebar-foot,
.app.collapsed .tool-submenu { display: none; }

.app.collapsed .tool-link {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}
.app.collapsed .sidebar-head {
  padding: 14px 11px;
  border-bottom: 0;
}
.app.collapsed .sidebar-brand { justify-content: center; }
.app.collapsed .tools-nav { padding: 4px 11px; }

.sidebar-head {
  padding: 18px 18px 14px;
  border-bottom: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.sidebar-brand {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 24px;
}
.sidebar-brand .eyebrow { margin: 0; white-space: nowrap; }
.sidebar-head .btn { width: 100%; }
/* The old collapse control. Replaced by .rail-toggle, which is injected into
   the same row by tools.ts and does both directions. */
#panel-close { display: none; }

/* ── The conversations bar, which belongs to the assistant PAGE ──────────
   Carson: "make the sub menu not a menu at all. make it apart of that page.
   when that page is open, that bar should be there."
   So it is inside main.chat and it is always there while that page is. It has
   no collapsed state, no slide, and nothing the menu does can touch it. */

.chat {
  display: grid;
  grid-template-columns: var(--assistant-w) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  min-height: 0;
  min-width: 0;
  position: relative;
}
.chat > .assistant-bar { grid-column: 1; grid-row: 1 / -1; }
.chat > .chat-head { grid-column: 2; grid-row: 1; }
.chat > .messages { grid-column: 2; grid-row: 2; }
.chat > .composer { grid-column: 2; grid-row: 3; }
.chat > .jump { grid-column: 2; grid-row: 2; }

.assistant-bar {
  background: var(--bg-deep);
  border-right: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.assistant-bar-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 16px 16px 10px;
}
.assistant-bar-head .eyebrow { margin: 0; white-space: nowrap; flex: 1; }

/* Only a phone needs a way to put it away: there both cannot fit at once. */
.assistant-bar-close { display: none; }
.convos-toggle { display: none; }

.assistant-bar > #new-thread { margin: 4px 12px 0; width: calc(100% - 24px); }
.assistant-bar > .thread-search { margin: 10px 12px 0; }
.assistant-bar > .threads { flex: 1; max-height: none; padding: 10px 8px; }

/* The handle, at the top of the bar. Both directions, always visible, and it
   is inside the bar so collapsing can never take it away. */
.rail-toggle {
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--muted);
  cursor: pointer;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: 0 0 32px;
  padding: 0;
  transition: background 0.18s ease, color 0.18s ease;
}
.rail-toggle:hover { background: rgba(var(--accent-rgb), 0.08); color: var(--accent); }
.rail-toggle svg { width: 18px; height: 18px; display: block; }

/* The assistant's own menu, under its rail item.
   Carson: "when this is selected it pops out another menu which has the New
   Convo and the history of the chats." Indented and ruled so it reads as
   belonging to the item above it rather than as more tools. */
.tool-submenu {
  margin: 4px 0 6px 12px;
  padding-left: 10px;
  border-left: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 0;
}
.tool-submenu .btn { width: 100%; }

.threads {
  max-height: 42vh;
  overflow-y: auto;
  padding: 0;
  min-height: 0;
  -webkit-overflow-scrolling: touch;
}

.thread-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-family: var(--sans);
  font-size: 13.5px;
  padding: 10px 12px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.thread-item:hover { background: rgba(var(--accent-rgb), 0.06); color: var(--ink); }
.thread-item.active { background: rgba(var(--accent-rgb), 0.1); color: var(--accent); }

.sidebar-foot {
  padding: 14px 18px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
}
.sidebar-foot .fine {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Dimmer behind the drawer on mobile. */
.scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.22s ease, visibility 0.22s ease;
  z-index: 15;
  border: 0;
  padding: 0;
}
.scrim.show { opacity: 1; visibility: visible; }

/* ── Chat ────────────────────────────────────────────────────────────────── */

/* `.chat` is laid out where the conversations bar is defined, above. It was a
   flex column until that bar became part of this page. */
.chat-head {
  padding: 12px 18px;
  border-bottom: 1px solid var(--line-soft);
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 60px;
  flex: 0 0 auto;
}
.chat-head h2 {
  font-family: var(--display);
  font-size: 17px;
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  text-transform: var(--display-transform);
  margin: 0;
  min-width: 0;
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Shown on desktop only; the mobile drawer uses #menu-toggle. */
#panel-open { display: none; }

.messages {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 26px 18px;
  display: flex;
  flex-direction: column;
  gap: 26px;
  min-height: 0;
}

.turn { max-width: var(--measure); width: 100%; margin: 0 auto; min-width: 0; }

.turn-role {
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted-2);
  margin-bottom: 8px;
}
.turn--assistant .turn-role { color: var(--accent); }

/* Long horse names and URLs must wrap rather than force a sideways scroll. */
/* ── The answer itself ───────────────────────────────────────────────────────
   The model writes markdown and the client renders it. This used to be
   pre-wrapped plain text, which is why a table arrived as a column of pipes.
   White space is normal now: the structure comes from the elements. */

.turn-body {
  overflow-wrap: anywhere;
  word-break: break-word;
}

.turn-body > :first-child { margin-top: 0; }
.turn-body > :last-child { margin-bottom: 0; }

.turn-body p { margin: 0 0 12px; }
.turn-body strong { color: var(--accent-soft); font-weight: 600; }
.turn-body em { color: var(--ink); font-style: italic; }

/* Headings inside an answer are section markers, not titles. Kept close in
   size to the body so a three-part answer reads as one piece of writing. */
.turn-body h2,
.turn-body h3,
.turn-body h4,
.turn-body h5 {
  font-family: var(--sans);
  color: var(--accent);
  font-weight: 600;
  line-height: 1.35;
  margin: 20px 0 8px;
  text-wrap: balance;
}
.turn-body h2 { font-size: 16px; }
.turn-body h3 { font-size: 15px; }
.turn-body h4,
.turn-body h5 { font-size: 14px; color: var(--accent-soft); }

.turn-body ul,
.turn-body ol {
  margin: 0 0 12px;
  padding-left: 22px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.turn-body li { padding-left: 2px; }
.turn-body li::marker { color: var(--accent); }
.turn-body ol { list-style: decimal; }
.turn-body ol li::marker { font-variant-numeric: tabular-nums; }

.turn-body blockquote {
  margin: 0 0 12px;
  padding: 2px 0 2px 14px;
  border-left: 2px solid var(--line);
  color: var(--muted);
}

.turn-body hr {
  border: 0;
  border-top: 1px solid var(--line-soft);
  margin: 18px 0;
}

.turn-body code {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.88em;
  background: var(--bg-input);
  border: 1px solid var(--line-soft);
  border-radius: 5px;
  padding: 1px 5px;
}
.turn-body pre {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 12.5px;
  line-height: 1.6;
  background: var(--bg-input);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin: 0 0 14px;
  overflow-x: auto;
}
.turn-body pre code { background: none; border: 0; padding: 0; font-size: inherit; }

/* ── Tables ──────────────────────────────────────────────────────────────────
   A card or a ticket is tabular, and these get read at a glance rather than
   left to right, so they get real rules and aligned figures. The wrapper is
   what scrolls, never the page. */

.turn-body .table-wrap {
  overflow-x: auto;
  margin: 4px 0 16px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  -webkit-overflow-scrolling: touch;
}

.turn-body table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13.5px;
  line-height: 1.45;
}

.turn-body thead th {
  background: var(--bg-raised);
  color: var(--accent);
  font-weight: 600;
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  border-bottom: 1px solid var(--line);
}

.turn-body th,
.turn-body td {
  padding: 8px 14px;
  text-align: left;
  vertical-align: top;
}

.turn-body tbody tr + tr td { border-top: 1px solid var(--line-soft); }
/* Every other row lifted a shade: on a twelve horse field the eye needs it. */
.turn-body tbody tr:nth-child(even) { background: rgba(var(--accent-rgb), 0.035); }

.turn-body td.a-right, .turn-body th.a-right { text-align: right; }
.turn-body td.a-center, .turn-body th.a-center { text-align: center; }
/* Odds, figures and lengths line up in their column instead of wandering. */
.turn-body td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* First column is the program number or the horse. Never let it wrap to two
   lines while the rest of the row stays on one. */
.turn-body tbody td:first-child { white-space: nowrap; }

.turn--user .turn-body {
  background: var(--bg-raised);
  border-left: 2px solid var(--line);
  padding: 12px 16px;
  border-radius: var(--radius-sm);
}

.tool-note {
  font-size: 12px;
  color: var(--muted-2);
  font-style: italic;
  margin: 6px 0;
}

/* The waiting state is the one place the app can look broken while working, so
   it carries the brand mark with the mane moving plus stepping dots: two
   independent signs of life, in case one is ever mistaken for a static image. */
.thinking {
  display: flex;
  align-items: center;
  gap: 9px;
  font-style: normal;
  letter-spacing: 0.02em;
}
/* Spins like a coin on its axis. The logo PNG is used directly, so this is the
   real artwork rather than a redraw, and the rotation reads as working without
   needing the mane to move. backface-visibility stays visible on purpose: the
   mark mirrors through the back half of the turn, which is what a real coin
   does. */
.thinking-mark {
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
  display: block;
  background-image: url(/logo-mark.png);
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  transform-style: preserve-3d;
  animation: dp-coin 2.6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
@keyframes dp-coin {
  from { transform: rotateY(0deg); }
  to   { transform: rotateY(360deg); }
}
}
.thinking .dots { display: inline-flex; }
.thinking .dots i {
  font-style: normal;
  animation: dp-dot 1.4s infinite;
}
.thinking .dots i:nth-child(2) { animation-delay: 0.2s; }
.thinking .dots i:nth-child(3) { animation-delay: 0.4s; }
@keyframes dp-dot {
  0%, 60%, 100% { opacity: 0.25; }
  30%           { opacity: 1; }
}

.login-mark {
  display: block;
  width: 56px;
  height: 56px;
  margin: 0 0 18px;
  border-radius: var(--radius-sm);
}

.turn-actions { margin-top: 10px; display: flex; gap: 8px; }
.rate {
  background: none;
  border: 1px solid var(--line-soft);
  border-radius: var(--btn-radius);
  color: var(--muted-2);
  cursor: pointer;
  font-size: 13px;
  padding: 4px 11px;
  line-height: 1.4;
}
.rate:hover { border-color: var(--accent); color: var(--accent); }
.rate.chosen { border-color: var(--accent); color: var(--accent); }

.empty {
  margin: auto;
  text-align: center;
  max-width: 460px;
  color: var(--muted);
  padding: 0 4px;
}
.empty h3 {
  font-family: var(--display);
  font-size: 24px;
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  text-transform: var(--display-transform);
  color: var(--ink);
  margin: 0 0 12px;
}

.caret::after {
  content: '▍';
  color: var(--accent);
  animation: blink 1s steps(2) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* ── Composer ────────────────────────────────────────────────────────────── */

/* ── Activity panel (internal, admin) ────────────────────────────────────── */

.activity-usage {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 24px;
  font-size: 13px;
}
.activity-usage th {
  text-align: left;
  font-weight: 500;
  font-size: 11px;
  letter-spacing: var(--eyebrow-tracking);
  text-transform: uppercase;
  color: var(--accent);
  padding: 0 12px 8px 0;
  border-bottom: 1px solid var(--line-soft);
  white-space: nowrap;
}
.activity-usage td {
  padding: 8px 12px 8px 0;
  border-bottom: 1px solid var(--line-soft);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.activity-usage td:first-child { color: var(--ink); }
.activity-usage td.money { color: var(--ink); font-weight: 500; }
.activity-usage tr.total td { border-bottom: none; color: var(--accent); padding-top: 12px; }
.cost-note { margin: -12px 0 24px; line-height: 1.5; }

.activity-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid var(--line-soft);
  padding: 12px 0;
  cursor: pointer;
  font-family: var(--sans);
  color: var(--ink);
}
.activity-item:hover { background: rgba(var(--accent-rgb), 0.05); }
.activity-item span { display: block; }
.activity-who {
  font-size: 11px;
  letter-spacing: var(--eyebrow-tracking);
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 4px;
}
.activity-item .activity-title { font-size: 14px; margin-bottom: 3px; }

/* ── Jump to the live end of an answer ───────────────────────────────────── */
/* Only on screen while an answer is being written and you have scrolled up off
   the bottom of it. Sits above the composer, out of the text. */
.jump {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 96px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 14px;
  border-radius: var(--btn-radius);
  border: 1px solid var(--line);
  background: var(--bg-raised);
  color: var(--accent);
  font-family: var(--sans);
  font-size: 12.5px;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}
.jump:hover { border-color: var(--accent); background: var(--bg-input); }
.jump svg { width: 14px; height: 14px; display: block; }
.jump[hidden] { display: none; }

.composer {
  border-top: 1px solid var(--line-soft);
  padding: 14px 18px calc(12px + env(safe-area-inset-bottom));
  flex: 0 0 auto;
  background: var(--bg);
}

/* The field and the button sit in one rounded shell, so they cannot drift out
   of alignment as the textarea grows. */
.composer form {
  max-width: var(--measure);
  margin: 0 auto;
  display: flex;
  gap: 8px;
  align-items: flex-end;
  background: var(--bg-input);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px 6px 6px 8px;
  transition: border-color 0.18s ease;
}
.composer form:focus-within { border-color: var(--accent); }

.composer textarea {
  flex: 1 1 auto;
  min-width: 0;
  resize: none;
  max-height: 168px;
  padding: 9px 6px;
  background: transparent;
  border: none;
  color: var(--ink);
  font-family: var(--sans);
  /* 16px: anything smaller makes iOS zoom on focus. */
  font-size: 16px;
  line-height: 1.5;
}
.composer textarea:focus { outline: none; }
.composer textarea::placeholder { color: var(--muted-2); }

#send {
  flex: 0 0 auto;
  border-radius: var(--btn-radius);
  padding: 10px 20px;
  /* Matches one line of textarea plus its padding, so it sits flush on the
     baseline at rest and stays pinned to the bottom as the field grows. */
  min-height: 40px;
  display: inline-grid;
  place-items: center;
}
.send-icon { display: none; width: 19px; height: 19px; }

.disclaimer {
  max-width: var(--measure);
  margin: 10px auto 0;
  font-size: 11.5px;
  color: var(--muted-2);
  text-align: center;
  text-wrap: balance;
}

/* ── Desktop ─────────────────────────────────────────────────────────────── */

@media (min-width: 861px) {
  .chat-head { padding: 14px 28px; }
  .messages { padding: 30px 28px; }
  .composer { padding: 16px 28px 14px; }
  /* The old pair of collapse buttons, one in the bar and one in the chat
     header. Both are gone: `.rail-toggle` is at the top of the bar, does both
     directions, and is the only one, so there is never a second control doing
     the same job in a different place. */
}

/* ── Mobile ──────────────────────────────────────────────────────────────── */

@media (max-width: 860px) {
  .app { grid-template-columns: minmax(0, 1fr); }
  .app.collapsed { grid-template-columns: minmax(0, 1fr); }

  /**
   * ONE COLUMN MEANS COLUMN ONE. The bar is a drawer over the page here, so
   * pinning the page to column 2 (which the desktop grid needs, see above)
   * made the browser invent a second track and hand the first one a share of
   * the width. Found 12 September on a 375px phone: Horizontal Tickets sat in
   * 252 of 375 pixels with 122 of nothing beside it, because the tiles give
   * that column a min-content width to fight with. Everything else on the
   * page is fluid, which is why only this tool showed it.
   */
  .app > .sidebar,
  .app > main,
  .app > .tool-view { grid-column: 1; }

  #menu-toggle { display: grid; }

  /* ON A PHONE THE CONVERSATIONS BAR IS A DRAWER OVER THE CONVERSATION.
     248 of 375 pixels is most of the screen, so the two cannot share it. The
     chat header carries its own button for this, separate from the menu
     hamburger, because "the tools" and "my conversations" are different things
     and one control cannot mean both. */
  .chat { grid-template-columns: minmax(0, 1fr); }
  .chat > .chat-head,
  .chat > .messages,
  .chat > .composer,
  .chat > .jump { grid-column: 1; }

  .chat > .assistant-bar {
    position: absolute;
    inset: 0 auto 0 0;
    width: min(var(--sidebar-w), 86vw);
    z-index: 18;
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 0.24s ease, visibility 0s linear 0.24s;
    box-shadow: 14px 0 38px rgba(0, 0, 0, 0.5);
  }
  .chat.convos-open > .assistant-bar {
    transform: translateX(0);
    visibility: visible;
    transition: transform 0.24s ease, visibility 0s;
  }

  .assistant-bar-close,
  .convos-toggle {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    flex: 0 0 40px;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    padding: 0;
  }
  .assistant-bar-close svg,
  .convos-toggle svg { width: 19px; height: 19px; display: block; }
  .convos-toggle:hover, .assistant-bar-close:hover { color: var(--accent); }

  /* THE ASSISTANT'S OWN BAR IS A DRAWER ON A PHONE, not a second column.
     There is no room for two bars side by side at 375px, so it slides over the
     menu drawer and carries a back chevron to get out of it again. It only
     exists while the drawer is open: with the drawer shut, the member is
     reading the conversation and neither bar belongs on screen. */
  /* ON A PHONE THE HANDLE HAS TO BE IN THE PAGE.
     The bar is a drawer here, so its own toggle goes off screen with it, and
     the chat header's hamburger is hidden while a tool is up. That left a
     phone on the watch list with no way to reach the menu at all: the same
     stranding as before, in the one place it is hardest to work around.
     So every tool's header carries it, on mobile only. */
  .tool-head-toggle { display: grid; }

  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(var(--sidebar-w), 86vw);
    z-index: 20;
    transform: translateX(-100%);
    opacity: 1;
    padding-top: env(safe-area-inset-top);
  }
  /* The desktop collapse class must not fight the drawer. */
  .app.collapsed .sidebar { transform: translateX(-100%); opacity: 1; }
  .sidebar.open,
  .app.collapsed .sidebar.open { transform: translateX(0); opacity: 1; pointer-events: auto; }

  .chat-head {
    padding: 10px 14px;
    padding-top: calc(10px + env(safe-area-inset-top));
    min-height: 56px;
  }
  .chat-head h2 { font-size: 16px; }

  .messages { padding: 20px 16px; gap: 22px; }
  .composer { padding: 12px 14px calc(10px + env(safe-area-inset-bottom)); }

  /* An arrow instead of the word, so the field keeps its width and the
     placeholder never wraps onto a clipped second line. */
  .send-label { display: none; }
  .send-icon { display: block; }
  #send { width: 44px; height: 44px; min-height: 44px; padding: 0; }
  .composer textarea { max-height: 132px; }

  .login-card { padding: 32px 24px; border-radius: var(--radius); }
  .login-card h1 { font-size: 28px; }

  .turn-body table { font-size: 13px; }
  .disclaimer { font-size: 11px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ── Findings (internal build) ───────────────────────────────────────────────
   Same surface as the chat so it reads as one tool, not a bolted-on admin
   screen. Never reachable on the members' build: the button that opens it stays
   hidden and every route behind it answers 404. */

.panel {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--line-soft);
  flex: 0 0 auto;
}
.panel-head h2 {
  font-family: var(--display);
  font-size: 20px;
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  text-transform: var(--display-transform);
  margin: 0;
}
.panel-actions { display: flex; align-items: center; gap: 16px; }
/* .icon-btn is display:none by default (it is a mobile affordance elsewhere),
   so every panel's close button has to opt back in by id. Forgetting this
   leaves a panel with no way out of it. */
#findings-close,
#activity-close,
#people-close { display: grid; }

/* AND THE ONE THAT MATTERS MOST, which was missed.
   Carson, 10 September 2026: "minimizing the left menu bar makes it dissapear
   completly and unable to come back unless you are on the assistant page."
   Exactly right. The handle was built, and put in every tool's header, and
   commented as "the ONLY handle on the left bar while a tool is up" - and then
   never opted back in here, so it has been display:none since the day it was
   written. The button existed, was in the DOM, answered to a click, and had no
   width or height. Collapsing the bar on any tool stranded you.
   Every control that opens something MUST be visible, and a rule that hides a
   whole class by default will keep catching things until it goes. */
/* On desktop the rail carries the only handle, at the top of the bar where
   Carson asked for it, and it is always on screen. Two controls for one job in
   two places is worse than the bug was.
   SCOPED TO DESKTOP rather than written bare: bare, it sits later in this file
   than the mobile rule that turns it back on, wins on source order, and leaves
   a phone with no way into the menu. Which is what it did. */
@media (min-width: 861px) {
  .tool-head-toggle { display: none; }
}

/* Back out of a transcript, and out of the panel. Left of the title so it reads
   as going back rather than as an action on what you are looking at. */
.panel-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  padding: 0;
  margin: 0 12px 0 0;
  color: var(--accent);
  font-family: var(--sans);
  font-size: 13px;
  cursor: pointer;
}
.panel-back:hover { text-decoration: underline; }
.panel-back svg { width: 15px; height: 15px; display: block; }
.panel-back[hidden] { display: none; }

 /* ── Activity: switching view, rated answers ─────────────────────────────── */
.view-switch {
  display: flex;
  gap: 20px;
  width: 100%;
  margin: 0 0 18px;
  border-bottom: 1px solid var(--line-soft);
}
.view-tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font-family: var(--sans);
  font-size: 14px;
  padding: 0 0 10px;
  cursor: pointer;
  margin-bottom: -1px;
}
.view-tab:hover { color: var(--ink); }
.view-tab.on { color: var(--accent); border-bottom-color: var(--accent); }

/* A rated answer in the Feedback list. The left rule carries the verdict, so a
   list of them is scannable without reading a word. */
.activity-item.rated { padding-left: 12px; border-left: 3px solid transparent; }
.activity-item.rated--down { border-left-color: var(--accent); }
.activity-item.rated--up { border-left-color: rgba(var(--ink-rgb), 0.35); }

.rated-note {
  font-size: 13px;
  color: var(--ink);
  margin: 4px 0 2px;
  font-style: italic;
}
.rated-excerpt {
  font-size: 12.5px;
  color: var(--muted);
  margin: 4px 0 6px;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* On the answer itself, inside a transcript. */
.rating-badge {
  margin-left: 10px;
  padding: 1px 7px;
  border-radius: var(--radius-sm);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  border: 1px solid var(--line);
}
.rating-badge--down { color: var(--accent); border-color: var(--accent); }
.rating-badge--up { color: var(--muted); }

/* ── Filtering activity by person ────────────────────────────────────────── */
.who-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 18px;
}
.who-chip {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--btn-radius);
  color: var(--muted);
  font-family: var(--sans);
  font-size: 12.5px;
  padding: 6px 12px;
  cursor: pointer;
}
.who-chip:hover { color: var(--accent); border-color: var(--accent); }
.who-chip.on {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--accent-ink);
}

/* ── People and roles ────────────────────────────────────────────────────── */
/* .panel-body is a centring flex column, so a flex child shrinks to its content
   unless it says otherwise. The table and .activity-item already set a width;
   these have to as well or the rows sit in a narrow column in the middle. */
.person,
#people-body > .fine {
  width: 100%;
  align-self: stretch;
}

.person {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: space-between;
  padding: 14px 0;
  border-bottom: 1px solid var(--line-soft);
}
.person-who { min-width: 0; }
.person-email { display: block; font-size: 14px; word-break: break-all; }
.person-meta { display: block; font-size: 12px; color: var(--muted-2); margin-top: 2px; }
.person select {
  background: var(--bg-input);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
  font-family: var(--sans);
  font-size: 13px;
}
.person .owner-tag {
  font-size: 11px;
  letter-spacing: var(--eyebrow-tracking);
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
}

.panel-body {
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: center;
}

.finding {
  width: 100%;
  max-width: var(--measure);
  background: var(--bg-raised);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 16px 18px;
}
.finding h3 {
  font-size: 15px;
  font-weight: 600;
  margin: 0 0 4px;
}
.finding-meta {
  font-size: 12px;
  color: var(--muted-2);
  margin: 0 0 12px;
}
/* Findings hold tables and query text, so whitespace is load-bearing and a long
   line scrolls inside the card rather than stretching the page. */
.finding-body {
  overflow-x: auto;
  font-size: 14px;
  color: var(--ink);
  margin: 0;
}
/* A finding carries .turn-body too, so it renders exactly like the answer did. */
.finding-note {
  margin: 12px 0 0;
  padding-left: 12px;
  border-left: 2px solid var(--line);
  color: var(--muted);
  font-size: 14px;
}
.finding-foot {
  margin-top: 12px;
  display: flex;
  align-items: center;
  gap: 14px;
}

/* Saving a turn: an inline form in place of the actions row, so nothing covers
   the answer being kept. */
.save-form { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.save-form input {
  background: var(--bg-input);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  padding: 9px 12px;
  width: 100%;
}
.save-form input:focus { outline: none; border-color: var(--line); }
.save-form-row { display: flex; align-items: center; gap: 10px; }

/* ── Context and cost gauge ──────────────────────────────────────────────────
   Structure only. Every colour is a token, per the brand rule: this file is
   shared by both builds and must not know which one it is rendering.

   The ring reads at a glance and the panel carries the detail, so nothing in
   the header competes with the conversation title for attention. */
.ctx {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
}
.ctx-dial {
  position: relative;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: inherit;
  cursor: default;
}
.ctx-dial svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.ctx-track { stroke: var(--line-soft); }
.ctx-fill {
  stroke: var(--accent);
  /* Long enough to read as movement, short enough not to lag the number. */
  transition: stroke-dasharray 420ms ease;
}
/* Past four fifths the window is the thing worth noticing, not the cost. */
.ctx.is-high .ctx-fill { stroke: var(--accent-soft); }

.ctx-pct {
  font-family: var(--mono);
  font-size: 9.5px;
  line-height: 1;
  letter-spacing: -0.02em;
  opacity: 0.75;
}

/* The panel. Anchored to the right edge so it never runs off a narrow window. */
.ctx-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 30;
  /* Wide enough that "Context used" and "840k / 1.00M" each stay on one line.
     At 210px both wrapped and the panel read as a cramped table. */
  min-width: 248px;
  padding: 12px 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--bg-raised);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-3px);
  transition: opacity 140ms ease, transform 140ms ease, visibility 140ms;
}
.ctx:hover .ctx-panel,
.ctx-dial:focus-visible ~ .ctx-panel {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.ctx-panel-head {
  margin: 0 0 8px;
  font-size: 10px;
  letter-spacing: var(--eyebrow-tracking);
  text-transform: uppercase;
  opacity: 0.6;
}
.ctx-rows { margin: 0; }
.ctx-rows > div {
  display: flex;
  justify-content: space-between;
  gap: 18px;
  padding: 3px 0;
}
.ctx-rows dt {
  font-size: 12.5px;
  opacity: 0.72;
  white-space: nowrap;
}
.ctx-rows dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 12.5px;
  white-space: nowrap;
  /* Tabular so the digits do not shuffle as the numbers update. */
  font-variant-numeric: tabular-nums;
}
.ctx-total {
  margin-top: 5px;
  padding-top: 7px;
  border-top: 1px solid var(--line-soft);
}
.ctx-total dt { opacity: 0.9; }
.ctx-total dd { color: var(--accent); }
.ctx-note {
  margin: 9px 0 0;
  font-size: 11px;
  line-height: 1.45;
  opacity: 0.55;
}

/* On a narrow screen the title needs the room more than the ring does. */
@media (max-width: 560px) {
  .ctx-pct { display: none; }
  .ctx-dial { width: 26px; height: 26px; }
}

/* ── Model and effort picker ─────────────────────────────────────────────────
   Sits above the composer, the way Claude puts it. Structure only; every colour
   is a token, because this file is shared by both builds. */
.picker {
  position: relative;
  display: flex;
  margin-bottom: 6px;
}
.picker-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border: 1px solid var(--line-soft);
  border-radius: var(--btn-radius);
  background: none;
  color: inherit;
  font-family: inherit;
  font-size: 11.5px;
  opacity: 0.72;
  cursor: pointer;
}
.picker-btn:hover { opacity: 1; }
.picker-btn svg { width: 11px; height: 11px; }

.picker-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  z-index: 40;
  width: min(370px, calc(100vw - 40px));
  /* Tall enough that the whole menu, caveats included, fits without scrolling
     on an ordinary window. It still scrolls on a short one. */
  max-height: 72vh;
  overflow-y: auto;
  padding: 12px 14px 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--bg-raised);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.34);
}
.picker-head {
  margin: 0 0 7px;
  font-size: 10px;
  letter-spacing: var(--eyebrow-tracking);
  text-transform: uppercase;
  opacity: 0.6;
}
.picker-head + * + .picker-head { margin-top: 14px; }

.picker-option {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 10px;
  margin-bottom: 5px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
}
.picker-option:hover { border-color: var(--line-soft); }
.picker-option.is-on { border-color: var(--accent); }
.picker-option-name {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 13px;
  margin-bottom: 3px;
}
.picker-option.is-on .picker-option-name { color: var(--accent); }
.picker-option-price {
  font-family: var(--mono);
  font-size: 11px;
  opacity: 0.6;
}
/* Upside and downside get the same weight on purpose. A picker that only
   lists benefits is a sales page, not a choice. */
.picker-line {
  margin: 2px 0 0;
  font-size: 11.5px;
  line-height: 1.45;
  opacity: 0.7;
}
.picker-line.is-down { opacity: 0.55; }

.picker-efforts {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}
.picker-effort {
  padding: 4px 9px;
  border: 1px solid var(--line-soft);
  border-radius: var(--btn-radius);
  background: none;
  color: inherit;
  font-family: inherit;
  font-size: 11.5px;
  opacity: 0.72;
  cursor: pointer;
}
.picker-effort:hover { opacity: 1; }
.picker-effort.is-on {
  border-color: var(--accent);
  color: var(--accent);
  opacity: 1;
}
.picker-note {
  margin: 9px 0 0;
  font-size: 11px;
  line-height: 1.45;
  opacity: 0.55;
}

/* ── Conversation search ─────────────────────────────────────────────────────
   Structure only; colours are tokens, because both builds share this file. */
.thread-search {
  position: relative;
  display: flex;
  align-items: center;
  margin-top: 8px;
}
.thread-search svg {
  position: absolute;
  left: 8px;
  width: 13px;
  height: 13px;
  opacity: 0.45;
  pointer-events: none;
}
.thread-search input {
  width: 100%;
  padding: 6px 26px 6px 26px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: inherit;
  font-family: inherit;
  font-size: 12.5px;
}
.thread-search input:focus {
  outline: none;
  border-color: var(--accent);
}
/* Safari draws its own clear affordance on type=search; ours is the one that
   also resets the list, so hide theirs rather than showing two. */
.thread-search input::-webkit-search-decoration,
.thread-search input::-webkit-search-cancel-button { -webkit-appearance: none; }
.thread-search-clear {
  position: absolute;
  right: 4px;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: inherit;
  font-size: 15px;
  line-height: 1;
  opacity: 0.5;
  cursor: pointer;
}
.thread-search-clear:hover { opacity: 1; }

/* A result carries a snippet, so the row is taller than a plain thread. */
.thread-item .thread-snippet {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  line-height: 1.4;
  opacity: 0.55;
  white-space: normal;
}
.thread-item mark {
  background: none;
  color: var(--accent);
  font-weight: 600;
}
.threads-empty {
  padding: 14px 10px;
  font-size: 12px;
  opacity: 0.55;
}

/* ── Borrowed brain bar (owner only) ─────────────────────────────────────────
   Deliberately conspicuous when active. Wearing somebody else's material and
   forgetting you are is how you mistake their opinion for the system's. */
.brainbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
  font-size: 11.5px;
}
.brainbar label {
  opacity: 0.55;
  letter-spacing: var(--eyebrow-tracking);
  text-transform: uppercase;
  font-size: 10px;
}
.brainbar select {
  padding: 3px 6px;
  border: 1px solid var(--line-soft);
  border-radius: var(--btn-radius);
  background: var(--bg-input);
  color: inherit;
  font-family: inherit;
  font-size: 11.5px;
}
.brainbar-note {
  opacity: 0.6;
  line-height: 1.35;
}
/* Borrowed and active: the whole bar takes the accent so it cannot be missed. */
.brainbar.is-borrowed select {
  border-color: var(--accent);
  color: var(--accent);
}
.brainbar.is-borrowed .brainbar-note { color: var(--accent); opacity: 0.85; }

/* Past the replay limit the exchange count is the thing worth noticing, not
   the cost, so it takes the accent. */
.ctx-rows .is-over dd { color: var(--accent); }
.ctx-rows .is-over dt { opacity: 0.9; }

/* ── Notebook ───────────────────────────────────────────────────────────────
   One document per person. Rendered with the same markdown subset as an
   answer, edited as plain text, every save a new version. */
.notebook-meta { width: 100%; max-width: var(--measure); margin: 0; }
.notebook-body {
  width: 100%;
  max-width: var(--measure);
  line-height: 1.55;
  font-size: 15px;
}
.notebook-body h2 { font-size: 20px; font-weight: 600; margin: 0 0 12px; }
.notebook-body h3 {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: var(--eyebrow-tracking, 0.08em);
  text-transform: uppercase;
  color: var(--accent);
  margin: 22px 0 8px;
}
.notebook-body ul { margin: 0 0 8px; padding-left: 20px; }
.notebook-body li { margin: 0 0 6px; }
.notebook-body p { margin: 0 0 10px; }
.notebook-editor { width: 100%; max-width: var(--measure); }
.notebook-editor textarea {
  width: 100%;
  min-height: 60vh;
  resize: vertical;
  font: 13px/1.5 var(--mono);
  color: var(--ink);
  background: var(--bg-input);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 12px 14px;
  box-sizing: border-box;
}
.notebook-editor textarea:focus { outline: none; border-color: var(--accent); }
.notebook-editor-foot { display: flex; gap: 18px; margin-top: 10px; }
.notebook-history { width: 100%; max-width: var(--measure); font-size: 13px; }
.notebook-history summary { cursor: pointer; opacity: 0.75; }
.notebook-version {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 6px 0;
  border-bottom: 1px solid var(--line-soft);
}
.notebook-version .fine { flex: 1; margin: 0; }
.notebook-version.on { color: var(--accent); }
.notebook-summary {
  width: 100%;
  margin-top: 10px;
  font: 13px/1.4 var(--sans);
  color: var(--ink);
  background: var(--bg-input);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  box-sizing: border-box;
}
.notebook-summary:focus { outline: none; border-color: var(--accent); }
.notebook-origin { opacity: 0.8; }

/* ══ The tools platform ═══════════════════════════════════════════════════
   Duke's Picks is a list of tools and the assistant is one of them. The nav
   below lives in the existing sidebar; a mounted tool fills #tool-view, which
   sits in the same grid cell as .chat and is never shown at the same time.

   Everything here reads theme.css tokens only, exactly like the rest of this
   file, so a rebrand still touches one file. */

/* The nav fills the bar so Settings can be pushed to the bottom of it.
   Carson: "Settings should go on the bottom... make that stick to the bottom of
   the menu bar rather than right underneith these other things." */
.tools-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.tool-rail-foot {
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.tool-link {
  appearance: none;
  border: 0;
  background: none;
  color: var(--muted);
  font: inherit;
  text-align: left;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: background 0.12s ease, color 0.12s ease;
}
.tool-link:hover:not([disabled]) { background: rgba(var(--ink-rgb), 0.05); color: var(--ink); }
.tool-link[aria-current="true"] {
  background: rgba(var(--accent-rgb), 0.14);
  color: var(--ink);
  font-weight: 600;
}
.tool-link[disabled] { opacity: 0.4; cursor: default; }
/* A tool the member's tier does not get. Greyed but live: it opens under
   the upgrade overlay. Lighter than a disabled tool so the two do not read
   the same, and the VIP badge says why. */
.tool-link--locked { opacity: 0.55; }
.tool-link--locked:hover { opacity: 0.8; }

/* The upgrade overlay. The tool is mounted underneath and blurred: the member
   sees the shape of what VIP gets, and cannot reach any of it, which is also
   what the server would say. Sits over the tool view, under the rail, so the
   way to every other tool stays open. */
.tool-view { position: relative; }
.tool-body--locked {
  filter: blur(3px);
  pointer-events: none;
  user-select: none;
  opacity: 0.6;
}
.tool-lock {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 96px 24px 24px;
  background: rgba(20, 16, 10, 0.55);
  z-index: 5;
}
.tool-lock-card {
  background: var(--bg-raised);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px 28px 24px;
  max-width: 420px;
  width: 100%;
  text-align: center;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
}
.tool-lock-title {
  font-family: var(--display);
  font-size: 22px;
  font-weight: 600;
  margin: 8px 0 10px;
}
.tool-lock-line { color: var(--muted); font-size: 14px; line-height: 1.5; margin: 0 0 18px; }
.tool-lock-go { display: inline-block; text-decoration: none; }

/* Report an issue: the pop-up's frame, a taller box for typing. */
.issue-box { max-width: 560px; }
.issue-box .w-note-box { margin-top: 10px; }
.issue-note { margin: 8px 0 0; }

/* The link back to the community is an <a> among <button>s: same shape, no underline. */
a.tool-link--out { text-decoration: none; }
a.tool-link--out:hover { background: rgba(var(--ink-rgb), 0.05); color: var(--ink); }
.tool-link-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The icon never shrinks, so the rail's column of them stays a column. */
.tool-link > svg { width: 19px; height: 19px; flex: 0 0 19px; display: block; }

.tool-soon {
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-2);
}

/* Every tool gets this, at every width. It carries the ONLY handle on the left
   bar while a tool is up: the chat header owns the others and is hidden here,
   which is how collapsing the bar used to strand you with no way back. */
.tool-head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 0 14px;
}
.tool-head-name {
  font-family: var(--display);
  font-size: 18px;
  color: var(--ink);
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.tool-view {
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  padding: 18px 24px 40px;
  -webkit-overflow-scrolling: touch;
  /* The scrollbar's gutter on both edges, so a centred tool is centred to the
     pixel and not eleven to the left of it. */
  scrollbar-gutter: stable both-edges;
}

/* CENTRED, AND THE HEADER AS WIDE AS THE TOOL UNDER IT.
   Every tool has a width it reads best at, and used to sit against the left
   edge with the rest of the page empty. Carson, 11 September: "All of our
   tools hug the left side? can we make that more symmetric and have it
   centered?" The width is one variable per tool, set from `data-tool` on the
   view, and both the header and the tool's own wrapper take it, so the name
   sits over the panel it names rather than at the far edge. */
.tool-view[data-tool="watchlist"] { --tool-w: 720px; }
.tool-view[data-tool="dutch"] { --tool-w: 820px; }
.tool-view[data-tool="breakeven"] { --tool-w: 780px; }
.tool-view[data-tool="liveodds"] { --tool-w: 700px; }
.tool-view[data-tool="wagers"] { --tool-w: 980px; }
.tool-view[data-tool="settings"] { --tool-w: 1100px; }
.tool-view[data-tool="usage"] { --tool-w: 1100px; }
.tool-view[data-tool="horizontal"] { --tool-w: 1141px; }
.tool-view > .tool-head,
.w-wrap, .h-wrap, .d-wrap, .b-wrap, .o-wrap, .g-wrap, .s-wrap, .u-wrap, .builder {
  max-width: var(--tool-w, 100%);
  margin-left: auto;
  margin-right: auto;
}

/* ── Ticket builder ─────────────────────────────────────────────────────── */

/* SETUP ACROSS THE TOP, LEGS UNDER IT, TICKET BESIDE THEM, AND NO WIDER
   THAN THE LEGS NEED.
   It was three columns: a 640px-tall Setup column, the legs, the ticket. On a
   laptop the bottom of Setup sat below the fold while the ticket panel stuck,
   and on a wide screen the legs grew to fill whatever was left. Carson,
   11 September: "the squares for the runners are very big. I think we can
   make all that info fit on the page better." Now the setup is one strip,
   every leg is a fixed 132px, the tiles cap at 46px, and the builder is the
   width of six legs and a ticket (1141px), centred, with fewer legs centred
   inside it, so nothing shifts when the bet type changes. */
.builder {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 290px;
  grid-template-areas:
    'setup setup'
    'legs ticket';
  gap: 14px;
  align-items: start;
}
.t-setup { grid-area: setup; }
.builder > section:not(.t-setup) { grid-area: legs; min-width: 0; }
.t-ticket { grid-area: ticket; }

/* The strip: fields side by side, each as wide as it needs and no wider,
   wrapping to a second row only when the page is narrow. */
.t-setup .t-body.t-stack {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 18px;
  align-items: flex-end;
}
.t-setup .t-stack > * + * { margin-top: 0; }
.t-setup .t-field { margin-bottom: 0; flex: 0 0 auto; }
.t-setup .t-money { width: 112px; }
.t-setup .t-field input[type="number"]:not(.t-money .t-control) { width: 64px; }
.t-setup .t-field input[maxlength="6"] { width: 84px; }
.t-setup label.t-fine { padding-bottom: 7px; white-space: nowrap; }
/* The download settings, on a row of their own in the strip. */
.t-download { flex-basis: 100%; display: flex; gap: 18px; align-items: flex-end; }

.t-panel {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.t-head {
  padding: 12px 15px;
  border-bottom: 1px solid var(--line-soft);
  display: flex;
  align-items: center;
  gap: 10px;
}
.t-head h2 { font-family: var(--display); font-size: 15px; color: var(--ink); margin: 0; font-weight: 600; }
.t-body { padding: 13px 15px; }
.t-stack > * + * { margin-top: 12px; }

.t-field { margin-bottom: 12px; }
.t-field:last-child { margin-bottom: 0; }
.t-field > label { display: block; margin-bottom: 5px; }

.t-control {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--ink);
  font: inherit;
  font-variant-numeric: tabular-nums;
}
.t-control:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.t-money { position: relative; }
.t-money .t-control { padding-left: 23px; }
.t-money::before {
  content: "$";
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted);
  pointer-events: none;
}

.t-seg { display: flex; gap: 4px; flex-wrap: wrap; }
.t-seg button {
  flex: 1 1 auto;
  min-width: 44px;
  padding: 7px 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--muted);
  font: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.t-seg button[aria-pressed="true"] {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--accent-ink);
  font-weight: 600;
}

/* Legs */

/* Six legs have to sit side by side: reading a pick 6 as six columns is the
   whole point, and a scrollbar hides a leg the member is comparing. Narrow
   enough that six fit at desktop width, scrolling only below that, and the
   scrollbar is toned down because the default one is a white bar across the
   middle of a dark page. */
/* A grid that WRAPS, never a strip that scrolls.
   Found 11 September 2026 on a 1280px laptop with the menu open: the legs were
   a flex row with overflow-x auto, so a Pick 5's fifth column sat half under
   the ticket panel and a Pick 6's sixth was off screen entirely, with nothing
   on the page to say there was more to the right. As many legs as fit go on
   one row and the rest start a second, every one the same width. */
.t-legs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 9px;
  padding-bottom: 4px;
}
/* The default scrollbar is a near-white bar across a dark page. Every scroller
   inside a tool gets the same quiet treatment. */
.t-legs, .t-ticket, .t-shape, .tool-view {
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--ink-rgb), 0.16) transparent;
}
.t-legs::-webkit-scrollbar,
.t-ticket::-webkit-scrollbar,
.t-shape::-webkit-scrollbar,
.tool-view::-webkit-scrollbar { width: 8px; height: 8px; }
.t-legs::-webkit-scrollbar-track,
.t-ticket::-webkit-scrollbar-track,
.t-shape::-webkit-scrollbar-track,
.tool-view::-webkit-scrollbar-track { background: transparent; }
.t-legs::-webkit-scrollbar-thumb,
.t-ticket::-webkit-scrollbar-thumb,
.t-shape::-webkit-scrollbar-thumb,
.tool-view::-webkit-scrollbar-thumb {
  background: rgba(var(--ink-rgb), 0.16);
  border-radius: 999px;
}

.t-leg {
  flex: 1 1 0;
  /* Fixed, so the tiles never grow with the screen. Wide enough for the race
     and its runner count on one line; below about 1300px six legs wrap onto a
     second row, which is what the wrapping grid is for. */
  flex: 0 0 148px;
  min-width: 0;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
}
.t-leg-head {
  padding: 7px 9px;
  border-bottom: 1px solid var(--line-soft);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.t-leg-name { font-family: var(--display); font-size: 13px; color: var(--ink); white-space: nowrap; }
.t-leg-count { font-size: 11px; color: var(--muted); white-space: nowrap; }
/* How many runners, on the race's own line: minus, the number, plus. The
   number is typable for a twelve horse field; the buttons are for the other
   nineteen taps. */
.t-size { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.t-size-btn {
  appearance: none;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg-input);
  color: var(--muted);
  font: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.t-size-btn:hover:not([disabled]) { color: var(--ink); border-color: var(--accent-fill); }
.t-size-btn[disabled] { opacity: 0.35; cursor: default; }
.t-leg-size {
  width: 26px;
  padding: 2px 3px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  color: var(--ink);
  text-align: center;
  font: inherit;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.t-leg-size:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.t-horses {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(38px, 46px));
  justify-content: center;
  gap: 5px;
  padding: 9px;
}

/* The tile is the whole interface: big enough for a thumb at the track, and
   the grade has to read across five columns without looking at a letter. */
.t-horse {
  position: relative;
  aspect-ratio: 1;
  min-height: 38px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--muted);
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: background 90ms, border-color 90ms, color 90ms;
}
.t-horse:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.t-horse[data-grade="C"]  { border-color: rgba(var(--accent-rgb), 0.45); color: var(--ink); }
.t-horse[data-grade="B"]  { border-color: rgba(var(--accent-rgb), 0.8); color: var(--ink); background: rgba(var(--accent-rgb), 0.1); }
.t-horse[data-grade="A"]  { border-color: var(--accent-fill); color: var(--ink); background: rgba(var(--accent-rgb), 0.3); }
.t-horse[data-grade="A+"] { border-color: var(--accent-fill); background: var(--accent-fill); color: var(--accent-ink); }

.t-horse-grade {
  position: absolute;
  top: 2px;
  right: 4px;
  font-size: 9.5px;
  font-weight: 700;
  opacity: 0.85;
}

.t-leg-foot {
  margin-top: auto;
  padding: 6px 8px 8px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-wrap: wrap;
  gap: 4px 6px;
  align-items: center;
}

/* The ticket */

.t-ticket { position: sticky; top: 0; max-height: calc(var(--vh) - 60px); overflow-y: auto; }

.t-cost { padding: 14px 15px; border-bottom: 1px solid var(--line-soft); }
.t-cost-total {
  font-family: var(--display);
  font-size: 31px;
  color: var(--ink);
  line-height: 1.1;
  display: block;
  font-variant-numeric: tabular-nums;
}
.t-cost-sub { font-size: 12.5px; color: var(--muted); margin-top: 3px; }

.t-shape {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink);
  background: var(--bg-deep);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  overflow-x: auto;
  white-space: nowrap;
}

/* One row per PLACEABLE ticket, not per combination. The wheel is what gets
   read out at a window, so it leads and the money is the quiet second line. */
.t-tickets { display: flex; flex-direction: column; gap: 9px; }
.t-row { border-top: 1px solid var(--line-soft); padding-top: 8px; }
.t-row:first-child { border-top: 0; padding-top: 0; }
.t-line {
  font-family: var(--mono);
  font-size: 13.5px;
  color: var(--ink);
  line-height: 1.45;
  word-break: break-word;
}
.t-meta { font-size: 12px; color: var(--muted); margin-top: 2px; }
.t-meta strong { color: var(--ink); font-weight: 600; }

.t-note {
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: 13px;
  background: rgba(var(--accent-rgb), 0.1);
  color: var(--ink);
  border: 1px solid rgba(var(--accent-rgb), 0.3);
}
.t-note strong { display: block; margin-bottom: 3px; color: var(--accent-soft); }
.t-note-actions { display: flex; gap: 8px; margin-top: 9px; flex-wrap: wrap; }

.t-btn {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--bg-input);
  color: var(--ink);
  padding: 7px 13px;
  border-radius: var(--btn-radius);
  cursor: pointer;
  font: inherit;
  font-size: 13px;
}
.t-btn:hover { border-color: var(--accent); }
.t-btn--primary { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--accent-ink); font-weight: 600; }
.t-btn--primary:hover { background: var(--accent-fill-hover); }
.t-btn:disabled { opacity: 0.45; cursor: default; }
.t-btn-row { display: flex; gap: 7px; flex-wrap: wrap; }

.t-fine { font-size: 12px; color: var(--muted); }
.t-legend { display: flex; gap: 10px; flex-wrap: wrap; font-size: 11.5px; color: var(--muted); }
.t-legend span { display: inline-flex; align-items: center; gap: 4px; }
.t-legend i { width: 11px; height: 11px; border-radius: 3px; display: inline-block; border: 1.5px solid var(--line); }
.t-legend i[data-grade="C"]  { border-color: rgba(var(--accent-rgb), 0.45); }
.t-legend i[data-grade="B"]  { border-color: rgba(var(--accent-rgb), 0.8); background: rgba(var(--accent-rgb), 0.1); }
.t-legend i[data-grade="A"]  { border-color: var(--accent-fill); background: rgba(var(--accent-rgb), 0.3); }
.t-legend i[data-grade="A+"] { border-color: var(--accent-fill); background: var(--accent-fill); }

.t-sheet-bar { display: none; }

/* Phone only: the race strip and the Previous/Next row. Hidden on desktop,
   where every leg is on screen and switching would be a step backwards. */
.t-legbar, .t-legstep { display: none; }

/* Phone. Members build these at the track, one handed: the setup collapses to
   one line so race 1 is on screen, and the cost never leaves the bottom. */
@media (max-width: 900px) {
  .tool-view { padding-left: 12px; padding-right: 12px; }
  /**
   * Room under the LEGS for the cost bar, which is pinned over them. On the
   * builder rather than on .tool-view, because the odds strip sets that one
   * and two rules on the same property is how the last leg ends up under the
   * cost with no way to see it.
   */
  .builder { padding-bottom: 104px; }
  .tool-head {
    padding: 10px 0 12px;
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--bg);
  }
  .builder { grid-template-columns: 1fr; grid-template-areas: none; gap: 14px; }
  .t-setup, .builder > section:not(.t-setup), .t-ticket { grid-area: auto; }
  .t-legs { display: block; overflow: visible; }
  .t-leg { margin-bottom: 12px; min-width: 0; flex: none; }
  .t-horses { justify-content: stretch; }
  .t-horses { grid-template-columns: repeat(auto-fill, minmax(54px, 1fr)); gap: 7px; padding: 11px; }
  .t-horse { min-height: 54px; font-size: 17px; }

  /* ONE LEG AT A TIME. The rest are still in the page, and still hold what
     was graded on them; they are simply not on screen. */
  .t-legs[data-active] > .t-leg { display: none; }
  .t-legs[data-active="0"] > .t-leg[data-leg="0"],
  .t-legs[data-active="1"] > .t-leg[data-leg="1"],
  .t-legs[data-active="2"] > .t-leg[data-leg="2"],
  .t-legs[data-active="3"] > .t-leg[data-leg="3"],
  .t-legs[data-active="4"] > .t-leg[data-leg="4"],
  .t-legs[data-active="5"] > .t-leg[data-leg="5"] { display: flex; }

  .t-legbar {
    display: flex;
    gap: 6px;
    margin-bottom: 12px;
    position: sticky;
    top: 46px;
    z-index: 4;
    background: var(--bg);
    padding: 4px 0;
  }
  .t-legbar-btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 7px 2px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--bg-input);
    color: var(--muted);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    line-height: 1.1;
  }
  .t-legbar-btn[aria-pressed="true"] {
    background: var(--accent-fill);
    border-color: var(--accent-fill);
    color: var(--accent-ink);
    font-weight: 600;
  }
  .t-legbar-race { font-variant-numeric: tabular-nums; }
  /* How many are graded in that leg. Blank until there is one, so an
     untouched card is quiet rather than a row of noughts. */
  .t-legbar-on { font-size: 10px; opacity: 0.75; min-height: 11px; }

  .t-legstep { display: flex; gap: 8px; padding: 0 11px 11px; }
  .t-legstep .t-btn { flex: 1 1 0; padding: 10px 8px; }
  .t-legstep .t-btn[disabled] { opacity: 0.4; }

  /* The race and the field size, on one line with room for a thumb. */
  .t-leg-head { padding: 10px 12px; }
  .t-leg-name { font-size: 15px; }
  .t-leg-foot { padding: 8px 11px 10px; }
  .t-size-btn { width: 34px; height: 34px; font-size: 18px; }
  .t-leg-size { width: 34px; font-size: 15px; }

  .t-setup[data-open="false"] .t-body { display: none; }
  /**
   * The whole bar opens the setup, so it has to LOOK like it does. A chevron
   * on its own did not read as one. Carson, 12 September: "Can we make it a
   * little more clear that the top bar is a drop down?" So: the input
   * background the rest of the tool uses for things you press, the word Edit
   * beside the chevron, and the summary in the middle saying what would
   * change.
   */
  .t-setup { background: none; border: 0; }
  .t-setup-toggle {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--bg-input);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 11px 13px;
    cursor: pointer;
    text-align: left;
    color: inherit;
  }
  .t-setup[data-open="true"] .t-setup-toggle {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    border-bottom-color: transparent;
  }
  .t-setup[data-open="true"] .t-body {
    background: var(--bg-raised);
    border: 1px solid var(--line);
    border-top: 0;
    border-radius: 0 0 var(--radius) var(--radius);
  }
  /* The numbers it would change, between the name and the control. */
  .t-setup-sum {
    margin-left: auto;
    text-align: right;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* The control itself: a word and a chevron, in a well, so it reads as the
     thing to press rather than as decoration. */
  .t-setup-chevron {
    flex: none;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 3px 9px;
    font-size: 11.5px;
    color: var(--accent);
    background: var(--bg-raised);
  }

  /**
   * The cost never leaves the bottom of the screen. On a phone that means
   * clearing the phone's own furniture: iOS puts a toolbar over the bottom of
   * the page, and `bottom: 0` alone tucks the bar behind it. The safe area
   * inset is what the browser says is actually free.
   */
  /**
   * The cost sits directly above the odds strip, which is itself above the
   * phone's own toolbar. Two things pinned to the same edge have to be told
   * about each other; this is that.
   */
  .t-ticket {
    position: fixed;
    left: 0;
    right: 0;
    bottom: calc(var(--peek-h) + env(safe-area-inset-bottom, 0px));
    top: auto;
    z-index: 20;
    border-radius: var(--radius) var(--radius) 0 0;
    max-height: min(74vh, calc(var(--vh) - 96px));
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.35);
  }
  .t-ticket[data-open="false"] .t-details { display: none; }
  .t-sheet-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 12px 15px;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
    text-align: left;
  }
  .t-sheet-bar .t-cost-total { font-size: 25px; }
  .t-chevron { color: var(--muted); font-size: 12px; }
  .t-cost { display: none; }
}

@media (min-width: 901px) {
  .t-details { display: block !important; }
}

/* ── Settings ─────────────────────────────────────────────────────────────
   The permissions grid: people down the side, tools across the top. */

.tool-link--admin {
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--line-soft);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
}

.s-wrap { max-width: 1100px; }
.s-tiers { font-weight: 400; text-transform: none; letter-spacing: 0; margin-top: 2px; }
.s-upgrade-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; cursor: pointer; }
.s-issue { padding: 10px 0; border-top: 1px solid var(--line-soft); }
.s-issue-body { white-space: pre-wrap; margin: 4px 0 6px; font-size: 13.5px; line-height: 1.5; }
.s-issue-agent { font-size: 11px; color: var(--muted-2); margin-bottom: 6px; word-break: break-all; }
.s-wrap > * + * { margin-top: 14px; }

.s-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--ink-rgb), 0.16) transparent;
}

.s-table { border-collapse: collapse; width: 100%; font-size: 13.5px; }
.s-table th {
  text-align: left;
  font-weight: 600;
  color: var(--muted);
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  padding: 11px 12px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.s-table td { padding: 10px 12px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
.s-table tr:last-child td { border-bottom: 0; }

.s-who { min-width: 210px; color: var(--ink); }
.s-role { width: auto; padding: 4px 8px; font-size: 13px; }

.s-cell { white-space: nowrap; }
.s-cell input[type="checkbox"] { accent-color: var(--accent-fill); width: 15px; height: 15px; cursor: pointer; }

/* Nobody has decided this one: it is whatever the tool's default says. Faded
   so a deliberate "off" is distinguishable from one nobody chose. */
.s-inherited input[type="checkbox"] { opacity: 0.45; }

.s-reset { margin-left: 7px; font-size: 11px; }

/* ══ Sharpen the tools ═════════════════════════════════════════════════════
   Carson, 9 September: "it kinda looks too soft and chubby rather than sharp
   and athletic like a race horse." He is right, and the cause is geometry
   rather than colour. The chat view he likes is mostly open space; a tool is a
   dense grid of boxes, and at the app's 14px and 10px radii a stack of them
   reads as a pile of pills. A 10px radius on a 38px tile is a quarter of its
   width.

   So the SHAPE tokens are re-scoped inside the tools and nowhere else. The
   chat keeps the softer geometry it was designed with, and one block here
   changes every tool at once rather than a hunt through fifty rules. */

.tools-nav,
.tool-view {
  --radius: 7px;
  --radius-sm: 4px;
  --btn-radius: 5px;
}

/* Hairlines, not walls. A 1.5px border on every panel is most of the weight. */
.tool-view .t-panel,
.tool-view .t-leg,
.tool-view .s-scroll { border-width: 1px; }

/* Tighter, so the grid reads as a card of runners rather than a set of pads. */
.tool-view .t-body { padding: 12px 13px; }
.tool-view .t-head { padding: 10px 13px; }
.tool-view .t-horses { gap: 4px; padding: 8px; }
.tool-view .t-cost { padding: 13px 13px 12px; }

/* The number is the loudest thing on the page, so it gets the tightest
   setting: no soft tracking, and the currency mark stays out of its way. */
.tool-view .t-cost-total { letter-spacing: -0.02em; font-size: 30px; }
.tool-view .t-line { letter-spacing: -0.01em; }

/* The tile IS the interface, so its edge carries the grade and stays crisp.
   Square-ish corners and a flat weight read as a program number, not a pill. */
.tool-view .t-horse { border-width: 1px; font-weight: 500; }
.tool-view .t-horse[data-grade] { border-width: 1.5px; }
.tool-view .t-horse[data-grade="A+"] { font-weight: 600; }

/* Segmented controls: butt them together into one bar instead of floating a
   row of separate lozenges. */
.tool-view .t-seg { gap: 3px; }
.tool-view .t-seg button { padding: 6px 4px; font-size: 13px; }

.tool-view .t-btn { padding: 6px 12px; }

.tools-nav .tool-link { padding: 7px 9px; }

/* ── Make your line ───────────────────────────────────────────────────────
   A field down the page: what you make it, what the board makes it, and the
   gap. The gap is the only column anybody is really reading, so it is the only
   one that carries colour. */

.l-wrap { max-width: 900px; }
.l-wrap > * + * { margin-top: 16px; }

.l-setup { display: flex; flex-direction: column; gap: 8px; }
.l-strip { display: flex; align-items: center; gap: 10px; }
.l-size { width: 74px; }
.l-hint { max-width: 62ch; }

.l-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
}
.l-table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
.l-table th {
  text-align: left;
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.l-table td { padding: 6px 12px; border-bottom: 1px solid var(--line-soft); }
.l-table tr:last-child td { border-bottom: 0; }

.l-num { font-weight: 600; color: var(--ink); width: 42px; }
.l-odds { width: 82px; text-align: center; }
.l-fair { color: var(--ink); font-weight: 600; white-space: nowrap; }
.l-true { color: var(--muted); white-space: nowrap; }

/* The one row that is worth acting on gets the only accent in the table. */
.l-overlay { background: rgba(var(--accent-rgb), 0.08); }
.l-overlay .l-num { color: var(--accent-soft); }

.l-edge { font-weight: 600; white-space: nowrap; }
.l-edge--up { color: var(--accent-soft); }
.l-edge--down { color: var(--muted); }

.l-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
.l-stat {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 13px 15px;
  background: var(--bg-raised);
}
.l-stat .eyebrow { margin: 0 0 4px; }
.l-stat-value {
  font-family: var(--display);
  font-size: 22px;
  color: var(--ink);
  margin: 0 0 6px;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.l-stat p:last-child { margin: 0; }

/* ── Break even ───────────────────────────────────────────────────────────
   One question, asked in the only unit a handicapper can answer in: out of
   ten. The per cent lives underneath in small type for anybody who wants it. */

.b-wrap { max-width: 780px; }
.b-wrap > * + * { margin-top: 18px; }

.b-ask { display: grid; grid-template-columns: 170px minmax(0, 1fr); gap: 18px; align-items: start; }
.b-q .eyebrow { display: block; margin: 0 0 6px; }
.b-q .t-fine { margin: 5px 0 0; }
.b-odds { text-align: center; font-size: 17px; font-weight: 600; }

.b-slider-value {
  font-family: var(--display);
  font-size: 25px;
  color: var(--ink);
  margin: 0 0 6px;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

.b-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 26px;
  background: none;
  cursor: pointer;
}
.b-slider::-webkit-slider-runnable-track {
  height: 5px;
  border-radius: 999px;
  background: var(--bg-deep);
  border: 1px solid var(--line);
}
.b-slider::-moz-range-track {
  height: 5px;
  border-radius: 999px;
  background: var(--bg-deep);
  border: 1px solid var(--line);
}
/* A thumb big enough to drag with a thumb, at the track, on a phone. */
.b-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 22px;
  height: 22px;
  margin-top: -9px;
  border-radius: 50%;
  background: var(--accent-fill);
  border: 2px solid var(--bg);
}
.b-slider::-moz-range-thumb {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent-fill);
  border: 2px solid var(--bg);
}
.b-slider:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 999px; }

/* The answer. The two numbers being compared sit side by side, because the
   comparison IS the tool. */
.b-answer-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  padding: 16px 18px;
}
.b-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
.b-big .eyebrow { margin: 0 0 4px; }
.b-big-value {
  margin: 0 0 3px;
  font-family: var(--display);
  color: var(--ink);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.b-big-value strong { font-size: 38px; font-weight: 600; }
.b-big-unit { font-size: 15px; color: var(--muted); font-family: var(--sans); }
.b-big p:last-child { margin: 0; }

.b-call {
  margin: 14px 0 0;
  padding-top: 13px;
  border-top: 1px solid var(--line-soft);
  font-size: 14.5px;
  color: var(--ink);
}
.b-call--good { color: var(--accent-soft); }

.b-extras { border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-raised); }
.b-extras summary {
  padding: 11px 15px;
  cursor: pointer;
  color: var(--ink);
  font-size: 14px;
  list-style: none;
}
.b-extras summary::-webkit-details-marker { display: none; }
.b-extras summary::before { content: "+\00a0"; color: var(--muted); }
.b-extras[open] summary::before { content: "\2212\00a0"; }
.b-extras-body { padding: 0 15px 14px; }
.b-rebate-row { display: flex; align-items: center; gap: 9px; }
.b-rebate { width: 88px; }
.b-extras-body .t-fine { margin: 8px 0 0; max-width: 62ch; }

.b-section > * + * { margin-top: 9px; }
.b-heading {
  font-family: var(--display);
  font-size: 16px;
  color: var(--ink);
  margin: 0;
  font-weight: 600;
}

.b-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
}
.b-table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; font-size: 13.5px; }
.b-table th {
  text-align: left;
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  padding: 10px 13px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.b-table td { padding: 7px 13px; border-bottom: 1px solid var(--line-soft); white-space: nowrap; }
.b-table tr:last-child td { border-bottom: 0; }
.b-price { color: var(--ink); font-weight: 600; }
/* The arithmetic of the price: true whoever is reading, so it is quieter than
   the verdict column on the right, which is the only part about the member. */
.b-pct-cell { color: var(--ink); }
.b-here { background: rgba(var(--accent-rgb), 0.1); }

.b-chart {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 15px;
  background: var(--bg-raised);
}
.b-chart svg { display: block; width: 100%; height: auto; }
.b-chart .t-fine { margin: 10px 0 0; max-width: 68ch; }
.b-grid { stroke: var(--line-soft); stroke-width: 1; }
.b-axis { font-family: var(--sans); font-size: 10.5px; fill: var(--muted); }
.b-curve { fill: none; stroke: var(--accent-fill); stroke-width: 2; }
.b-you { stroke: var(--ink); stroke-width: 1.5; stroke-dasharray: 5 4; opacity: 0.65; }
.b-cross { stroke: var(--accent-fill); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.7; }
.b-dot { fill: var(--accent-fill); }
.b-note { font-family: var(--sans); font-size: 11px; fill: var(--ink); font-weight: 600; }
.b-muted-note { font-family: var(--sans); font-size: 11px; fill: var(--muted); }

@media (max-width: 640px) {
  .b-ask { grid-template-columns: 1fr; gap: 14px; }
}

/* The only colour on the page, and it sits on the number rather than the row:
   above a dollar you get more back than you put in, below it you do not.
   Deliberately not the gold accent, because this is a verdict on the member's
   own call and not a selection. Nothing is ever coloured by the price alone. */
.b-ret--up { color: #7ea67a; font-weight: 600; }
.b-ret--down { color: #c4685f; font-weight: 600; }

/* ── Live odds ─────────────────────────────────────────────────────────────
   The Off To The Races board with the broadcast taken out. What carried over
   is the reading of it: dark glass, a gold hairline, saddle cloth badges, and
   green when money is coming for a horse against orange when it drifts. What
   did not carry over is everything that was there for OBS. */

.o-wrap { max-width: 700px; }
.o-wrap > * + * { margin-top: 14px; }

.o-picker { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start; }
.o-field { display: flex; flex-direction: column; gap: 5px; }
.o-races { flex: 1; min-width: 0; }
.o-race-row { display: flex; flex-wrap: wrap; gap: 4px; }

.o-race {
  min-width: 30px;
  padding: 5px 7px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.o-race[aria-current="true"] {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--accent-ink);
  font-weight: 600;
}
/* A race with no board yet still belongs on the card, just quieter. */
.o-race--dark { opacity: 0.45; }

.o-board {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: linear-gradient(180deg, rgba(var(--ink-rgb), 0.03), transparent), var(--bg-raised);
  overflow: hidden;
  position: relative;
}
/* The gold hairline off the studio board. */
.o-board::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--accent-fill), transparent);
}

.o-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 15px 11px;
  border-bottom: 1px solid var(--line-soft);
}
.o-head-race { font-family: var(--display); font-size: 16px; color: var(--ink); font-weight: 600; }

.o-mtp {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}
.o-mtp--soon { color: var(--accent-soft); font-weight: 600; }
.o-mtp--done { color: var(--muted); }

/* Odds | Exacta | Double. The board is one thing with three faces rather than
   three tools, because a horseplayer flips between them on the same race. */
.o-tabs {
  display: flex;
  gap: 2px;
  padding: 0 15px;
  border-bottom: 1px solid var(--line-soft);
}
.o-tab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 9px 12px;
  font: inherit;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
}
.o-tab:hover { color: var(--ink); }
.o-tab[aria-selected='true'] { color: var(--ink); border-bottom-color: var(--accent); }

.o-grid-wrap { padding: 13px 15px 16px; }
.o-grid-note {
  margin: 0 0 11px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}
/* The grid is as wide as the field, so it scrolls in its own box and never
   drags the page sideways with it. */
.o-grid-scroll { overflow-x: auto; }
.o-grid {
  border-collapse: collapse;
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.o-grid th,
.o-grid td {
  border: 1px solid var(--line-soft);
  padding: 5px 7px;
  text-align: right;
  white-space: nowrap;
  color: var(--ink);
}
.o-grid th { padding: 4px 5px; }
.o-grid-corner {
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: left;
}
/* The winner's column stays put while the second half scrolls past it. */
.o-grid-side { position: sticky; left: 0; background: var(--panel); z-index: 1; }
.o-grid-dead { color: var(--line); text-align: center; }
/* Scratched: still on the grid, still on its own program number, plainly out. */
.o-grid-out { color: var(--muted); text-align: center; opacity: 0.55; }
.o-grid-best { color: var(--accent); font-weight: 600; }

/* A cloth number, sized for a table edge rather than a runner line. */
.o-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 4px;
  border-radius: 2px;
  font-size: 11px;
  font-weight: 700;
}
.o-chip--out {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--line);
  text-decoration: line-through;
}

.o-runners { display: flex; flex-direction: column; }
/* cloth | horse and jockey | morning line | live price | movement.
   The live price is hard right and is the loudest thing on the line, because
   it is the only number that moves. */
.o-row {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) auto auto 16px;
  align-items: center;
  gap: 10px;
  padding: 7px 14px;
  border-bottom: 1px solid var(--line-soft);
}

.o-who { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.o-name {
  color: var(--ink);
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.o-jockey {
  color: var(--muted);
  font-size: 11.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Quiet on purpose: near enough to compare, never mistakable for the live one. */
.o-ml {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  text-align: right;
  min-width: 38px;
}
.o-row:last-child { border-bottom: 0; }

/* The saddle cloth. */
/* The saddle cloth, in its real colour. See client/lib/silks.ts: those are
   fixed real-world colours rather than brand tokens, which is why they are set
   inline and are the one exception to "no colour outside theme.css". */
.o-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 3px;
  border: 1px solid transparent;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25);
}

/* THE number. Brand gold, the largest thing on the row, hard right. */
.o-odds {
  font-family: var(--mono);
  font-size: 17px;
  font-weight: 600;
  color: var(--accent-soft);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  text-align: right;
  min-width: 54px;
}

.o-move { font-size: 12px; text-align: right; }
.o-move--steam { color: #5fd08a; }
.o-move--drift { color: #e0795f; }

.o-scratched {
  margin: 0;
  padding: 9px 15px 12px;
  font-size: 12px;
  color: var(--muted);
  border-top: 1px solid var(--line-soft);
}
.o-message { margin: 0; padding: 22px 15px; color: var(--muted); font-size: 14px; text-align: center; }

/* ── Dutching ─────────────────────────────────────────────────────────────
   A field with ticks: the money follows the prices, so the only thing the
   member decides is which horses are in. */

.d-wrap { max-width: 820px; }
.d-wrap > * + * { margin-top: 16px; }

.d-setup { display: grid; grid-template-columns: 240px 200px; gap: 14px; align-items: start; }
.d-setup .t-field { margin-bottom: 0; }
.d-loader-row { display: flex; gap: 7px; align-items: center; }
.d-loader-row select { width: auto; min-width: 82px; }
.d-race { width: 62px; text-align: center; }
.d-loader .t-fine { margin-top: 6px; }

.d-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
}
.d-table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
.d-table th {
  text-align: left;
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.d-table td { padding: 6px 12px; border-bottom: 1px solid var(--line-soft); }
.d-table tr:last-child td { border-bottom: 0; }

/* A horse that is not in the bet stays readable but recedes. */
.d-off .d-prog, .d-off .d-odds { opacity: 0.55; }
.d-tick { width: 34px; }
.d-tick input { accent-color: var(--accent-fill); width: 15px; height: 15px; cursor: pointer; }
.d-prog { width: 58px; text-align: center; }
.d-odds { width: 82px; text-align: center; }
.d-stake { color: var(--ink); font-weight: 600; white-space: nowrap; }
.d-collect { color: var(--accent-soft); font-weight: 600; white-space: nowrap; }

.d-add { margin-top: 9px; display: inline-block; }

.d-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; }
.d-stat {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 13px 15px;
  background: var(--bg-raised);
}
.d-stat .eyebrow { margin: 0 0 4px; }
.d-stat-value {
  font-family: var(--display);
  font-size: 23px;
  color: var(--ink);
  margin: 0 0 5px;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.d-stat p:last-child { margin: 0; }

.d-profit { grid-column: 1 / -1; margin: 0; color: var(--accent-soft); font-size: 14px; }
.d-notice { grid-column: 1 / -1; }

/* Dutching: the two toggles, and the live card. */
.d-setup { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.d-loader { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 14px; background: var(--bg-raised); }
.d-loader > * + * { margin-top: 10px; }
.d-source { max-width: 300px; }
.d-race-row { gap: 4px; }
.d-loader .t-fine { margin: 0; }

/* ── The odds strip ────────────────────────────────────────────────────────
   The board, on every screen but its own. Pinned to the bottom, collapses to
   one line, and shares its track with the Live odds tool. It is a nicety: it
   never takes space a tool needs and it is never the reason a page is busy. */

/**
 * The odds strip owns the very bottom of the window, so anything else pinned
 * there has to sit ON TOP OF IT, not under it. `--peek-h` is how much room it
 * takes: zero when it is not on screen, its own height when it is. Carson,
 * 12 September 2026, with a photograph: the ticket's cost bar was behind the
 * strip AND behind Safari's toolbar.
 */
:root { --peek-h: 0px; }

.peek {
  position: fixed;
  left: var(--sidebar-w);
  right: 0;
  bottom: 0;
  z-index: 30;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: var(--bg-deep);
  border-top: 1px solid var(--line);
  transition: left 0.24s ease;
}
.app.collapsed .peek { left: 0; }

.peek-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px;
  max-width: 1400px;
  margin: 0 auto;
  min-height: 38px;
  overflow-x: auto;
  scrollbar-width: none;
}
.peek-bar::-webkit-scrollbar { display: none; }

.peek-toggle {
  appearance: none;
  border: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 12px;
  padding: 3px 5px;
  line-height: 1;
  flex: none;
}
.peek-toggle:hover { color: var(--ink); }

.peek-track {
  appearance: none;
  background: var(--bg-input);
  border: 1px solid var(--line);
  border-radius: 5px;
  color: var(--ink);
  font: inherit;
  font-size: 12px;
  padding: 3px 6px;
  flex: none;
  cursor: pointer;
  /* A name and a time, "Belterra Park · 1:35 PM", in the bar without pushing
     the odds off it. The open list is as wide as its longest name. */
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.peek-race {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink);
  font-weight: 600;
  flex: none;
}
.peek-mtp {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--muted);
  flex: none;
  font-variant-numeric: tabular-nums;
}
.peek-mtp--soon { color: var(--accent-soft); font-weight: 600; }

.peek-runners { display: flex; gap: 12px; align-items: center; min-width: 0; }
.peek-runner { display: inline-flex; align-items: baseline; gap: 5px; white-space: nowrap; }
/* The saddle cloth, in its real colour. The background and the ink are set
   from client/lib/silks.ts, because those are real-world colours and not
   anything a brand gets to swap. Only the shape is here. */
.peek-runner b {
  font-size: 11px;
  font-weight: 600;
  border-radius: 3px;
  padding: 1px 5px;
  min-width: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.peek-runner i {
  font-style: normal;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.peek-none { font-size: 12px; color: var(--muted); }

/**
 * Room for it, so a tool's last row is never hidden behind it. From the
 * strip's OWN height, so a tool is not padded for something that is not on
 * screen (Live Odds, the assistant, a member without the tool) and is padded
 * correctly when the strip is taller than one row.
 */
.tool-view { padding-bottom: calc(40px + var(--peek-h) + env(safe-area-inset-bottom, 0px)); }

@media (max-width: 900px) {
  /**
   * The strip owns the bottom edge on a phone too; the ticket sheet sits on
   * top of it (see `--peek-h`). The page's own room for the strip comes from
   * the rule above, which measures it, so there is no second number here to
   * fall out of step.
   */
  .peek { left: 0; bottom: 0; }
}

/* ── Watch list ───────────────────────────────────────────────────────────
   A horse that is entered is the only thing on this page worth interrupting
   somebody for, so it is the only thing that carries the accent. Everything
   else is a quiet row holding a paragraph they wrote months ago. */

.w-wrap { max-width: 720px; }
.w-wrap > * + * { margin-top: 14px; }

.w-heading { margin-top: 22px; }
.w-heading .eyebrow { margin-bottom: 2px; }
.w-heading .t-fine { margin: 0; }

.w-add .t-fine { margin: 8px 0 0; }
.w-note { color: var(--accent-soft); }

.w-results {
  margin-top: 12px;
  border-top: 1px solid var(--line-soft);
}
.w-result {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line-soft);
}
.w-result-who { min-width: 0; flex: 1; }
.w-result-name { font-family: var(--display); font-size: 15px; color: var(--ink); }
/* The sire and the year are how a member tells two horses of a name apart, so
   they must never be the thing that gets truncated away. */
.w-result .t-fine { margin: 2px 0 0; }
.w-result .t-btn { white-space: nowrap; }

.w-horse { padding: 14px 15px; }
.w-horse-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.w-horse-name {
  font-family: var(--display);
  font-size: 17px;
  font-weight: var(--display-weight);
  color: var(--ink);
  margin: 0;
  flex: 1;
  min-width: 0;
}
.w-remove { font-size: 12px; }

/* Entered. The whole point of the tool, so it gets the only fill on the page. */
.w-horse--running { border-color: var(--accent); }
.w-entered {
  font-size: 12px;
  font-weight: 600;
  color: var(--accent-ink);
  background: var(--accent-fill);
  border-radius: var(--btn-radius);
  padding: 3px 11px;
  white-space: nowrap;
}

/* The note itself. Bigger than the furniture around it, because it is the
   product: a member is here to read this sentence, not to admire a card. */
.w-note-text {
  margin: 9px 0 0;
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink);
  white-space: pre-wrap;
}
.w-horse--running .w-note-text { color: var(--accent-soft); }
.w-note-view .t-fine { margin: 9px 0 0; }

.w-note-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
}
.w-note-foot .t-fine { margin: 0; }
.w-note-foot .linkish { font-size: 12px; }

.w-note-edit { margin-top: 10px; }
.w-note-box { resize: vertical; min-height: 92px; line-height: 1.55; font-family: var(--sans); }
.w-note-edit .t-btn-row { margin-top: 9px; }

.w-empty { margin-top: 16px; }

@media (max-width: 620px) {
  /* The button goes under the horse rather than squeezing the name that tells
     you which horse it is. */
  .w-result { flex-wrap: wrap; }
  .w-result .t-btn { width: 100%; }
}

/* ── Dashboard ── two columns of boxes, in the member's order. Wide on the
   left (the lists, the tracks), narrow on the right (the news). One column
   on a phone. Each widget is its own box so the eye reads them as separate
   things; bodies cap their height and scroll inside, so the page fits a
   screen without looking crushed. */
.tool-view[data-tool="dashboard"] { --tool-w: 1180px; }
.h-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: start; }
@media (min-width: 1000px) {
  .h-grid { grid-template-columns: minmax(0, 1fr) 320px; }
}
.h-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.h-widget {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px 12px;
  min-width: 0;
}
.h-widget-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding-bottom: 8px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--line-soft);
}
.h-widget-title { font-family: var(--display); font-size: 16px; font-weight: 600; margin: 0; white-space: nowrap; }
.h-widget-controls { margin-left: auto; display: flex; align-items: center; gap: 10px; }
/**
 * A widget's body scrolls inside itself ONLY in the two-column layout, where
 * capping each box is what fits the day on one screen. In one column, which
 * is every phone and most tablets, the page scrolls and the boxes are as long
 * as they need to be. Carson, 12 September: "the scroll in the track list on
 * mobile makes the page stick weird because it thinks we want to scroll in
 * the widget and not the page."
 */
@media (min-width: 1000px) {
  .h-widget-body { max-height: 46vh; overflow-y: auto; scrollbar-gutter: stable; }
  .h-widget--news .h-widget-body { max-height: 60vh; }
}
.h-sub { font-family: var(--display); font-size: 14px; margin: 8px 0 2px; color: var(--ink); }
.h-manage { padding: 4px 10px; font-size: 12px; text-decoration: none; white-space: nowrap; }
.h-day button { padding: 3px 9px; font-size: 12px; }
.h-move { display: inline-flex; gap: 2px; }
.h-move-btn {
  appearance: none; border: 1px solid var(--line-soft); background: none; color: var(--muted-2);
  border-radius: 4px; width: 22px; height: 20px; font-size: 11px; line-height: 1; cursor: pointer;
}
.h-move-btn:hover:not([disabled]) { color: var(--ink); border-color: var(--line); }
.h-move-btn[disabled] { opacity: 0.3; cursor: default; }
@media (max-width: 620px) {
  .h-widget { padding: 10px 11px; }
  .h-widget-head { flex-wrap: wrap; }
  .h-widget-controls { margin-left: 0; width: 100%; justify-content: space-between; }
}

/* ── Dashboard (blocks) ────────────────────────────────────────────────────────────
   A reading page. Blocks are the Watch List's blocks, lines are the Watch
   List's lines, so it reads as the same product. Nothing here is a control
   except the doors into the tools. */

.h-wrap > * + * { margin-top: 18px; }
.h-block { position: relative; }
.h-list--next { margin-top: 14px; }
.h-quiet { margin: 8px 0 0; }
.h-more { display: inline-block; margin-top: 8px; font-size: 12.5px; }
/* Why a horse is here: the trainer, jockey or owner it came in through. */
.h-via {
  font-size: 11px;
  color: var(--muted-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
}

/* Duke's block for somebody below VIP: three empty lines under a note, the
   same "greyed, with the upgrade" a locked tool gets. */
.h-block--locked .h-ghost { filter: blur(2px); opacity: 0.45; pointer-events: none; }
.h-ghost-row > .w-cloth { background: var(--line); min-width: 22px; }
.h-ghost-row > .w-runner-name { background: var(--line); height: 10px; width: 140px; border-radius: 3px; }
.h-ghost-row > .w-runner-where { background: var(--line-soft); height: 10px; width: 120px; border-radius: 3px; }
.h-ghost-row > .w-runner-odds { background: var(--line-soft); height: 10px; width: 40px; border-radius: 3px; }
.h-upgrade {
  position: absolute;
  left: 0; right: 0;
  top: 34px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 10px 12px;
}
.h-upgrade-title { font-family: var(--display); font-size: 16px; font-weight: 600; margin: 0 0 2px; }
.h-upgrade .t-fine { margin: 0 0 8px; }
.h-upgrade-go { text-decoration: none; padding: 6px 12px; font-size: 13px; }

/* Today's tracks: a star, the name (the door to Live Odds), first post,
   how many races. Starred tracks first, and their scratches under them. */
.h-tracks { margin-top: 2px; }
.h-track {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-top: 1px solid var(--line-soft);
  font-size: 13.5px;
}
.h-track:first-child { border-top: 0; }
.h-star {
  appearance: none;
  border: 0;
  background: none;
  color: var(--muted-2);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
  width: 22px;
}
.h-track--fav .h-star { color: var(--accent); }
.h-star:hover { color: var(--accent); }
.h-track-name { color: var(--ink); text-decoration: none; font-weight: 500; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.h-track-name:hover { text-decoration: underline; }
.h-track-post { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* The track's clock, next to its time. Quiet: it is a unit, not a fact. */
.h-zone { font-size: 10px; letter-spacing: 0.06em; color: var(--muted-2); }
.h-track-races { color: var(--muted-2); font-size: 11.5px; white-space: nowrap; min-width: 56px; text-align: right; }
/* The scratches under a starred track. Lines, the way the rest of the product
   reads a horse: the race in small caps once per group, the cloth in its
   colours, the horse, the reason in plain words. Carson: "pretty, sharp and
   high end like our branding." */
.h-scr {
  margin: 0 0 8px 32px;
  padding: 8px 12px 6px;
  border-left: 2px solid rgba(var(--accent-rgb), 0.55);
  background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.06), transparent 70%);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.h-scr-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; }
.h-scr-label {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
}
.h-scr-at { font-size: 11px; color: var(--muted-2); }
.h-scr-none { margin: 2px 0 0; }
.h-scr-group { padding: 3px 0; border-top: 1px solid rgba(var(--ink-rgb), 0.07); }
.h-scr-group:first-of-type { border-top: 0; }
.h-changes-toggle { display: inline-block; margin: -2px 0 8px 32px; font-size: 12px; }
.h-scr-row {
  display: grid;
  grid-template-columns: 30px 26px minmax(0, 1fr) auto;
  gap: 10px;
  align-items: baseline;
  padding: 3px 0;
  font-size: 13px;
}
.h-scr-race {
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.h-scr-name { color: var(--ink); font-weight: 500; text-decoration: line-through; text-decoration-color: rgba(var(--ink-rgb), 0.35); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.h-scr-why { font-size: 11.5px; color: var(--muted); white-space: nowrap; }
/* The going, as chips above the scratches. */
.h-going { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; }
.h-chip {
  font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink); border: 1px solid var(--line); border-radius: 999px; padding: 2px 9px;
  background: rgba(var(--ink-rgb), 0.04);
}
.h-chip--warn { color: var(--accent); border-color: rgba(var(--accent-rgb), 0.5); }
.h-chip--bad { color: #e5484d; border-color: rgba(229, 72, 77, 0.5); }
.h-cancelled { color: #e5484d; border-color: rgba(229, 72, 77, 0.5); }
/* The other changes, under the scratches. */
.h-changes { margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--line-soft); }
.h-chg-name { color: var(--ink); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.h-chg-what { font-size: 11.5px; color: var(--muted); white-space: nowrap; }
/* A rider change on a member's own line. */
.w-rider {
  font-size: 10px; letter-spacing: 0.06em; color: var(--accent);
  border: 1px solid rgba(var(--accent-rgb), 0.45); border-radius: 3px; padding: 1px 5px; margin-left: 6px; white-space: nowrap;
}
/* On a phone the reason and the change drop to a second line under the name
   rather than off the page. They used to be hidden at this width, which left
   a rider change reading as a horse's name and nothing else. 12 Sep 2026. */
@media (max-width: 620px) {
  .h-scr { margin-left: 12px; }
  .h-scr-why, .h-chg-what { grid-column: 3 / -1; white-space: normal; font-size: 11px; margin-top: -2px; }
}
.h-off-turf {
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid rgba(var(--accent-rgb), 0.5);
  border-radius: 3px;
  padding: 1px 5px;
  white-space: nowrap;
}

/* A horse the tote says is out. The line stays, because the member's note is
   still the point of it; the badge and the fade say it is not running. */
.w-runner--scr .w-runner-name,
.w-runner--scr .w-runner-where,
.w-runner--scr .w-runner-odds { opacity: 0.5; text-decoration: line-through; }
.w-scr {
  font-size: 9.5px;
  letter-spacing: 0.12em;
  font-weight: 700;
  color: #e5484d;
  border: 1px solid rgba(229, 72, 77, 0.5);
  border-radius: 3px;
  padding: 1px 4px;
  margin-left: 6px;
  white-space: nowrap;
}

/* News: headlines only, credited. The outlet's name is the tag on the block
   and a link to them; each headline is a link to their article. */
.h-news-from { text-decoration: none; }
.h-news-from:hover { text-decoration: underline; }
.h-headline {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 6px 0;
  border-top: 1px solid var(--line-soft);
  font-size: 13.5px;
}
.h-headline:first-child { border-top: 0; }
.h-headline-link { color: var(--ink); text-decoration: none; flex: 1; min-width: 0; }
.h-headline-link:hover { text-decoration: underline; }
.h-headline-when { color: var(--muted-2); font-size: 11.5px; white-space: nowrap; }

/* Nothing on the list yet: one sentence and the box. */
.h-empty-line { font-size: 14px; line-height: 1.55; margin: 0 0 10px; }
.h-find { display: flex; gap: 8px; }
.h-find .t-control { flex: 1; min-width: 0; }

/* ── My wagers ────────────────────────────────────────────────────────────
   The report is the reason anybody keeps a log, so it is the top of the page
   and the typing is underneath it. An open bet reads as open everywhere: it is
   never shown as a nought, because a nought here means it lost. */

.g-wrap { max-width: 980px; }
.g-wrap > * + * { margin-top: 16px; }

.g-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
.g-stat {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  padding: 13px 15px;
}
.g-stat .eyebrow { margin: 0 0 4px; }
.g-stat-value {
  font-family: var(--display);
  font-size: 26px;
  line-height: 1.1;
  color: var(--ink);
  margin: 0 0 3px;
  font-variant-numeric: tabular-nums;
}
.g-stat .t-fine { margin: 0; }

.g-up { color: var(--accent-soft); }
.g-down { color: var(--loss); }

.g-pending strong { color: var(--accent-soft); }
.g-add { margin-top: 14px; }

.g-scroll { overflow-x: auto; }
.g-table { border-collapse: collapse; width: 100%; font-size: 13.5px; font-variant-numeric: tabular-nums; }
.g-table th {
  text-align: left;
  font-size: 10.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  padding: 9px 12px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.g-table td { padding: 9px 12px; border-bottom: 1px solid var(--line-soft); color: var(--ink); }
.g-table tr:last-child td { border-bottom: 0; }
.g-num { text-align: right; white-space: nowrap; }
.g-table th.g-num { text-align: right; }
.g-what { color: var(--muted); }

/* Still out. Quieter than a settled row, and never wearing a number it has
   not earned. */
.g-open td { color: var(--muted); }
.g-open .g-num:first-of-type { color: var(--ink); }

.g-actions { white-space: nowrap; text-align: right; }
.g-actions .linkish { font-size: 12px; margin-left: 10px; }

.g-form { margin: 0; }
.g-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.g-grid .t-field { margin-bottom: 0; }
.g-pool .t-control { width: 100%; }
.g-error { grid-column: 1 / -1; color: var(--loss); margin: 0; }

.g-cut { margin-left: auto; }
.g-cut button { font-size: 12px; padding: 4px 11px; }
.g-fine { padding: 10px 15px 13px; margin: 0; }

@media (max-width: 620px) {
  /* The note is the widest field and the least urgent, so it drops to its own
     row rather than making every other box unusably narrow. */
  .g-grid { grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); }
  .g-table th, .g-table td { padding: 8px 9px; }
  .g-actions .linkish { margin-left: 7px; }
}

/* Asking to be emailed. Deliberately plain and deliberately near the top, with
   the whole row a label so the words are part of the tap target. */
.w-mail {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
  color: var(--muted);
  cursor: pointer;
}
.w-mail input[type="checkbox"] {
  accent-color: var(--accent-fill);
  width: 15px;
  height: 15px;
  margin: 2px 0 0;
  cursor: pointer;
  flex: none;
}

/* The suggestion list.
   An overlay rather than a block in the flow: it opens over whatever is under
   the box instead of shoving the member's own horses down the page while they
   type, and it goes away without anything moving. */
.w-search { position: relative; }
.w-search-input { width: 100%; }

.w-suggestions {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  right: 0;
  z-index: 30;
  max-height: 320px;
  overflow-y: auto;
  background: var(--bg-raised);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  /* Off the page rather than part of it: the shadow is what says this is a
     layer that will close, not a section that has appeared. */
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55);
  padding: 4px;
}

.w-suggestion {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  cursor: pointer;
  font: inherit;
  color: var(--ink);
}
.w-suggestion:hover,
.w-suggestion--on {
  background: rgba(var(--accent-rgb), 0.14);
}
.w-suggestion .t-fine { display: block; margin: 2px 0 0; }

.w-suggestion-line { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.w-suggestion-name {
  font-family: var(--display);
  font-size: 15px;
  color: var(--ink);
}

/* The year, on every horse.
   Carson: "all the horses should say the year so its easy for the viewer to
   know they need to select the newer one." So it is not prose in the second
   line, it is a chip beside the name, in the same place on every row, which is
   what makes two horses of one name separable at a glance. */
.w-suggestion-year {
  font-size: 11.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--accent-ink);
  background: var(--accent-fill);
  border-radius: 3px;
  padding: 1px 6px;
  white-space: nowrap;
}
/* Missing is said out loud rather than left blank, so a member never reads an
   absent year as an old one. */
.w-suggestion-year--none {
  background: none;
  color: var(--muted-2);
  border: 1px solid var(--line);
  font-weight: 500;
}

.w-suggestion-have {
  font-size: 11px;
  color: var(--accent-soft);
  margin-left: auto;
  white-space: nowrap;
}

/* The kind picker and the box are one control: what you are adding, then what
   you are looking for. */
.w-search { display: flex; gap: 8px; align-items: stretch; position: relative; }
/* The kind picker.
   Carson, twice: "not fixed. still dead space to the right of the text."
   A native select lays out at the width of its WIDEST option and then adds room
   for its own arrow, so a width that looked right for "Trainer" left a gap
   after "Horse". Fixed at the width "Trainer" actually needs, with the arrow
   drawn by us so there is no second one underneath taking space. */
.w-search .w-kind {
  width: 92px;
  flex: 0 0 92px;
  padding-right: 22px;
  -webkit-appearance: none;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 13px) calc(50% + 1px), calc(100% - 8px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.w-search .w-search-input { flex: 1 1 auto; min-width: 0; width: auto; }
/* The list drops under the whole row, not under the text box alone. */
.w-suggestions { left: 0; right: 0; }

/* Which of the three somebody is. Quiet: the name is what you read. */
.w-role {
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 1px 6px;
  white-space: nowrap;
}

/* A followed person's runners for the day. This is the reason to follow
   anybody, so it is the body of the card and not a footnote to it. */
.w-runners { margin-top: 11px; }
.w-runner {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto auto;
  gap: 10px;
  align-items: baseline;
  padding: 6px 0;
  border-top: 1px solid var(--line-soft);
  font-size: 13.5px;
}
.w-runner:first-child { border-top: 0; }

.w-runner-name { color: var(--ink); font-weight: 500; }
/*
 * Carson, 10 September: "clicking on the horses name should open the pop up."
 * So the name is a real button where it opens one. It must not look like a
 * link in a paragraph, because the whole line is quiet on purpose, so it reads
 * as text until the pointer is on it.
 */
.w-runner-name--open,
.w-horse-open {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  border-bottom: 1px solid transparent;
}
.w-runner-name--open:hover,
.w-horse-open:hover { color: var(--accent); border-bottom-color: var(--accent-soft); }
.w-modal-why { margin: 14px 0 10px; line-height: 1.5; }
.w-runner-name--open:focus-visible,
.w-horse-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }
.w-runner-where { color: var(--muted); font-size: 12.5px; white-space: nowrap; }
.w-runner-odds {
  color: var(--accent-soft);
  font-size: 12.5px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.w-runner-more { margin: 8px 0 0; }
.w-next { margin: 9px 0 0; }

/* One entry on the whole list: what it is, the name with its stars, the most
   recent note, and the way off. The same line as a runner above, without the
   cloth and the race, because here nothing is running.
   Carson, 11 September: "each watch takes up too much space." */
.w-item {
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr) minmax(0, 1.3fr) auto;
  gap: 10px;
  align-items: baseline;
  padding: 6px 0;
  border-top: 1px solid var(--line-soft);
  font-size: 13.5px;
}
.w-item-kind {
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.w-item .w-note-chip { max-width: none; }
.w-item-remove { font-size: 12px; white-space: nowrap; }

@media (max-width: 620px) {
  /* The price is the first thing to go: it is the morning line, not the board. */
  .w-runner { grid-template-columns: 24px minmax(0, 1fr) auto; }
  /* The note drops to its own line under the name rather than being cut. */
  .w-item { grid-template-columns: minmax(0, 1fr) auto; }
  .w-item-kind { display: none; }
  .w-item .w-note-chip { grid-column: 1 / -1; display: block; }
  .w-runner-odds { display: none; }
  .w-search .w-kind { width: 88px; flex-basis: 88px; }
}

@media (max-width: 620px) {
  /* On a phone the name and the chips fight for one line and the name loses,
     breaking "Correa Yarmarie L" across the badge. The name takes the row. */
  .w-horse-head { row-gap: 7px; }
  .w-horse-name { flex: 0 0 100%; }
}

/* ── The days ─────────────────────────────────────────────────────────────
   Carson: "we need a today, 1 day and 2 day watch list. then anything longer
   than that that has an entry already... these can be different tabs." The
   count rides on the tab so a member can see Thursday is empty without
   opening Thursday. */

.w-tabs { margin-top: 16px; }
.w-tabs button { display: inline-flex; align-items: center; gap: 7px; }

.w-tab-count {
  font-size: 10.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 17px;
  padding: 0 4px;
  border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.22);
  color: var(--accent-soft);
  text-align: center;
}
.w-tabs button[aria-pressed="true"] .w-tab-count {
  background: rgba(var(--accent-ink-rgb, 20, 17, 13), 0.22);
  color: var(--accent-ink);
}
/* Nothing that day. Said quietly rather than hidden, because "none" is an
   answer and an absent badge is not. */
.w-tab-count--none { background: none; color: var(--muted-2); }

/* Why these horses are in front of you. The heading does the work Carson
   asked for: "so they know this horse is showing because you have this owner
   in your watch list." */
.w-source { margin-top: 20px; }
.w-source .eyebrow { margin-bottom: 2px; }
.w-nothing { margin-top: 14px; }

/* A saddle cloth, in its real colour. The background and ink come from
   client/lib/silks.ts: real-world colours, not brand tokens, so they are set
   inline and only the shape lives here. */
.w-cloth {
  font-size: 11.5px;
  font-weight: 700;
  border-radius: 3px;
  padding: 2px 0;
  min-width: 24px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  background: var(--line);
  color: var(--ink);
}
.w-runner { grid-template-columns: 24px minmax(0, 1fr) auto auto; }

/* When a horse or a person next turns up, in the whole-list view. */
.w-when { font-size: 12px; color: var(--accent-soft); white-space: nowrap; }

/* The whole list, under the day. Closed by default: on a race morning the day
   is what they came for, and this is the index behind it. */
.w-all { margin-top: 26px; border-top: 1px solid var(--line); padding-top: 14px; }
.w-all > summary {
  cursor: pointer;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  list-style: none;
}
.w-all > summary::-webkit-details-marker { display: none; }
.w-all > summary::before { content: '+ '; }
.w-all[open] > summary::before { content: '- '; }
.w-all > * + * { margin-top: 12px; }

/* ── The day, densely ─────────────────────────────────────────────────────
   Carson: "I want everything to be able to show on one screen with minimal
   scrolling... right now not even three trainers can fit on one screen and I
   have a big screen."

   The panels this replaced spent most of their height on borders, padding and
   an empty note. A block spends it on horses: one heading line, then nothing
   but runners at a line each. */

.w-day { margin-top: 16px; }
.w-block { margin-top: 14px; }
.w-block:first-child { margin-top: 0; }

.w-block-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 0 0 5px;
  border-bottom: 1px solid var(--line);
}

/* Which kind of thing this block is. The label a member scans for. */
.w-block-tag {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
  white-space: nowrap;
}
.w-block-name {
  font-family: var(--display);
  font-size: 15px;
  color: var(--ink);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.w-block-count {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  margin-left: auto;
  white-space: nowrap;
}
.w-block-actions { display: flex; gap: 10px; white-space: nowrap; }
.w-block-actions .linkish { font-size: 11.5px; }

/* A followed person's note. One line by default: it is context for the horses
   under it, not the headline. */
.w-block-note {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--accent-soft);
  white-space: pre-wrap;
}

/* One runner, one line. */
.w-runner {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto auto minmax(0, auto);
  gap: 10px;
  align-items: baseline;
  padding: 4px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 13.5px;
}
.w-runner:last-child { border-bottom: 0; }

/* A horse's own note, sitting on its row. Clipped to one line so a long note
   can never push the day off the screen; the full text is in the tooltip and
   in the editor. */
.w-note-chip {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 12px;
  color: var(--accent-soft);
  cursor: pointer;
  text-align: left;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.w-note-chip--empty { color: var(--muted-2); font-size: 11px; }
.w-note-chip:hover { color: var(--accent); }

@media (max-width: 620px) {
  /* The morning line and the note give way first: the horse, where and when
     are what a member needs at the track. */
  .w-runner { grid-template-columns: 24px minmax(0, 1fr) auto; }
  .w-runner-odds, .w-note-chip { display: none; }
  .w-block-name { font-size: 14px; }
}

/* Somebody else's list.
   Carson: "users cannot edit or add any horses to Dukes watch list but they can
   view it." So a shared block carries no note button and no remove on its
   horses; the only control is Unfollow, on the block. The left rule is there to
   say at a glance that this section is not yours. */
.w-block--shared { border-left: 2px solid var(--accent); padding-left: 11px; }
.w-block--shared .w-block-tag { color: var(--accent-soft); }
/* Their note, marked as theirs: not a button, because there is nothing to open. */
.w-note-chip--theirs { color: var(--muted); cursor: default; font-style: italic; }

/* Finding a list to follow. Behind a disclosure so it never competes with the
   day, which is what people open this page for. */
.w-offer { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.w-offer-open { font-size: 12.5px; }
.w-offer-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line-soft);
}
.w-offer-row:last-child { border-bottom: 0; }
.w-offer-who { flex: 1; min-width: 0; }
.w-offer-title { font-family: var(--display); font-size: 14.5px; color: var(--ink); }
.w-offer-who .t-fine { margin: 2px 0 0; }
.w-offer-row .t-btn { white-space: nowrap; }

@media (max-width: 620px) {
  .w-offer-row { flex-wrap: wrap; }
  .w-offer-row .t-btn { width: 100%; }
}

/* ── Thumbs ───────────────────────────────────────────────────────────────
   Carson: "we need to optimize for mobile... People need to feel like they can
   navigate easily on desktop and mobile."

   Measured across every tool on a 375px screen: the text links sit at about
   15px tall, which is half what a thumb needs and the single biggest reason
   this would feel fiddly on a phone. The hit area is grown with a pseudo
   element rather than with padding, so every one of them gets a real target
   without a single layout moving. The horizontal reach is deliberately small:
   "Note" and "Remove" sit a few pixels apart in a block heading, and a generous
   one would have them stealing each other's taps. */
@media (max-width: 860px) {
  .linkish { position: relative; }
  .linkish::after {
    content: '';
    position: absolute;
    inset: -13px -5px;
  }

  /* The saddle cloth is the thing people aim at on a runner row. */
  .w-cloth { min-width: 28px; padding: 4px 0; }

  /* Day tabs are the main navigation on this page, so they get real height. */
  .w-tabs button { padding: 9px 12px; }

  /* Checkboxes, which are otherwise the smallest thing on the page. */
  .w-mail input[type="checkbox"],
  .s-cell input[type="checkbox"] { width: 20px; height: 20px; }
  .w-mail { padding: 4px 0; }
}

/* Who may share their watch list. Carson's grant, on the page where he decides
   things about people. */
.s-sharing { margin-bottom: 22px; }
.s-sharing .eyebrow { margin-bottom: 3px; }
.s-sharing > .t-fine { margin: 0 0 12px; }
.s-share-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line-soft);
}
.s-share-row:last-child { border-bottom: 0; }
.s-share-who { flex: 1; min-width: 0; color: var(--ink); font-size: 13.5px; }
.s-share-who .t-fine { margin: 2px 0 0; }
.s-share-title { width: 260px; flex: none; }
.s-share-row .t-btn { white-space: nowrap; }

@media (max-width: 860px) {
  .s-share-row { flex-wrap: wrap; }
  .s-share-title { width: 100%; }
  .s-share-row .t-btn { width: 100%; }
}

/* "Searching..." while the answer is on its way.
   Carson: "we need the drop down to show when someone starts typing with ... or
   searching for horse or something to show that the system is doing something."
   A line of text where the first result will be, rather than a spinner
   somewhere else, so nothing jumps when the answers arrive. */
.w-searching {
  color: var(--muted);
  font-size: 13px;
  cursor: default;
  display: flex;
  align-items: baseline;
  gap: 2px;
}
.w-searching:hover { background: none; }
.w-dots {
  display: inline-block;
  width: 14px;
  overflow: hidden;
  vertical-align: bottom;
  animation: w-dots 1s steps(4, end) infinite;
  color: var(--accent);
}
@keyframes w-dots { from { width: 0; } to { width: 14px; } }
/* Somebody who has asked not to see movement gets the dots, just still. */
@media (prefers-reduced-motion: reduce) {
  .w-dots { animation: none; width: 14px; }
}
/* "Nothing found", in the same row the answers would take. Ink rather than
   muted, because it is the answer and not a wait, and it must never move the
   input: a note under the box needed a redraw, and the redraw ate the cursor. */
.w-nothing-found { color: var(--ink); }

/* ── The pop-up ───────────────────────────────────────────────────────────
   Carson: "once selecting a horse, a pop up should show, this pop up is where
   people can put 1-5 stars, and notes."
   It replaced an editor that only appeared where the horse happened to be
   drawn, which on a day it was not running meant inside a collapsed section. */

.w-modal-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.62);
  z-index: 60;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 6vh 16px 16px;
  overflow-y: auto;
}

.w-modal {
  width: min(520px, 100%);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 26px 70px rgba(0, 0, 0, 0.6);
  padding: 20px 22px 22px;
}
.w-modal-wait { margin: 0; }

.w-modal-head { display: flex; align-items: baseline; gap: 9px; }
.w-modal-name {
  font-family: var(--display);
  font-size: 21px;
  font-weight: var(--display-weight);
  color: var(--ink);
  margin: 0;
  flex: 1;
  min-width: 0;
}
.w-modal-close {
  background: none;
  border: 0;
  color: var(--muted);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}
.w-modal-close:hover { color: var(--accent); }

/* What it last did on a racetrack. A scratch is not a run, and when there is
   nothing this says so rather than leaving a gap that reads as lost data. */
.w-modal-last { margin: 6px 0 0; font-size: 13px; color: var(--accent-soft); }

.w-stars { display: flex; align-items: center; gap: 5px; margin-top: 16px; }
.w-stars .t-fine { margin: 0 6px 0 0; }
.w-star {
  background: none;
  border: 0;
  padding: 2px;
  font-size: 21px;
  line-height: 1;
  cursor: pointer;
  color: var(--line);
  transition: color 0.12s ease, transform 0.12s ease;
}
.w-star:hover { transform: scale(1.12); color: var(--accent-soft); }
.w-star--on { color: var(--accent); }
/* The rating on a row, beside the name. Small and gold, so it reads as a mark
   the member made rather than as part of the horse's name. */
.w-stars-inline {
  margin-left: 7px;
  font-size: 11px;
  letter-spacing: 1px;
  color: var(--accent);
  vertical-align: 1px;
  white-space: nowrap;
}
.w-star-clear { margin-left: 8px; font-size: 11.5px; }

.w-modal-write { margin-top: 16px; }
.w-modal-write .t-btn-row { margin-top: 8px; }

.w-modal-notes { margin-top: 20px; border-top: 1px solid var(--line-soft); padding-top: 14px; }
.w-modal-notes .eyebrow { margin-bottom: 10px; }

/* One note, and the stamp that makes it worth keeping. */
.w-note-entry { padding: 9px 0; border-top: 1px solid var(--line-soft); }
.w-note-entry:first-of-type { border-top: 0; padding-top: 0; }
.w-note-when {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 3px;
  font-size: 11.5px;
  color: var(--muted-2);
  font-variant-numeric: tabular-nums;
}
.w-note-bin { font-size: 11px; }
.w-note-body {
  margin: 0;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink);
  white-space: pre-wrap;
}

@media (max-width: 620px) {
  /* Nearly full screen on a phone: it is the whole task while it is open. */
  .w-modal-scrim { padding: 0; align-items: stretch; }
  .w-modal {
    width: 100%;
    min-height: 100%;
    border: 0;
    border-radius: 0;
    padding: 18px 16px 28px;
  }
  .w-star { font-size: 26px; padding: 4px; }
}

/* ══ Usage: who is using what ══════════════════════════════════════════════
   Carson, 12 September 2026. Admin only. One hue, the accent, because every
   chart here is one series: text is never coloured, marks are. */

.u-wrap { max-width: 1100px; }
.u-wrap > * + * { margin-top: 18px; }
.u-toolbar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.u-toolbar .t-fine { margin: 0; }
.u-windows { display: inline-flex; border: 1px solid var(--line); border-radius: var(--btn-radius); overflow: hidden; }
.u-window {
  font: inherit; font-size: 13px; padding: 6px 12px; cursor: pointer;
  background: transparent; color: var(--muted); border: 0; border-right: 1px solid var(--line-soft);
}
.u-window:last-child { border-right: 0; }
.u-window--on { background: rgba(var(--accent-rgb), 0.14); color: var(--ink); }

.u-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.u-tile .l-stat-value { margin-bottom: 2px; }
.u-tile .t-fine { margin: 0; }

.u-section { border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; background: var(--bg-raised); }
.u-section > .eyebrow { margin-bottom: 2px; }
.u-section > .t-fine { margin: 0 0 12px; }
.u-section .s-scroll { border: 0; border-radius: 0; background: transparent; }
.u-two { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

/* Bars. Thin, a 2px gap, rounded only at the data end, on a recessive baseline. */
.u-bar-frame { display: flex; gap: 8px; align-items: stretch; }
.u-bar-scale { display: flex; flex-direction: column; justify-content: space-between; font-size: 11px; color: var(--muted-2); padding-bottom: 18px; font-variant-numeric: tabular-nums; }
.u-bars { display: flex; align-items: flex-end; gap: 2px; height: 140px; flex: 1; border-bottom: 1px solid var(--line); position: relative; }
.u-bar-col { flex: 1; min-width: 0; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; position: relative; }
.u-bar { background: var(--accent-fill); border-radius: 3px 3px 0 0; min-height: 0; transition: opacity 0.15s; }
.u-bar-col:hover .u-bar { opacity: 0.75; }
.u-bar-label { position: absolute; top: 100%; left: 0; padding-top: 4px; font-size: 10.5px; color: var(--muted-2); white-space: nowrap; }
.u-bars--hours { height: 110px; }
.u-bars + .u-device, .u-bars--hours + .u-device { margin-top: 26px; }
.u-flip { margin-top: 26px; font-size: 12px; }

.u-table td.u-num, .u-table th.u-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.u-share { height: 4px; background: rgba(var(--ink-rgb), 0.08); border-radius: 2px; margin-top: 6px; max-width: 220px; }
.u-share-fill { height: 100%; background: var(--accent-fill); border-radius: 2px; }

.u-sort { font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
.u-sort--on { color: var(--ink); }
.u-member { cursor: pointer; }
.u-member:hover td { background: rgba(var(--accent-rgb), 0.05); }
.u-member--quiet td { color: var(--muted); }
.u-breakdown td { background: rgba(var(--ink-rgb), 0.025); padding: 10px 14px 12px; }
.u-breakdown-list { display: grid; gap: 6px; max-width: 520px; margin-bottom: 8px; }
.u-breakdown-row { display: grid; grid-template-columns: 150px 1fr 60px; gap: 10px; align-items: center; font-size: 13px; }
.u-breakdown-row .u-share { margin: 0; max-width: none; }
.u-breakdown-time { text-align: right; font-variant-numeric: tabular-nums; }
.u-breakdown .t-fine { margin: 0; }
.u-foot { margin: 0; }

@media (max-width: 860px) {
  .u-two { grid-template-columns: 1fr; }
  .u-tiles { grid-template-columns: 1fr 1fr; }
  .u-breakdown-row { grid-template-columns: 110px 1fr 50px; }
  .u-bar-label { font-size: 9.5px; }
}

/* Dutching on a phone: five columns in 358px. Found 12 September 2026 with
   the page open at 390px: "Collect", the number the member came for, was cut
   off at "$16" behind the edge of a table that scrolled sideways without
   saying so. Tighter cells and narrower boxes put the whole row on screen. */
@media (max-width: 860px) {
  .d-table th { padding: 9px 7px; letter-spacing: 0.08em; }
  .d-table td { padding: 6px 7px; }
  .d-tick { width: 28px; }
  .d-prog { width: 44px; padding-left: 4px; padding-right: 4px; }
  .d-odds { width: 64px; padding-left: 4px; padding-right: 4px; }
  .d-stake, .d-collect { font-size: 14px; }
}

/* The "why it is here" tag on a phone. Found 12 September 2026 at 390px:
   the runner grid drops to three columns there and this fourth item wrapped
   into the 24px cloth column, reading "Jo..." for "Jockey Reyes Leonel". It
   takes its own line under the name now, full width, no ellipsis. */
@media (max-width: 620px) {
  .h-via { grid-column: 2 / -1; max-width: none; white-space: normal; }
}

/* The sticky header on a phone. Found 12 September 2026, scrolled at 390px:
   a strip of the page showed ABOVE the "Dashboard" header. A sticky element
   cannot leave its containing block's content box, and .tool-view has 18px
   of top padding, so the header could only ever stick 18px down and rows
   slid through the gap. The padding moves onto the header, whose background
   then reaches the top edge. */
@media (max-width: 900px) {
  .tool-view { padding-top: 0; }
  .tool-head { padding-top: 18px; }
}

/* A track's name on a phone wraps rather than being cut: "Belte..." beside
   an OFF THE TURF chip told nobody which track. */
@media (max-width: 620px) {
  .h-track-name { white-space: normal; overflow: visible; text-overflow: clip; }
}

/* Wagering Reports on a phone: the four headline tiles two across, not
   stacked. Stacked, they filled the first screen and "Log a bet" was a whole
   swipe down. 12 September 2026. */
@media (max-width: 620px) {
  .g-summary { grid-template-columns: 1fr 1fr; gap: 8px; }
  .g-stat { padding: 11px 12px; }
  .g-stat-value { font-size: 22px; }
}

/* Done, at the foot of the horse pop-up. Right-aligned on a desktop; full
   width on a phone so it is under the thumb. */
.w-modal-foot { display: flex; justify-content: flex-end; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.w-modal-done { min-width: 96px; }
@media (max-width: 620px) {
  .w-modal-done { width: 100%; }
}

/* A button that is coming soon: shown, greyed, with the same "soon" tag the
   rail uses, so the two read as one thing. */
.t-btn--soon { opacity: 0.55; cursor: not-allowed; }
.t-btn--soon .tool-soon { margin-left: 7px; }

/* Break Even's opening lines: the mindset before the numbers. */
.b-lead { margin: 0 0 18px; font-size: 14.5px; line-height: 1.6; color: var(--ink); max-width: 640px; }

/* ══ Keep it for X starts (13 September 2026) ══════════════════════════════ */
.w-keep { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.w-keep .t-fine { margin: 0; }
.w-keep-pick { width: auto; padding: 5px 30px 5px 9px; font-size: 13px; }
.w-keep-state { color: var(--accent-soft); }
/* "1 of 3 starts" beside a name on the whole list. */
.w-item-starts {
  font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--line-soft); border-radius: 3px; padding: 1px 6px; margin-left: 8px; white-space: nowrap;
  vertical-align: middle;
}
.w-block--finished .w-item { grid-template-columns: 58px minmax(0, 1fr) auto auto; }
.w-block--finished .t-fine { margin: 0 0 6px; }
@media (max-width: 620px) {
  .w-block--finished .w-item { grid-template-columns: minmax(0, 1fr) auto auto; }
  .w-keep-pick { flex: 1; }
}

/* How a shared list has done, in Settings. Admin only. */
.s-record { margin: 8px 0 14px; }
.s-record > summary { cursor: pointer; font-size: 13px; color: var(--accent); }
.s-record-body { padding-top: 10px; }
.s-record-body > * + * { margin-top: 12px; }
.s-record-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.s-record-tiles .l-stat-value { margin-bottom: 2px; }
.s-record-tiles .t-fine { margin: 0; }
.s-record-table td { white-space: nowrap; }
.s-record-win { color: var(--accent-soft); font-weight: 600; }

/* The staging site says so, on the brand. */
.staging-tag { margin-left: 8px; color: var(--accent); border: 1px solid rgba(var(--accent-rgb), 0.5); border-radius: 3px; padding: 1px 5px; }
.app.collapsed .staging-tag { display: none; }

/* The beta tag on the brand and the one-line notice at the top of the Dashboard.
   Both go together when the beta ends. */
.beta-tag { margin-left: 8px; color: var(--accent); border: 1px solid rgba(var(--accent-rgb), 0.5); border-radius: 3px; padding: 1px 5px; }
.app.collapsed .beta-tag { display: none; }
.h-beta { max-width: var(--tool-w, 100%); margin: 0 auto 14px; font-size: 13px; line-height: 1.5; color: var(--muted); }
.h-beta-link { font-size: inherit; color: var(--accent); }
