/* promptodo
   One token layer, four themes. Every colour, spacing step, radius, shadow and duration is
   a token, so a change lands in one place and a theme is a swap rather than a second
   stylesheet. Nothing below hardcodes a colour.

   The chosen theme is written onto <html> by the server from the user's saved setting, so
   the page paints in the right palette on the first frame instead of flashing white and
   correcting itself. */

:root {
  --shadow-1: 0 1px 2px rgba(20, 20, 18, .05);
  --shadow-2: 0 1px 2px rgba(20, 20, 18, .05), 0 10px 28px -14px rgba(20, 20, 18, .22);
  --shadow-3: 0 2px 4px rgba(20, 20, 18, .06), 0 18px 44px -18px rgba(20, 20, 18, .28);
  --skeleton: linear-gradient(90deg, var(--surface-2) 0%, var(--surface-3) 40%, var(--surface-2) 80%);

  --s1: .25rem; --s2: .5rem; --s3: .75rem; --s4: 1rem;
  --s5: 1.5rem; --s6: 2rem; --s7: 3rem;
  --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-full: 999px;
  --dur-1: 110ms; --dur-2: 200ms; --dur-3: 420ms;
  --ease: cubic-bezier(.2, .6, .3, 1);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  --sans: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;

  /* One type scale, whole pixels at the default root size. Every size in this sheet is one
     of these eight steps; it used to carry twenty-five, which is why nothing lined up. */
  --t-2xs: .6875rem;  /* 11 - uppercase eyebrows, nav groups */
  --t-xs: .75rem;     /* 12 - pills, model names, footnotes */
  --t-sm: .8125rem;   /* 13 - meta, counts, row actions */
  --t-md: .875rem;    /* 14 - secondary text, menu items, controls */
  --t-base: .9375rem; /* 15 - body */
  --t-lg: 1.0625rem;  /* 17 - card and section titles */
  --t-xl: 1.3125rem;  /* 21 - page titles */
  --t-2xl: 1.625rem;  /* 26 - the one place a number needs to be big */

  /* Four weights. Regular reads, medium labels, semibold titles, bold the wordmark. */
  --w-normal: 400; --w-medium: 500; --w-semi: 600; --w-bold: 700;
  --lh-tight: 1.28; --lh: 1.5; --lh-loose: 1.6;

  /* The reading column. The chrome derives its own rail from this where it is used rather
     than storing a second token: a custom property declared here resolves against the value
     here, so a page that widens the column would have widened the content and left the
     header behind. */
  --content: 720px;
}

/* Paper: the light half of the default palette, and what "System" shows in daylight. */
:root,
:root[data-theme="paper"],
:root[data-theme="system"] {
  color-scheme: light;

  /* Surfaces, back to front: page, card, inset, sunken. Four steps rather than two so a
     card on the page and a code block inside the card are told apart without a border. */
  --bg: oklch(.972 .005 95);
  --surface: oklch(1 0 0);
  --surface-2: oklch(.955 .006 95);
  --surface-3: oklch(.922 .008 95);
  --line: oklch(.902 .008 95);
  --line-strong: oklch(.820 .010 95);

  --fg: oklch(.235 .010 95);
  --fg-soft: oklch(.400 .011 95);
  --muted: oklch(.498 .014 95);
  --faint: oklch(.556 .015 95);

  --accent: oklch(.505 .185 302);
  --accent-fg: oklch(1 0 0);
  --accent-soft: oklch(.962 .026 302);
  --accent-line: oklch(.870 .065 302);
  --accent-ring: oklch(.505 .185 302 / .24);

  --ok: oklch(.480 .120 155);
  --ok-soft: oklch(.960 .030 155);
  --ok-line: oklch(.880 .060 155);

  --warn: oklch(.520 .110 75);
  --warn-soft: oklch(.965 .035 85);
  --warn-line: oklch(.885 .070 85);

  --danger: oklch(.505 .170 27);
  --danger-soft: oklch(.962 .025 27);
  --danger-line: oklch(.880 .060 27);
}

/* Graphite: the dark half of the same palette, and the default for a new account.
   Its values are written twice on purpose. CSS has no way to say "apply that palette
   here" without a second layer of aliasing variables, which costs more than the repeat. */
@media (prefers-color-scheme: dark) {
  :root[data-theme="system"] {
    color-scheme: dark;

    --bg: oklch(.168 .010 280);
    --surface: oklch(.212 .013 280);
    --surface-2: oklch(.248 .015 280);
    --surface-3: oklch(.288 .017 280);
    --line: oklch(.295 .015 280);
    --line-strong: oklch(.395 .020 280);

    --fg: oklch(.945 .006 280);
    --fg-soft: oklch(.835 .010 280);
    --muted: oklch(.700 .014 280);
    --faint: oklch(.625 .014 280);

    --accent: oklch(.760 .145 302);
    --accent-fg: oklch(.180 .030 302);
    --accent-soft: oklch(.262 .052 302);
    --accent-line: oklch(.380 .090 302);
    --accent-ring: oklch(.760 .145 302 / .28);

    --ok: oklch(.780 .130 155);
    --ok-soft: oklch(.255 .045 155);
    --ok-line: oklch(.350 .070 155);

    --warn: oklch(.815 .120 82);
    --warn-soft: oklch(.262 .045 82);
    --warn-line: oklch(.360 .070 82);

    --danger: oklch(.740 .145 25);
    --danger-soft: oklch(.262 .050 25);
    --danger-line: oklch(.360 .080 25);

    --shadow-1: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-2: 0 1px 2px rgba(0, 0, 0, .4), 0 12px 30px -16px rgba(0, 0, 0, .8);
    --shadow-3: 0 2px 6px rgba(0, 0, 0, .45), 0 22px 50px -20px rgba(0, 0, 0, .9);
  }
}

