/* Dumb Degens — design system.
 *
 * Hand-written rather than Tailwind: this is eight templates with a small
 * component vocabulary, and a utility framework would mean adding a Node
 * build step to a Python-only deploy for no gain the tokens below do not
 * already give.
 *
 * Dark-first, because this gets used on a phone on a Sunday evening. The
 * light theme is a real theme, not an afterthought.
 */

/* ---------------------------------------------------------------- tokens */

:root {
  color-scheme: dark light;

  /* Neutrals are cool-tinted rather than pure grey — pure grey on a screen
     reads as cheap, and a slight blue cast makes the accent sing. */
  --ink-0: #f7f9fc;
  --ink-1: #c8d0dc;
  --ink-2: #8b96a8;
  --ink-3: #5d6779;

  --bg-0: #0a0d13;
  --bg-1: #10141c;
  --bg-2: #161b25;
  --bg-3: #1d2431;
  --line: #232b39;
  --line-soft: #1a212c;

  --accent: #00e08a;
  --accent-ink: #04120c;
  --accent-dim: #0c5c3e;

  --up: #00e08a;
  --down: #ff5c5c;
  --warn: #ffb020;
  --info: #4d9fff;

  /* Type scale: 1.2 ratio, fluid at the display end only. */
  --t-display: clamp(1.5rem, 1.2rem + 1.4vw, 2rem);
  --t-h1: 1.375rem;
  --t-h2: 0.8125rem;
  --t-body: 0.9375rem;
  --t-small: 0.8125rem;
  --t-micro: 0.6875rem;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;

  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-full: 999px;

  --shadow-1: 0 1px 2px rgb(0 0 0 / 0.4);
  --shadow-2: 0 4px 16px -4px rgb(0 0 0 / 0.5);
  --ring: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent);

  --tap: 44px;
  --ease: cubic-bezier(0.2, 0, 0, 1);
}

@media (prefers-color-scheme: light) {
  :root {
    --ink-0: #0b1017;
    --ink-1: #354052;
    --ink-2: #6b7688;
    --ink-3: #97a1b0;

    --bg-0: #f2f5f9;
    --bg-1: #ffffff;
    --bg-2: #f7f9fc;
    --bg-3: #eef2f7;
    --line: #dde3ec;
    --line-soft: #e8edf4;

    --accent: #00a866;
    --accent-ink: #ffffff;
    --accent-dim: #b6f0d7;

    --up: #00a866;
    --down: #d92d20;
    --warn: #b25e00;
    --info: #1f6fd0;

    --shadow-1: 0 1px 2px rgb(16 24 40 / 0.06);
    --shadow-2: 0 8px 24px -8px rgb(16 24 40 / 0.16);
  }
}

/* ------------------------------------------------------------------ base */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg-0);
  color: var(--ink-0);
  font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: var(--t-body);
  line-height: 1.45;
  font-feature-settings: "cv05" 1, "ss01" 1;
  /* Room for the tab bar plus the home indicator. */
  padding-bottom: calc(64px + env(safe-area-inset-bottom));
}

@supports (font-variation-settings: normal) {
  body { font-family: InterVariable, Inter, -apple-system, sans-serif; }
}

h1 { font-size: var(--t-h1); font-weight: 680; letter-spacing: -0.02em; margin: 0; }
h2 {
  font-size: var(--t-h2); font-weight: 620; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-2);
  margin: var(--space-5) 0 var(--space-2);
}
p { margin: 0; }
a { color: var(--accent); text-decoration: none; }

.num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* --------------------------------------------------------------- chrome */

.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  padding-top: calc(var(--space-3) + env(safe-area-inset-top));
  background: color-mix(in srgb, var(--bg-1) 82%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--line-soft);
}
.brand {
  display: flex; align-items: center; gap: var(--space-2);
  font-weight: 700; letter-spacing: -0.02em; color: var(--ink-0);
}
/* The mark: the same interlocking D's as the app icon, inlined from
   design/icon/mark.svg so the two cannot drift apart. `currentColor` lets it
   take the accent, which reads on the dark topbar — the icon's own deep
   forest gradient is designed to sit on a home screen against wallpaper, not
   on a near-black bar. */
