/*
 * Naitei — the visual contract from DESIGN.md, transcribed once.
 *
 * Every color, size, radius, and duration in the app comes from a custom
 * property defined here. Nothing downstream hand-writes a token value: the
 * board markup and the Worker-rendered login page both link this sheet for
 * exactly that reason.
 *
 * Dark is the default and the design source (DESIGN.md "Identity"); light is
 * derived and ships from day one via prefers-color-scheme. There is no in-app
 * theme toggle — that is an opinion, not a setting.
 */

:root {
  color-scheme: dark;

  /* Color roles — DESIGN.md dark column */
  --bg: #0e0e12;
  --surface: #1e212a;
  --surface-raised: #2a2d37;
  --hairline: #383c46;
  --dot-track: #545966;
  --text: #e8e6e1;
  --text-2: #9a9891;
  --text-3: #8a887f;
  --accent: #c9a961;
  --accent-strong: #e3c77f;
  --on-accent: #101014;
  --warn: #d98e73;
  --danger: #c25e5e;

  /* Accent-as-text is safe on dark surfaces at any size; light mode overrides
     this to --accent-strong, since the base gold misses 4.5:1 on light bg. */
  --accent-text: var(--accent);

  /* Elevation. In dark mode elevation is surface color, not shadow — the only
     shadow is the drag lift. */
  --shadow-raised: none;
  --shadow-drag: 0 8px 24px rgb(0 0 0 / 0.4);

  /* Type */
  --font: ui-sans-serif, -apple-system, "Segoe UI", Roboto, sans-serif;
  --size-xl: 22px;
  --size-l: 15px;
  --size-m: 14px;
  --size-s: 13px;
  --size-xs: 12px;
  --size-reader: 17px;

  /* Spacing — 4px base grid */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;

  /* Radii */
  --radius-card: 10px;
  --radius-control: 8px;
  --radius-sheet: 14px;

  /* Motion — one curve, one duration */
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --duration: 150ms;
}

@media (prefers-color-scheme: light) {
  :root {
    color-scheme: light;

    --bg: #f7f5f1;
    --surface: #ffffff;
    --surface-raised: #ffffff;
    --hairline: #d5d0c5;
    --dot-track: #b5afa3;
    --text: #211f1b;
    --text-2: #6e6b64;
    /* DESIGN.md lists #74716A here, but measured against --bg that is 4.47:1 —
       just under AA, and this token carries the column headers, tab strip, and
       drawer bar, all 12px. Darkened to clear 4.5 (now 4.74:1). DESIGN.md's
       "AA contrast in both modes for every pairing" rule outranks the specific
       hex, and its table is updated to match. */
    --text-3: #706d66;
    --accent: #8f6e20;
    --accent-strong: #6f5518;
    --on-accent: #ffffff;
    --warn: #a85a38;
    --danger: #a03d3d;

    --accent-text: var(--accent-strong);

    --shadow-raised: 0 4px 16px rgb(0 0 0 / 0.1);
    --shadow-drag: 0 8px 24px rgb(0 0 0 / 0.18);
  }
}

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

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

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 var(--size-m) / 1.5 var(--font);
  -webkit-font-smoothing: antialiased;
}

/* Focus is always visible — DESIGN.md accessibility. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* DESIGN.md motion: reduced motion disables transitions everywhere. Drag still
   works, it just snaps. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* Visually hidden but available to assistive tech — the move announcer. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

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

/* The page itself never scrolls vertically on desktop — columns scroll
   independently under sticky headers (DESIGN.md board layout). */
.board-body {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Wordmark on the left, the quality lens toggle at the right end. Wrap is
   allowed rather than decorative: at 320px and at 200% zoom the toggle takes its
   own line instead of squeezing the wordmark into an ellipsis. */
.board-header {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-4);
}

@media (min-width: 768px) {
  .board-header {
    padding: var(--space-6) var(--space-6) var(--space-4);
  }
}

/* ---------- quality lens toggle ---------- */

/* Borrows the tab strip's active vocabulary — an XS caps label over a 2px accent
   underline — because it is the same kind of statement: this is the view you are
   in. 44px tall so it is a real target on a phone; the underline is on a
   transparent border at rest, so switching it on shifts nothing. */
.lens-toggle {
  flex: none;
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: var(--space-2) 0;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--text-3);
  font: 600 var(--size-xs) / 1 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  transition: color var(--duration) var(--ease);
}