:root[data-theme="graphite"] {
  color-scheme: dark;

  --bg: oklch(.168 .010 280);
  --surface: oklch(.212 .013 280);
  --surface-2: oklch(.248 .015 280);
  --surface-3: oklch(.288 .017 280);
  --line: oklch(.295 .015 280);
  --line-strong: oklch(.395 .020 280);

  --fg: oklch(.945 .006 280);
  --fg-soft: oklch(.835 .010 280);
  --muted: oklch(.700 .014 280);
  --faint: oklch(.625 .014 280);

  --accent: oklch(.760 .145 302);
  --accent-fg: oklch(.180 .030 302);
  --accent-soft: oklch(.262 .052 302);
  --accent-line: oklch(.380 .090 302);
  --accent-ring: oklch(.760 .145 302 / .28);

  --ok: oklch(.780 .130 155);
  --ok-soft: oklch(.255 .045 155);
  --ok-line: oklch(.350 .070 155);

  --warn: oklch(.815 .120 82);
  --warn-soft: oklch(.262 .045 82);
  --warn-line: oklch(.360 .070 82);

  --danger: oklch(.740 .145 25);
  --danger-soft: oklch(.262 .050 25);
  --danger-line: oklch(.360 .080 25);

  --shadow-1: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-2: 0 1px 2px rgba(0, 0, 0, .4), 0 12px 30px -16px rgba(0, 0, 0, .8);
  --shadow-3: 0 2px 6px rgba(0, 0, 0, .45), 0 22px 50px -20px rgba(0, 0, 0, .9);
}

/* Tide: deep sea surfaces, cyan accent. */
:root[data-theme="tide"] {
  color-scheme: dark;

  --bg: oklch(.170 .022 232);
  --surface: oklch(.214 .026 232);
  --surface-2: oklch(.250 .028 232);
  --surface-3: oklch(.290 .030 232);
  --line: oklch(.298 .028 232);
  --line-strong: oklch(.398 .034 232);

  --fg: oklch(.945 .010 210);
  --fg-soft: oklch(.840 .016 210);
  --muted: oklch(.705 .022 212);
  --faint: oklch(.630 .024 215);

  --accent: oklch(.805 .115 205);
  --accent-fg: oklch(.185 .035 205);
  --accent-soft: oklch(.268 .050 205);
  --accent-line: oklch(.385 .075 205);
  --accent-ring: oklch(.805 .115 205 / .28);

  --ok: oklch(.790 .135 148);
  --ok-soft: oklch(.258 .045 148);
  --ok-line: oklch(.352 .070 148);

  --warn: oklch(.820 .120 78);
  --warn-soft: oklch(.264 .045 78);
  --warn-line: oklch(.362 .070 78);

  --danger: oklch(.745 .150 22);
  --danger-soft: oklch(.264 .050 22);
  --danger-line: oklch(.362 .080 22);

  --shadow-1: 0 1px 2px rgba(0, 0, 0, .45);
  --shadow-2: 0 1px 2px rgba(0, 0, 0, .45), 0 12px 30px -16px rgba(0, 0, 0, .85);
  --shadow-3: 0 2px 6px rgba(0, 0, 0, .5), 0 22px 50px -20px rgba(0, 0, 0, .9);
}

/* Solar: warm sand paper, deep teal accent. */
:root[data-theme="solar"] {
  color-scheme: light;

  --bg: oklch(.953 .018 85);
  --surface: oklch(.987 .007 85);
  --surface-2: oklch(.932 .020 85);
  --surface-3: oklch(.898 .024 85);
  --line: oklch(.884 .022 85);
  --line-strong: oklch(.798 .028 82);

  --fg: oklch(.255 .024 62);
  --fg-soft: oklch(.410 .024 62);
  --muted: oklch(.492 .026 68);
  --faint: oklch(.548 .025 72);

  --accent: oklch(.478 .100 205);
  --accent-fg: oklch(.987 .007 85);
  --accent-soft: oklch(.944 .028 205);
  --accent-line: oklch(.858 .055 205);
  --accent-ring: oklch(.478 .100 205 / .24);

  --ok: oklch(.470 .115 150);
  --ok-soft: oklch(.940 .032 150);
  --ok-line: oklch(.862 .062 150);

  --warn: oklch(.510 .108 72);
  --warn-soft: oklch(.938 .040 82);
  --warn-line: oklch(.858 .072 82);

  --danger: oklch(.498 .170 27);
  --danger-soft: oklch(.942 .028 27);
  --danger-line: oklch(.862 .062 27);

  --shadow-1: 0 1px 2px rgba(60, 45, 25, .06);
  --shadow-2: 0 1px 2px rgba(60, 45, 25, .06), 0 10px 28px -14px rgba(60, 45, 25, .22);
  --shadow-3: 0 2px 5px rgba(60, 45, 25, .08), 0 18px 44px -18px rgba(60, 45, 25, .26);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: var(--w-normal) var(--t-base)/var(--lh) var(--sans);
  background: var(--bg);
  color: var(--fg);
  -webkit-font-smoothing: antialiased;
}

/* A column layout on the body is what lets the footer sit at the bottom of a short page
   instead of floating under the last card. */
body { display: flex; flex-direction: column; min-height: 100vh; }
main { width: 100%; max-width: calc(var(--content) + var(--s4) * 2); margin: 0 auto; padding: var(--s5) var(--s4) var(--s7); flex: 1 0 auto; }
/* Settings carries a sidebar and four-across stat rows; the reading column does not. The
   variable is set on the body rather than main so the header and footer widen with it. */
body:has(.settings) { --content: 880px; }

h1, h2, h3 { letter-spacing: -.011em; }
h1 { font-size: var(--t-xl); font-weight: var(--w-bold); margin: 0 0 var(--s2); }
h2 { font-size: var(--t-lg); font-weight: var(--w-semi); margin: var(--s5) 0 var(--s2); }
p { margin: 0 0 var(--s3); }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent); }

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

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

/* The chrome spans the window, but its contents stop at the rail: 40px outside the reading
   column on each side, so the logo brackets the page instead of hugging the viewport. */
header.top {
  border-bottom: 1px solid var(--line); background: var(--surface);
  position: sticky; top: 0; z-index: 5;
}
/* 40px outside the reading column on each side, so the logo brackets the page. */
.shell {
  width: 100%; max-width: calc(var(--content) + 80px); margin: 0 auto; padding: 0 var(--s4);
}
header.top .shell {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--s4); height: 52px;
}

/* The word already contains the product: promp + todo, sharing a t. Tinting the tail says
   what the thing does, and gives the brand one colour that follows the theme. */
