/* ==========================================================================
   Lyllume Ledger — sports bet ledger (AUD)
   Design language: "deep field" — ink-black ground, parchment type,
   day-cells rendered as points of light (Lyra / Lumen).
   ========================================================================== */

:root {
  /* Ground */
  --ink:        #06070B;
  --slate:      #0A0C12;
  --panel:      #0E1118;
  --raise:      #141822;
  --raise-hi:   #1A1F2B;

  /* Hairlines — never a flat grey border */
  --line:       rgba(233, 236, 247, 0.065);
  --line-mid:   rgba(233, 236, 247, 0.11);
  --line-hi:    rgba(233, 236, 247, 0.18);

  /* Type */
  --text:       #E9ECF7;
  --text-dim:   #8B91A7;
  --text-faint: #565C71;

  /* Signal */
  --gain:       #7BE0B2;
  --gain-soft:  rgba(123, 224, 178, 0.14);
  --gain-glow:  rgba(123, 224, 178, 0.40);
  --loss:       #F0736A;
  --loss-soft:  rgba(240, 115, 106, 0.14);
  --loss-glow:  rgba(240, 115, 106, 0.38);
  --star:       #C9D8FF;   /* Vega — accent, focus, selection */
  --star-soft:  rgba(201, 216, 255, 0.12);
  --amber:      #E8C07D;   /* needs review */
  --violet:     #C3A6F0;   /* Strategy-kind sources — the only remaining source-kind
                               colour slot; gain/loss were deliberately avoided here
                               since a green or red left-edge on a card would read as
                               a PnL signal, not a source category */
  --violet-soft: rgba(195, 166, 240, 0.14);

  /* Form */
  --r-xl: 22px;
  --r-lg: 16px;
  --r-md: 11px;
  --r-sm: 7px;
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);

  /* Horizontal gutter. Safe-area insets are NOT stored in custom properties:
     Safari fails to resolve env() through a var() chain inside calc(), which
     invalidates the whole declaration and silently drops the padding to zero.
     env() therefore appears directly in each property below. */
  --gutter: 22px;

  --font-ui: 'Geist', ui-sans-serif, -apple-system, 'Segoe UI', sans-serif;
  --font-num: 'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --font-display: 'Instrument Serif', ui-serif, Georgia, serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  /* `overflow-x: hidden` here would create a scroll container and silently kill
     `position: sticky` on every descendant, including the header. `clip` does
     the same visual job without becoming a scrollport. */
  max-width: 100%;
  overflow-x: clip;
  overscroll-behavior-y: none;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Ambient deep field. Fixed + pointer-events:none so it never repaints on scroll. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(900px 600px at 12% -8%, rgba(201, 216, 255, 0.055), transparent 62%),
    radial-gradient(700px 500px at 96% 4%, rgba(123, 224, 178, 0.035), transparent 60%),
    radial-gradient(1000px 800px at 50% 120%, rgba(90, 110, 180, 0.05), transparent 65%);
}

button, input, select, textarea { font: inherit; color: inherit; }
/* Reads as an app, not a document: no accidental text selection on chrome,
   but real content stays selectable and copyable. */
.topbar, .menu-panel, .seg, .pager, .cal-grid, .stat, .headline { -webkit-user-select: none; user-select: none; }
input, textarea, .market, .ledger-table td { -webkit-user-select: text; user-select: text; }
button { cursor: pointer; border: none; background: none; }

:focus-visible {
  outline: 2px solid var(--star);
  outline-offset: 2px;
  border-radius: 4px;
}

.num { font-family: var(--font-num); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.pos { color: var(--gain); }
.neg { color: var(--loss); }
.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.hidden { display: none !important; }

/* Eyebrow / micro-label */
.eyebrow {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--text-faint);
  font-weight: 500;
}

/* --------------------------------------------------------------------------
   Nested enclosure: outer tray + inner plate (concentric radii)
   -------------------------------------------------------------------------- */
.shell {
  background: rgba(255, 255, 255, 0.022);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: 6px;
}
.plate {
  background: linear-gradient(180deg, var(--panel), var(--slate));
  border-radius: calc(var(--r-xl) - 6px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.055);
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   Gate
   -------------------------------------------------------------------------- */
#gate {
  position: relative;
  z-index: 1;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
  padding-top: calc(24px + env(safe-area-inset-top, 0px));
  padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
}
.gate-card { width: min(400px, 100%); }
.gate-card .plate { padding: 34px 30px 30px; }
.gate-mark {
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 1;
  letter-spacing: 0.005em;
}
.gate-mark span { color: var(--star); }
.gate-sub { margin: 10px 0 26px; color: var(--text-dim); font-size: 13.5px; }

.field { position: relative; margin-bottom: 14px; }
.field input {
  width: 100%;
  padding: 13px 15px;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--line-mid);
  border-radius: var(--r-md);
  color: var(--text);
  font-family: var(--font-num);
  letter-spacing: 0.12em;
  transition: border-color 0.4s var(--ease), background 0.4s var(--ease);
}
.field input:focus {
  outline: none;
  border-color: var(--star);
  background: rgba(0, 0, 0, 0.55);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.055);
  border: 1px solid var(--line-mid);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 500;
  transition: background 0.5s var(--ease), border-color 0.5s var(--ease), transform 0.4s var(--ease);
}
.btn:hover { background: rgba(255, 255, 255, 0.09); border-color: var(--line-hi); }
.btn:active { transform: scale(0.98); }
.btn[disabled] { opacity: 0.4; pointer-events: none; }

.btn-primary {
  width: 100%;
  justify-content: space-between;
  background: var(--star);
  color: #06070B;
  border-color: transparent;
  padding: 8px 8px 8px 20px;
  font-weight: 600;
}
.btn-primary:hover { background: #DCE6FF; }
/* button-in-button trailing glyph */
.btn-primary .cap {
  width: 30px; height: 30px;
  border-radius: 999px;
  background: rgba(6, 7, 11, 0.12);
  display: grid; place-items: center;
  transition: transform 0.5s var(--ease), background 0.5s var(--ease);
}
.btn-primary:hover .cap { transform: translate(3px, -1px); background: rgba(6, 7, 11, 0.2); }

.gate-error {
  margin-top: 14px;
  font-size: 12.5px;
  color: var(--loss);
  min-height: 18px;
}

/* --------------------------------------------------------------------------
   App chrome
   -------------------------------------------------------------------------- */
#app {
  position: relative;
  z-index: 1;
  max-width: 1440px;
  margin: 0 auto;
  /* Portrait deliberately does NOT consult env() for the side gutters. The
     insets are zero in portrait anyway, and on a freshly launched iOS standalone
     PWA env() can resolve before the viewport is settled — which collapsed the
     padding to nothing until a rotation forced a recalculation. Plain units
     cannot fail that way. Landscape, where the insets actually matter, opts in
     below. */
  padding: 4px var(--gutter) 90px;
  padding-bottom: calc(90px + env(safe-area-inset-bottom, 0px));
}

/* Only here do the horizontal insets do real work. */
@media (orientation: landscape) {
  #app {
    padding-left: calc(var(--gutter) + env(safe-area-inset-left, 0px));
    padding-right: calc(var(--gutter) + env(safe-area-inset-right, 0px));
  }
  .topbar-inner {
    padding-left: calc(var(--gutter) + env(safe-area-inset-left, 0px));
    padding-right: calc(var(--gutter) + env(safe-area-inset-right, 0px));
  }
}

