/* Magic Board — Bauhaus "Primaries".
   Kandinsky's three shapes carry the meaning: a blue SQUARE is a weekly goal,
   a red CIRCLE is a daily pick, yellow blocks are labels and counts.
   Hard black rules, 2px borders, offset shadows, no rounded corners anywhere. */

/* Jost stands in for Futura. Self-hosted: the CSP allows styles and fonts from
   'self' only, and bundling keeps the board working offline. Variable file,
   one weight axis, latin subset. */
@font-face {
  font-family: "Jost";
  src: url("fonts/jost-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --paper: #f4f1ea;
  --card: #ffffff;
  --ink: #111111;
  --red: #e0301e;
  --yellow: #f2c018;
  --blue: #1f4fa3;
  --muted: #6b6b66;
  --faint: #9a9a94;
  --hairline: #cfcbc0;

  --shadow: 5px 5px 0 var(--ink);
  --shadow-sm: 3px 3px 0 var(--ink);
  --rule: 4px solid var(--ink);
  --border: 2px solid var(--ink);

  /* Uppercase tracked labels appear everywhere; keep them one declaration. */
  --track: 0.16em;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 15px/1.45 "Jost", "Futura", "Avenir Next", "Century Gothic", sans-serif;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}

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

.topbar {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 16px 28px;
  background: var(--paper);
  border-bottom: var(--rule);
}

/* The three shapes are the mark; inline SVG in the topbar so the geometry is
   exact and recolours with the palette. */
.brand .mark { flex: 0 0 auto; display: block; }
.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  font-weight: 700;
  font-size: 19px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
}

.topbar nav { display: flex; gap: 22px; }
.topbar nav a,
.topbar .logout button {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
}
.topbar nav a:hover,
.topbar .logout button:hover { color: var(--red); background: transparent; }

main { padding: 22px 28px 48px; }
h1 {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 0 0 18px;
}

.toast {
  display: none;
  margin: 0 28px;
  padding: 10px 14px;
  background: var(--card);
  color: var(--red);
  border: var(--border);
  box-shadow: var(--shadow-sm);
  font-weight: 500;
}
.toast.visible { display: block; }

/* ---------- Magic 3 rail ---------- */

.rail {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  margin: 0 0 26px;
  border-top: var(--rule);
  border-bottom: var(--rule);
}
.rail-col { padding: 18px 24px 20px; }
.rail-weekly { border-right: var(--rule); }

.rail-col h2 {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 14px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
}
/* Each tier is announced by its own shape. */
.rail-weekly h2::before {
  content: "";
  width: 20px;
  height: 20px;
  background: var(--blue);
}
.rail-daily h2::before {
  content: "";
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--red);
}

/* The count is the loudest number in the region: big, not a pill. */
.pick-count {
  margin-left: auto;
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  color: var(--ink);
}
.pick-count .of { color: var(--faint); font-weight: 400; }
.pick-count.full { color: var(--red); }
.pick-count.over { color: var(--red); text-decoration: underline; text-decoration-thickness: 3px; }

button.reset {
  background: transparent;
  border: none;
  padding: 0;
  color: var(--faint);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: var(--track);
  text-transform: uppercase;
}
button.reset:hover { color: var(--red); background: transparent; }

/* ---------- weekly goals ---------- */

.goals, .picks { list-style: none; margin: 0 0 14px; padding: 0; }
.goals li, .picks li { margin: 0 0 10px; }
.goals li.empty, .picks li.empty {
  color: var(--faint);
  font-style: normal;
  font-size: 14px;
}

.goal { display: flex; flex-direction: column; gap: 7px; }
.goal-head { display: flex; align-items: flex-start; gap: 14px; }

/* The blue square, numbered by position in the list. */
.goals { counter-reset: goal; }
.goals > li { counter-increment: goal; }
.goal-head::before {
  content: counter(goal);
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--blue);
  color: var(--card);
  font-weight: 700;
  font-size: 17px;
}
.goal.is-done .goal-head::before { background: var(--muted); }