.brand { display: inline-flex; align-items: center; gap: var(--s2); text-decoration: none; color: var(--fg); }
.brand:hover { color: var(--fg); }
.brand .mark { width: 22px; height: 22px; flex: none; color: var(--accent); }
.brand .word {
  font-weight: var(--w-bold); font-size: var(--t-lg);
  letter-spacing: -.03em; color: var(--fg);
}
.brand .word b { font-weight: var(--w-semi); color: var(--accent); }

header.top nav { display: flex; align-items: center; gap: var(--s3); }
header.top nav a { color: var(--muted); text-decoration: none; font-size: var(--t-md); }
header.top nav a:hover { color: var(--fg); }

/* A <details> rather than a scripted popover, so the menu opens with JavaScript off. The
   script only adds what markup cannot: closing it on an outside click or Escape. */
.account { position: relative; }
.account > summary {
  display: inline-flex; align-items: center; gap: var(--s2); cursor: pointer; list-style: none;
  background: none; border: 1px solid transparent; border-radius: var(--r-full);
  padding: .25rem .5rem .25rem .3rem; color: var(--fg-soft); font-size: var(--t-md);
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease);
}
.account > summary::-webkit-details-marker { display: none; }
.account > summary:hover, .account[open] > summary {
  background: var(--surface-2); border-color: var(--line); color: var(--fg);
}
.account .avatar {
  width: 24px; height: 24px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: var(--accent-soft); border: 1px solid var(--accent-line);
  color: var(--accent); font-size: var(--t-2xs); font-weight: var(--w-bold);
}
.account .caret { width: 12px; height: 12px; opacity: .7; flex: none; }
.account .who { max-width: 16ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 20;
  min-width: 210px; padding: var(--s1);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow-2);
  display: flex; flex-direction: column; gap: 1px;
}
.menu .head { padding: .45rem .55rem .35rem; }
.menu .head b { display: block; font-size: var(--t-sm); font-weight: var(--w-semi); }
.menu .head span { display: block; font-size: var(--t-xs); color: var(--faint); overflow: hidden; text-overflow: ellipsis; }
.menu a, .menu button {
  display: flex; align-items: center; gap: var(--s2);
  padding: .42rem .55rem; border-radius: var(--r-sm);
  font: inherit; font-size: var(--t-md); font-weight: var(--w-normal); color: var(--fg-soft); text-decoration: none;
  background: none; border: 0; cursor: pointer; text-align: left; width: 100%;
}
.menu a:hover, .menu button:hover { background: var(--surface-2); color: var(--fg); opacity: 1; }
.menu svg { width: 15px; height: 15px; flex: none; color: var(--faint); }
.menu hr { border: 0; border-top: 1px solid var(--line); margin: var(--s1) 0; width: 100%; }
.menu .out:hover, .menu .out:hover svg { color: var(--danger); }

.crumbs { font-size: var(--t-md); color: var(--muted); margin: 0 0 var(--s4); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--accent); text-decoration: underline; }
.crumbs span { margin: 0 var(--s1); opacity: .5; }
.crumbs strong { color: var(--fg); font-weight: var(--w-semi); }

.back {
  display: inline-flex; align-items: center; gap: var(--s1);
  color: var(--muted); text-decoration: none; font-size: var(--t-md); margin-bottom: var(--s4);
  transition: color var(--dur-1) var(--ease);
}
.back:hover { color: var(--fg); }
.back svg { width: 14px; height: 14px; }

.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s4); margin-bottom: var(--s4);
}
.page-head .sub { color: var(--muted); font-size: var(--t-md); margin: var(--s1) 0 0; }
.page-head h1 { margin: 0; }
.page-link { color: var(--muted); text-decoration: none; font-size: var(--t-md); white-space: nowrap; }
.page-link:hover { color: var(--fg); }
/* The project heading carries the same icon as its card, so the two read as one object. */
.title-row { display: flex; align-items: center; gap: var(--s3); }

/* One tile, two states: the site's own icon, or the first letter of the project name when
   there is no domain or the domain ships no icon of its own. */
.fav {
  width: 22px; height: 22px; border-radius: 5px; flex: none;
  display: grid; place-items: center; overflow: hidden;
}
/* A real favicon arrives with its own shape, ground and padding, and browsers draw it on
   chrome with nothing behind it. A tile behind it here only fought with it. The image is
   sized in percentages rather than pixels because the border used to eat two of them and
   clip the icon. */
.fav--icon { background: none; border: 0; }
.fav--icon img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* The letter is not a picture of anything, so it needs a ground to read as a mark. */
.fav--letter {
  background: var(--surface-3); border: 1px solid var(--line);
  font-size: var(--t-2xs); font-weight: var(--w-bold); color: var(--muted); line-height: 1;
}
.fav--lg { width: 30px; height: 30px; border-radius: 7px; }
.fav--lg.fav--letter { font-size: var(--t-md); }


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

input, textarea, button, select { font: inherit; color: var(--fg); }

input, textarea, select {
  width: 100%; padding: .55rem .7rem;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
input::placeholder, textarea::placeholder { color: var(--faint); }
input:focus, textarea:focus, select:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring);
}
textarea { resize: vertical; }
select {
  appearance: none; padding-right: 2rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 15px) 55%, calc(100% - 10px) 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

button {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  padding: .5rem .85rem; border-radius: var(--r-sm); cursor: pointer;
  border: 1px solid var(--fg); background: var(--fg); color: var(--bg);
  font-size: var(--t-md); font-weight: var(--w-medium); white-space: nowrap;
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease),
              opacity var(--dur-1) var(--ease);
}
button:hover { opacity: .88; }
button:active { transform: translateY(.5px); }

button.small { padding: .28rem .6rem; font-size: var(--t-sm); }
/* A square box for a lone glyph, so the + is not stranded in a text-shaped button. */
button.icon-only { padding: 0; width: 2.25rem; height: 2.25rem; flex: none; }
button.icon-only svg { width: 17px; height: 17px; }

/* Outlined secondary: the default for anything inside a settings card. */
button.ghost {
  background: var(--surface); color: var(--fg); border-color: var(--line-strong);
  font-weight: var(--w-medium);
}
button.ghost:hover { background: var(--surface-2); border-color: var(--fg-soft); opacity: 1; }
button.ghost--danger { color: var(--danger); border-color: var(--line-strong); }
button.ghost--danger:hover { color: var(--danger); border-color: var(--danger); background: var(--danger-soft); }