/* ── Sticky header ─────────────────────────────────────────────────────────
   Sits outside #app so it can span the full width and stay put on scroll.

   No fill and no rule: the effect is a *dissolve*, not a bar. A pseudo-element
   carries a blur that is itself masked to nothing by its own bottom edge, so
   content scrolling underneath softens and disappears instead of sliding under
   a visible boundary. Masking the blur rather than painting a solid colour also
   avoids a seam against the ambient radial gradient on body::before. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 60;
  background: none;
  border: none;
  /* The fade needs room below the wordmark to happen in. */
  padding-top: 0;
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: 20px;
  margin-bottom: -20px;
  pointer-events: none;   /* re-enabled on the inner row */
}
.topbar::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  background: linear-gradient(
    to bottom,
    rgba(6, 7, 11, 0.92) 0%,
    rgba(6, 7, 11, 0.80) 46%,
    rgba(6, 7, 11, 0.42) 74%,
    rgba(6, 7, 11, 0) 100%
  );
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 48%, rgba(0,0,0,0.55) 76%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 48%, rgba(0,0,0,0.55) 76%, transparent 100%);
}
.topbar-inner {
  position: relative;
  pointer-events: auto;
  max-width: 1440px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px var(--gutter) 4px;
}
.topbar .mark { flex: 1 1 auto; min-width: 0; }

.hamburger {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
  display: grid;
  place-content: center;
  gap: 4px;
  transition: background 0.45s var(--ease), border-color 0.45s var(--ease);
}
.hamburger:hover { background: rgba(255, 255, 255, 0.075); border-color: var(--line-mid); }
.hamburger span {
  display: block;
  width: 16px; height: 1.5px;
  border-radius: 2px;
  background: var(--text);
  transition: transform 0.4s var(--ease), opacity 0.3s var(--ease);
}
.hamburger.open span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.hamburger.open span:nth-child(2) { opacity: 0; }
.hamburger.open span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* ── Menu ───────────────────────────────────────────────────────────────── */
.menu-scrim {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(4, 5, 9, 0.62);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  animation: fade 0.3s var(--ease-out);
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  padding: 66px var(--gutter) 24px 16px;
  padding-top: calc(env(safe-area-inset-top, 0px) + 66px);
}
.menu-panel {
  width: min(360px, 100%);
  background: linear-gradient(180deg, var(--raise-hi), var(--panel));
  border: 1px solid var(--line-mid);
  border-radius: var(--r-lg);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
  animation: menu-in 0.42s var(--ease-out);
  /* The menu now has four sections (Sports/Predictions/Trading/General)
     plus Log out — comfortably taller than one screen on a phone. Rather
     than let it grow past the viewport and clip whatever doesn't fit
     (which is what "overflow: hidden" here used to do), the panel caps its
     own height and scrolls internally, so every item stays reachable
     regardless of display size. */
  max-height: calc(100vh - 90px - env(safe-area-inset-top, 0px));
  max-height: calc(100dvh - 90px - env(safe-area-inset-top, 0px));
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
@keyframes menu-in {
  from { opacity: 0; transform: translateY(-10px) scale(0.98); }
  to { opacity: 1; transform: none; }
}
.menu-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 12px;
  border-bottom: 1px solid var(--line);
  /* Stays put as the panel scrolls, so the close button is never scrolled
     out of reach — a solid-enough background of its own so item text
     scrolling underneath doesn't show through. */
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--raise-hi);
}

.menu-item {
  width: 100%;
  display: flex;
  align-items: flex-start;
  gap: 13px;
  padding: 13px 16px;
  text-align: left;
  color: var(--text);
  transition: background 0.35s var(--ease);
}
.menu-item:hover, .menu-item:focus-visible { background: rgba(255, 255, 255, 0.05); }
.menu-item svg { width: 17px; height: 17px; flex: 0 0 auto; margin-top: 2px; color: var(--text-dim); }
.menu-item span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.menu-item strong { font-size: 14px; font-weight: 500; }
.menu-item em { font-style: normal; font-size: 11.5px; color: var(--text-faint); }
.menu-item.danger:hover { background: rgba(240, 115, 106, 0.08); }
.menu-item.danger strong { color: var(--loss); }
.menu-item.danger svg { color: var(--loss); }
.menu-rule { height: 1px; background: var(--line); margin: 4px 0; }
/* Groups the menu into Sports / Predictions / General — same small-caps
   language as every other section header in the app (.eyebrow), just with
   menu-appropriate padding rather than a standalone block. */
.menu-section-label {
  padding: 12px 16px 4px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-faint);
  font-weight: 500;
}

/* ── Pager ──────────────────────────────────────────────────────────────── */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 16px;
  border-top: 1px solid var(--line);
}
.pager .btn { padding: 7px 15px; font-size: 12.5px; }
.pager-info { font-size: 12px; color: var(--text-dim); min-width: 120px; text-align: center; }

.mark {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.mark span { color: var(--star); }
.topbar .spacer { flex: 1 1 auto; }

/* Header summary */
.summary { display: grid; grid-template-columns: minmax(280px, 1.15fr) 2fr; gap: 14px; margin: 8px 0 14px; }
.headline .plate { padding: 24px 26px 22px; }
.headline .eyebrow { margin-bottom: 12px; }
.big-aud {
  font-family: var(--font-display);
  font-size: clamp(40px, 6vw, 62px);
  line-height: 0.95;
  letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums;
}
.big-usd { margin-top: 10px; font-family: var(--font-num); font-size: 13px; color: var(--text-dim); }

.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
/* A proper class, not an inline style, for the 3-column stat grids (Sports
   Settled, Predictions, Daily) — an inline style has higher specificity
   than any class-based media query, which is exactly what was silently
   defeating the responsive breakpoints below for all three of them. */
.stats-3 { grid-template-columns: repeat(3, 1fr); }
.stat .plate { padding: 18px 18px 16px; height: 100%; display: flex; flex-direction: column; justify-content: space-between; }
.stat .eyebrow { margin-bottom: 14px; }
.stat-value { font-family: var(--font-num); font-size: 21px; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
/* Clamped rather than left to wrap freely — a long market name in a Biggest
   Win/Loss note (Amplifi's especially) could otherwise wrap to 3-4 lines and
   stretch every card in that grid row to match, which is exactly what made
   the Predictions grid look broken on a phone even before the column count
   was fixed above. Two lines is enough to identify the market; the full
   name is still in the card's own title attribute if needed. */
.stat-note {
  margin-top: 5px;
  font-size: 11.5px;
  color: var(--text-faint);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   Star chart calendar — the signature
   -------------------------------------------------------------------------- */
.calendar { margin: 0 0 14px; }
.calendar .plate { padding: 20px 22px 24px; }
.cal-head { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.cal-title { font-family: var(--font-display); font-size: 22px; line-height: 1; }
.cal-nav { display: flex; gap: 6px; margin-left: auto; align-items: center; }
.icon-btn {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-dim);
  transition: background 0.45s var(--ease), color 0.45s var(--ease), border-color 0.45s var(--ease);
}
.icon-btn:hover { background: rgba(255, 255, 255, 0.075); color: var(--text); border-color: var(--line-mid); }

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-dow {
  text-align: center;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding-bottom: 8px;
}

.day {
  --m: 0;
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  background: rgba(255, 255, 255, 0.014);
  overflow: hidden;
  display: grid;
  place-items: center;
  transition: border-color 0.45s var(--ease), transform 0.45s var(--ease);
}
.day.empty { background: none; }

/* The tint lives on a pseudo-element so its strength can be driven by --m
   through plain opacity, which is far better supported than an alpha channel
   computed inside a colour function. */
.day.has-trades::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: calc(0.07 + var(--m) * 0.16);
  transition: opacity 0.5s var(--ease);
}
.day.gain::before { background: var(--gain); }
.day.loss::before { background: var(--loss); }

.day.has-trades { cursor: pointer; }
.day.gain { border-color: rgba(123, 224, 178, 0.22); }
.day.loss { border-color: rgba(240, 115, 106, 0.22); }
.day.has-trades:hover { transform: translateY(-1px); }
.day.gain:hover { border-color: rgba(123, 224, 178, 0.5); }
.day.loss:hover { border-color: rgba(240, 115, 106, 0.5); }
.day.has-trades:hover::before { opacity: calc(0.12 + var(--m) * 0.2); }

.day.selected { border-color: var(--star); }
.day.selected::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--star);
  pointer-events: none;
}
.day.today .day-num { color: var(--star); }