.goal-title { flex: 1; font-size: 17px; font-weight: 500; line-height: 1.3; padding-top: 4px; }
.goal-notes {
  margin: 0 0 0 46px;
  font-size: 13px;
  color: var(--muted);
  white-space: pre-wrap;
}
.goal-foot { display: flex; align-items: center; gap: 12px; margin-left: 46px; }

.bar {
  flex: 0 0 auto;
  width: 110px;
  height: 10px;
  background: var(--card);
  border: var(--border);
  overflow: hidden;
}
.bar-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--blue);
  transition: width .45s ease;
}
.bar-label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--muted);
  white-space: nowrap;
}
.bar-label.empty { color: var(--faint); }

/* ---------- daily picks ---------- */

.pick { display: flex; align-items: center; gap: 14px; }
.pick-main { flex: 1 1 auto; display: flex; align-items: center; gap: 12px; min-width: 0; }
/* Both sides size from their content, but the chip gives way four times
   faster: at middle widths a long goal name used to hold its full width and
   squeeze the pick title down to one word per line. */
.pick-side { display: flex; align-items: center; gap: 12px; flex: 0 4 auto; min-width: 0; }
.pick-side .goal-chip { flex: 0 1 auto; min-width: 0; }
.pick-side .done-btn { flex: 0 0 auto; }
.pick-title { min-width: 0; font-size: 17px; font-weight: 500; }

/* The red circle marks a pick; it hollows out once the card is done. */
.pick-main::before {
  content: "";
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--red);
}
.pick.is-done .pick-main::before {
  background: transparent;
  border: 3px solid var(--muted);
}

.rail-list {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}

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

/* The columns are a pipeline, so they always read left to right and never
   wrap — Done under Inbox would mean nothing. One track rule does both jobs:
   `1fr` shares the width out while the columns still fit, and the 240px floor
   takes over when they do not, at which point the board scrolls sideways with
   the order intact. */
.board {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(220px, 1fr);
  align-items: start;
  /* The gutter tightens as the board narrows, the way any grid should, so the
     columns get the room instead of the whitespace between them. */
  gap: clamp(18px, 2.2vw, 30px);
  padding-bottom: 14px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
}
.list { scroll-snap-align: start; }

/* When it does scroll, the bar is part of the design rather than the OS
   chrome: a black rule on a hairline track. */
.board { scrollbar-width: thin; scrollbar-color: var(--ink) transparent; }
.board::-webkit-scrollbar { height: 8px; }
.board::-webkit-scrollbar-track {
  background: transparent;
  border-top: 1px solid var(--hairline);
}
.board::-webkit-scrollbar-thumb { background: var(--ink); }
.board::-webkit-scrollbar-thumb:hover { background: var(--red); }

.list {
  min-width: 0;
  background: transparent;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.list-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0 0;
  border-top: var(--rule);
}
.list-header .rename { flex: 1; }
.list-header input {
  width: 100%;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: var(--track);
  text-transform: uppercase;
  background: transparent;
  border: 2px solid transparent;
  padding: 2px 4px;
  color: var(--ink);
}
.list-header input:focus {
  background: var(--card);
  border-color: var(--ink);
  outline: none;
}

/* Yellow square: the third primary, carrying the counts. */
.count {
  flex: 0 0 auto;
  min-width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 6px;
  background: var(--yellow);
  color: var(--ink);
  font-size: 14px;
  font-weight: 700;
}

button.icon {
  background: transparent;
  border: none;
  color: var(--faint);
  font-size: 20px;
  line-height: 1;
  padding: 0 4px;
}
button.icon:hover { color: var(--red); background: transparent; }