.lens-toggle[aria-pressed="true"] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

/* Column flow: tab strip, board-scroll, drawer. board-scroll takes the slack so
   the drawer stays pinned in view rather than being pushed below the fold. */
.board {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Containing block for the loading overlay. */
  position: relative;
}

/* Holds the Up-next rail beside the pager. Below 1024px the rail is CSS-hidden
   and this is just a vertical passthrough wrapping the pager; at ≥1024px it
   turns into a flex row so the rail sits to the left of the horizontally
   scrolling columns. Keeping the rail a sibling of the pager — not a pane inside
   it — is what exempts it from the pager's horizontal scroll and leaves the drag
   hot zones anchored to the pager's own box. */
.board-scroll {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

@media (min-width: 1024px) {
  .board-scroll {
    flex-direction: row;
  }
}

/* ---------- tab strip (mobile) ---------- */

.tabs {
  display: flex;
  gap: var(--space-4);
  padding: 0 var(--space-4) var(--space-2);
  overflow-x: auto;
  scrollbar-width: none;
  flex: none;
}

.tabs::-webkit-scrollbar {
  display: none;
}

.tab {
  flex: none;
  /* Touch target floor — this strip is the mobile-only control, so it is the
     one place DESIGN.md's 44px rule actually bites. */
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: var(--space-2) 0;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--text-3);
  font: 600 var(--size-xs) / 1 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
  cursor: pointer;
  /* Tap targets, not prose: a tap should never leave a selection highlight. */
  user-select: none;
}

.tab[aria-current="true"] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

.tab-count {
  margin-left: var(--space-1);
  color: var(--text-3);
}

@media (min-width: 768px) {
  .tabs {
    display: none;
  }
}

/* ---------- pager / columns ---------- */

/* Mobile: one pane per stage, scroll-snapped, Up-next first. */
.pager {
  flex: 1;
  /* min-width:0 so the pager can shrink beside the ≥1024px rail instead of
     overflowing the flex row; min-height:0 for its own vertical children. */
  min-width: 0;
  min-height: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.pager::-webkit-scrollbar {
  display: none;
}

.pane {
  flex: 0 0 100%;
  scroll-snap-align: start;
  min-width: 0;
}

/* Desktop: all six columns visible, the board scrolls horizontally when they
   do not fit, and each column scrolls vertically on its own. */
@media (min-width: 768px) {
  .pager {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(260px, 1fr);
    gap: var(--space-3);
    padding: 0 var(--space-6);
    scroll-snap-type: none;
  }

  .pane {
    flex: none;
  }
}

.column,
.pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Declared after the base rule above, not inside the earlier media block: an
   equal-specificity `display: flex` later in the sheet would otherwise win and
   the agenda would show up as a seventh desktop column.
   The agenda is a mobile affordance — on desktop the whole board is visible. */
@media (min-width: 768px) {
  .pane--agenda {
    display: none;
  }
}

@media (min-width: 768px) {
  .column + .column,
  .pane + .pane {
    border-left: 1px solid var(--hairline);
    padding-left: var(--space-3);
  }
}

.column-header {
  position: sticky;
  top: 0;
  z-index: 1;
  flex: none;
  margin: 0;
  padding: var(--space-2) var(--space-4);
  background: var(--bg);
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

@media (min-width: 768px) {
  .column-header {
    padding-inline: 0;
  }
}

/* The Offer column's header underline — the app's one celebration. */
.column--offer .column-header {
  border-bottom: 2px solid var(--accent);
}

.column-count {
  margin-left: var(--space-1);
}

.column-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4) var(--space-4);
}

@media (min-width: 768px) {
  .column-body {
    padding-inline: 0;
  }
}

.column-empty {
  margin: var(--space-4) 0;
  color: var(--text-3);
  font-size: var(--size-s);
  text-align: center;
}

/* The loading scaffold: column shells with no header text and no cards. It
   holds the board's shape while the payload is in flight. */
.pager--scaffold .column-header {
  min-height: 1.4em;
}

/* ---------- Up-next rail (desktop ≥1024px) ---------- */

/* Hidden until there is room for it. Below 1024px the near-term agenda lives in
   the mobile pager's first pane instead (.pane--agenda). */
.rail {
  display: none;
}

