/* Neutral and low-contrast, in both schemes. The category tints are the only
   colour in the app; everything else is greyscale so the buckets do the sorting
   visually. Red appears in exactly one place — a due date that has passed.

   Every colour is a token, and dark mode redefines the tokens rather than the
   rules — so a rule written once is right in both. The one exception is the
   bucket tints, which come from shared/vocabulary.json as light pastels: in dark
   they are mixed into the surface instead of used neat.

   Which scheme is on is decided by `data-scheme` on <html>, never by a media
   query here. The page always carries the attribute — set before first paint by
   the small script in app/layout.tsx — because the choice is the user's and only
   defaults to the system's. One dark block, not one per way of reaching it. */

:root {
  color-scheme: light;

  --bg: #fafafa;
  --surface: #ffffff;
  --surface-2: #f5f5f5;
  --border: #eaeaea;
  --border-strong: #dcdcdc;

  --ink: #111111;
  --ink-strong: #000000;      /* the primary button, pressed */
  --on-ink: #ffffff;          /* text on top of --ink */
  --body: #3f3f46;
  --muted: #6b7280;
  --faint: #9ca3af;
  --overdue: #dc2626;

  /* Cards sit on a tinted bucket, so they are a wash over it rather than a
     colour of their own. */
  --card-bg: rgba(255, 255, 255, 0.72);
  --card-border: rgba(17, 17, 17, 0.06);
  --chip-bg: rgba(17, 17, 17, 0.05);
  --drop-bg: rgba(17, 17, 17, 0.045);
  --drop-border: rgba(17, 17, 17, 0.16);
  --shadow: 0 6px 20px rgba(17, 17, 17, 0.14);

  /* One padding value for every card, on all four sides. */
  --card-pad: 24px;

  --radius: 10px;
  --ring: rgba(17, 17, 17, 0.14);
}

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

    --bg: #0f1011;
    --surface: #17181a;
    --surface-2: #202225;
    --border: #26282c;
    --border-strong: #383b41;

    --ink: #f2f3f4;
    --ink-strong: #ffffff;
    --on-ink: #0f1011;
    --body: #c9ccd1;
    --muted: #949aa3;
    --faint: #71767e;
    --overdue: #ff6b6b;

    --card-bg: rgba(255, 255, 255, 0.045);
    --card-border: rgba(255, 255, 255, 0.07);
    --chip-bg: rgba(255, 255, 255, 0.08);
    --drop-bg: rgba(255, 255, 255, 0.05);
    --drop-border: rgba(255, 255, 255, 0.18);
    --shadow: 0 6px 20px rgba(0, 0, 0, 0.5);

    --ring: rgba(255, 255, 255, 0.18);
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto,
        system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

button, input, textarea, select { font: inherit; color: inherit; }

.page {
  max-width: 1080px;
  margin: 0 auto;
  padding: max(28px, env(safe-area-inset-top)) 20px calc(64px + env(safe-area-inset-bottom));
}

/* --- masthead --- */
.masthead {
  margin-bottom: 22px;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.masthead-end { display: flex; align-items: center; gap: 10px; }
.masthead h1 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.01em; }
.tagline { margin: 2px 0 0; font-size: 13px; color: var(--faint); }
.next-resolve { margin: 0; font-size: 12px; color: var(--faint); white-space: nowrap; }

/* --- capture --- */
.capture {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}
.capture-prompt {
  display: block;
  font-size: 15px;
  font-weight: 550;
  margin-bottom: 10px;
}
#capture-text {
  width: 100%;
  min-height: 62px;
  resize: vertical;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  padding: 10px 12px;
  outline: none;
}
#capture-text:focus { border-color: var(--border-strong); box-shadow: 0 0 0 3px var(--ring); }
#capture-text::placeholder { color: var(--faint); }

.capture-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.spacer { flex: 1; }
.hint { font-size: 12px; color: var(--faint); }