.day-num {
  position: absolute;
  top: 7px; left: 9px;
  font-family: var(--font-num);
  font-size: 11px;
  color: var(--text-faint);
  z-index: 2;
}
.day.has-trades .day-num { color: var(--text-dim); }

.day-body {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 0 6px;
  text-align: center;
  min-width: 0;
}
.day-net {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: clamp(12px, 1.15vw, 15px);
  font-weight: 500;
  letter-spacing: -0.015em;
  white-space: nowrap;
}
.day-count {
  font-size: 10.5px;
  color: var(--text-dim);
  white-space: nowrap;
}

/* A day cell is ~42px on a phone: room for "+27", but not for "+A$27.01" and
   certainly not for a second line under it. Both switch at the same breakpoint —
   they were split before, which left the count overlapping the day number. */
.net-short { display: none; }
@media (max-width: 900px) {
  .net-full { display: none; }
  .net-short { display: inline; }
  .day-count { display: none; }
  .day-body { gap: 0; }
}

.cal-legend {
  display: flex; align-items: center; gap: 16px;
  margin-top: 18px; padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--text-faint);
}
.cal-legend .dot { width: 8px; height: 8px; border-radius: 999px; display: inline-block; margin-right: 6px; vertical-align: middle; }

/* --------------------------------------------------------------------------
   Filter bar
   -------------------------------------------------------------------------- */
.controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 22px 0 12px; }
.seg {
  display: flex;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--line);
}
.seg button {
  padding: 6px 13px;
  border-radius: 999px;
  font-size: 12.5px;
  color: var(--text-dim);
  white-space: nowrap;
  transition: background 0.45s var(--ease), color 0.45s var(--ease);
}
.seg button:hover { color: var(--text); }
.seg button.on { background: rgba(255, 255, 255, 0.09); color: var(--text); }

/* The filter overlay is much narrower than the full-width control rows this
   segmented control was originally built for — six buttons never fit one
   row at 460px, which is what pushed the leftmost ones off-screen requiring
   a horizontal scroll to reach them. Wrapping, plus a more modest corner
   radius that still looks right across one row or two (the full 999px pill
   shape only reads correctly as a single row). */
.filter-seg {
  flex-wrap: wrap;
  border-radius: var(--r-lg);
}
.filter-seg button { flex: 1 1 auto; text-align: center; }

.date-range { display: flex; align-items: center; gap: 8px; }
.date-range input[type="date"] {
  padding: 7px 11px;
  background: rgba(0, 0, 0, 0.32);
  border: 1px solid var(--line-mid);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--font-num);
  font-size: 12.5px;
  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   Ledger table
   -------------------------------------------------------------------------- */
.ledger .plate { padding: 0; }
.table-wrap { overflow-x: auto; }
table.ledger-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.ledger-table thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--panel);
  text-align: right;
  padding: 13px 14px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--text-faint);
  border-bottom: 1px solid var(--line-mid);
  white-space: nowrap;
}
.ledger-table thead th:first-child,
.ledger-table thead th.l { text-align: left; }
.ledger-table td {
  padding: 12px 14px;
  text-align: right;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  vertical-align: middle;
}
.ledger-table td.l { text-align: left; }
.ledger-table tbody tr { transition: background 0.35s var(--ease); }
.ledger-table tbody tr:hover { background: rgba(255, 255, 255, 0.028); }
.ledger-table tbody tr:last-child td { border-bottom: none; }
.market { max-width: 420px; overflow: hidden; text-overflow: ellipsis; }

.pill {
  display: inline-block;
  padding: 2.5px 9px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
}
.pill.yes { background: var(--gain-soft); color: var(--gain); }
.pill.no  { background: var(--loss-soft); color: var(--loss); }
/* A team or fighter name is not a direction, so it gets no directional colour. */
.pill.neutral { background: rgba(233, 236, 247, 0.07); color: var(--text-dim); }
.pill { max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; }
.tag { font-size: 11px; color: var(--text-faint); }
.tag.liq { color: var(--amber); }
.flag {
  display: inline-grid; place-items: center;
  width: 16px; height: 16px; border-radius: 999px;
  background: rgba(232, 192, 125, 0.16);
  color: var(--amber);
  font-size: 10px; font-weight: 700;
  margin-left: 6px;
}

.row-actions { opacity: 0; transition: opacity 0.3s var(--ease); }
tr:hover .row-actions, tr:focus-within .row-actions { opacity: 1; }

tfoot td {
  padding: 14px;
  border-top: 1px solid var(--line-mid);
  font-family: var(--font-num);
  font-size: 13.5px;
  text-align: right;
  background: rgba(255, 255, 255, 0.018);
}
tfoot td.l { text-align: left; font-family: var(--font-ui); color: var(--text-dim); }

.empty-state { padding: 64px 24px; text-align: center; color: var(--text-dim); }
.empty-state h3 { font-family: var(--font-display); font-size: 24px; font-weight: 400; margin: 0 0 8px; color: var(--text); }
.empty-state p { margin: 0 0 20px; font-size: 13.5px; }

/* --------------------------------------------------------------------------
   Scan overlay
   -------------------------------------------------------------------------- */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 18px;
  padding-top: calc(18px + env(safe-area-inset-top, 0px));
  padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  background: rgba(4, 5, 9, 0.78);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
  animation: fade 0.4s var(--ease-out);
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.overlay .shell { width: min(1100px, 100%); max-height: 88dvh; display: flex; }
.overlay .plate { display: flex; flex-direction: column; width: 100%; min-height: 0; }
.ov-head {
  display: flex; align-items: center; gap: 14px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--line);
}
.ov-title { font-family: var(--font-display); font-size: 22px; line-height: 1; }
.ov-body { padding: 22px 24px; overflow: auto; flex: 1 1 auto; min-height: 0; }
.ov-foot {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 24px;
  border-top: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.018);
}

.drop {
  border: 1px dashed var(--line-hi);
  border-radius: var(--r-lg);
  padding: 52px 24px;
  text-align: center;
  background: rgba(255, 255, 255, 0.016);
  transition: border-color 0.5s var(--ease), background 0.5s var(--ease);
}
.drop.over { border-color: var(--star); background: var(--star-soft); }
.drop h4 { margin: 14px 0 6px; font-size: 15px; font-weight: 500; }
.drop p { margin: 0; font-size: 12.5px; color: var(--text-faint); }
.preview { margin-top: 18px; border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--line); }
.preview img { display: block; width: 100%; }

.review-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.review-table th {
  text-align: right; padding: 9px 8px;
  font-size: 9.5px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-faint); font-weight: 500;
  border-bottom: 1px solid var(--line-mid);
}
.review-table th.l, .review-table td.l { text-align: left; }
.review-table td { padding: 7px 8px; text-align: right; border-bottom: 1px solid var(--line); }
.review-table input {
  width: 100%;
  padding: 6px 8px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-family: var(--font-num);
  font-size: 12.5px;
  text-align: right;
  transition: border-color 0.4s var(--ease);
}
.review-table input.l { text-align: left; font-family: var(--font-ui); }
.review-table input:focus { outline: none; border-color: var(--star); }
.review-table tr.flagged input { border-color: rgba(232, 192, 125, 0.4); }
.review-table tr.off { opacity: 0.35; }
input[type="checkbox"] { accent-color: var(--star); width: 15px; height: 15px; }