/* Row actions repeat on every todo, so they read as quiet text until pointed at. */
button.quiet {
  background: none; border-color: transparent; color: var(--muted);
  padding: .25rem .55rem; font-size: var(--t-sm); border-radius: var(--r-sm); font-weight: var(--w-medium);
}
button.quiet:hover { background: var(--surface-2); color: var(--fg); border-color: var(--line); opacity: 1; }
button.quiet--danger:hover { color: var(--danger); border-color: var(--danger-line); background: var(--danger-soft); }

button.accent { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
button.link { background: none; border: none; color: var(--muted); padding: 0; font-size: var(--t-md); font-weight: var(--w-normal); }
button.link:hover { color: var(--fg); opacity: 1; }
button.danger { background: var(--surface); color: var(--danger); border-color: var(--danger); }
button.danger:hover { background: var(--danger); color: var(--surface); opacity: 1; }

button.copy { background: var(--surface-2); border-color: var(--line-strong); color: var(--fg-soft); }
button.copy:hover { background: var(--surface-3); border-color: var(--fg-soft); color: var(--fg); opacity: 1; }
button.copy.copied { background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok); }

.button-link {
  display: inline-flex; align-items: center; gap: var(--s2);
  padding: .5rem .85rem; border-radius: var(--r-sm);
  background: var(--fg); color: var(--bg); text-decoration: none;
  font-size: var(--t-md); font-weight: var(--w-medium);
}
.button-link:hover { color: var(--bg); opacity: .88; }

/* --- primitives ------------------------------------------------------------ */

.stack { display: flex; flex-direction: column; gap: var(--s3); }
.row { display: flex; gap: var(--s2); align-items: flex-end; }
.row input, .row select { flex: 1; min-width: 0; }
.narrow { max-width: 340px; }
.inline { display: inline; }
.muted { color: var(--muted); font-size: var(--t-md); }
.error { color: var(--danger); }

.warn, .flash {
  padding: .6rem .8rem; border-radius: var(--r-sm); font-size: var(--t-md);
  margin: 0 0 var(--s4); border: 1px solid transparent;
}
.warn { background: var(--warn-soft); border-color: var(--warn-line); color: var(--warn); }
.warn a { color: var(--warn); }
.flash { background: var(--accent-soft); border-color: var(--accent-line); color: var(--fg-soft); }

.banner { padding: .55rem .75rem; border-radius: var(--r-sm); font-size: var(--t-md); margin: var(--s2) 0; border: 1px solid transparent; }
.banner--error { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger); }
.banner--warn { background: var(--warn-soft); border-color: var(--warn-line); color: var(--warn); }

.pill {
  margin-left: auto; font-size: var(--t-xs); font-weight: var(--w-medium); color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-full);
  padding: .1rem .5rem; white-space: nowrap;
}
.pill--empty { color: var(--warn); background: var(--warn-soft); border-color: var(--warn-line); }
.pill--ok { color: var(--ok); background: var(--ok-soft); border-color: var(--ok-line); }
.pill.mono { font-family: var(--mono); font-size: var(--t-2xs); }
.card__head .pill + .pill { margin-left: var(--s1); }

.dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.dot.ok { background: var(--ok); }
.dot.off { background: var(--line-strong); }

pre.code {
  background: var(--surface-2); border: 1px solid var(--line);
  padding: var(--s3); border-radius: var(--r-sm); overflow-x: auto;
  font-family: var(--mono); font-size: var(--t-xs); line-height: 1.5;
  white-space: pre-wrap; word-break: break-word; margin: 0;
  color: var(--fg-soft);
}
pre.code--scroll { max-height: 320px; overflow: auto; white-space: pre; }

/* --- projects list --------------------------------------------------------- */

/* Creating a project was a small input parked under the list, which read as an
   afterthought on the one page whose job is to start one. */
.composer {
  display: flex; gap: var(--s2); align-items: stretch;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--s2);
  box-shadow: var(--shadow-1);
  margin-bottom: var(--s5);
  transition: border-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.composer:focus-within { border-color: var(--accent-line); box-shadow: var(--shadow-2); }
.composer input {
  flex: 1; min-width: 0; border-color: transparent; background: transparent;
  font-size: var(--t-base); padding: .45rem .6rem;
}
.composer input:focus { box-shadow: none; border-color: transparent; }
.composer button { flex: none; }

/* The same card as the project composer, stood on end: a todo is written in a paragraph,
   not a line, so the field takes the full width and the button sits under it. */
.composer--todo { flex-direction: column; align-items: stretch; gap: var(--s2); padding: var(--s3); }
.composer--todo textarea {
  border-color: transparent; background: transparent; padding: .2rem .3rem;
  resize: none; min-height: 3.4rem; font-size: var(--t-base);
}
.composer--todo textarea:focus { box-shadow: none; border-color: transparent; }
.composer__foot { display: flex; justify-content: space-between; align-items: center; gap: var(--s3); }
.attach-help { display: flex; align-items: center; gap: var(--s2); color: var(--faint); font-size: var(--t-xs); min-width: 0; }
.attach-help input {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.attach-button {
  display: inline-flex; align-items: center; gap: .35rem; color: var(--muted);
  border: 1px solid transparent; border-radius: var(--r-sm); padding: .3rem .45rem;
  cursor: pointer; font-weight: var(--w-medium); transition: color var(--dur-1) var(--ease),
    background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease);
}
.attach-button svg { width: 15px; height: 15px; }
.attach-button:hover { color: var(--fg); background: var(--surface-2); border-color: var(--line); }
.attach-help:has(input:focus-visible) .attach-button { outline: 2px solid var(--accent); outline-offset: 2px; }

.image-preview:empty { display: none; }
.image-preview { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: var(--s2); }
.image-preview__item {
  display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; align-items: center; gap: var(--s2);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: .3rem; min-width: 0;
}
.image-preview__item img { width: 38px; height: 32px; object-fit: cover; border-radius: 4px; display: block; }
.image-preview__item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--t-xs); color: var(--fg-soft); }
.image-preview__item button { width: 24px; height: 24px; padding: 0; border: 0; background: transparent; color: var(--faint); }
.image-preview__item button:hover { color: var(--danger); background: var(--danger-soft); opacity: 1; }
.image-preview__item button svg { width: 14px; height: 14px; }
.image-preview__message { grid-column: 1 / -1; margin: 0; color: var(--danger); font-size: var(--t-xs); }

