/* Design tokens + base element styling, duplicated from
   apps/backend/src/web-shell.ts's DESIGN_TOKENS/ELEMENT_STYLE — there is no
   shared CSS pipeline between the Hono templates there and this island's own
   `bun build`, so the two copies are kept in sync by hand. This used to be
   Pico's job (index.html linked /assets/pico.classless.min.css, precached
   by the service worker as the one out-of-scope entry); app.css is now
   fully self-contained, which is also what let that precache entry go away
   (see src/sw-routes.ts). Same "reading room" rationale as the backend
   pages: a warm palette with a dark-mode twin, serif reserved for
   headings, mono for literal data. */

:root {
  color-scheme: light dark;
  --paper: #fbf9f5;
  --paper-raised: #ffffff;
  --ink: #211e1a;
  --muted: #8a8375;
  --line: #dedace;
  --line-strong: #c9c3b4;
  --accent-cards: #0aa66a;
  --danger: #b3261e;
  --focus: #0aa66a;
  --radius: 4px;
  --mark-bg: #f3e2a6;
  --font-display: Charter, 'Bitstream Charter', 'Sitka Text', Cambria, 'Iowan Old Style', Georgia, serif;
  --font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #1b1815;
    --paper-raised: #242019;
    --ink: #ede7da;
    --muted: #a69c88;
    --line: #3a342a;
    --line-strong: #4a4436;
    --accent-cards: #4fd79a;
    --danger: #ff6b60;
    --focus: #4fd79a;
    --mark-bg: #4a3b12;
  }
}

/* Cross-document view transitions — see the matching comment in
   apps/backend/src/web-shell.ts's ELEMENT_STYLE for the full rationale.
   Chromium/Safari 18.2+ only; Firefox support is still in progress, so
   this doesn't fix the Firefox white flash today, but costs nothing on
   browsers that don't understand it. */
@view-transition {
  navigation: auto;
}

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

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.55;
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.25;
  margin: 1.2em 0 0.5em;
}

h1:first-child, h2:first-child {
  margin-top: 0;
}

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.15em;
}

a:hover {
  text-decoration-color: currentColor;
}

.hint, .muted {
  color: var(--muted);
}

code {
  font-family: var(--font-mono);
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.1em 0.35em;
  font-size: 0.9em;
}

label {
  display: block;
  font-weight: 600;
  margin-bottom: 1em;
}

input, textarea, select {
  font-family: var(--font-body);
  font-size: 1em;
  color: var(--ink);
  background: var(--paper-raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  padding: 0.5em 0.6em;
}

input:not([type="checkbox"], [type="radio"]), textarea, select {
  width: 100%;
}

button {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 1em;
  font-weight: 600;
  text-align: center;
  padding: 0.55em 1.1em;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  cursor: pointer;
}

button:hover {
  opacity: 0.82;
}

button:disabled {
  opacity: 0.5;
  cursor: default;
}

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

/* The cross-subsystem tab strip — static markup in index.html, styled to
   match apps/backend/src/web-shell.ts's BASE_STYLE exactly. `flex: 0 0
   auto` keeps it out of the space-distribution below, so #app's flex-grow
   gets the rest of the viewport (see #app and .review-session below). */
.app-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  list-style: none;
  padding: 0.5rem 1rem 0;
  margin: 0;
  background: var(--paper-raised);
  border-bottom: 1px solid var(--line-strong);
  flex: 0 0 auto;
}

.app-nav a {
  display: inline-block;
  padding: 0.5rem 0.75rem;
  margin-bottom: -1px;
  text-decoration: none;
  color: var(--muted);
  border-bottom: 2px solid transparent;
}

.app-nav a:hover {
  color: var(--ink);
}

.app-nav a[aria-current="page"] {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--ink);
}

/* Shell: header, sync status, the home screen's preset list, and the
   token gate. `flex: 1 1 auto` makes #app take up exactly the viewport
   height left over below .app-nav, whatever that nav's rendered height
   turns out to be — see .review-session's `min-height: 100%` below, which
   depends on this. */

#app {
  flex: 1 1 auto;
  max-width: 720px;
  margin: 0 auto;
  padding: 1rem;
  width: 100%;
}

.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 0.75rem;
  margin-bottom: 1rem;
}

.app-header h1 {
  margin: 0;
  font-size: 1.4rem;
}

.sync-status {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.85rem;
}

.sync-status .syncing {
  color: var(--muted);
}

.unsynced-count {
  background: var(--mark-bg);
  border-radius: 3px;
  padding: 0.1em 0.5em;
}

/* Nothing pending: the chip disappears instead of rendering an empty
   highlight. views/browse.ts writes '' into its own count rather than
   rebuilding the header it lives in. */
.unsynced-count:empty {
  display: none;
}

.sync-message {
  color: var(--danger);
}

.preset-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.preset-row {
  border-bottom: 1px solid var(--line);
  padding: 0.6rem 0;
}

.preset-row a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  text-decoration: none;
}

.preset-row .due-count {
  font-weight: bold;
  min-width: 2em;
  text-align: right;
}