.notice {
  padding: 11px 14px;
  border-radius: var(--r-md);
  font-size: 12.5px;
  margin-bottom: 16px;
  border: 1px solid var(--line-mid);
  background: rgba(255, 255, 255, 0.03);
}
.notice.warn { border-color: rgba(232, 192, 125, 0.35); background: rgba(232, 192, 125, 0.07); color: #F2DDB6; }
.notice.bad { border-color: rgba(240, 115, 106, 0.35); background: rgba(240, 115, 106, 0.07); color: #FFC9C4; }

/* Working indicator — a slow pulse, not a spinner */
.pulse { display: inline-flex; gap: 5px; align-items: center; }
.pulse i {
  width: 5px; height: 5px; border-radius: 999px; background: var(--star);
  animation: breathe 1.5s var(--ease) infinite;
}
.pulse i:nth-child(2) { animation-delay: 0.18s; }
.pulse i:nth-child(3) { animation-delay: 0.36s; }
@keyframes breathe { 0%, 100% { opacity: 0.25; transform: scale(0.8); } 50% { opacity: 1; transform: scale(1); } }

/* Toast */
#toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  bottom: calc(28px + env(safe-area-inset-bottom, 0px));
  transform: translate(-50%, 20px);
  z-index: 200;
  padding: 11px 20px;
  border-radius: 999px;
  background: var(--raise-hi);
  border: 1px solid var(--line-mid);
  font-size: 13px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.55);
}
#toast.show { opacity: 1; transform: translate(-50%, 0); }

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 1080px) {
  .summary { grid-template-columns: 1fr; }
  .stats, .stats-3 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 720px) {
  :root { --gutter: 14px; }

  #app {
    padding-top: 2px;
    padding-bottom: 72px;
    padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
  }

  .topbar { padding-bottom: 16px; margin-bottom: -16px; }
  .topbar-inner { padding-top: 10px; padding-bottom: 4px; }
  .topbar .mark { font-size: 20px; }
  .hamburger { width: 38px; height: 38px; }

  .menu-scrim {
    align-items: flex-end;
    justify-content: center;
    /* iOS's status bar (clock, battery) sits in this space — the previous
       16px top padding let a tall menu grow right up underneath it, putting
       the close button under the status bar and making it untappable. A
       custom property rather than repeating this expression in both rules
       below, so the scrim's padding and the panel's max-height can never
       drift out of sync with each other. */
    --menu-top-clear: max(10vh, calc(env(safe-area-inset-top, 0px) + 16px));
    padding: 16px 12px;
    padding-top: var(--menu-top-clear);
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  }
  .menu-panel {
    width: 100%;
    animation: sheet-in 0.42s var(--ease-out);
    /* The scrim's own padding is completely different here (a bottom sheet,
       not a top-right dropdown), so the desktop max-height calc above
       doesn't apply — recomputed against this breakpoint's actual padding
       (--menu-top-clear top, 16px + safe-area bottom) rather than the
       desktop one. */
    max-height: calc(100vh - var(--menu-top-clear) - 16px - env(safe-area-inset-bottom, 0px));
    max-height: calc(100dvh - var(--menu-top-clear) - 16px - env(safe-area-inset-bottom, 0px));
  }
  @keyframes sheet-in {
    from { opacity: 0; transform: translateY(16px); }
    to { opacity: 1; transform: none; }
  }
  .pager { padding: 14px 12px; gap: 10px; }
  .pager-info { min-width: 0; font-size: 11.5px; }

  .summary { gap: 10px; margin-bottom: 10px; }
  .headline .plate { padding: 18px 18px 16px; }
  .big-aud { font-size: clamp(34px, 11vw, 46px); }
  .stats, .stats-3 { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .stat .plate { padding: 14px; }
  .stat-value { font-size: 18px; }

  .calendar .plate { padding: 16px 14px 18px; }
  .cal-head { gap: 10px; flex-wrap: wrap; }
  .cal-title { font-size: 19px; }
  .cal-grid { gap: 4px; }
  .day-num { top: 4px; left: 5px; font-size: 9.5px; }
    .cal-legend { flex-wrap: wrap; gap: 10px; font-size: 11px; }

  /* Six range presets can't wrap into a pill, so let the pill scroll instead. */
  .controls { gap: 8px; margin: 16px 0 10px; }
  .seg {
    flex: 1 1 100%;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .seg::-webkit-scrollbar { display: none; }
  .seg button { padding: 6px 12px; font-size: 12px; }
  .mini-select { flex: 1 1 auto; max-width: none; }

  .ledger-table { font-size: 12.5px; }
  .ledger-table td, .ledger-table thead th { padding: 10px 11px; }
  .market { max-width: 190px; }
  .acct { max-width: 96px; font-size: 10px; }
  /* Row actions are hover-revealed on desktop; there is no hover on a phone. */
  .row-actions { opacity: 1; }

  .ov-head, .ov-foot { padding: 14px 16px; }
  .ov-body { padding: 16px; }
  .ov-title { font-size: 19px; }
  .drop { padding: 34px 16px; }
  .account-picker { padding: 10px 12px; }
  .account-form input { flex-basis: 100%; }
  .gate-card .plate { padding: 28px 22px 26px; }
}

@media (max-width: 400px) {
  .topbar .btn { padding: 7px 11px; font-size: 12px; }
  .market { max-width: 140px; }
  .stats, .stats-3 { grid-template-columns: 1fr 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* --------------------------------------------------------------------------
   Print / Save as PDF — this is the accountant's view
   -------------------------------------------------------------------------- */
.print-only { display: none; }

@media print {
  /* A4 landscape at 15mm gives 267mm of usable width. Widened back up from an
     earlier 8mm — that was tuned purely for table width and didn't leave room
     for the browser's own print header/footer (page title top-left, date
     top-right), which sits inside this margin and was crowding straight into
     "Predictions ledger" underneath it. That header/footer is the browser's
     own "Headers and footers" print option, not something this stylesheet
     renders — it can't be suppressed from CSS, only from the print dialog
     itself, so the fix here is giving it enough room instead. */
  @page { size: A4 landscape; margin: 15mm; }

  /* Both elements need clearing: with a background on <html>, the canvas paints
     from <html>, and <body>'s fill only covers the body box — which is what left
     a black slab under the table. Sizes reduced again on top of two earlier
     passes — the figures use a monospace font (Geist Mono) for column
     alignment, which is meaningfully wider per character than the proportional
     body font at the same point size, and the widths below hadn't accounted
     for that, which is what was truncating even short headers like "Closed". */
  html, body {
    background: #fff !important;
    color: #000;
    font-size: 7.5pt;
    overflow: visible !important;
  }
  html::before, body::before, .topbar::before { display: none !important; }

  .topbar, .calendar, .chart-wrap, .controls, .overlay, .menu-scrim, .pager, #toast,
  .summary, .row-actions, .no-print { display: none !important; }

  /* A scroll container clips rather than reflows on paper, which is what cut the
     right-hand columns off mid-word. */
  .table-wrap, .review-wrap, .ov-body { overflow: visible !important; }

  #app { max-width: none; padding: 0; }
  .shell { border: none; padding: 0; background: none; }
  .plate { background: #fff; box-shadow: none; border-radius: 0; overflow: visible; }

  .print-only { display: block; margin-bottom: 7pt; }
  .print-only h1 { font-family: var(--font-display); font-size: 13pt; margin: 0 0 2pt; color: #000; }
  .print-only .meta { font-size: 6pt; color: #444; }

  /* Fixed layout is what stops a long market name from stealing width from the
     figures. Percentages total 100 across the ten real columns — kept
     identical to the previous pass, which your own test PDF confirmed works
     correctly (full market names, only the longest outliers truncating, as
     expected). Only the font sizes below change here, not the column
     proportions — a bigger font needs more width per character, not less, so
     redesigning the widths downward while also sizing up would have made the
     truncation worse, not better.

     font-size is forced with !important on every level below — table, td, th,
     .num — rather than left to inherit down from .ledger-table alone. An
     earlier pass set it only on .ledger-table itself and the header row
     correctly shrank while the body rows stayed at their on-screen size; rather
     than chase the exact cascade reason blind without being able to render this
     myself, forcing it explicitly everywhere removes the ambiguity entirely. */
  .ledger-table {
    table-layout: fixed;
    width: 100%;
    font-size: 5.2pt !important;
    border-collapse: collapse;
  }

  /* `table-layout: fixed` allocates the widths but does not enforce them —
     without this, a cell's contents simply overflow into its neighbour, which is
     what put the timestamp on top of the market name. Truncation is the right
     trade on paper: the CSV carries the untruncated values. */
  .ledger-table th,
  .ledger-table td {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 5.2pt !important;
  }
  .ledger-table th:nth-child(1), .ledger-table td:nth-child(1)  { width: 11%; } /* closed    */
  .ledger-table th:nth-child(2), .ledger-table td:nth-child(2)  { width: 28%; } /* event     */
  .ledger-table th:nth-child(3), .ledger-table td:nth-child(3)  { width: 13%; } /* selection */
  .ledger-table th:nth-child(4), .ledger-table td:nth-child(4)  { width: 8%;  } /* account   */
  .ledger-table th:nth-child(5), .ledger-table td:nth-child(5)  { width: 7%;  } /* stake     */
  .ledger-table th:nth-child(6), .ledger-table td:nth-child(6)  { width: 9%;  } /* odds      */
  .ledger-table th:nth-child(7), .ledger-table td:nth-child(7)  { width: 8%;  } /* pnl       */
  .ledger-table th:nth-child(8), .ledger-table td:nth-child(8)  { width: 8%;  } /* rate      */
  .ledger-table th:nth-child(9), .ledger-table td:nth-child(9)  { width: 8%;  } /* pnl aud   */
  .ledger-table th:nth-child(10),.ledger-table td:nth-child(10) { display: none; } /* actions */

  /* Daily's own summary table has a genuinely different shape (five columns,
     no hidden actions column) — its own width rules rather than trying to
     force it through the ten-column layout above. */
  .daily-print-table th:nth-child(1), .daily-print-table td:nth-child(1) { width: 24%; }
  .daily-print-table th:nth-child(n+2), .daily-print-table td:nth-child(n+2) { width: 19%; }

  /* Figures are set slightly smaller than the surrounding table font to
     offset the monospace face's wider average character — without this the
     .num spans were the actual widest content in several columns even
     though they visually "look" short (a handful of digits and a symbol). */
  .ledger-table .num { font-size: 4.7pt !important; }

  .ledger-table thead th {
    position: static;
    background: #fff;
    color: #000;
    font-size: 4.9pt !important;
    letter-spacing: 0.02em;
    padding: 2.5pt 1.6pt;
    border-bottom: 0.8pt solid #000;
  }
  .ledger-table td {
    padding: 2.5pt 2pt;
    border-bottom: 0.4pt solid #d4d4d4;
    color: #000;
    vertical-align: middle;
  }
  .ledger-table tbody tr:hover { background: none; }

  /* Repeat the header on every sheet and never split a trade across a page. */
  thead { display: table-header-group; }
  tfoot { display: table-footer-group; }
  tr { break-inside: avoid; page-break-inside: avoid; }

  /* Market names are given the widest column and truncate with an ellipsis if
     they still exceed it. Wrapping was worse: a two-line cell threw the whole
     row's vertical rhythm out for one long name. */
  .market { max-width: none; }

  /* Badges are screen affordances. The CSV carries review and edit provenance. */
  .flag, .acct::before { display: none !important; }
  .pill {
    border: none;
    color: #000 !important;
    background: none !important;
    padding: 0;
    font-size: 5.2pt !important;
    letter-spacing: 0.02em;
  }
  .acct {
    border: none;
    padding: 0;
    max-width: none;
    color: #333 !important;
    font-size: 5.2pt !important;
  }

  .pos { color: #0A6B3D !important; }
  .neg { color: #A32017 !important; }
  .muted, .faint { color: #555 !important; }
  .tag.liq { color: #8a5a00 !important; }

  tfoot td {
    background: none;
    border-top: 0.8pt solid #000;
    border-bottom: none;
    color: #000;
    font-size: 6.2pt !important;
    padding: 3pt 2pt;
  }

  /* Keep the green and red in the printed copy rather than letting the browser
     strip them for ink. */
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* Cross-source overlap marker — the same event arriving from two ingestion paths */
.flag.dupe {
  background: rgba(201, 216, 255, 0.16);
  color: var(--star);
  font-size: 11px;
}

/* --------------------------------------------------------------------------
   Accounts — attribution is a first-class part of a tax record, so account
   identity reads as a quiet label rather than a coloured status chip.
   -------------------------------------------------------------------------- */
.mini-select {
  padding: 7px 11px;
  background: rgba(0, 0, 0, 0.32);
  border: 1px solid var(--line-mid);
  border-radius: 999px;
  color: var(--text);
  font-size: 12.5px;
  color-scheme: dark;
  max-width: 200px;
}
.mini-select:focus { outline: none; border-color: var(--star); }

.acct {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 10.5px;
  letter-spacing: 0.03em;
  border: 1px solid var(--line-mid);
  color: var(--text-dim);
  white-space: nowrap;
  max-width: 170px;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}

/* "Add from account" header on the scan overlay */
.account-picker {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 12px 14px;
  margin-bottom: 16px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.028);
  border: 1px solid var(--line);
}
.account-picker label { font-size: 12.5px; color: var(--text-dim); white-space: nowrap; }
.account-picker select {
  flex: 1 1 200px;
  padding: 8px 11px;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--line-mid);
  border-radius: var(--r-sm);
  color: var(--text);
  font-size: 13px;
  color-scheme: dark;
}
.account-picker select:focus { outline: none; border-color: var(--star); }
.account-picker .faint { font-size: 11.5px; }

/* Settings panel */
.account-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 4px;
  border-bottom: 1px solid var(--line);
}
.account-row:last-child { border-bottom: none; }
.account-meta { flex: 1 1 auto; min-width: 0; }
.account-label { font-size: 13.5px; }
.account-row .icon-btn { opacity: 0; transition: opacity 0.3s var(--ease); }
.account-row:hover .icon-btn, .account-row:focus-within .icon-btn { opacity: 1; }

.empty-accounts { padding: 26px 4px; text-align: center; font-size: 13px; }

.add-account {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--line-mid);
}
/* Same divider treatment as .add-account above, applied to the two
   .accounts-section blocks that follow one — Sources and Quick currency
   view — so every section boundary in Settings gets a line, not just the
   ones that happen to be a form. The very first section (Accounts, right
   under the notice) deliberately doesn't get this class — nothing needs
   separating from the notice above it. */
.settings-divider {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--line-mid);
}
.account-form { display: flex; gap: 8px; flex-wrap: wrap; }
.account-form input, .account-form select,
#filter-account, #filter-source {
  padding: 9px 12px;
  background: rgba(0, 0, 0, 0.32);
  border: 1px solid var(--line-mid);
  border-radius: var(--r-sm);
  color: var(--text);
  font-size: 13px;
  color-scheme: dark;
  line-height: 1.3;
  box-sizing: border-box;
}
/* A native <select> carries its own chrome around the dropdown arrow, which
   made it render a few pixels taller than a text input given the identical
   padding above — appearance:none strips that chrome so both controls
   share exactly the same box, with a hand-drawn arrow standing in for it. */
.account-form select, #filter-account, #filter-source {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238A90A8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 13px;
  padding-right: 32px;
}
/* Wrapping the inputs in a .field-group made this rule harmful: the group is a
   COLUMN flex container, so a flex-basis of 200px sizes the input's height, not
   its width — which is what produced a 200px-tall name box. The group carries
   the width now; the control just fills it. */
.account-form .field-group > input,
.account-form .field-group > select { flex: 0 0 auto; width: 100%; }
.account-form input:focus, .account-form select:focus { outline: none; border-color: var(--star); }

@media (max-width: 720px) {
  .mini-select { max-width: 140px; }
  .acct { max-width: 90px; }
}

/* --------------------------------------------------------------------------
   Edit overlay — the ledger is correctable by hand, and the form should feel
   like a form, not a dialogue box bolted onto a table.
   -------------------------------------------------------------------------- */
.edit-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
/* --------------------------------------------------------------------------
   Hedge calculator — a live "what should I stake on the other side" tool.
   Two symmetric panels either side of a "vs" divider; the right panel's
   stake is the answer, not an input, so it gets the same emphasis treatment
   as a PnL field rather than a plain input. -------------------------------------------------------------------------- */
.hedge-panels {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.hedge-panel { min-width: 0; }
.hedge-panel-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-faint);
  margin-bottom: 10px;
}
.hedge-fields { display: flex; gap: 10px; }
.hedge-fields .edit-field { flex: 1 1 0; min-width: 0; }
.hedge-fields .edit-field:first-child { flex: 1.3 1 0; }

/* A small marker between the two full-width rows, not a column divider. */
.hedge-vs {
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border-radius: 999px;
  border: 1px solid var(--line-mid);
  background: var(--panel);
  font-family: var(--font-display);
  font-size: 11px;
  color: var(--text-faint);
  align-self: center;
  margin: -10px 0;
  position: relative;
  z-index: 1;
}

/* Read-only — the suggested stake is computed, never typed. Sized to match
   the plain inputs beside it exactly (same padding, near-same font-size) so
   the whole row reads as one height; a full .num-lg-style emphasis here
   made this box visibly taller than its neighbours and, in a narrower
   layout, clipped anything past four digits. */
.hedge-output {
  padding: 10px 12px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(201, 216, 255, 0.3);
  background: var(--star-soft);
  color: var(--star);
  font-family: var(--font-num);
  font-size: 15px;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hedge-output.empty { color: var(--text-faint); font-size: 13px; }

.hedge-results {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.hedge-outcomes { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 20px; }
.hedge-outcome { padding: 14px 16px; border-radius: var(--r-md); background: rgba(255, 255, 255, 0.025); border: 1px solid var(--line); }
.hedge-outcome-value {
  display: block;
  margin-top: 6px;
  font-family: var(--font-num);
  font-size: 19px;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

.hedge-edge-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 10px; }
.hedge-edge-pct {
  font-family: var(--font-num);
  font-size: 26px;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  transition: color 0.4s var(--ease);
}
.hedge-bar {
  height: 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.34);
  border: 1px solid var(--line-mid);
  overflow: hidden;
}
.hedge-bar-fill {
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: var(--gain);
  transition: width 0.6s var(--ease-out), background 0.6s var(--ease-out);
}
.hedge-edge-caption { margin-top: 10px; font-size: 12px; color: var(--text-dim); line-height: 1.5; }

/* The rare case: the odds disagree enough that both outcomes profit. */
.hedge-edge.arb .hedge-edge-pct { color: var(--star); }
.hedge-edge.arb .hedge-bar-fill { background: var(--star); box-shadow: 0 0 12px var(--star-soft); }
.hedge-edge.arb .hedge-edge-caption { color: var(--star); }


.edit-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.edit-field.span2 { grid-column: 1 / -1; }
.edit-field > span {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--text-faint);
}
.edit-field > span em { font-style: normal; text-transform: none; letter-spacing: 0; }
.edit-field input, .edit-field select {
  padding: 10px 12px;
  background: rgba(0, 0, 0, 0.34);
  border: 1px solid var(--line-mid);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--font-num);
  font-size: 13px;
  color-scheme: dark;
  width: 100%;
  transition: border-color 0.4s var(--ease);
}
.edit-field #e-market, .edit-field select, .edit-field #e-status { font-family: var(--font-ui); }
.edit-field input:focus, .edit-field select:focus { outline: none; border-color: var(--star); }

/* The net PnL is the figure that gets filed — it earns visual weight. */
.edit-field.emphasis > span { color: var(--star); }
.edit-field input.num-lg {
  font-size: 19px;
  padding: 12px;
  letter-spacing: -0.01em;
  border-color: rgba(201, 216, 255, 0.3);
  background: rgba(201, 216, 255, 0.05);
}

.flag.edited { background: rgba(233, 236, 247, 0.1); color: var(--text-dim); }
.flag.typed { background: rgba(201, 216, 255, 0.14); color: var(--star); font-size: 9px; }

@media (max-width: 620px) {
  .edit-grid { grid-template-columns: 1fr; }
  .edit-field.span2 { grid-column: 1; }
  .hedge-outcomes { grid-template-columns: 1fr; }
}

@media (max-width: 420px) {
  .hedge-fields { flex-direction: column; }
}

/* A long toast must not push the viewport wider than the screen. */
#toast {
  max-width: min(90vw, 420px);
  text-align: center;
}

/* Live fee/PnL derivation under the editor's PnL field. */
.derivation {
  font-family: var(--font-num);
  font-size: 12px;
  line-height: 1.5;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, 0.026);
  border: 1px solid var(--line);
  min-height: 20px;
}
.derivation .warn-text { color: var(--amber); }