/* Cards were 4px apart, which read as one striped block rather than six projects. */
.cards { list-style: none; padding: 0; display: grid; gap: 10px; margin: 0 0 var(--s5); }
.cards li {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.cards li:hover { border-color: var(--line-strong); box-shadow: var(--shadow-1); }
.card-link {
  display: flex; justify-content: space-between; align-items: center; gap: var(--s4);
  padding: .8rem .9rem; text-decoration: none; color: var(--fg); border-radius: var(--r-md);
}
.card-id { display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.card-title { font-weight: var(--w-semi); letter-spacing: -.005em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The repository name used to sit here. It named the same codebase on every row of a list
   whose rows are already named, so the only thing left is whether one is attached at all. */
.card-meta {
  display: flex; align-items: center; gap: var(--s3); color: var(--muted); font-size: var(--t-sm);
  flex: none;
}
.count { color: var(--fg-soft); font-variant-numeric: tabular-nums; }
.count.muted { color: var(--faint); }

.empty {
  background: var(--surface); border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg); padding: var(--s6) var(--s5); text-align: center;
  margin: 0 0 var(--s5);
}
.empty h2 { margin: 0 0 var(--s2); font-size: var(--t-lg); }
.empty p { color: var(--muted); margin: 0 auto var(--s4); max-width: 34ch; }
.empty .composer { margin: 0 auto; max-width: 400px; text-align: left; }

/* --- todos ----------------------------------------------------------------- */

.todo {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  margin-bottom: var(--s2);
  transition: border-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease),
              opacity var(--dur-2) var(--ease);
}
.todo:hover { border-color: var(--line-strong); }
.todo.done { opacity: .6; }
.todo.done:hover { opacity: .85; }
.todo.done .todo-title { text-decoration: line-through; color: var(--muted); }

/* One pre-wrapped block for the whole todo meant a two-paragraph one tore a hole in the
   list. The first line is the thing to do; the rest is elaboration, so it gets its own
   weight and leading and folds away until asked for. */
.todo-head { display: flex; align-items: flex-start; gap: var(--s3); padding: .8rem .9rem; }
.todo-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .35rem; }
.todo-title {
  margin: 0; white-space: pre-wrap; font-weight: var(--w-semi); line-height: 1.45;
  letter-spacing: -.005em; padding-top: 1px;
}
.todo-text {
  margin: 0; white-space: pre-wrap; color: var(--muted); font-size: var(--t-md); line-height: 1.55;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* An empty <details> used as a disclosure control: no JavaScript, and :has() reaches back
   up to the body so the clamp lifts on the paragraph above it. */
.todo-body:has(.todo-more[open]) .todo-text { -webkit-line-clamp: unset; }
.todo-more { align-self: flex-start; }
.todo-more > summary {
  cursor: pointer; list-style: none; font-size: var(--t-xs); color: var(--faint);
  transition: color var(--dur-1) var(--ease);
}
.todo-more > summary::-webkit-details-marker { display: none; }
.todo-more > summary:hover { color: var(--fg-soft); }
.todo-more > summary .less, .todo-more[open] > summary .more { display: none; }
.todo-more[open] > summary .less { display: inline; }

.todo-images { display: flex; gap: var(--s2); overflow-x: auto; padding-top: var(--s1); scrollbar-width: thin; }
.todo-image {
  position: relative; flex: 0 0 104px; min-width: 0; margin: 0;
  border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden;
  background: var(--surface-2);
}
.todo-image a { display: block; color: inherit; }
.todo-image img { display: block; width: 100%; height: 68px; object-fit: cover; background: var(--surface-3); }
.todo-image figcaption {
  padding: .28rem .4rem; color: var(--muted); font-size: var(--t-2xs);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.image-copy {
  position: absolute; top: .3rem; right: .3rem; width: 25px; height: 25px; padding: 0;
  border-radius: 6px; border-color: color-mix(in oklch, var(--line-strong) 75%, transparent);
  background: color-mix(in oklch, var(--surface) 88%, transparent); color: var(--fg-soft);
  opacity: 0; transform: translateY(-2px); backdrop-filter: blur(5px);
}
.image-copy svg { width: 13px; height: 13px; }
.todo-image:hover .image-copy, .image-copy:focus-visible, .image-copy.copied { opacity: 1; transform: none; }
.image-copy:hover { background: var(--surface); color: var(--fg); opacity: 1; }
.image-copy.copied { color: var(--ok); border-color: var(--ok-line); background: var(--ok-soft); }

/* Finished work is a record, not a list of things to do, so it folds away. The heading is
   the control: same weight and rhythm as the "Open" heading it sits under. */
.done-fold { margin-top: var(--s5); }
.done-fold > summary {
  cursor: pointer; list-style: none; display: flex; align-items: center; gap: var(--s2);
  font-size: var(--t-lg); font-weight: var(--w-semi); letter-spacing: -.011em;
  color: var(--fg); padding: var(--s1) 0; width: fit-content;
}
.done-fold > summary::-webkit-details-marker { display: none; }
.done-fold > summary::before {
  content: "\25B8"; color: var(--faint); font-size: var(--t-md); line-height: 1; flex: none;
  transition: transform var(--dur-2) var(--ease);
}
.done-fold[open] > summary::before { transform: rotate(90deg); }
.done-fold > summary:hover { color: var(--fg); }
.done-fold > summary:hover::before { color: var(--fg-soft); }
.done-fold > summary .muted { font-weight: var(--w-normal); }
.done-list { margin-top: var(--s3); }

/* The completion toggle is a real form post; it only looks like a checkbox. */
.check {
  flex: none; width: 20px; height: 20px; padding: 0; margin-top: 1px;
  border-radius: 50%; border: 1.5px solid var(--line-strong);
  background: transparent; color: transparent;
}
.check svg { width: 12px; height: 12px; }
.check:hover { border-color: var(--ok); color: var(--ok); opacity: 1; }
.todo.done .check { border-color: var(--ok); background: var(--ok); color: var(--surface); }
.todo.done .check:hover { background: transparent; color: var(--ok); }

/* Copy sat on the todo row, where the nearest thing to it was the todo text. It belongs in
   the prompt row, and says which of the two it copies. The row is a <summary>, so the
   script stops the click from toggling the disclosure. */
.copy-prompt {
  display: inline-flex; align-items: center; gap: .35rem; flex: none;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .22rem .5rem .22rem .42rem;
  color: var(--fg-soft); font-size: var(--t-xs); font-weight: var(--w-medium);
}
.copy-prompt svg { width: 13px; height: 13px; }
.copy-prompt:hover {
  background: var(--surface-3); border-color: var(--line-strong); color: var(--fg); opacity: 1;
}
.copy-prompt.copied { background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok); }

/* --- prompt disclosure ------------------------------------------------------ */

/* Everything except the todo and its primary action lives behind this summary: model,
   grounding, assumptions, the prompt itself, and the destructive actions. */
.prompt {
  border-top: 1px solid var(--line);
  position: relative;
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
.prompt--enter { opacity: 0; transform: translateY(-3px); }

.prompt > summary {
  cursor: pointer; display: flex; gap: var(--s2); align-items: center; position: relative;
  list-style: none; padding: .5rem .8rem; font-size: var(--t-sm); color: var(--muted);
  transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
  border-radius: 0 0 var(--r-md) var(--r-md);
}
.prompt > summary:hover { color: var(--fg); background: var(--surface-2); }
.prompt > summary::-webkit-details-marker { display: none; }
/* A disclosure triangle the summary draws itself, so it can sit inside a flex row. */
.prompt > summary::before {
  content: "\25B8"; color: var(--faint); font-size: var(--t-md); line-height: 1; flex: none;
  transition: transform var(--dur-2) var(--ease);
}
.prompt[open] > summary::before { transform: rotate(90deg); }
.prompt[open] > summary { border-radius: 0; }
.prompt > summary .label { font-weight: var(--w-semi); color: var(--fg-soft); }
.prompt > summary .spacer { flex: 1; }

.prompt__body { padding: 0 .8rem .8rem; display: flex; flex-direction: column; gap: var(--s3); }
.prompt__foot {
  display: flex; align-items: center; gap: var(--s2);
  border-top: 1px solid var(--line); padding-top: var(--s2); flex-wrap: wrap;
}
.prompt__foot .spacer { flex: 1; }
.prompt__foot .model { font-family: var(--mono); font-size: var(--t-xs); color: var(--faint); }

.prompt textarea {
  font-family: var(--mono); font-size: var(--t-xs); line-height: 1.6;
  background: var(--surface-2); border-color: var(--line); color: var(--fg-soft);
}
.prompt textarea:focus { background: var(--surface); color: var(--fg); }
.prompt-edit { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2); }

.prompt--error > summary .label { color: var(--danger); }
.prompt__note { font-size: var(--t-sm); color: var(--muted); margin: 0; }
.prompt__note .error { color: var(--danger); }

/* --- generation state ------------------------------------------------------- */

/* Pending, done and error all render the same collapsed summary row, so a prompt
   arriving never moves anything below it. */
.prompt__state { display: inline-flex; align-items: center; gap: var(--s2); }
.prompt__state .spark {
  width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex: none;
  animation: pulse 1.4s var(--ease) infinite;
}
@keyframes pulse { 0%, 100% { opacity: .25; transform: scale(.8); } 50% { opacity: 1; transform: scale(1); } }

/* An indeterminate hairline along the bottom of the summary row: it is positioned, so
   it costs no height whether it is there or not. */
.todo[data-prompt-status="pending"] .prompt > summary::after,
.todo[data-prompt-status="generating"] .prompt > summary::after,
.todo.is-busy .prompt > summary::after {
  content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1.5px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  background-size: 40% 100%; background-repeat: no-repeat;
  animation: sweep 1.5s var(--ease) infinite;
}
@keyframes sweep {
  0% { background-position: -45% 0; }
  100% { background-position: 145% 0; }
}

.todo.is-busy .prompt__body { opacity: .5; pointer-events: none; }

.skeleton { display: flex; flex-direction: column; gap: var(--s2); padding-top: var(--s1); }
.skeleton i {
  display: block; height: 9px; border-radius: var(--r-full);
  background: var(--skeleton); background-size: 300% 100%;
  animation: shimmer 1.6s linear infinite;
}
.skeleton i:nth-child(2) { width: 92%; animation-delay: .1s; }
.skeleton i:nth-child(3) { width: 74%; animation-delay: .2s; }
.skeleton i:nth-child(4) { width: 84%; animation-delay: .3s; }
.skeleton i:nth-child(5) { width: 46%; animation-delay: .4s; }
@keyframes shimmer { from { background-position: 150% 0; } to { background-position: -150% 0; } }

/* The composer button while a generation is being posted. */
button.is-busy { pointer-events: none; opacity: .7; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
  .prompt__state .spark { opacity: .8; }
  .skeleton i { background: var(--surface-3); }
  .todo[data-prompt-status="pending"] .prompt > summary::after,
  .todo[data-prompt-status="generating"] .prompt > summary::after,
  .todo.is-busy .prompt > summary::after { background: var(--accent); background-size: 100% 100%; opacity: .5; }
}

@media (max-width: 540px) {
  .composer__foot { align-items: stretch; flex-direction: column; gap: var(--s2); }
  .attach-help { align-items: center; flex-direction: row; gap: var(--s2); }
  .composer__foot > .accent { width: 100%; }
  .todo-image { flex-basis: 92px; }
  .todo-image img { height: 60px; }
  .image-copy { opacity: 1; transform: none; }
}

/* --- retrieval grounding ---------------------------------------------------- */

/* Why the prompt says what it says. Both fold, because the answer only matters when the
   prompt looks wrong; the summary carries the count so nothing is hidden outright. */
.fold { margin: 0; font-size: var(--t-sm); }
.fold > summary { cursor: pointer; color: var(--muted); list-style: none; padding: .1rem 0; }
.fold > summary:hover { color: var(--fg); }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before {
  content: "\25B8"; display: inline-block; width: .9em; font-size: var(--t-md); color: var(--faint);
  transition: transform var(--dur-2) var(--ease);
}
.fold[open] > summary::before { transform: rotate(90deg); }
.fold ul { list-style: none; margin: var(--s1) 0 0; padding: 0 0 0 1em; }
.fold li { padding: .1rem 0; }

.grounded li { font-family: var(--mono); font-size: var(--t-xs); overflow-wrap: anywhere; color: var(--fg-soft); }
.grounded .dir { color: var(--faint); }
.grounded b { font-weight: var(--w-semi); color: var(--fg); }

/* Assumptions stay open: they are the thing most likely to be wrong, and reading them is
   the last check before the prompt gets pasted into an agent. */
.assumptions {
  background: var(--warn-soft); border: 1px solid var(--warn-line);
  border-radius: var(--r-sm); padding: .5rem .7rem;
}
.assumptions > summary { color: var(--warn); font-weight: var(--w-semi); }
.assumptions > summary:hover { color: var(--warn); }
.assumptions > summary::before { color: var(--warn); }
.assumptions li { padding: .18rem 0; color: var(--fg-soft); }
.assumptions li + li { border-top: 1px solid var(--warn-line); }
.assumptions .hint { margin: var(--s2) 0 0; padding-left: 1em; font-size: var(--t-xs); color: var(--muted); }

/* --- settings shell --------------------------------------------------------- */

.settings { display: grid; grid-template-columns: 176px minmax(0, 1fr); gap: var(--s5); align-items: start; }
.settings-nav { position: sticky; top: 4.25rem; display: flex; flex-direction: column; gap: 1px; }
.nav-group {
  font-size: var(--t-2xs); font-weight: var(--w-bold); letter-spacing: .09em; text-transform: uppercase;
  color: var(--faint); padding: var(--s3) var(--s2) var(--s1);
}
.nav-group:first-child { padding-top: 0; }
.settings-nav a {
  display: flex; align-items: center; gap: var(--s2); padding: .35rem var(--s2);
  border-radius: var(--r-sm); color: var(--muted); text-decoration: none; font-size: var(--t-md);
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.settings-nav a:hover { background: var(--surface-2); color: var(--fg); }
.settings-nav a.on { background: var(--surface-2); color: var(--fg); font-weight: var(--w-semi); }
.nav-dot { width: 6px; height: 6px; border-radius: 50%; margin-left: auto; flex: none; }
.nav-dot--ok { background: var(--ok); }
.nav-dot--warn { background: var(--warn); }
.nav-dot--bad { background: var(--danger); }
.settings-body { min-width: 0; }
.settings-body > h1 { margin-top: 0; }

.section { margin: 0 0 var(--s6); scroll-margin-top: 4.5rem; }
.section__head { margin-bottom: var(--s3); }
.section__head h2 { margin: 0; font-size: var(--t-xs); font-weight: var(--w-bold); letter-spacing: .08em; text-transform: uppercase; color: var(--faint); }
.section__head p { margin: var(--s2) 0 0; color: var(--muted); font-size: var(--t-md); max-width: 56ch; }

.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s4); margin-bottom: var(--s3);
}
.card__head { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s2); }
.card__head h2, .card__head h3 { margin: 0; font-size: var(--t-base); font-weight: var(--w-semi); letter-spacing: -.005em; text-transform: none; }
.card > .muted { margin-top: 0; max-width: 60ch; }
.card > .muted:last-child { margin-bottom: 0; }