.cards {
  flex: 1;
  min-height: 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* The Done column is spent: its cards lose the shadow and go quiet. */
.list-done .card {
  box-shadow: none;
  border-color: var(--hairline);
  background: var(--paper);
}
.list-done .card-title { color: var(--muted); }

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

.card {
  background: var(--card);
  border: var(--border);
  box-shadow: var(--shadow);
  padding: 12px 14px 10px;
  cursor: grab;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* A daily pick is called out by the red rule down its edge. A card feeding a
   weekly goal is marked by its chip instead: a second coloured edge fought
   with the offset shadow. */
.card.is-daily { border-left: 8px solid var(--red); }

/* The offset shadow is the signature of this design, so it is what reacts:
   the card steps toward you and the shadow opens up behind it. */
.card {
  transition: transform .16s cubic-bezier(.2, .7, .3, 1),
              box-shadow .16s cubic-bezier(.2, .7, .3, 1);
}
.card:hover:not(.card-ghost):not(.sortable-chosen):not(.card-edit) {
  transform: translate(-2px, -2px);
  box-shadow: 8px 8px 0 var(--ink);
}
.list-done .card:hover:not(.card-ghost):not(.sortable-chosen) {
  box-shadow: 4px 4px 0 var(--hairline);
}

/* While dragging, the gap left behind is an empty dashed slot rather than a
   ghost of the card — the same dashed rule the "add a card" box uses. */
.card-ghost {
  opacity: 1;
  background: transparent;
  border: 2px dashed var(--ink);
  border-left-width: 2px;
  box-shadow: none;
  transform: none;
}
.card-ghost > * { visibility: hidden; }

/* The card under the cursor tilts, so it reads as picked up off the board. */
.sortable-drag {
  box-shadow: 10px 10px 0 var(--ink);
  transform: rotate(-1.5deg);
}

.card-title {
  font-size: 16px;
  font-weight: 500;
  line-height: 1.3;
  white-space: pre-wrap;
  word-break: break-word;
}

.card-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.chip {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 8px;
  background: var(--yellow);
  color: var(--ink);
}
.chip.due {
  background: transparent;
  color: var(--muted);
  padding: 0;
  letter-spacing: 0.06em;
  font-weight: 500;
}
.chip.due.overdue { color: var(--red); font-weight: 700; }
.chip.kind-daily { background: var(--red); color: var(--card); }
.chip.kind-weekly { background: var(--blue); color: var(--card); }

.card-notes, .card-source {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
  white-space: pre-wrap;
  word-break: break-word;
}
.card-source {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  color: var(--faint);
}

.card-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--ink);
}
.card-actions .spacer { flex: 1; }

/* ---------- buttons ---------- */