/* --------------------------------------------------------------------------
   Review table: the panel must never be widened by its own contents.
   A flex/grid child defaults to min-width:auto, which lets a wide table push
   the whole dialog past the viewport — hence min-width:0 on every ancestor.
   -------------------------------------------------------------------------- */
.overlay .shell { max-width: 100%; min-width: 0; }
.overlay .plate { min-width: 0; }
.ov-body { min-width: 0; }

.review-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  overscroll-behavior-x: contain;
}
.review-wrap .review-table { min-width: 720px; }
.scroll-hint { display: none; font-size: 11px; margin-top: 10px; text-align: center; }

@media (max-width: 720px) {
  .overlay {
    padding: 10px;
    padding-top: calc(10px + env(safe-area-inset-top, 0px));
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    align-items: flex-end;
  }
  .overlay .shell { width: 100%; max-height: 92dvh; padding: 4px; }
  .scroll-hint { display: block; }

  /* Let the table bleed to the panel edges so the scrollable area is obvious. */
  .review-wrap {
    margin-left: -16px;
    margin-right: -16px;
    padding-left: 16px;
    padding-right: 16px;
  }
  .review-table th, .review-table td { padding: 8px 6px; }
  .review-table input { font-size: 16px; }  /* 16px stops iOS zooming on focus */

  .ov-foot { flex-wrap: wrap; gap: 8px; }
  .ov-foot .btn { flex: 1 1 auto; justify-content: center; }
  #ov-status { flex: 1 1 100%; }
}