/* Destructive work gets its own colour, not just a red word inside a neutral card. */
.card--danger { border-color: var(--danger-line); background: var(--danger-soft); }
.card--danger .card__head h2 { color: var(--danger); }
.card--danger .muted { color: var(--fg-soft); }

.sub-block { margin: var(--s3) 0 0; display: flex; flex-direction: column; gap: var(--s3); }
details.sub { border-top: 1px solid var(--line); margin-top: var(--s3); }
details.sub > summary {
  cursor: pointer; font-size: var(--t-md); color: var(--muted); padding: var(--s2) 0 var(--s1);
  list-style: none; display: flex; align-items: center; gap: var(--s2);
  transition: color var(--dur-1) var(--ease);
}
details.sub > summary::-webkit-details-marker { display: none; }
details.sub > summary::before {
  content: "\25B8"; font-size: var(--t-md); line-height: 1; color: var(--faint);
  transition: transform var(--dur-2) var(--ease);
}
details.sub[open] > summary::before { transform: rotate(90deg); }
details.sub > summary:hover { color: var(--fg); }
details.sub[open] > summary { color: var(--fg); }
details.sub > * + * { margin-top: var(--s2); }
details.sub > form, details.sub > p, details.sub > pre { margin-bottom: var(--s2); }

.connected {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; font-size: var(--t-md);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .45rem .65rem;
}
.connected .dot { background: var(--ok); }
.connected .spacer { flex: 1 1 auto; }
.connected form { margin-left: 0; }
.connected a { color: var(--muted); font-size: var(--t-sm); }