.brand .mark {
  /* 594x456 in the viewBox, so 1.303:1. Sized off the height and given the
     matching width, because letting the two disagree is what squashed it. */
  height: 22px; width: 29px; flex: 0 0 auto;
  color: var(--accent);
}

.topbar .who {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--t-small); color: var(--ink-2);
}

main {
  max-width: 34rem; margin: 0 auto;
  padding: var(--space-4) var(--space-4) var(--space-6);
}

/* Bottom tab bar — the single biggest thing that makes this read as an app
   rather than a web page on a phone. */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  background: color-mix(in srgb, var(--bg-1) 88%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  border-top: 1px solid var(--line-soft);
  padding-bottom: env(safe-area-inset-bottom);
}
.tabbar a {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: var(--space-2) 0 var(--space-3);
  min-height: var(--tap);
  color: var(--ink-3); font-size: var(--t-micro); font-weight: 600;
  letter-spacing: 0.02em; transition: color .15s var(--ease);
}
.tabbar a svg { width: 21px; height: 21px; stroke-width: 1.9; }
.tabbar a[aria-current="page"] { color: var(--accent); }
.tabbar a:active { color: var(--ink-1); }

/* --------------------------------------------------------------- surface */

.card {
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.card + .card { margin-top: var(--space-3); }

.sub { color: var(--ink-2); font-size: var(--t-small); margin-top: var(--space-1); }

/* Small uppercase label above a heading — says which season without spending
   a line of the title on it. */
.eyebrow {
  font-size: var(--t-micro); font-weight: 650; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 2px;
}

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

.row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  min-height: 60px;
  border-bottom: 1px solid var(--line-soft);
  color: inherit;
}
.row:last-child { border-bottom: 0; }
a.row, .rowlink { transition: background .12s var(--ease); }
a.row:active, .rowlink:active { background: var(--bg-2); }
.row.muted { background: var(--bg-2); }
.row.dim { opacity: .42; }

.rowlink {
  display: flex; align-items: center; gap: var(--space-3);
  flex: 1 1 auto; min-width: 0; color: inherit;
}