/* Reasons a scanned row was flagged, listed rather than summarised. */
.reason-list { margin: 8px 0 0; padding-left: 18px; }
.reason-list li { margin-bottom: 4px; line-height: 1.45; }
.reason-list li:last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Accounts: an account is a name plus the layout its screenshots use, so both
   fields carry a visible label rather than relying on a placeholder.
   -------------------------------------------------------------------------- */
.field-group { display: flex; flex-direction: column; gap: 5px; flex: 1 1 200px; min-width: 0; }
.field-label {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-faint);
}
.account-form { align-items: flex-end; }
.account-form .btn { margin-bottom: 1px; }

/* Per-row layout selector in the account list. */
.row-scanner { display: flex; flex-direction: column; gap: 4px; flex: 0 0 auto; }
.row-scanner select {
  padding: 6px 10px;
  background: rgba(0, 0, 0, 0.32);
  border: 1px solid var(--line-mid);
  border-radius: var(--r-sm);
  color: var(--text);
  font-size: 12.5px;
  color-scheme: dark;
  max-width: 150px;
}
.row-scanner select:focus { outline: none; border-color: var(--star); }
/* An account with no layout will fall back to the default and may misread
   every row, so it is worth an amber edge rather than a silent gap. */
.row-scanner select.unset { border-color: rgba(232, 192, 125, 0.55); color: var(--amber); }