button {
  font: inherit;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: var(--track);
  text-transform: uppercase;
  padding: 5px 10px;
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
button:hover { color: var(--ink); background: transparent; }

/* The daily toggle IS the red circle — filled when on, outlined when off. */
button.m3 {
  width: 22px;
  height: 22px;
  padding: 0;
  border: 2px solid var(--ink);
  border-radius: 50%;
  background: var(--card);
  font-size: 0;
  line-height: 0;
  color: transparent;
  flex: 0 0 auto;
}
button.m3:hover { background: #f7d9d5; }
.card-actions button.m3.on {
  background: var(--red);
  border-color: var(--red);
}
.card-actions button.m3.on:hover { background: #c22817; }

/* Edit / Archive / Save / Cancel: quiet until hovered. */
.card-actions button:not(.m3):hover,
.goal-foot button:hover { color: var(--red); }

button.done-btn {
  background: var(--card);
  border: var(--border);
  color: var(--ink);
  padding: 4px 10px;
}
button.done-btn:hover { background: var(--ink); color: var(--card); }

button.link-btn { background: transparent; border: none; }
button.link-btn.danger:hover { color: var(--red); background: transparent; }

/* ---------- forms ---------- */

.add-card input,
.add-goal input,
.add-list input {
  width: 100%;
  padding: 10px 12px;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.06em;
  background: transparent;
  border: 2px dashed var(--ink);
  color: var(--ink);
}
.add-card input::placeholder,
.add-goal input::placeholder,
.add-list input::placeholder {
  color: var(--faint);
  text-transform: uppercase;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: var(--track);
}
.add-card input:focus,
.add-goal input:focus,
.add-list input:focus {
  outline: none;
  border-style: solid;
  background: var(--card);
}
.add-goal { margin-bottom: 14px; }

.card-edit { cursor: default; }
.card-edit input,
.card-edit textarea,
.card-edit select,
.goal-edit input,
.goal-edit textarea {
  width: 100%;
  font: inherit;
  font-size: 14px;
  padding: 7px 9px;
  border: var(--border);
  background: var(--card);
  color: var(--ink);
}
.card-edit input:focus,
.card-edit textarea:focus,
.card-edit select:focus,
.goal-edit input:focus,
.goal-edit textarea:focus { outline: 2px solid var(--red); outline-offset: 1px; }
.card-edit .row { display: flex; gap: 10px; }
.card-edit label,
.card-edit label.source {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--muted);
  flex: 1;
}

.goal-edit { display: flex; flex-direction: column; gap: 8px; }
.goal-edit .goal-foot { margin-left: 0; }

/* Adding a list is board chrome, below the pipeline and outside the scroller:
   as a trailing column it forced a sideways scrollbar even when the real
   columns fit. */
.add-list {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 12px;
  max-width: 460px;
  margin-top: 26px;
}
.add-list input { flex: 1; min-width: 0; }
.add-list button { flex: 0 0 auto; }
.add-list button {
  border: var(--border);
  background: var(--card);
  color: var(--ink);
  padding: 9px 12px;
}
.add-list button:hover { background: var(--ink); color: var(--card); }

/* ---------- inline title editing ---------- */

.card-title { cursor: text; padding: 1px 2px; margin: -1px -2px; }
.card-title:hover { background: var(--paper); }
.card-title-editing { cursor: default; }
.title-input {
  width: 100%;
  font: inherit;
  font-size: 16px;
  padding: 7px 9px;
  border: var(--border);
  background: var(--card);
  resize: vertical;
}
.title-input:focus { outline: 2px solid var(--red); outline-offset: 1px; }
.hint {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  margin-left: auto;
}

/* ---------- goal chip ---------- */

/* A card that feeds a weekly goal points at it with the blue square. */
/* Block, not inline-flex: text-overflow only ellipsises a real line box, and
   as a flex container the label could not truncate — it forced the pick title
   to wrap one word per line instead. */
.goal-chip {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 500;
  color: var(--blue);
}
.goal-chip::before {
  content: "";
  display: inline-block;
  vertical-align: 0.06em;
  width: 10px;
  height: 10px;
  margin-right: 7px;
  background: var(--blue);
}

/* ---------- finished items ---------- */

.goal.is-done .goal-title,
.pick.is-done .pick-title,
.card.is-done .card-title {
  text-decoration: line-through;
  text-decoration-thickness: 2px;
  color: var(--muted);
}
.pick.is-done { opacity: .7; }
.card.is-done { box-shadow: none; border-color: var(--hairline); background: var(--paper); }

/* ---------- archive & history ---------- */

.plain-list { list-style: none; padding: 0; max-width: 760px; margin: 0; }
.plain-list li {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--card);
  border: var(--border);
  box-shadow: var(--shadow-sm);
  padding: 12px 16px;
  margin-bottom: 14px;
}
.plain-list li strong { font-weight: 500; font-size: 16px; }
.plain-list li form { margin-left: auto; }
.plain-list li button {
  border: var(--border);
  padding: 5px 12px;
  color: var(--ink);
}
.plain-list li button:hover { background: var(--ink); color: var(--card); }

.history {
  border-collapse: collapse;
  background: var(--card);
  max-width: 760px;
  width: 100%;
  border: var(--border);
  box-shadow: var(--shadow-sm);
}
.history th, .history td {
  text-align: left;
  padding: 10px 16px;
  border-bottom: 1px solid var(--hairline);
}
.history tr:last-child td { border-bottom: none; }
.history th {
  font-size: 11px;
  font-weight: 700;
  color: var(--ink);
  text-transform: uppercase;
  letter-spacing: var(--track);
  border-bottom: var(--border);
}

.empty { color: var(--faint); font-style: normal; }

/* ---------- motion ---------- */

/* The board is constructed rather than just shown: columns rule in from the
   top, then their cards land in sequence. JS sets --d per card and drops the
   class afterwards, so this plays on arrival and never on an HTMX swap. */
@keyframes rule-in {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes card-in {
  from { opacity: 0; transform: translateY(12px) scale(.985); }
  to   { opacity: 1; transform: none; }
}

.board.is-entering .list-header {
  animation: rule-in .34s cubic-bezier(.2, .7, .3, 1) both;
  transform-origin: left center;
}
.board.is-entering .card {
  animation: card-in .3s cubic-bezier(.2, .7, .3, 1) both;
  animation-delay: var(--d, 0ms);
}

/* A card added from the compose box slides in on its own. */
@keyframes card-new {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}
.card.card-new { animation: card-new .26s cubic-bezier(.2, .7, .3, 1) both; }

/* Picking a card for today is the gesture this whole app exists for, so the
   red circle answers with a pop. The card is re-rendered on toggle, which is
   what replays it. */
@keyframes m3-pop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.32); }
  100% { transform: scale(1); }
}
.card-actions button.m3.on { animation: m3-pop .3s cubic-bezier(.2, .7, .3, 1); }