.row .tag {
  flex: 0 0 2.6rem; font-size: var(--t-micro); font-weight: 700;
  letter-spacing: 0.06em; color: var(--ink-3);
}
.row .body { flex: 1 1 auto; min-width: 0; }
.row .name {
  font-weight: 600; letter-spacing: -0.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.row .meta {
  font-size: var(--t-small); color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.row .empty { color: var(--ink-3); font-style: italic; }
.row .value {
  flex: 0 0 auto; font-weight: 680; font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.row .value.up { color: var(--up); }
.row .value.down { color: var(--down); }

/* Sort control for the season table. A scrolling strip of links rather than
   a table header, because five numeric columns do not fit on a phone and the
   chosen one becomes the number in the row instead. */
.sorter {
  display: flex; gap: var(--space-2);
  margin: var(--space-4) 0 var(--space-3);
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.sorter::-webkit-scrollbar { display: none; }
.sorter a {
  flex: 0 0 auto;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--bg-1);
  color: var(--ink-2);
  font-size: var(--t-small); font-weight: 600;
  white-space: nowrap;
  transition: color .12s var(--ease), border-color .12s var(--ease);
}
.sorter a[aria-current="true"] {
  background: var(--accent); border-color: var(--accent);
  color: var(--accent-ink);
}
.sorter a:active { border-color: var(--accent); }

/* Pick'em: a two-team toggle, sized to be tapped rather than aimed at.
   Built from real radio inputs so the sheet submits with no JavaScript; the
   input itself is hidden and its label carries the whole hit area. */
.matchup {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2);
  margin-top: var(--space-2);
}
.matchup .side {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--bg-2); color: var(--ink-1);
  font-weight: 700; letter-spacing: 0.02em;
  cursor: pointer;
  transition: background .12s var(--ease), border-color .12s var(--ease),
              color .12s var(--ease);
}
.matchup .side input { position: absolute; opacity: 0; pointer-events: none; }
.matchup .side:active { transform: scale(.985); }

/* Driven by `:has(input:checked)`, not by a server-rendered class. The class
   only updates on reload, so tapping through sixteen games would light up
   nothing at all until you saved — the one moment feedback matters most, and
   the app has no JavaScript to fake it with. */
.matchup .side:has(input:checked) {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}
.matchup .side:has(input:focus-visible) { box-shadow: var(--ring); }

/* Locked games: no longer a control, so it must not look like one. */
.matchup.locked .side {
  min-height: 34px; cursor: default; background: transparent;
  font-size: var(--t-small); color: var(--ink-3);
}
.matchup.locked .side.chosen {
  background: var(--bg-3); border-color: var(--line); color: var(--ink-0);
}
/* The team that actually won, whoever picked it. */
.matchup.locked .side.won { border-color: var(--accent-dim); color: var(--up); }
.matchup.locked .side.chosen.won {
  background: color-mix(in srgb, var(--up) 18%, transparent);
  border-color: var(--up); color: var(--up);
}

.tick { font-weight: 800; font-size: 1rem; }
.tick.ok { color: var(--up); }
.tick.no { color: var(--down); }
.tick.miss { color: var(--ink-3); }

/* Rank chips: the top three earn colour, the rest stay quiet. */
.rank {
  flex: 0 0 1.75rem; height: 1.75rem; border-radius: var(--r-full);
  display: grid; place-items: center;
  font-size: var(--t-micro); font-weight: 800;
  background: var(--bg-3); color: var(--ink-2);
}
.rank.g1 { background: linear-gradient(140deg, #ffd76a, #e8a908); color: #2a1e00; }
.rank.g2 { background: linear-gradient(140deg, #dfe6f0, #a8b4c6); color: #1a2030; }
.rank.g3 { background: linear-gradient(140deg, #e0a579, #b4703c); color: #24130a; }

/* ------------------------------------------------------------------ tiles */

.tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.tile {
  background: var(--bg-1); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--space-3) var(--space-4);
  box-shadow: var(--shadow-1);
}
.tile .label {
  font-size: var(--t-micro); font-weight: 650; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--ink-3);
}
.tile .figure {
  font-size: 1.6rem; font-weight: 700; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums; margin-top: 2px;
}
.tile .figure.up { color: var(--up); }
.tile .figure.down { color: var(--down); }
.tile.wide { grid-column: 1 / -1; }

/* ---------------------------------------------------------------- pieces */

.avatar {
  flex: 0 0 auto; width: 2.5rem; height: 2.5rem; border-radius: var(--r-full);
  background: var(--bg-3); object-fit: cover;
  font-size: var(--t-micro); font-weight: 700; color: var(--ink-3);
  text-align: center; line-height: 2.5rem;
}
.avatar.lg { width: 4.5rem; height: 4.5rem; font-size: 1.1rem; line-height: 4.5rem; }

/* Account avatars: colour and letters both derived from the username, so
   there is no image to fetch and nothing to store. The disc paints its own
   background, which is why one set of values works in both themes. */
.avatar.ident {
  /* OKLCH, not HSL: its lightness is perceptual, so one pair of L values
     holds every hue to the same contrast against the initials. The HSL line
     is a fallback for browsers without oklch() and is simply overwritten
     where it is supported. */
  background: linear-gradient(140deg, hsl(var(--a-h) 82% 74%), hsl(var(--a-h2) 74% 64%));
  background: linear-gradient(140deg,
    oklch(0.85 0.13 var(--a-h)), oklch(0.75 0.14 var(--a-h2)));
  color: #0b1210;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.avatar.ident.sm {
  width: 1.75rem; height: 1.75rem; line-height: 1.75rem; font-size: var(--t-micro);
}
.avatar.ident.lg { font-size: 1.375rem; }

.pill {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--t-micro); font-weight: 700; letter-spacing: 0.03em;
  padding: 2px 7px; border-radius: var(--r-sm);
  background: var(--bg-3); color: var(--ink-2);
}
.pill.live { background: color-mix(in srgb, var(--up) 16%, transparent); color: var(--up); }
.pill.live::before {
  content: ""; width: 5px; height: 5px; border-radius: var(--r-full);
  background: currentColor; animation: pulse 1.8s var(--ease) infinite;
}
.pill.warn { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); }
.pill.bad { background: color-mix(in srgb, var(--down) 16%, transparent); color: var(--down); }
.pill.ok { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); }

@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
@media (prefers-reduced-motion: reduce) {
  .pill.live::before { animation: none }
  * { transition: none !important }
}

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  width: 100%; min-height: var(--tap); padding: 0 var(--space-4);
  font: inherit; font-weight: 620; letter-spacing: -0.01em;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--bg-2); color: var(--ink-0); cursor: pointer;
  transition: transform .1s var(--ease), background .15s var(--ease);
}
.btn:active { transform: scale(.985); }
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn.primary {
  background: var(--accent); color: var(--accent-ink); border-color: transparent;
  font-weight: 700;
}
.btn.ghost { background: transparent; }
.btn.sm { min-height: 36px; width: auto; font-size: var(--t-small); }