.preset-row.paused {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--muted);
}

.preset-row .badge {
  font-size: 0.75em;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  padding: 0 0.4em;
}

.preset-list .empty {
  color: var(--muted);
  padding: 0.6rem 0;
}

.manage-link .hint {
  color: var(--muted);
  font-size: 0.85em;
}

.token-gate {
  max-width: 420px;
  margin: 3rem auto;
}

.token-gate .error {
  color: var(--danger);
}

/* --- Review session (docs/cards-design.md §8.3) --------------------------
   Big tap targets and a grade bar that never shifts position between
   cards — this runs on a phone, and a moving "Good" button gets you
   misgrades. `.grade-bar` is `position: sticky; bottom: 0`, always the
   same height regardless of card content, so its buttons sit in exactly
   the same place on screen for every card in a session — before reveal
   (one wide "Reveal" button) and after (two or four grade buttons occupy
   the same row). */

.review-session {
  display: flex;
  flex-direction: column;
  /* 100% of #app's own (flex-computed) height, not `calc(100vh - 2rem)` —
     #app's height already accounts for .app-nav's height above it (see
     #app's `flex: 1 1 auto`), so hand-carrying viewport arithmetic down
     here would double-count or drift if the nav ever changes height. */
  min-height: 100%;
}

.review-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding-bottom: 0.5rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--line-strong);
  font-size: 0.85rem;
}

.review-header .preset-name {
  font-weight: bold;
  margin-right: auto;
}

.review-header .remaining {
  color: var(--muted);
}

.review-header .outbox-count {
  background: var(--mark-bg);
  border-radius: 3px;
  padding: 0.1em 0.5em;
  min-width: 0;
}

/* Nothing pending (the common case between grades): no empty highlight
   chip — mirrors `.unsynced-count:empty`. `unsyncedLabel` writes '' both
   into the header template and via `renderOutboxCount`. */
.review-header .outbox-count:empty {
  display: none;
}

.card-area {
  flex: 1 1 auto;
  padding: 1rem 0 2rem;
}

.card-area .breadcrumb {
  color: var(--muted);
  font-size: 0.8rem;
  margin-bottom: 0.25rem;
  word-break: break-word;
}

.card-area .card-title {
  margin: 0 0 0.5rem;
}

.card-area .card-prompt {
  font-size: 1.1rem;
}

.card-back {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px dashed var(--line-strong);
  overflow-wrap: break-word;
}

.card-back img {
  max-width: 100%;
}

/* Sticky, fixed-height bar so the buttons never move between cards —
   the one CSS requirement worth calling out by name (chunk brief). */
.grade-bar {
  position: sticky;
  bottom: 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0.6rem;
  padding: 0.75rem 0;
  background: var(--paper);
  border-top: 1px solid var(--line-strong);
}

.grade-bar button {
  min-height: 3.5rem;
  font-size: 1.05rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
}

.grade-bar .key-hint {
  font-size: 0.7rem;
  color: var(--muted);
}

.reveal-btn {
  grid-column: 1 / -1;
}

.review-summary {
  text-align: center;
  padding-top: 2rem;
}

/* "Edit this card" / "Copy" — plain, quiet escape hatches shown under the
   revealed answer (§8.4): a link out to the server-rendered editor, and a
   button that copies id + path/title/prompt/tags + body to the clipboard
   for pasting into a chat (asking about the card, or updating it via the
   MCP tools). Neither is part of the review rhythm. */
.card-edit {
  margin-top: 1.75rem;
  font-size: 0.85rem;
  display: flex;
  gap: 1.25rem;
}

/* Resets `button`'s solid-ink look (see the base `button` rule above) back
   down to plain-link quietness, so "Copy" reads as the same weight of
   affordance as "Edit this card" next to it, not a competing CTA. */
.link-btn {
  display: inline;
  font: inherit;
  font-size: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
}

.link-btn:hover {
  opacity: 1;
  text-decoration-color: currentColor;
}

.card-edit .key-hint,
.browse-actions .edit-link .key-hint {
  font-size: 0.7rem;
  color: var(--muted);
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  padding: 0 0.3em;
  margin-left: 0.4em;
}

/* --- Browse (docs/cards-design.md §8.2) ---------------------------------
   Master + detail, decided entirely in CSS: both panes are always
   rendered and the layout picks which are visible. There is NO JS
   branching on viewport size anywhere in views/browse.ts — the only thing
   JS sets is `data-pane`, which is SELECTION state (is there a card at
   this path?), and which this stylesheet honours only below the
   breakpoint. */

/* The island is 720px wide elsewhere, which is right for a single column
   of review text and too narrow for 320px + content. `:has()` widens it
   for this one view without main.ts having to know about layout; a browser
   without `:has()` simply keeps the 720px shell, which still works. */
#app:has(.browse) {
  max-width: 1080px;
}

.browse-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1rem;
}

/* Lives OUTSIDE #browse-master, which is what gets swapped on every
   keystroke — see views/browse.ts's header. Moving it inside would
   destroy focus and the caret on each character typed. */