/* Three of three: the counter swells once as the rail redraws. */
@keyframes count-pop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}
.pick-count.full { animation: count-pop .38s cubic-bezier(.2, .7, .3, 1); }

/* A finished pick's circle hollows out rather than snapping. */
.pick-main::before { transition: background .2s ease, border-color .2s ease; }

button.done-btn,
button.primary,
a.button-link,
.count,
button.m3 { transition: background .15s ease, color .15s ease, border-color .15s ease; }

/* ---------- celebration ---------- */

.celebrate-layer {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 9999;
}
.celebrate-layer i { position: absolute; will-change: transform, opacity; }

/* Every piece is one of the three shapes, in one of the three primaries, so a
   finished item scatters the board's own vocabulary across the screen. */
.piece {
  width: var(--size, 11px);
  height: var(--size, 11px);
  background: var(--fill, var(--red));
}
.piece.is-circle { border-radius: 50%; }
.piece.is-tri { clip-path: polygon(50% 0%, 100% 100%, 0% 100%); }

.piece.fly { animation: piece-fly 1.35s cubic-bezier(.15, .6, .35, 1) forwards; }
@keyframes piece-fly {
  0%   { transform: translate(0, 0) rotate(0deg); opacity: 1; }
  55%  { opacity: 1; }
  100% { transform: translate(var(--dx), calc(var(--dy) + 360px)) rotate(var(--spin)); opacity: 0; }
}

.piece.radiate { animation: piece-radiate 1.15s cubic-bezier(.2, .7, .3, 1) forwards; }
@keyframes piece-radiate {
  0%   { transform: translate(0, 0) scale(.2) rotate(0deg); opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) scale(1) rotate(var(--spin)); opacity: 0; }
}

.piece.drift { animation: piece-drift 1.7s cubic-bezier(.2, .6, .3, 1) forwards; }
@keyframes piece-drift {
  0%   { transform: translate(0, 0) scale(.3) rotate(0deg); opacity: 0; }
  18%  { opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) scale(1) rotate(var(--spin)); opacity: 0; }
}

/* Concentric rings, the most Bauhaus gesture available. */
.shockwave {
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  border: 3px solid var(--ring, var(--red));
  border-radius: 50%;
  animation: shockwave .8s ease-out forwards;
  animation-delay: var(--d, 0ms);
}
@keyframes shockwave {
  0%   { transform: scale(1); opacity: .9; }
  100% { transform: scale(16); opacity: 0; }
}

/* ---------- reduced motion ---------- */

/* Everything decorative stops; nothing that conveys state disappears with it.
   The progress bar keeps its width, cards keep their shadow, picks stay red. */
@media (prefers-reduced-motion: reduce) {
  .piece, .shockwave { animation-duration: .01ms; opacity: 0; }
  .board.is-entering .card,
  .board.is-entering .list-header,
  .card.card-new,
  .card-actions button.m3.on,
  .pick-count.full { animation: none; }
  .card,
  .bar-fill,
  .pick-main::before,
  button.done-btn,
  button.primary,
  a.button-link,
  .count,
  button.m3 { transition: none; }
  .card:hover:not(.card-ghost):not(.sortable-chosen):not(.card-edit) { transform: none; }
  .sortable-drag { transform: none; }
}

/* ---------- phone ---------- */