/* Shows what the domain above it resolved to. Its own class rather than `.connected`,
   which lives in a flex column that supplies the gap this one has to bring itself: without
   it the row sat flush against the input and read as part of the field. */
.fav-preview {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .55rem .7rem; margin-top: var(--s3);
  font-size: var(--t-md);
}
.fav-preview .spacer { flex: 1 1 auto; }
.fav-preview .muted { margin: 0; }

.row--split { display: flex; align-items: center; gap: var(--s4); justify-content: space-between; flex-wrap: wrap; }
.row--split .muted { margin: 0; }

.row label.grow { flex: 1; display: flex; flex-direction: column; gap: var(--s1); font-size: var(--t-xs); color: var(--muted); }
.row label.grow select { width: 100%; }
.row input[type="password"] { flex: 1; }

.code-block { position: relative; }
.code-block .copy { position: absolute; top: var(--s2); right: var(--s2); }
.code-block pre.code { padding-right: 5rem; }

.stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3); }
.stats > div { display: flex; flex-direction: column; gap: 2px; }
.stats strong { font-size: var(--t-2xl); font-weight: var(--w-bold); line-height: 1.1; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.stats .muted { font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .06em; color: var(--faint); }

/* --- context health --------------------------------------------------------- */

.meter-label { display: flex; justify-content: space-between; font-size: var(--t-xs); color: var(--muted); margin: var(--s3) 0 var(--s1); }
.meter { display: flex; height: 8px; border-radius: var(--r-full); overflow: hidden; background: var(--surface-3); }
.meter i { display: block; height: 100%; }
.legend { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3); margin-top: var(--s2); font-size: var(--t-xs); color: var(--muted); }
.legend span { display: flex; align-items: center; gap: var(--s1); white-space: nowrap; }
.legend i { width: 7px; height: 7px; border-radius: 2px; display: inline-block; flex: none; }
.legend i.track { background: var(--surface-3); }