/* --- buttons --- */
.btn {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  border-radius: 8px;
  padding: 7px 13px;
  cursor: pointer;
  font-size: 14px;
  line-height: 1.2;
}
.btn:hover:not(:disabled) { background: var(--surface-2); }
.btn:disabled { opacity: 0.45; cursor: default; }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.btn-primary { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.btn-primary:hover:not(:disabled) { background: var(--ink-strong); }
/* A disabled primary shouldn't read as a heavy grey slab waiting to be pressed. */
.btn-primary:disabled { background: var(--surface); border-color: var(--border); color: var(--faint); opacity: 1; }
.btn-quiet { display: inline-flex; align-items: center; gap: 7px; }
.btn-ghost { border-color: transparent; color: var(--muted); }
.btn-ghost:hover { color: var(--overdue); background: transparent; }

.mic {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--faint); flex: none;
}
.recording .mic { background: var(--overdue); animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.35; } }
.record-timer { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* The scheme switch. One small button, three states, and the icon says which one
   you are in rather than which one you would get — the label under it says that.
   Quiet enough to ignore, which is most days. */
.scheme {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  background: none; border: 1px solid transparent; border-radius: 7px;
  color: var(--faint); cursor: pointer;
}
.scheme:hover { color: var(--ink); background: var(--surface-2); border-color: var(--border); }
.scheme:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.scheme svg { display: block; }

/* --- resolve --- */
.resolve {
  display: block;
  width: 100%;
  margin: 18px 0 0;
  padding: 13px 16px;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 15px;
  font-weight: 550;
}
/* An author `display` beats the UA rule behind the `hidden` attribute, so the
   button has to be told to actually go away — otherwise an empty inbox leaves an
   empty button-shaped box sitting where Resolve was. */
.resolve[hidden] { display: none; }
.resolve:hover:not(:disabled) { background: var(--surface-2); }
.resolve:disabled { cursor: default; color: var(--muted); }
.resolve:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.resolve.busy { animation: breathe 2.2s ease-in-out infinite; }
@keyframes breathe { 50% { background: var(--surface-2); } }

.run-note { margin: 8px 2px 0; font-size: 12.5px; color: var(--muted); }
.run-note.bad { color: var(--overdue); }

.banner {
  margin-top: 14px; padding: 10px 12px;
  border: 1px solid var(--border-strong); border-radius: 8px;
  background: var(--surface); font-size: 13px; color: var(--body);
}

/* --- sections --- */
.section-title {
  margin: 30px 0 10px;
  font-size: 15px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted);
}
.count { color: var(--faint); font-weight: 500; }
/* A count is an aside, so it stays put while its heading grows. */
.section-title .count { font-size: 12px; }

.inbox-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }

.raw-row {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: var(--card-pad);
  display: flex; gap: 10px; align-items: flex-start;
}
.raw-row .grip { margin-top: 2px; margin-left: calc(-12px - 10px); }
.raw-row p { margin: 0; flex: 1; color: var(--body); }
.raw-row .stamp { font-size: 12px; color: var(--faint); white-space: nowrap; }
.raw-row.pending p { color: var(--faint); font-style: italic; }
.raw-row.needs-info { border-style: dashed; }
.needs-note { margin: 4px 0 0 !important; font-size: 12.5px; color: var(--muted); }
.raw-row p.editable {
  border-bottom: 1px dashed var(--border-strong); outline: none; font-style: normal;
}
.raw-row p.editable:focus { border-bottom-color: var(--ink); }
.raw-row input.inline-edit {
  flex: 1; border: 1px solid var(--border-strong); border-radius: 6px; padding: 6px 8px;
}
/* A capture that has not reached the server yet. It is held in this browser's
   IndexedDB until it does, so it is a real thought sitting in the inbox rather
   than an error message — the dashed border says only that it is still in
   transit. */
.raw-row.queued { border-style: dashed; }
.raw-row.queued > div > p:first-child { color: var(--body); }
.queued-note { margin: 4px 0 0 !important; font-size: 12.5px; color: var(--muted); }
.queued-note.bad { color: var(--overdue); }
.row-retry {
  flex: none; align-self: flex-start;
  border: 1px solid var(--border-strong); background: var(--surface);
  border-radius: 6px; padding: 3px 9px;
  font-size: 12px; color: var(--muted); cursor: pointer;
}
.row-retry:hover { color: var(--ink); }