@media (max-width: 760px) {
  body { font-size: 16px; }
  main { padding: 16px 16px 64px; }
  .topbar { gap: 14px; padding: 14px 16px; flex-wrap: wrap; }
  .brand { font-size: 17px; letter-spacing: 0.14em; }
  .toast { margin: 0 16px; }

  /* One column each: the rail stacks, then the board scrolls vertically. */
  .rail { grid-template-columns: 1fr; }
  .rail-weekly { border-right: none; border-bottom: var(--rule); }
  .rail-col { padding: 16px 16px 18px; }
  /* On a phone the board becomes a pager: one column fills most of the screen
     and the next one peeks in at the edge, which says "swipe" without needing
     a scrollbar to say it. Snapping is mandatory here so a swipe lands on a
     column rather than between two. */
  .board {
    grid-auto-columns: minmax(78vw, 1fr);
    gap: 18px;
    scroll-snap-type: x mandatory;
    scroll-padding-left: 16px;
    /* Full-bleed, so a column can sit flush against the edge it snaps to. */
    margin: 0 -16px;
    padding: 0 16px 14px;
  }

  /* Comfortable touch targets: 44px minimum on anything tappable. */
  button { padding: 12px 14px; font-size: 12px; }
  button.m3 { width: 44px; height: 44px; }
  button.done-btn { padding: 12px 16px; }
  button.icon { padding: 10px 12px; font-size: 24px; }
  .card { padding: 14px 16px 12px; }
  .add-card input, .add-goal input, .add-list input { padding: 13px; font-size: 16px; }
  .card-edit input, .card-edit textarea, .card-edit select,
  .goal-edit input, .goal-edit textarea, .title-input { font-size: 16px; }
  .hint { display: none; }

  /* A pick's title gets the full width; chip and Done sit on their own row. */
  .pick { flex-direction: column; align-items: stretch; gap: 8px; }
  .pick-side { justify-content: flex-end; }
  .pick-side .goal-chip { margin-right: auto; }
  .pick .goal-chip { min-width: 0; }
  .goal-head { flex-wrap: wrap; }
  .goal-foot { flex-wrap: wrap; margin-left: 0; }
  .goal-notes { margin-left: 0; }
}

/* ---------- sign-in ---------- */

.topbar .logout { margin-left: auto; }

.auth {
  max-width: 380px;
  margin: 56px auto;
  background: var(--card);
  border: var(--border);
  box-shadow: var(--shadow);
  padding: 28px;
}
.auth-wide { max-width: 440px; }
.auth h1 { font-size: 17px; margin: 0 0 20px; }
.auth form { display: flex; flex-direction: column; gap: 16px; }
.auth label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: var(--track);
  text-transform: uppercase;
  color: var(--muted);
}
.auth input {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 10px 12px;
  font: inherit;
  font-size: 16px;
  font-weight: 500;
  border: var(--border);
  background: var(--card);
  color: var(--ink);
}
.auth input:focus { outline: 2px solid var(--red); outline-offset: 1px; }
.auth p { font-size: 14px; }

button.primary,
a.button-link {
  display: inline-block;
  background: var(--ink);
  color: var(--card);
  border: var(--border);
  padding: 12px 18px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: var(--track);
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
}
button.primary:hover,
a.button-link:hover { background: var(--red); border-color: var(--red); color: var(--card); }

.auth-error {
  margin: 0 0 18px;
  padding: 11px 13px;
  background: var(--card);
  border: 2px solid var(--red);
  color: var(--red);
  font-weight: 500;
  font-size: 14px;
}
.auth-note { font-size: 13px; color: var(--muted); }
.qr {
  display: block;
  margin: 18px auto;
  image-rendering: pixelated;
  border: var(--border);
}
.secret {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  word-break: break-all;
  background: var(--paper);
  border: 2px dashed var(--ink);
  padding: 10px 12px;
  margin: 0 0 18px;
}

.auth .checkbox {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}
.auth .checkbox input[type="checkbox"] {
  display: inline-block;
  width: 18px;
  min-width: 18px;
  height: 18px;
  margin: 0;
  flex: 0 0 auto;
  accent-color: var(--red);
}
.auth a { color: var(--blue); }
.auth a:hover { color: var(--red); }