@media (min-width: 1024px) {
  .rail {
    flex: none;
    width: 280px;
    min-height: 0;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--hairline);
  }
}

/* Sticky header over the rail's own scroll, like a column header but with the
   rail's inset restored (the ≥768px column rule zeroes side padding). */
.rail-header {
  padding-inline: var(--space-3);
}

/* An empty rail keeps its 280px — a rail that reflowed the board when its last
   item cleared would be worse (DESIGN.md). Nothing to add here; the fixed width
   above already holds it. */

.rail .column-body {
  padding: var(--space-2) var(--space-3) var(--space-4);
}

/* ---------- Up-next groups (rail + mobile pane 1) ---------- */

.upnext-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* The 8px column-body gap already spaces cards; add breathing room above each
   day header past the first so groups read as groups. */
.upnext-group + .upnext-group {
  margin-top: var(--space-3);
}

/* All four header kinds — OVERDUE, TODAY, TOMORROW, an absolute day — get the
   same XS caps --text-3 treatment. The word carries "overdue"; the entry dates
   carry the --warn colour (no --warn header — DESIGN.md's no-badge rule). */
.upnext-day {
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Mobile: the day header past TOMORROW is a full-width toggle (44px tap target)
   showing its count, collapsed on every load. */
.upnext-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  width: 100%;
  min-height: 44px;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}

.upnext-day-count {
  color: var(--text-3);
}

/* Collapsed groups' bodies, and the rail's "+ n more" overflow, both toggle via
   the hidden attribute — spelled out so an author display can't leave them open. */
.upnext-entries[hidden],
.upnext-rest[hidden] {
  display: none;
}

.upnext-entries,
.upnext-rest {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.upnext-more {
  align-self: flex-start;
  min-height: 44px;
  padding: var(--space-1) 0;
  background: none;
  border: 0;
  color: var(--text-3);
  font: 400 var(--size-s) / 1.4 var(--font);
  text-align: left;
  cursor: pointer;
}

/* ---------- card ---------- */

.card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-raised);
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: grab;
  user-select: none;
  /* Touch never drags in v1 — the tap opens the move sheet instead. */
  touch-action: pan-x pan-y;
  transition: transform var(--duration) var(--ease),
    opacity var(--duration) var(--ease);
}

.card--offer {
  border-color: var(--accent);
}

/* A strong company: the row's existing 1px border turns accent on its left edge
   alone. Same "this one is special" vocabulary as the Offer column, one quarter
   of the volume — and on an Offer card the full accent border already says it.
   A colour change on a border the row always draws means an agent rating a card
   mid-session costs zero reflow: no reserved slot, no truncation shifting. */
.card--strong {
  border-left-color: var(--accent);
}

.card-company-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.card-company {
  flex: 1;
  min-width: 0;
  font-size: var(--size-l);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- ghost flag ---------- */

/* The slot is rendered on every card whether or not the flag is showing, and
   holds its width when empty: without that, a card's company line would reflow
   on the morning of day fourteen — the layout shifting under the owner's eye to
   announce that nothing has happened. */
.quiet-slot {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-1);
  min-width: 8px;
  min-height: 8px;
}