.warn-text { color: var(--amber); }

@media (max-width: 720px) {
  .account-row { flex-wrap: wrap; }
  .account-meta { flex: 1 1 100%; }
  .row-scanner { flex: 1 1 auto; }
  .row-scanner select { max-width: none; width: 100%; }
  .account-form { gap: 10px; }
  .field-group { flex-basis: 100%; }
  .account-form .btn { width: 100%; justify-content: center; }
}

/* Single account list in the Accounts overlay — kept as its own block for the
   spacing rule below, even though there's now only ever one of it. */
.accounts-section { margin-bottom: 18px; }
.accounts-section > .eyebrow { margin-bottom: 8px; }
.accounts-section:last-of-type { margin-bottom: 0; }

/* A cost nets against results but is not a bet. */
.flag.cost { background: rgba(232, 192, 125, 0.16); color: var(--amber); font-size: 10px; }

/* --------------------------------------------------------------------------
   Settled / Pending toggle — reuses the .seg pill from the date-range filter
   so a second segmented control doesn't invent a second visual language.
   -------------------------------------------------------------------------- */
.view-toggle { margin: 10px 0 16px; display: inline-flex; }
.view-toggle button { min-width: 76px; text-align: center; }

/* --------------------------------------------------------------------------
   Quick currency view — three read-only conversion bubbles, no heading.
   Deliberately quieter than the .pill labels used for bet outcomes, so it
   reads as ambient reference information rather than another data column.
   -------------------------------------------------------------------------- */
.qfx-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 18px; min-height: 28px; }
.qfx-bubble {
  display: inline-flex;
  align-items: center;
  padding: 6px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line-mid);
  font-family: var(--font-num);
  font-size: 12.5px;
  color: var(--text-dim);
  white-space: nowrap;
}
.qfx-bubble.qfx-base {
  color: var(--star);
  border-color: rgba(201, 216, 255, 0.35);
  background: var(--star-soft);
}

/* --------------------------------------------------------------------------
   Pending page — a bet still waiting on a result. A card list rather than a
   table: each row carries four outcome buttons plus edit/delete, and a table
   cell with five actions in it is exactly what forces sideways scrolling on
   a phone. This mirrors .account-row's card rhythm instead.
   -------------------------------------------------------------------------- */
#pending-rows { padding: 4px 4px; }
.pending-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 16px 18px;
  border-bottom: 1px solid var(--line);
}
.pending-row:last-child { border-bottom: none; }
.pending-main { flex: 1 1 260px; min-width: 0; }
.pending-market {
  font-size: 13.5px;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Full selection text, on its own line — not a truncated pill. This is the
   line that says what side of the market the exposure is actually on, so
   cutting it off with an ellipsis defeats the point of showing it at all. */
.pending-selection {
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.4;
  margin-bottom: 8px;
}
.pending-sub { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; font-size: 11.5px; }
.pending-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; flex: 0 0 auto; }

.outcome-btn {
  padding: 7px 13px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 500;
  border: 1px solid var(--line-mid);
  background: rgba(255, 255, 255, 0.035);
  color: var(--text-dim);
  white-space: nowrap;
  transition: background 0.35s var(--ease), border-color 0.35s var(--ease), color 0.35s var(--ease);
}
.outcome-btn:hover { color: var(--text); background: rgba(255, 255, 255, 0.07); }
.outcome-btn.won:hover { background: var(--gain-soft); border-color: rgba(123, 224, 178, 0.4); color: var(--gain); }
.outcome-btn.lost:hover { background: var(--loss-soft); border-color: rgba(240, 115, 106, 0.4); color: var(--loss); }
.outcome-btn.void:hover { background: rgba(233, 236, 247, 0.09); border-color: var(--line-hi); color: var(--text); }
.outcome-btn.cashout:hover { background: var(--star-soft); border-color: rgba(201, 216, 255, 0.4); color: var(--star); }

.pending-actions .row-actions { display: inline-flex; gap: 2px; opacity: 1; }

@media (max-width: 720px) {
  .pending-row { padding: 14px 14px; gap: 10px; }
  .pending-actions { flex: 1 1 100%; justify-content: flex-start; }
  .pending-market { max-width: none; white-space: normal; }
}

@media (max-width: 420px) {
  .outcome-btn { flex: 1 1 calc(50% - 6px); text-align: center; }
}

/* --------------------------------------------------------------------------
   Source pills and the coloured left edge they drive on a bet card, a
   pending row, or an event-group line. Deliberately reuses --star and
   --amber rather than inventing new hues — Hedge already means "this app's
   blue accent" everywhere else (the hedge calculator's own arb state), so a
   Hedge-sourced bet picking up the same colour is a callback, not a new
   colour introduced just for this. Tipster gets the quietest possible
   treatment: present, but never competing with the money figures.
   -------------------------------------------------------------------------- */
.src-pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 10.5px;
  letter-spacing: 0.03em;
  white-space: nowrap;
  vertical-align: middle;
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.src-pill.src-hedge { background: var(--star-soft); color: var(--star); }
.src-pill.src-other { background: rgba(232, 192, 125, 0.14); color: var(--amber); }
.src-pill.src-tipster { background: rgba(233, 236, 247, 0.07); color: var(--text-dim); }
.src-pill.src-strategy { background: var(--violet-soft); color: var(--violet); }

.bet-card, .pending-row, .event-bet { border-left: 3px solid transparent; }
.bet-card.src-hedge, .pending-row.src-hedge, .event-bet.src-hedge { border-left-color: var(--star); }
.bet-card.src-other, .pending-row.src-other, .event-bet.src-other { border-left-color: var(--amber); }
.bet-card.src-tipster, .pending-row.src-tipster, .event-bet.src-tipster { border-left-color: var(--line-hi); }
.bet-card.src-strategy, .pending-row.src-strategy, .event-bet.src-strategy { border-left-color: var(--violet); }

/* --------------------------------------------------------------------------
   Settled bet cards — the on-screen counterpart to the print-only table.
   Each card is a row's worth of information laid out for scanning rather
   than for column alignment: title + meta on the left, the five money
   figures as small labelled stats, actions on the right. The print table
   (in a .print-only wrapper elsewhere in this file) is untouched by any of
   this — it has its own column-width rules and stays exactly as tuned.
   -------------------------------------------------------------------------- */
#bet-cards { padding: 4px 4px; }
.bet-card {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  padding: 15px 18px;
  border-bottom: 1px solid var(--line);
  transition: background 0.35s var(--ease);
}
.bet-card:last-child { border-bottom: none; }
.bet-card:hover { background: rgba(255, 255, 255, 0.022); }

.bet-card-main { flex: 1 1 220px; min-width: 0; }
.bet-card-title {
  font-size: 13.5px;
  margin-bottom: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Full selection text, on its own line — see the identical note on
   .pending-selection above. This is the whole reason this card layout
   exists over a table: room to actually show what the exposure is on. */
.bet-card-selection {
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.4;
  margin-bottom: 7px;
}
.bet-card-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 11.5px; }

.bet-card-figures { display: flex; align-items: flex-end; gap: 18px; flex: 0 0 auto; }
.bcf { display: flex; flex-direction: column; gap: 4px; min-width: 54px; }
.bcf .eyebrow { font-size: 9px; }
.bcf .num { font-size: 12.5px; }
.bcf.emphasis .num { font-size: 14px; font-weight: 600; }