.row-delete {
  flex: none; align-self: flex-start;
  border: none; background: none; cursor: pointer;
  width: 24px; height: 24px; line-height: 1; font-size: 18px;
  color: var(--faint); opacity: 0.55; transition: opacity 0.12s, color 0.12s;
}
.row-delete:hover, .row-delete:active { opacity: 1; color: var(--overdue); }

/* --- buckets toolbar --- */
.buckets-toolbar { display: flex; justify-content: flex-end; margin: 22px 0 8px; }
/* Same quiet icon-button treatment as the colour scheme switch: small, mostly
   invisible, and legible the moment you look for it. */
.view-toggle {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  background: none; border: 1px solid transparent; border-radius: 7px;
  color: var(--faint); cursor: pointer;
}
.view-toggle:hover { color: var(--ink); background: var(--surface-2); border-color: var(--border); }
.view-toggle:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.view-toggle svg { display: block; }

/* --- buckets --- */
/* Columns rather than a grid: buckets differ a lot in height, and a grid would
   align them into rows and leave a band of dead space beside every short one.
   List view drops to one column of its own accord — see [data-view="list"]
   below — because a column of collapsed rows has no height left to stagger. */
.buckets {
  margin-top: 0;
  columns: 3 300px;
  column-gap: 14px;
}
.buckets[data-view="list"] { columns: 1; }
.buckets[data-view="list"] .bucket { margin-bottom: 8px; }
.bucket {
  /* --tint is set per bucket by app.js, straight from shared/vocabulary.json. */
  background: var(--tint);
  container: bucket / inline-size;
  break-inside: avoid;
  display: inline-block;
  width: 100%;
  margin-bottom: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
}
/* The tints are pastels chosen for a white page, and far too bright on a dark
   one. Mixing them down into the surface is not enough on its own: they are so
   pale that what survives the mix is the lightness and not the hue, and nine
   buckets all come out the same grey — which loses the one thing the tints are
   for. So the hue is kept, the lightness is replaced outright, and the chroma is
   amplified to make up for what the darkness takes away. Capped, because the
   yellows start out with three times the chroma of the blues; and multiplied
   rather than set, so Someday — a true grey, with no hue to speak of — stays one.

   Three declarations, weakest first: the browser keeps whichever it understands
   last, and a browser that understands neither gets a plain dark bucket. */
:root[data-scheme="dark"] .bucket {
  background: var(--surface);
  background: color-mix(in oklab, var(--tint) 20%, var(--surface));
  background: oklch(from var(--tint) 0.235 min(c * 3.2, 0.055) h);
}
.bucket.drop { border-color: var(--border-strong); box-shadow: inset 0 0 0 1px var(--border-strong); }
.bucket-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 9px; }
.bucket-head h3 {
  margin: 0; font-size: 15px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--body);
}
.bucket-head .count { font-size: 12px; }
/* In list view the head is a <summary> instead of a <div> — same look, plus a
   pointer and a chevron that says there is more underneath. Collapsed, the head
   drops the margin it normally keeps for the list below it, so a closed bucket
   is just its own name and the container's padding, not a name-sized gap too. */
summary.bucket-head { list-style: none; cursor: pointer; user-select: none; }
summary.bucket-head::-webkit-details-marker { display: none; }
summary.bucket-head::after {
  content: "";
  width: 6px; height: 6px;
  margin-left: auto;
  border-right: 1.4px solid var(--faint); border-bottom: 1.4px solid var(--faint);
  transform: rotate(-45deg);
  transition: transform 0.15s;
}
details[open] > summary.bucket-head::after { transform: rotate(45deg); }
details:not([open]) > summary.bucket-head { margin-bottom: 0; }
.bucket-empty { margin: 0; font-size: 12.5px; color: var(--faint); }
.bucket ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; min-height: 26px; }

.card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 8px;
}
.card.dragging { opacity: 0.4; }
.card.open { background: var(--surface); border-color: var(--border-strong); }
/* --surface is the bucket's own base in dark, so an open card needs to lift off
   it rather than match it. */
:root[data-scheme="dark"] .card.open { background: var(--surface-2); }

/* The card that follows the pointer, and the gap it left behind. Both are built
   by app.js, but their looks belong here. */