.browse-search {
  flex: 1 1 auto;
  margin: 0;
}

.browse-panes {
  display: grid;
  gap: 1.5rem;
}

@media (min-width: 720px) {
  .browse-panes {
    grid-template-columns: 320px 1fr;
    align-items: start;
  }

  /* Independently scrollable, so drilling through a long list doesn't
     scroll the card you're reading away. */
  .browse-master,
  .browse-detail {
    max-height: calc(100vh - 8rem);
    overflow-y: auto;
  }

  /* Both panes render the same trail for the same path, side by side, which
     reads as a mistake at this width. It's rendered unconditionally and
     hidden HERE rather than the other way round because below the
     breakpoint the master pane is hidden and the detail pane's breadcrumb
     is the only way back up. */
  .browse-detail .browse-crumbs {
    display: none;
  }
}

/* Below the breakpoint there's only room for one, and the selection
   decides which: a path with a card at it shows the card, everything else
   shows the list. The breadcrumb in the detail pane is the way back up. */
@media (max-width: 719.98px) {
  .browse-panes[data-pane='detail'] .browse-master {
    display: none;
  }

  .browse-panes[data-pane='master'] .browse-detail {
    display: none;
  }
}

.browse-crumbs {
  font-size: 0.85rem;
  margin-bottom: 0.75rem;
  word-break: break-word;
}

.browse-crumbs .sep {
  color: var(--muted);
  margin: 0 0.35em;
}

.browse-crumbs .here {
  font-weight: bold;
}

.browse-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.browse-row {
  border-bottom: 1px solid var(--line);
}

.browse-row a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  /* A phone tap target, same reasoning as the grade bar's 3.5rem. */
  padding: 0.7rem 0.2rem;
  text-decoration: none;
}

.browse-row.folder .label {
  color: var(--muted);
}

.browse-row.folder .label::after {
  content: '/';
  opacity: 0.5;
}

.browse-row.current {
  background: var(--paper-raised);
}

.browse-row.current > a {
  font-weight: bold;
}

.browse-row .count {
  font-size: 0.8rem;
  color: var(--muted);
}

.browse-row .result-path {
  font-size: 0.75rem;
  color: var(--muted);
  word-break: break-all;
}

.browse-list .empty,
.browse-note {
  color: var(--muted);
  font-size: 0.85rem;
  padding: 0.4rem 0;
  margin: 0;
}

.browse-tags {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0;
  margin: 0 0 0.75rem;
}

.browse-tags li {
  font-size: 0.75rem;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  padding: 0.1em 0.5em;
}

.card-scheduling {
  font-size: 0.85rem;
  margin: 0 0 1rem;
}

.card-scheduling dt {
  color: var(--muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.card-scheduling dd {
  margin: 0 0 0.4rem;
}

.browse-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.browse-actions button {
  width: auto;
  margin: 0;
}

/* The out-of-turn grade bar is inline here, not the session's sticky
   bottom bar — this is one card being graded on purpose, not a rhythm of
   dozens where the buttons must never move. */
.browse-actions .grade-bar {
  position: static;
  border: 0;
  padding: 0;
  gap: 0.5rem;
}

.browse-actions .grade-bar button {
  min-height: 2.5rem;
}

.browse-actions .graded-note {
  font-size: 0.85rem;
  background: var(--mark-bg);
  border-radius: 3px;
  padding: 0.1em 0.5em;
}

.browse-detail .card-body {
  overflow-wrap: break-word;
}

.browse-detail .card-body img {
  max-width: 100%;
}

.card-children {
  margin-top: 2rem;
  border-top: 1px solid var(--line);
}

.card-children h2 {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  margin: 1rem 0 0.25rem;
}

/* --- Keyboard shortcuts (src/keyboard.ts) ------------------------------- */

kbd {
  font-family: var(--font-mono);
  font-size: 0.8em;
  background: var(--paper-raised);
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  padding: 0.05em 0.4em;
  white-space: nowrap;
}

.kbd-hint {
  color: var(--muted);
  font-size: 0.85rem;
  margin-top: 2rem;
}

/* The `?` help overlay. Fixed, above everything, dismissed by esc / `?` /
   a backdrop click (all in keyboard.ts). */
.kbd-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(0, 0, 0, 0.45);
}

.kbd-card {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  max-width: 420px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
}

.kbd-card h2 {
  margin: 0 0 0.5rem;
  font-size: 1.2rem;
}

.kbd-card h3 {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  margin: 1rem 0 0.35rem;
}

.kbd-card dl {
  display: grid;
  grid-template-columns: minmax(6.5rem, auto) 1fr;
  gap: 0.3rem 0.9rem;
  margin: 0;
}

.kbd-card dt {
  text-align: right;
}

.kbd-card dd {
  margin: 0;
}

.kbd-card .kbd-close {
  margin-top: 1.25rem;
  width: 100%;
}

.kbd-card .kbd-close kbd {
  color: inherit;
  background: transparent;
  border-color: currentColor;
}