.bet-card-actions { flex: 0 0 auto; opacity: 0; transition: opacity 0.3s var(--ease); }
.bet-card:hover .bet-card-actions, .bet-card:focus-within .bet-card-actions { opacity: 1; }

.cards-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-top: 1px solid var(--line-mid);
  background: rgba(255, 255, 255, 0.018);
  font-family: var(--font-num);
  font-size: 13px;
}

@media (max-width: 900px) {
  .bet-card-figures { flex: 1 1 100%; justify-content: space-between; gap: 10px; }
}
@media (max-width: 720px) {
  .bet-card { padding: 13px 14px; gap: 10px; }
  .bet-card-title { white-space: normal; }
  .bet-card-actions { opacity: 1; }
  .bcf { min-width: 44px; }
}

/* --------------------------------------------------------------------------
   Active events — a fixture with more than one pending bet on it, so the
   real cross-account exposure is visible at a glance rather than scattered
   across separate rows in the flat list below.
   -------------------------------------------------------------------------- */
.events .plate { padding: 20px 22px 20px; }
.events-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 4px; }

.event-card {
  padding: 14px 16px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--line);
  margin-top: 12px;
}
.event-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.event-title { font-size: 14px; font-weight: 500; }
.event-total { font-size: 11.5px; color: var(--text-dim); font-family: var(--font-num); white-space: nowrap; }

/* Wraps each group's individual .pending-row bets — was .event-bets holding
   a simpler read-only .event-bet summary row, before every pending bet
   became a fully-interactive group member with its own settle buttons
   rather than a supplementary cross-account exposure note. */
.pending-group-body { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.pending-group-body .pending-row {
  border-bottom: none;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, 0.022);
}

@media (max-width: 720px) {
  .event-card { padding: 12px 14px; }
}

/* --------------------------------------------------------------------------
   Sports / Predictions / Daily — the top-level mode switcher, one level
   above the Settled/Pending toggle. Slightly more prominent than that
   sub-toggle since it's the outermost piece of navigation in the app.
   -------------------------------------------------------------------------- */
.mode-toggle { margin: 4px 0 14px; display: inline-flex; }
.mode-toggle button { min-width: 92px; text-align: center; font-weight: 500; }

/* A Sports vs Predictions badge next to an account's name in Settings —
   quiet by default, since most operators will mostly see one or the other
   and don't need it shouting. */
.mode-tag {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  vertical-align: middle;
  margin-left: 6px;
  background: rgba(233, 236, 247, 0.07);
  color: var(--text-dim);
}
.mode-tag.predictions { background: var(--star-soft); color: var(--star); }
.mode-tag.trading { background: rgba(123, 224, 178, 0.14); color: var(--gain); }

/* A row whose PnL could not be read. Blocked from saving, never zeroed —
   only relevant to a Predictions scan, since a position is only ever filed
   once it has closed and PnL is the one figure that can't be guessed at. */
.review-table tr.blank input[data-f="pnl"] {
  border-color: var(--loss);
  background: rgba(240, 115, 106, 0.08);
}
.review-table tr.blank input::placeholder { color: var(--loss); opacity: 0.7; }

/* A position that didn't simply close on its own terms — liquidated rather
   than exited. Red rather than the amber used for "needs a look": this is a
   fact about the position, not a data-quality warning. */
.flag.liq { background: rgba(240, 115, 106, 0.16); color: var(--loss); }

/* --------------------------------------------------------------------------
   PnL curve — a hand-rolled inline SVG line chart, no library. Sits above
   the calendar on every tab that has one (Sports Settled, Predictions,
   Daily), sharing the same .chart-wrap shell so it reads as one family of
   surface rather than three different treatments.
   -------------------------------------------------------------------------- */
.chart-wrap { margin: 0 0 14px; }
.chart-wrap .plate { padding: 20px 22px 18px; }
.chart-empty { padding: 30px 0; text-align: center; font-size: 12.5px; }

.pnl-chart-svg { width: 100%; height: 160px; display: block; overflow: visible; }
.pnl-chart-zero { stroke: var(--line-mid); stroke-width: 1; stroke-dasharray: 3 4; vector-effect: non-scaling-stroke; }

.pnl-chart-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
  font-family: var(--font-num);
  font-size: 11.5px;
  flex-wrap: wrap;
}
.pnl-chart-range { flex: 1 1 auto; text-align: center; order: 3; }

@media (max-width: 560px) {
  .pnl-chart-svg { height: 120px; }
  .pnl-chart-range { display: none; }
}

/* --------------------------------------------------------------------------
   Daily — the combined Sports + Predictions + Trading ledger, day-level
   only. A grid of five columns (reusing the ledger-table's own alignment
   conventions — .l for the leading text column, everything else
   right-aligned numerals) rather than a table, since there is exactly one
   kind of row here and a full <table> would be structure with nothing to
   structure.
   -------------------------------------------------------------------------- */
.symbol-table-head,
.symbol-row {
  display: grid;
  grid-template-columns: 1.3fr 1fr 1fr 1fr;
  gap: 10px;
  align-items: center;
  padding: 12px 18px;
}
.symbol-table-head {
  font-size: 10.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-faint);
  border-bottom: 1px solid var(--line-mid);
}
.symbol-table-head span:not(.l),
.symbol-row span:not(.l) { text-align: right; font-family: var(--font-num); }

.symbol-row {
  border-bottom: 1px solid var(--line);
  font-size: 13px;
}
.symbol-row:last-child { border-bottom: none; }

/* Full label/date/precision vs condensed — same .full/.short swap pattern
   the calendar day cells already use, just under this table's own class
   names so the two responsive concerns don't couple to the same breakpoint
   by accident. Total is never wrapped in this pair — it stays full
   precision at every width, per an explicit decision to round only the
   three per-ledger columns. */
.dl-short { display: none; }

@media (max-width: 640px) {
  .daily-list-head, .daily-row { grid-template-columns: 1fr 0.8fr 0.8fr 0.8fr 0.9fr; padding: 10px 12px; font-size: 12px; gap: 4px; }
  .dl-full { display: none; }
  .dl-short { display: inline; }
}

/* --------------------------------------------------------------------------
   Shared filter overlay — replaces what used to be a full range-seg +
   account-select row inline on every tab. The button stays lighter than a
   primary action (Scan, Add, Import) since it's a secondary, always-present
   utility control, not something the row wants to draw the eye to first.
   -------------------------------------------------------------------------- */
.filter-btn {
  padding: 8px 14px;
  background: rgba(255, 255, 255, 0.03);
  font-size: 12.5px;
}
.filter-btn svg { flex: 0 0 auto; }
.filter-summary { font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

@media (max-width: 560px) {
  .filter-summary { max-width: 40vw; }
}

/* --------------------------------------------------------------------------
   Bonus bet — the checkbox sits in the same grid cell an input would, sized
   to roughly match one so it doesn't look like a stray control dropped into
   the form. The placeholder span above it (a non-breaking space) exists only
   so this field's layout matches every sibling field's label-then-control
   shape, keeping the grid's vertical rhythm even though there's no real
   label text to show here.
   -------------------------------------------------------------------------- */
.check-inline {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 12px;
  border: 1px solid transparent;
}
.check-inline span {
  font-size: 13px;
  color: var(--text);
  text-transform: none;
  letter-spacing: normal;
}

/* Same amber used for "needs review" elsewhere — a bonus bet isn't a
   problem, but it is a fact worth the same level of visual attention as
   one, since it changes what a loss actually costs. */
.flag.bonus { background: rgba(232, 192, 125, 0.16); color: var(--amber); font-size: 9px; }