.facts-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s2); margin-top: var(--s3); }
.facts-grid .fact { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: .45rem .6rem; }
.facts-grid .fact span { display: block; font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .06em; color: var(--faint); font-weight: var(--w-semi); }
.facts-grid .fact b { font-size: var(--t-md); font-weight: var(--w-semi); }

.found-row { display: flex; align-items: baseline; gap: var(--s3); padding: .45rem 0; border-top: 1px solid var(--line); flex-wrap: wrap; }
.found-row:first-of-type { border-top: 0; }
.found-row .nm { font-weight: var(--w-semi); font-size: var(--t-sm); min-width: 7.5rem; }
.found-row .mt { color: var(--muted); font-size: var(--t-sm); flex: 1; min-width: 0; overflow-wrap: anywhere; }
.found-row .pill { margin-left: 0; }

/* --- footer ----------------------------------------------------------------- */

/* Two hairline rows on the same rail as the header: what the product does, and the quiet
   line where the theme lives. The theme swatches are the only colour in it, which is the
   whole idea: the footer is where you change the look, not a directory of links. */
footer.foot { border-top: 1px solid var(--line); flex: none; }
.foot-main {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s5); padding: var(--s5) 0 var(--s4); flex-wrap: wrap;
}
.foot-say { font-size: var(--t-sm); color: var(--muted); margin: var(--s1) 0 0; max-width: 34ch; }
.foot-links { display: flex; gap: var(--s4); }
.foot-links a {
  color: var(--muted); display: inline-flex; padding: var(--s1); border-radius: var(--r-sm);
  transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.foot-links a:hover { color: var(--fg); background: var(--surface-2); }
.foot-links svg { width: 18px; height: 18px; display: block; }
.foot-base {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  border-top: 1px solid var(--line); padding: var(--s3) 0;
  font-size: var(--t-xs); color: var(--faint); flex-wrap: wrap;
}
.foot-base .mono { font-family: var(--mono); font-size: var(--t-xs); }

/* The theme picker lives behind a button. Five 18px dots asked people to tell Graphite
   from Tide by a sliver of colour, and gave System no way to say it was showing Paper. */
.theme-open {
  display: inline-flex; align-items: center; gap: var(--s2);
  color: var(--muted); text-decoration: none; font-size: var(--t-sm);
  border: 1px solid var(--line); border-radius: var(--r-full); padding: .2rem .6rem .2rem .3rem;
  transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              background var(--dur-1) var(--ease);
}
.theme-open:hover { color: var(--fg); border-color: var(--line-strong); background: var(--surface-2); }
.theme-chip {
  width: 16px; height: 16px; border-radius: 50%; flex: none; position: relative;
  border: 1px solid var(--line-strong); overflow: hidden; display: block;
}
.theme-chip i { position: absolute; right: 2px; bottom: 2px; width: 7px; height: 7px; border-radius: 50%; }
.theme-chip--split,
.theme-swatch--split { background: linear-gradient(105deg, oklch(.972 .005 95) 50%, oklch(.168 .010 280) 50%); }

/* Opened by :target, so a theme can still be changed with scripting off, like every other
   control here. The post lands back on #theme so a second can be tried without reopening. */
.modal { display: none; }
.modal:target { display: grid; position: fixed; inset: 0; z-index: 60; place-items: center; padding: var(--s4); }
.modal__scrim { position: absolute; inset: 0; background: rgba(10, 10, 14, .55); }
.modal__card {
  position: relative; width: 100%; max-width: 440px;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); box-shadow: var(--shadow-3); padding: var(--s5);
}
.modal__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s4); }
.modal__head h2 { margin: 0; font-size: var(--t-lg); font-weight: var(--w-semi); }
.modal__x { color: var(--muted); text-decoration: none; font-size: var(--t-xl); line-height: 1; padding: 0 var(--s1); }
.modal__x:hover { color: var(--fg); }

.theme-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2); }
button.theme-tile {
  width: 100%; display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s3); color: var(--fg); text-align: left; font-weight: var(--w-normal);
}
button.theme-tile:hover { background: var(--surface-2); border-color: var(--line-strong); opacity: 1; }
button.theme-tile[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.theme-name { font-size: var(--t-md); font-weight: var(--w-semi); }
.theme-note { font-size: var(--t-xs); color: var(--muted); font-weight: var(--w-normal); }
/* A miniature of the page it selects rather than a colour chip: ground, a line of text, an
   accent. Enough to tell two dark themes apart at a glance. */
.theme-swatch {
  width: 100%; height: 38px; border-radius: var(--r-sm); margin-bottom: var(--s2);
  border: 1px solid var(--line); position: relative; overflow: hidden; display: block;
}
.theme-swatch .s-line { position: absolute; left: 8px; top: 11px; width: 44%; height: 3px; border-radius: 2px; opacity: .85; }
.theme-swatch .s-dot { position: absolute; left: 8px; top: 21px; width: 22px; height: 6px; border-radius: 3px; }

/* --- responsive ------------------------------------------------------------- */

@media (max-width: 720px) {
  .settings { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .settings-nav { position: static; flex-direction: row; flex-wrap: wrap; gap: var(--s1); }
  .nav-group { display: none; }
  .settings-nav a { background: var(--surface); border: 1px solid var(--line); }
}
@media (max-width: 600px) {
  main { padding: var(--s4) var(--s3) var(--s6); }
  .shell { padding: 0 var(--s3); }
  .account .who { display: none; }
  .foot-main { flex-direction: column; gap: var(--s4); }
  .foot-base { flex-direction: column; align-items: flex-start; gap: var(--s3); }
  .theme-grid { grid-template-columns: minmax(0, 1fr); }
  .modal__card { padding: var(--s4); }
  .row { flex-direction: column; }
  .card { padding: var(--s3); }
  .connected .spacer { display: none; }
  .row--split { flex-direction: column; align-items: stretch; }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .facts-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .found-row .nm { min-width: 0; }
  .row label.grow { width: 100%; }
  .composer { flex-direction: column; }
  .composer button { width: 100%; }
}