.drag-ghost {
  position: fixed; left: 0; top: 0; z-index: 50;
  pointer-events: none; opacity: 0.9;
  border-radius: 8px; background: var(--surface); box-shadow: var(--shadow);
}
.drop-placeholder { background: var(--drop-bg); border: 1px dashed var(--drop-border); }

.card-main {
  display: flex; gap: 8px; align-items: flex-start;
  padding: var(--card-pad); cursor: pointer;
}
.grip {
  flex: none; width: 12px; height: 16px; cursor: grab;
  /* Claim the gesture so a drag on a phone moves the card, not the page. */
  touch-action: none;
  color: var(--faint); opacity: 0; transition: opacity 0.12s;
  background: radial-gradient(circle, currentColor 1px, transparent 1.2px) 0 2px / 5px 5px repeat;
}
.card:hover .grip, .raw-row:hover .grip { opacity: 0.75; }
/* The handle belongs to the gutter, not to the content. Pulling it back by its
   own width plus the row's gap leaves the first real element — the checkbox —
   sitting 24px from the edge, the same 24px the text keeps on the right. */
.card-main .grip { margin-left: calc(-12px - 8px); }
.grip:active { cursor: grabbing; }

/* Always visible, unlike the grip: this is the one action worth doing without
   opening anything, so it should never need to be hunted for. */
.card-check {
  flex: none;
  width: 15px; height: 15px;
  /* All four sides, not just the top: a checkbox carries a UA margin that would
     otherwise push the content gutter wider on the left than on the right. */
  margin: 1px 0 0;
  accent-color: var(--body);
  cursor: pointer;
}

.card-body { flex: 1; min-width: 0; }
.card-text { margin: 0; color: var(--ink); font-size: 14px; overflow-wrap: anywhere; }
.card.is-done .card-text { color: var(--faint); text-decoration: line-through; }
.card-meta {
  margin: 4px 0 0; display: flex; flex-wrap: wrap; gap: 8px;
  font-size: 11.5px; color: var(--muted);
}
.chip {
  background: var(--chip-bg); border-radius: 4px; padding: 1px 6px;
}
.meta-due.overdue { color: var(--overdue); }
.meta-high { font-weight: 600; color: var(--body); }
/* The one link on a card. It reads as another piece of meta rather than a call to
   action, because it is: most thoughts never have one. */
.meta-notion {
  color: var(--muted); text-decoration: none;
  border-bottom: 1px solid var(--border-strong);
}
.meta-notion:hover { color: var(--ink); }

/* --- editor --- */
.editor { padding: 0 var(--card-pad) var(--card-pad); display: grid; gap: 10px; }
.field { display: grid; gap: 4px; }
.field-label { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
.field-row { display: grid; gap: 10px; }
/* Two fields side by side only once the bucket is wide enough for a date picker;
   in a narrow column they stack instead of being crushed to a few pixels. */
@container bucket (min-width: 420px) {
  .field-row { grid-template-columns: 1fr 1fr; }
}
.editor input, .editor select, .editor textarea {
  width: 100%; border: 1px solid var(--border-strong); border-radius: 6px;
  padding: 7px 9px; background: var(--surface); outline: none;
  font-size: 14px;
}
.editor input:focus, .editor select:focus, .editor textarea:focus {
  box-shadow: 0 0 0 3px var(--ring);
}
.editor textarea { resize: vertical; }
.raw summary { font-size: 12px; color: var(--muted); cursor: pointer; }
.raw p { margin: 6px 0 0; font-size: 13px; color: var(--body); }
.raw audio { width: 100%; margin-top: 8px; }
.editor-actions { display: flex; align-items: center; gap: 8px; }
/* "Open in Notion" is an anchor wearing .btn — it navigates, so it stays an
   anchor, and needs the few things a <button> gets for free. It is a grid item in
   .editor, so it fills the row on its own without being told to.

   The [hidden] guard is not optional: an author `display` beats the UA rule
   behind the hidden attribute, and without it the link sat on screen next to the
   Expand button it replaces. Third time in this stylesheet — see .resolve. */
a.btn {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink); text-decoration: none;
}
a.btn[hidden] { display: none; }