.quiet-count {
  color: var(--text-3);
  font-size: var(--size-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

/* A hollow circle, not a filled dot: the overdue treatment already owns the
   filled --warn dot, and these two can appear on the same card. */
.quiet-glyph {
  width: 8px;
  height: 8px;
  border: 1px solid var(--warn);
  border-radius: 50%;
}

.panel-company-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  /* Room for the ✕ so a long company name never runs under it. */
  padding-right: 44px;
}

.card-role {
  color: var(--text-3);
  font-size: var(--size-s);
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Always rendered, even when empty, so cards keep a uniform height. */
.card-action {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-top: var(--space-1);
  font-size: var(--size-m);
}

.card-action-text {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-action-text--empty {
  color: var(--text-3);
}

.card-date {
  flex: none;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

/* Overdue: the date turns warn and gains a dot. No badge, no exclamation. */
.card-date--overdue {
  color: var(--warn);
}

/* No date at all: an em dash in the faint tier, so the slot still reads. */
.card-date--empty {
  color: var(--text-3);
}

.card-date--overdue::before {
  content: "";
  display: inline-block;
  width: 4px;
  height: 4px;
  margin-right: var(--space-1);
  border-radius: 50%;
  background: var(--warn);
  vertical-align: middle;
}

/* ---------- compact row (the six active columns) ---------- */

/* One line: company · action · ghost glyph · date. The column names the stage,
   so no role and no dots — this is the density the whole change buys. */
.card--compact {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
}

/* Holds its natural width up to a 45% cap: short names show in full, long ones
   ellipsize there rather than shrinking to nothing and starving the action.
   `flex: 0 0 auto` — no grow, no shrink; the cap alone bounds it. */
.compact-company {
  flex: 0 0 auto;
  max-width: 45%;
  min-width: 0;
  font-size: var(--size-m);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* The quality lens demotes this colour; the fade is the standard curve, and
     reduced motion snaps it via the global override at the top of this file. */
  transition: color var(--duration) var(--ease);
}

/* The action takes the remaining width. */
.compact-action {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.compact-action--empty {
  color: var(--text-3);
}

/* ---------- rail entry (Up next, both viewports) ---------- */

/* Two stacked lines; the ghost glyph and date sit on line 1 with the company. */
.card--rail {
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  /* Not a drag source — no grab cursor to promise one. */
  cursor: pointer;
}

.rail-line {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}

/* On line 1 the company takes the whole line beside the fixed date, unlike the
   capped share it gets on a one-line compact row. */
.card--rail .compact-company {
  flex: 1 1 auto;
  max-width: none;
}

.rail-stage {
  flex: none;
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ---------- the quality lens ---------- */

/* Lens on: every non-strong card in the six active columns and the rail demotes
   to the terminal-card idiom — the company title in --text-2, and nothing else
   touched. No opacity (it would compound with the pending-move dim and fall
   under contrast), no hiding, no reordering, and no count anywhere changes.
   Demoted cards stay fully clickable and keyboard-focusable.
   The terminal drawer is exempt by construction: its cards draw `.card-company`,
   not `.compact-company`, so no selector here can reach them. */
.board--lens .card--compact:not(.card--strong) .compact-company,
.board--lens .card--rail:not(.card--strong) .compact-company {
  color: var(--text-2);
}

/* Terminal cards: muted, not shameful. No accent anywhere. */
.card--terminal {
  border-color: var(--hairline);
}

.card--terminal .card-company {
  color: var(--text-2);
}

/* ---------- stage dots ---------- */

.dots {
  display: flex;
  gap: var(--space-1);
  margin-top: var(--space-2);
}

.dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--dot-track);
}

.dot--filled {
  background: var(--accent);
  box-shadow: none;
}

.card--terminal .dot--filled {
  background: none;
  box-shadow: inset 0 0 0 1px var(--dot-track);
}

/* ---------- terminal drawer ---------- */

.drawer {
  flex: none;
  border-top: 1px solid var(--hairline);
}

.drawer-bar {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: var(--space-3) var(--space-4);
  background: none;
  border: 0;
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: left;
  cursor: pointer;
}

@media (min-width: 768px) {
  .drawer-bar {
    padding-inline: var(--space-6);
  }
}

/* An author `display` beats the `hidden` attribute's UA `display: none`, so the
   collapsed state has to be spelled out or the drawer ships open. */
.drawer-body[hidden] {
  display: none;
}

.drawer-body {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
  max-height: 40vh;
  overflow-y: auto;
  padding: 0 var(--space-4) var(--space-4);
}

@media (min-width: 768px) {
  .drawer-body {
    padding-inline: var(--space-6);
  }
}

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

.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 20;
  background: rgb(0 0 0 / 0.4);
}

.sheet {
  position: fixed;
  z-index: 21;
  display: flex;
  flex-direction: column;
  padding: var(--space-2);
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sheet);
  box-shadow: var(--shadow-raised);
}

/* Mobile: a bottom sheet. */
.sheet {
  left: var(--space-2);
  right: var(--space-2);
  bottom: var(--space-2);
}

/* Desktop: a menu anchored to the card, positioned inline by sheet.ts. */
@media (min-width: 768px) {
  .sheet {
    right: auto;
    bottom: auto;
    width: 15rem;
  }
}

.sheet-title {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  /* --text-2, not --text-3: the raised surface is lighter than --surface, and
     --text-3 on it measures 3.86:1 in dark mode — under AA for 12px text. */
  color: var(--text-2);
  font-size: var(--size-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.sheet-option {
  min-height: 44px;
  padding: 0 var(--space-3);
  background: none;
  border: 0;
  border-radius: var(--radius-control);
  color: var(--text);
  /* Body text, not a label: DESIGN.md reserves XS caps for headers, the tab
     strip, badges, and labels. Sheet rows are M. */
  font: 400 var(--size-m) / 44px var(--font);
  text-align: left;
  cursor: pointer;
}

.sheet-option:hover:not(:disabled) {
  background: var(--surface);
}

/* The card's own stage: a check, and unselectable. --text-2 for the same
   contrast reason as .sheet-title above. */
.sheet-option--current {
  color: var(--text-2);
  cursor: default;
}

.sheet-option--current::after {
  content: " ✓";
  color: var(--accent-text);
}

.sheet-divider {
  height: 1px;
  margin: var(--space-2) var(--space-3);
  background: var(--hairline);
}

/* ---------- detail panel ---------- */

/* The board behind an open panel: dimmed to 60% and inert. The dim is opacity
   on the containers rather than a colour layer over them — DESIGN.md exempts
   this surface from AA precisely because it is neither readable nor reachable
   while the panel is up. */
body.panel-open .board-header,
body.panel-open .board {
  opacity: 0.6;
  transition: opacity var(--duration) var(--ease);
}

/* Transparent: it exists for the backdrop tap and to hold DESIGN.md's z-order
   slot for the dim, which is drawn by the opacity above. */
.panel-backdrop {
  position: fixed;
  inset: 0;
  z-index: 15;
}

.panel {
  position: fixed;
  z-index: 16;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  /* dvh, not vh: the software keyboard must not push the close-guard bar off
     the bottom of the screen. */
  height: 100dvh;
  inset: 0;
  padding-bottom: env(safe-area-inset-bottom);
}

@media (min-width: 768px) {
  .panel {
    left: auto;
    width: 480px;
    max-width: 100vw;
    border-left: 1px solid var(--hairline);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .panel {
    animation: panel-in var(--duration) var(--ease);
  }
}

@keyframes panel-in {
  from {
    transform: translateX(100%);
  }
}

/* Head and stage row are sticky; the body scrolls beneath them. */
.panel-head {
  flex: none;
  position: relative;
  padding: var(--space-4) var(--space-4) var(--space-2);
  border-bottom: 1px solid var(--hairline);
}

.panel-close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  width: 44px;
  height: 44px;
  background: none;
  border: 0;
  border-radius: var(--radius-control);
  color: var(--text-2);
  font: 400 var(--size-m) / 1 var(--font);
  cursor: pointer;
}

.panel-company {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: var(--size-xl);
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Wraps fully here, unlike the card's two-line clamp — this is the one place
   the whole role title is worth reading. */
.panel-role {
  margin: var(--space-1) 0 0;
  color: var(--text-2);
  font-size: var(--size-s);
}

.panel-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  margin: var(--space-3) 0 0;
}

.panel-meta-item {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.panel-meta-label {
  color: var(--text-3);
  font-size: var(--size-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.panel-meta-value {
  margin: 0;
  font-size: var(--size-m);
}

.panel-stage {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-2);
  padding: 0;
  background: none;
  border: 0;
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: left;
  cursor: pointer;
}

.panel-stage .dots {
  margin-top: 0;
}

.panel-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-4);
}

.panel-loading {
  margin: var(--space-6) 0;
  color: var(--text-2);
  text-align: center;
}

/* The board's error treatment, inset — head and stage row stay live above it. */
.panel-notice {
  height: auto;
  padding: var(--space-6) 0;
}

/* The company blurb: the first line of the scrolling body, above the next
   action. --text-3 on --surface measures 4.52:1 — a verified pairing, unlike
   the same tier on --bg. Wraps freely; 200 chars on a 320px phone is a few
   lines of body text and pins nothing. */
.panel-blurb {
  margin: 0 0 var(--space-2);
  color: var(--text-3);
  font-size: var(--size-s);
}

.panel-section + .panel-section {
  margin-top: var(--space-6);
}

.panel-section-title {
  margin: 0 0 var(--space-2);
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.panel-empty {
  margin: 0;
  color: var(--text-3);
  font-size: var(--size-s);
}

/* ---------- panel: writing controls ---------- */

.panel-input,
.panel-date,
.panel-textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
  font: inherit;
}

.panel-textarea {
  display: block;
  min-height: 44px;
  resize: none;
  overflow: hidden;
  line-height: 1.5;
}

.panel-date {
  width: auto;
  min-height: 44px;
  font-variant-numeric: tabular-nums;
}

.panel-date--overdue {
  color: var(--warn);
  border-color: var(--warn);
}

/* The Save row is always there, showing or not: a section must not jump under
   the reader's eye when they start typing, and nothing may shift when a save
   begins. No spinner — the app has none anywhere. */
.panel-save-slot {
  display: flex;
  justify-content: flex-end;
  min-height: 44px;
  margin-top: var(--space-2);
}

.panel-save-slot .panel-save {
  visibility: hidden;
}

.panel-save-slot--on .panel-save {
  visibility: visible;
}

.panel-save {
  min-height: 44px;
  padding: 0 var(--space-4);
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-control);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--duration) var(--ease);
}

.panel-save:hover:not(:disabled) {
  background: var(--accent-strong);
}

/* Save in flight: disabled at 60%, its row height already reserved above. */
.panel-save--busy {
  opacity: 0.6;
  cursor: default;
}

/* ---------- panel: next action ---------- */

.panel-next-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.panel-clear {
  min-height: 44px;
  padding: 0 var(--space-2);
  background: none;
  border: 0;
  color: var(--text-2);
  font: 400 var(--size-s) / 1.4 var(--font);
  cursor: pointer;
}

.panel-clear[hidden] {
  display: none;
}

/* ---------- panel: context and prep ---------- */

.panel-entries,
.panel-preps {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* An inset record, not another card: --bg on a hairline reads as recessed in
   both modes, where --surface on --surface would vanish in light mode. */
.panel-well {
  padding: var(--space-3);
  background: var(--bg);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
}

.panel-well-stamp,
.panel-prep-stamp {
  margin: 0 0 var(--space-1);
  color: var(--text-3);
  font-size: var(--size-xs);
  font-variant-numeric: tabular-nums;
}

.panel-prep-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

.panel-prep-identity {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
  padding-top: var(--space-2);
}

.panel-prep-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  margin-left: auto;
}

.panel-prep-stage {
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.panel-prep-stamp {
  margin: 0;
}

.panel-prep {
  padding: var(--space-3);
  background: var(--bg);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
}

/* Context records only. Prep has its own semantic, fixed-height preview. */
.panel-clamp-body {
  display: -webkit-box;
  -webkit-line-clamp: 6;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: var(--size-m);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.panel-clamp-body--open {
  display: block;
  -webkit-line-clamp: none;
}

.panel-more {
  margin-top: var(--space-1);
  padding: var(--space-1) 0;
  background: none;
  border: 0;
  color: var(--accent-text);
  font: 400 var(--size-s) / 1.4 var(--font);
  cursor: pointer;
}

.panel-prep-read,
.panel-prep-edit {
  min-height: 44px;
  padding: 0 var(--space-2);
  background: none;
  border: 0;
  color: var(--accent-text);
  font: 400 var(--size-s) / 1.4 var(--font);
  cursor: pointer;
}

.panel-prep-read[hidden] {
  display: none;
}

.panel-prep-view[hidden],
.panel-prep-editor[hidden] {
  display: none;
}

/* ---------- formatted prep documents ---------- */

.prep-document {
  color: var(--text);
  overflow-wrap: anywhere;
}

.prep-document--preview {
  max-height: 9em;
  overflow: hidden;
  font-size: var(--size-m);
  line-height: 1.5;
}

.prep-document > :first-child {
  margin-top: 0;
}

.prep-document > :last-child {
  margin-bottom: 0;
}

.prep-heading {
  color: var(--text);
  font-weight: 600;
}

.prep-heading--canonical {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 1.8em 0 0.75em;
  font-size: 1.05em;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.prep-heading--canonical::before {
  content: "";
  flex: none;
  width: var(--space-6);
  height: 2px;
  background: var(--accent);
}

.prep-heading--minor {
  margin: 1.4em 0 0.55em;
  font-size: 1em;
}

.prep-paragraph,
.prep-followup {
  margin: 0 0 1em;
  white-space: pre-line;
}

.prep-followup {
  margin-left: var(--space-3);
  padding-left: var(--space-3);
  color: var(--text-2);
  border-left: 1px solid var(--hairline);
}

.prep-list {
  margin: 0 0 1em;
  padding-left: var(--space-6);
}

.prep-list li + li {
  margin-top: var(--space-1);
}

.prep-document :not(pre) > code {
  padding: 0 var(--space-1);
  background: var(--surface-raised);
  border-radius: var(--radius-control);
  color: var(--text);
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 0.9em;
}

.prep-code-block {
  margin: 0 0 1em;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
}

.prep-code-language {
  display: block;
  padding: var(--space-2) var(--space-3) 0;
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.prep-code-block pre {
  margin: 0;
  padding: var(--space-3);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.prep-code-block code {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 0.9em;
}

/* ---------- full-viewport prep Reader ---------- */

.prep-reader {
  position: fixed;
  inset: 0;
  z-index: 18;
  display: flex;
  flex-direction: column;
  height: 100dvh;
  background: var(--bg);
}

.prep-reader-head {
  flex: none;
  padding-top: env(safe-area-inset-top);
  background: var(--surface);
  border-bottom: 1px solid var(--hairline);
}

.prep-reader-head-inner {
  position: relative;
  width: 100%;
  max-width: 760px;
  min-height: 76px;
  margin: 0 auto;
  padding: var(--space-4) 60px var(--space-4) var(--space-4);
}

.prep-reader-close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-4);
  width: 44px;
  height: 44px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--radius-control);
  color: var(--text-2);
  font: 400 var(--size-m) / 1 var(--font);
  cursor: pointer;
}

.prep-reader-company {
  margin: 0;
  font-size: var(--size-l);
  font-weight: 600;
}

.prep-reader-role {
  margin: var(--space-1) 0 0;
  color: var(--text-2);
  font-size: var(--size-s);
}

.prep-reader-stage {
  margin: var(--space-2) 0 0;
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.prep-reader-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.prep-document--reader {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-4)
    calc(var(--space-6) + env(safe-area-inset-bottom));
  font-size: var(--size-reader);
  line-height: 1.7;
}

@media (min-width: 768px) {
  .prep-reader-head-inner {
    padding-right: 72px;
    padding-left: 32px;
  }

  .prep-reader-close {
    right: 32px;
  }

  .prep-document--reader {
    padding-right: 32px;
    padding-left: 32px;
  }
}

/* The agent-collision answer. Not --danger: nothing is being destroyed here,
   a choice is being offered — DESIGN.md keeps --danger for destructive
   confirmations, of which v1 has none. */
.panel-conflict {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding: var(--space-2);
  border: 1px solid var(--warn);
  border-radius: var(--radius-control);
}

.panel-conflict[hidden] {
  display: none;
}

.panel-conflict-text {
  flex: 1;
  min-width: 12rem;
  margin: 0;
  color: var(--text);
  font-size: var(--size-s);
}

.panel-conflict-action {
  min-height: 44px;
  padding: 0 var(--space-3);
  background: none;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
  color: var(--accent-text);
  font: inherit;
  font-size: var(--size-s);
  cursor: pointer;
}

/* ---------- panel: close guard ---------- */

/* One destructive choice, pinned to the panel's bottom. It blocks the close
   until answered; Escape with it showing focuses it rather than closing. */
.panel-guard {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-raised);
  border-top: 1px solid var(--hairline);
}

.panel-guard-label {
  flex: 1;
  min-width: 0;
  color: var(--text);
  font-size: var(--size-m);
}

.panel-guard-keep,
.panel-guard-discard {
  min-height: 44px;
  padding: 0 var(--space-3);
  background: none;
  border: 0;
  border-radius: var(--radius-control);
  font: inherit;
  cursor: pointer;
}

.panel-guard-keep {
  color: var(--accent-strong);
  font-weight: 600;
}

.panel-guard-discard {
  color: var(--text-2);
}

/* ---------- panel: submitted resume and job description ---------- */

.panel-jd-row,
.panel-resume-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.panel-jd-toggle,
.panel-resume-toggle {
  flex: 1;
  min-height: 44px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--text-3);
  font: 600 var(--size-xs) / 1.4 var(--font);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: left;
  cursor: pointer;
}

.panel-resume-toggle {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-2);
}

.panel-resume-label {
  flex: none;
}

.panel-resume-filename {
  min-width: 0;
  color: var(--text-2);
  font-size: var(--size-s);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  overflow-wrap: anywhere;
}

.panel-jd-link {
  flex: none;
  color: var(--accent-text);
  font-size: var(--size-s);
}

.panel-jd-body[hidden],
.panel-resume-body[hidden] {
  display: none;
}

.panel-jd-body,
.panel-resume-body {
  margin-top: var(--space-2);
  color: var(--text-2);
  font-size: var(--size-s);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---------- panel: history ---------- */

.panel-history {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.panel-history-move {
  margin: 0;
  color: var(--text-2);
  font-size: var(--size-s);
}

.panel-history-note {
  margin: 0;
  color: var(--text-3);
  font-size: var(--size-s);
}

/* ---------- toast ---------- */

/* Toasts stay above everything, and step up over the panel's close-guard bar
   by its height + 8px whenever one is showing (DESIGN.md z-order). */
.toast {
  position: fixed;
  z-index: 22;
  left: 50%;
  bottom: calc(var(--space-4) + var(--toast-offset, 0px));
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: calc(100vw - 2 * var(--space-4));
  padding: var(--space-2) var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
  box-shadow: var(--shadow-raised);
  font-size: var(--size-m);
}

.toast-action {
  flex: none;
  min-height: 44px;
  padding: 0 var(--space-2);
  background: none;
  border: 0;
  color: var(--accent-text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

/* ---------- move states ---------- */

/* Pending move: dimmed and inert. No spinner anywhere in the app.
   Still focusable — see the aria-disabled note in board.ts — so it keeps its
   place in the tab order while its POST is in flight. */
.card--pending {
  opacity: 0.6;
  cursor: default;
}

.card--dragging {
  /* Lifted out of flow visually so elementFromPoint finds the column beneath. */
  pointer-events: none;
  position: relative;
  z-index: 10;
  box-shadow: var(--shadow-drag);
  cursor: grabbing;
  /* The lift follows the pointer directly — a transition here would lag it. */
  transition: none;
}

.column--drop-target {
  outline: 1px solid var(--accent);
  outline-offset: -1px;
  border-radius: var(--radius-card);
}

/* ---------- states ---------- */

.notice {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: 100%;
  padding: var(--space-6);
  text-align: center;
}

.notice-body {
  margin: 0;
  max-width: 28rem;
  font-size: var(--size-m);
}

.notice--loading .notice-body,
.notice--first-run .notice-body {
  color: var(--text-2);
}

/* The Loading line sits over the scaffold rather than beside it: as a flex
   sibling its `height: 100%` collapsed the column shells to nothing, which is
   the blank page the scaffold exists to prevent. */
.notice--loading {
  position: absolute;
  inset: 0;
  height: auto;
  pointer-events: none;
}

.notice-detail {
  margin: 0;
  color: var(--text-2);
  font-size: var(--size-s);
}

.retry {
  min-height: 44px;
  margin-top: var(--space-2);
  padding: 0 var(--space-4);
  background: none;
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  color: var(--accent-text);
  font: inherit;
  cursor: pointer;
}

/* ---------- wordmark ---------- */

.wordmark {
  margin: 0;
  font-size: var(--size-xl);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.wordmark-jp {
  margin-left: var(--space-2);
  color: var(--text-3);
  font-size: var(--size-m);
  font-weight: 400;
}

/* ---------- login ---------- */

.login-body {
  display: grid;
  place-items: center;
  padding: var(--space-4);
}

.login-card {
  width: 100%;
  max-width: 22rem;
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sheet);
  box-shadow: var(--shadow-raised);
}

.login-card form {
  margin-top: var(--space-4);
}

.login-card label {
  display: block;
  margin-bottom: var(--space-1);
  color: var(--text-3);
  font-size: var(--size-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.login-card input {
  width: 100%;
  padding: 0.6rem 0.7rem;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
  font: inherit;
}

.login-card button {
  width: 100%;
  /* 44px minimum hit target — DESIGN.md accessibility. */
  min-height: 44px;
  margin-top: var(--space-3);
  padding: 0.65rem;
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-control);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--duration) var(--ease);
}

.login-card button:hover {
  background: var(--accent-strong);
}

.login-notice {
  margin: var(--space-3) 0 0;
  font-size: var(--size-s);
}

.login-notice--warn {
  color: var(--warn);
}

.login-notice--muted {
  color: var(--text-2);
}