.icon-btn {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 2.25rem; height: 2.25rem; border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--ink-3); cursor: pointer;
  transition: color .15s var(--ease), background .15s var(--ease);
}
.icon-btn:hover { color: var(--down); background: var(--bg-3); }
.icon-btn.info:hover { color: var(--accent); }

input[type=text], input[type=password], input[type=search], input[type=number] {
  width: 100%; min-height: var(--tap); padding: 0 var(--space-3);
  font: inherit; color: var(--ink-0);
  background: var(--bg-1); border: 1px solid var(--line);
  border-radius: var(--r-md);
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
input:focus { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
label { display: block; font-size: var(--t-small); color: var(--ink-2); margin: var(--space-3) 0 var(--space-1); }

/* Help text under a field. Says what a value is for before someone types
   the wrong thing, rather than after. */
.hint {
  font-size: var(--t-small); color: var(--ink-3);
  margin-top: var(--space-1); line-height: 1.35;
}
.hint.error { color: var(--down); }

/* A price you cannot currently afford. A warning in the player pool, not a
   barrier — the cap is enforced on submit, not on every pick. */
.over { color: var(--warn); font-weight: 650; }

/* The Rotowire note on a player panel. Quiet — it is context for the numbers
   below it, not the headline of the screen. */
.blurb {
  margin-top: var(--space-4);
  padding: var(--space-3);
  border-left: 2px solid var(--accent-dim);
  background: var(--bg-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.blurb p { font-size: var(--t-small); line-height: 1.5; color: var(--ink-1); }
.blurb .attribution {
  margin-top: var(--space-2);
  font-size: var(--t-micro); color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.blurb .attribution a { color: var(--info); }

/* The Venmo button. Money is not the app's accent colour: green here would
   read as "this is your score" next to a page full of scores. */
.btn.venmo {
  color: var(--info);
  border-color: color-mix(in srgb, var(--info) 45%, var(--line));
  background: color-mix(in srgb, var(--info) 10%, transparent);
}
.btn.venmo:active { background: color-mix(in srgb, var(--info) 18%, transparent); }

/* The file picker. Browsers render this as a grey OS button that ignores
   everything around it, so the control is quieted and only its own button
   is restyled. */
input[type=file] {
  width: 100%;
  font-size: var(--t-small); color: var(--ink-2);
  padding: var(--space-2) 0;
}
input[type=file]::file-selector-button {
  margin-right: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-3); color: var(--ink-0);
  font: inherit; font-weight: 600;
  cursor: pointer;
}
input[type=file]::file-selector-button:hover { border-color: var(--accent); }

/* Checkboxes get the accent rather than the browser's blue. */
input[type=checkbox] { accent-color: var(--accent); width: 1.1rem; height: 1.1rem; }

.note {
  display: flex; gap: var(--space-2); align-items: flex-start;
  padding: var(--space-3) var(--space-4); margin-bottom: var(--space-3);
  border-radius: var(--r-md); font-size: var(--t-small);
  background: var(--bg-1); border: 1px solid var(--line); color: var(--ink-1);
}
.note.success { background: color-mix(in srgb, var(--up) 12%, var(--bg-1)); border-color: color-mix(in srgb, var(--up) 30%, var(--line)); }
.note.error { background: color-mix(in srgb, var(--down) 12%, var(--bg-1)); border-color: color-mix(in srgb, var(--down) 30%, var(--line)); color: var(--down); }
.note.warning { background: color-mix(in srgb, var(--warn) 12%, var(--bg-1)); border-color: color-mix(in srgb, var(--warn) 30%, var(--line)); }

.back {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--ink-2); font-size: var(--t-small); font-weight: 550;
  margin-bottom: var(--space-3);
}

.blank { padding: var(--space-6) var(--space-4); text-align: center; color: var(--ink-2); }
.blank .big { font-size: 1.6rem; margin-bottom: var(--space-2); }

.stack > * + * { margin-top: var(--space-3); }
.errorlist { color: var(--down); font-size: var(--t-small); padding-left: var(--space-4); margin: var(--space-1) 0; }
.helptext { display: block; font-size: var(--t-micro); color: var(--ink-3); margin-top: var(--space-1); }

/* ------------------------------------------------------------------ sheet */

/* A bottom sheet on a phone, a centred panel on anything wider. Native
   <dialog>, so focus trapping, Escape and inertness come from the browser
   rather than from code that has to be maintained. */
.sheet {
  padding: 0; border: 0; color: var(--ink-0); background: var(--bg-1);
  width: 100%; max-width: 34rem;
  max-height: 88dvh;
  margin: auto auto 0;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-2);
  overflow: hidden;
}
.sheet::backdrop { background: rgb(0 0 0 / 0.55); backdrop-filter: blur(3px); }
.sheet[open] { animation: sheet-in .22s var(--ease); }
.sheet::backdrop { animation: fade-in .22s var(--ease); }

@media (min-width: 40rem) {
  .sheet { margin: auto; border-radius: var(--r-lg); max-height: 82dvh; }
}

.sheet-body {
  padding: var(--space-5) var(--space-4) calc(var(--space-5) + env(safe-area-inset-bottom));
  overflow-y: auto; max-height: 88dvh;
  overscroll-behavior: contain;
}
.sheet-head { display: flex; align-items: center; gap: var(--space-4); }
.sheet-close {
  position: absolute; top: var(--space-3); right: var(--space-3); z-index: 2;
  width: 2rem; height: 2rem; display: grid; place-items: center;
  border: 0; border-radius: var(--r-full);
  background: var(--bg-3); color: var(--ink-2);
  font-size: 1.2rem; line-height: 1; cursor: pointer;
}

@keyframes sheet-in { from { transform: translateY(14px); opacity: .6 } }
@keyframes fade-in { from { opacity: 0 } }
@media (prefers-reduced-motion: reduce) {
  .sheet[open], .sheet::backdrop { animation: none }
}

/* The pool row: tapping the player explains him, the button on the right
   drafts him. Two targets, two different jobs. */
.draft-btn {
  flex: 0 0 auto; min-height: 34px; padding: 0 var(--space-3);
  font: inherit; font-size: var(--t-small); font-weight: 700;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent); cursor: pointer;
  transition: background .15s var(--ease), transform .1s var(--ease);
}
.draft-btn:active { transform: scale(.95); }
.draft-btn:hover { background: color-mix(in srgb, var(--accent) 24%, transparent); }

/* A value that just changed. Brief, and only on the number that moved — the
   point is to catch the eye of somebody already looking at the screen, not to
   make the page restless. */
.value.bumped {
  animation: bump 1.1s var(--ease);
}
@keyframes bump {
  0%   { color: var(--accent); transform: scale(1.18); }
  40%  { color: var(--accent); transform: scale(1); }
  100% { color: inherit; }
}
@media (prefers-reduced-motion: reduce) {
  .value.bumped { animation: none }
}