/* --- tabs --- */
/* Done is somewhere you go, not a drawer that falls open under the buckets. The
   backlog and the record of what is finished are two views of the app, and the
   strip is the one place that says which one you are looking at. */
.tabs {
  display: flex; align-items: flex-end;
  margin: 26px 0 0;
  border-bottom: 1px solid var(--border);
}
/* As with Resolve: an author `display` beats the UA rule behind `hidden`, and
   without this the strip is still drawn on a cold offline start, offering two
   views of a backlog that has not been loaded. */
.tabs[hidden] { display: none; }
.tab {
  background: none; border: 0; border-bottom: 2px solid transparent;
  margin: 0 20px -1px 0; padding: 8px 1px;
  cursor: pointer;
  font-size: 13px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--faint);
}
.tab:hover { color: var(--body); }
.tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--ink); }
.tab:focus-visible { outline: none; border-radius: 4px; box-shadow: 0 0 0 3px var(--ring); }
.tab .count { margin-left: 5px; font-size: 12px; }

/* --- done --- */
.done { margin-top: 18px; }
.done-row p { color: var(--faint); text-decoration: line-through; }
.done-empty { margin: 20px 2px; font-size: 13px; color: var(--faint); }
/* Every finished thought is kept, but a list of four hundred of them is not a
   view. The recent ones are the useful ones; the rest are one press away. */
.show-all {
  margin-top: 12px; padding: 6px 0;
  background: none; border: 0; cursor: pointer;
  font-size: 12.5px; color: var(--muted);
}
.show-all:hover { color: var(--ink); }

/* --- the are-you-sure window --- */
/* Deleting is the only thing in the app that cannot be taken back, so it is the
   only thing that asks first. A real <dialog> rather than confirm(), so the
   question is asked in the app's own voice and not the browser's, with the
   hostname stamped on top of it. */
.ask {
  width: min(360px, calc(100vw - 40px));
  padding: 20px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.ask::backdrop { background: rgba(17, 17, 17, 0.3); }
.ask h2 { margin: 0; font-size: 15px; font-weight: 600; }
.ask-detail {
  margin: 8px 0 0; font-size: 13px; color: var(--muted); overflow-wrap: anywhere;
}
.ask-note { margin: 10px 0 0; font-size: 12.5px; color: var(--faint); }
.ask-actions { display: flex; align-items: center; gap: 8px; margin-top: 20px; }
.btn-danger { background: var(--overdue); border-color: var(--overdue); color: #fff; }
.btn-danger:hover:not(:disabled) { background: var(--overdue); filter: brightness(0.94); }
/* The red lightens in dark mode, and white text on it stops being readable. */
:root[data-scheme="dark"] .btn-danger { color: #23100f; }

.footer { margin-top: 40px; font-size: 12px; color: var(--faint); }
/* Two registers. The Claude login's date is ordinary footer furniture — it is
   true every day and should read as such. The worker being away, or that login
   about to lapse, is the one that has to catch the eye. */
.worker-note { color: var(--faint); }
.worker-note.warn { color: var(--overdue); }

/* The passphrase gate. Its own page, so it carries its own handful of rules. */
.gate { max-width: 340px; margin: 18vh auto 0; padding: 0 20px; }
.gate h1 { margin: 0 0 2px; font-size: 17px; font-weight: 600; }
.gate p.tagline { margin: 0 0 20px; }
.gate form { display: grid; gap: 10px; }
.gate input {
  width: 100%; border: 1px solid var(--border-strong); border-radius: 8px;
  padding: 10px 12px; background: var(--surface); outline: none;
}
.gate input:focus { box-shadow: 0 0 0 3px var(--ring); }
.gate .bad { font-size: 13px; color: var(--overdue); min-height: 18px; margin: 0; }

/* iOS zooms a focused input under 16px, which throws the layout on capture. */
@media (max-width: 640px) {
  .page { padding-left: 14px; padding-right: 14px; }
  input, textarea, select { font-size: 16px; }
  .buckets { columns: 1; }
  .grip { opacity: 0.6; }
  .card-check { width: 18px; height: 18px; }
}
