/*
 * app.css — the ONE stylesheet. Tokens, reset, components, canvas.
 *
 * DESIGN BRIEF, in the user's own words: ideas pile up unlisted and it causes
 * "headache, panic, fear of failure, fear of lacking behind". The app must
 * reduce that feeling rather than mirror it back.
 *
 * Three rules follow from that and they are not negotiable:
 *
 *   1. NO RED. Nothing on this canvas is coloured to alarm. A project going
 *      cold FADES and COOLS instead — it recedes rather than shouting. The
 *      information is identical; the emotional register is not.
 *   2. NO COUNTERS OF FAILURE. No "7 overdue" pill anywhere.
 *   3. SPACE. The canvas is mostly empty on purpose. Emptiness is the feature
 *      the user asked for when they said "not suffocating".
 *
 * ONE FILE ON PURPOSE. The first cut split canvas.css from a page stylesheet
 * and immediately had two copies of the token block. Tarly's notes are full of
 * what that costs: a legacy stylesheet per page, each with its own :root and
 * reset, and a rule that only holds on some pages. There is one writer for
 * these tokens and it is this file.
 *
 * Canvas-only rules are scoped to `body.canvas-page`, because the canvas needs
 * `overflow: hidden` and a document page needs to scroll.
 */

:root {
  --bd-ground:    #F5F2EC;
  --bd-grid:      rgba(27, 26, 22, .055);
  --bd-grid-big:  rgba(27, 26, 22, .085);
  --bd-card:      #FFFDF9;
  --bd-ink:       #1B1A16;
  --bd-soft:      #56534A;
  --bd-faint:     #8B8577;
  --bd-rule:      #E2DBCC;
  --bd-chrome:    rgba(255, 253, 249, .88);
  --bd-shadow:    rgba(27, 26, 22, .13);

  --bd-brass:  #9A6E22;
  --bd-patina: #3E7264;
  --bd-clay:   #A2553A;
  --bd-slate:  #4A5A6B;
  --bd-plum:   #6E4462;
  --bd-moss:   #5B6B37;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bd-ground:   #131519;
    --bd-grid:     rgba(233, 229, 220, .05);
    --bd-grid-big: rgba(233, 229, 220, .08);
    --bd-card:     #1C2027;
    --bd-ink:      #E9E5DC;
    --bd-soft:     #A29C90;
    --bd-faint:    #6F6A62;
    --bd-rule:     #2E333C;
    --bd-chrome:   rgba(28, 32, 39, .88);
    --bd-shadow:   rgba(0, 0, 0, .45);

    --bd-brass:  #D9A94F;
    --bd-patina: #6FAA96;
    --bd-clay:   #D08363;
    --bd-slate:  #8AA0B8;
    --bd-plum:   #B486A6;
    --bd-moss:   #9DB169;
  }
}

:root[data-theme="dark"] {
  --bd-ground:   #131519;
  --bd-grid:     rgba(233, 229, 220, .05);
  --bd-grid-big: rgba(233, 229, 220, .08);
  --bd-card:     #1C2027;
  --bd-ink:      #E9E5DC;
  --bd-soft:     #A29C90;
  --bd-faint:    #6F6A62;
  --bd-rule:     #2E333C;
  --bd-chrome:   rgba(28, 32, 39, .88);
  --bd-shadow:   rgba(0, 0, 0, .45);

  --bd-brass:  #D9A94F;
  --bd-patina: #6FAA96;
  --bd-clay:   #D08363;
  --bd-slate:  #8AA0B8;
  --bd-plum:   #B486A6;
  --bd-moss:   #9DB169;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bd-ground);
  color: var(--bd-ink);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* Only the canvas locks the document. A page that scrolls must be allowed to. */
html:has(body.canvas-page), body.canvas-page {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;     /* no rubber-band / pull-to-refresh while panning */
}

/* ── viewport ─────────────────────────────────────────────────────
   The window onto the world. Fills the screen and clips; all motion
   happens on .world inside it. */
.viewport {
  position: fixed;
  inset: 0;
  overflow: hidden;
  cursor: grab;
  touch-action: none;            /* we handle pan and pinch ourselves */
  /* The grid is drawn on the VIEWPORT, not the world, and its
     background-position is driven from JS. Scaling a 20px grid on a
     transformed element makes it disappear at low zoom and turn into
     stripes at high zoom. */
  background-image:
    linear-gradient(var(--bd-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--bd-grid) 1px, transparent 1px),
    linear-gradient(var(--bd-grid-big) 1px, transparent 1px),
    linear-gradient(90deg, var(--bd-grid-big) 1px, transparent 1px);
}
.viewport.is-panning { cursor: grabbing; }

/* ── world ────────────────────────────────────────────────────────
   Infinite in every direction including negative. Only transform
   changes, never layout, so panning never triggers reflow. */
.world {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  will-change: transform;
}

/* ── card ─────────────────────────────────────────────────────────
   Positioned in world coordinates. */
.card {
  position: absolute;
  background: var(--bd-card);
  border: 1px solid var(--bd-rule);
  border-radius: 10px;
  padding: 14px 15px 12px;
  box-shadow: 0 1px 2px var(--bd-shadow);
  display: flex;
  flex-direction: column;
  gap: 7px;
  cursor: grab;
  /* Set from JS per card: 0 warm, 1 cold. Everything that expresses
     staleness reads from this one number. */
  --cool: 0;
  /* Going cold RECEDES. It does not turn red and it does not flash. */
  opacity: calc(1 - var(--cool) * 0.42);
  transition: opacity .25s ease, box-shadow .18s ease, transform .18s ease;
}
.card:hover {
  opacity: 1;                    /* looking at it brings it fully back */
  box-shadow: 0 6px 18px var(--bd-shadow);
  z-index: 5;
}
.card.is-dragging {
  cursor: grabbing;
  opacity: 1;
  box-shadow: 0 14px 34px var(--bd-shadow);
  z-index: 20;
}
.card:focus-visible {
  outline: 2px solid var(--bd-brass);
  outline-offset: 3px;
  opacity: 1;
}

/* The accent stripe is the one saturated thing on a card. */
.card::before {
  content: "";
  position: absolute;
  left: 0;
  top: 12px;
  bottom: 12px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--accent);
  opacity: calc(1 - var(--cool) * 0.5);
}

.card.a-brass  { --accent: var(--bd-brass); }
.card.a-patina { --accent: var(--bd-patina); }
.card.a-clay   { --accent: var(--bd-clay); }
.card.a-slate  { --accent: var(--bd-slate); }
.card.a-plum   { --accent: var(--bd-plum); }
.card.a-moss   { --accent: var(--bd-moss); }

.card-name {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -.008em;
  margin: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.card-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--bd-faint);
  margin-top: auto;
}

.stage {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--accent);
}

/* Days since last touched. Uses the SAME muted colour at every value.
   A number that turns red at 90 days is a tool telling you off. */
.since {
  font-variant-numeric: tabular-nums;
  margin-left: auto;
}

/* ── the next action, or its absence ──────────────────────────────
   A project with no next action is stalled, and that is the most useful
   signal in the app. It is stated plainly and quietly, never in red. */
.next {
  font-size: 12px;
  line-height: 1.4;
  color: var(--bd-soft);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.next.none {
  color: var(--bd-faint);
  font-style: italic;
}

/* A hairline progress rule, only when steps exist. */
.steps {
  height: 2px;
  border-radius: 2px;
  background: var(--bd-rule);
  overflow: hidden;
}
.steps > i {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: 2px;
  opacity: .75;
}

/* ── chrome ───────────────────────────────────────────────────────
   Deliberately tiny. A toolbar that fills the top of the screen is the
   suffocating shape the brief rules out. */
.bar {
  position: fixed;
  top: 14px;
  left: 14px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  background: var(--bd-chrome);
  backdrop-filter: blur(14px) saturate(1.5);
  border: 1px solid var(--bd-rule);
  border-radius: 10px;
  box-shadow: 0 2px 10px var(--bd-shadow);
  z-index: 40;
}
.bar-name {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -.01em;
  padding: 0 8px 0 4px;
}
.bar-sep { width: 1px; height: 20px; background: var(--bd-rule); }

.btn {
  font: inherit;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--bd-ink);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 7px;
  padding: 6px 10px;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { background: var(--bd-rule); }
.btn:focus-visible { outline: 2px solid var(--bd-brass); outline-offset: 1px; }
.btn-primary { color: var(--bd-card); background: var(--bd-brass); }
.btn-primary:hover { background: var(--bd-brass); filter: brightness(1.08); }

.zoomer {
  position: fixed;
  bottom: 14px;
  left: 14px;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  background: var(--bd-chrome);
  backdrop-filter: blur(14px) saturate(1.5);
  border: 1px solid var(--bd-rule);
  border-radius: 10px;
  z-index: 40;
}
.zoomer .btn { padding: 5px 9px; }
.zoom-level {
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--bd-faint);
  min-width: 44px;
  text-align: center;
}

/* ── empty state ──────────────────────────────────────────────────
   The first thing a new user reads. It sets the tone for the whole app,
   so it explains the gesture and then gets out of the way. */
.blank {
  position: fixed;
  inset: 0;
  display: grid;
  place-content: center;
  text-align: center;
  gap: 10px;
  pointer-events: none;
  color: var(--bd-faint);
  padding: 2rem;
}
.blank h1 {
  font-size: 19px;
  font-weight: 600;
  color: var(--bd-soft);
  margin: 0;
  letter-spacing: -.01em;
}
.blank p { font-size: 13.5px; margin: 0; max-width: 30rem; line-height: 1.55; }

/* ── dialog ───────────────────────────────────────────────────────
   A panel that must OCCLUDE cannot be built from translucency alone: on a
   real iPhone the backdrop-filter never arrives and the content behind
   shows through. Opaque base, blur as enhancement. */
dialog.sheet {
  border: 1px solid var(--bd-rule);
  border-radius: 14px;
  background: var(--bd-card);
  color: var(--bd-ink);
  padding: 0;
  width: min(30rem, calc(100vw - 2rem));
  box-shadow: 0 24px 60px var(--bd-shadow);
}
dialog.sheet::backdrop { background: rgba(0, 0, 0, .38); }
.sheet-body { padding: 20px 22px 18px; display: flex; flex-direction: column; gap: 14px; }
.sheet h2 { font-size: 16px; font-weight: 600; margin: 0; letter-spacing: -.01em; }
.sheet-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 13px 22px;
  border-top: 1px solid var(--bd-rule);
}

.f { display: flex; flex-direction: column; gap: 5px; }
.f > label { font-size: 11.5px; font-weight: 600; color: var(--bd-soft); letter-spacing: .02em; }
.f > input, .f > select, .f > textarea {
  font: inherit;
  /* 16px, or iOS zooms the page on focus and leaves you zoomed. This is a
     documented Tarly bug; the rule is not optional on a touch device. */
  font-size: 16px;
  color: var(--bd-ink);
  background: var(--bd-ground);
  border: 1px solid var(--bd-rule);
  border-radius: 8px;
  padding: 9px 11px;
  min-height: 44px;
}
.f > textarea { min-height: 76px; resize: vertical; line-height: 1.5; }
.f > input:focus, .f > select:focus, .f > textarea:focus {
  outline: 2px solid var(--bd-brass);
  outline-offset: -1px;
  border-color: transparent;
}
.f-hint { font-size: 11.5px; color: var(--bd-faint); line-height: 1.45; }

.swatches { display: flex; gap: 7px; flex-wrap: wrap; }
.swatch {
  width: 30px;
  height: 30px;
  min-height: 30px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
  background: var(--sw);
}
.swatch:focus-visible { outline: 2px solid var(--bd-ink); outline-offset: 2px; }
.swatch[aria-pressed="true"] { border-color: var(--bd-ink); }
.sw-brass  { --sw: var(--bd-brass); }
.sw-patina { --sw: var(--bd-patina); }
.sw-clay   { --sw: var(--bd-clay); }
.sw-slate  { --sw: var(--bd-slate); }
.sw-plum   { --sw: var(--bd-plum); }
.sw-moss   { --sw: var(--bd-moss); }

@media (prefers-reduced-motion: reduce) {
  .card { transition: none; }
}

@media (max-width: 640px) {
  .bar {
    left: 10px;
    right: 10px;
    top: 10px;
    /* SCROLL, do not clip. The bar grew to ten items and on a 375px phone
       Team, Account and Sign out were cut off past the right edge with no
       way to reach them — you could not sign out on a phone at all.
       Found by looking at the page; nothing automated catches it. */
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* A fade on the right edge that disappears at the end of the scroll, so
       it never claims there is more when there is not. */
    background-image: linear-gradient(to left, var(--bd-chrome), transparent 28px);
    background-attachment: local, scroll;
  }
  .bar::-webkit-scrollbar { display: none; }
  /* Without this the items compress to nothing instead of overflowing,
     which is a different way of being unreadable. */
  .bar > * { flex: none; }
  .bar-name { display: none; }
  .zoomer { bottom: 10px; left: 10px; }
}

/* ══════════════════════════════════════════════════════════════════
   DOCUMENT PAGES
   Everything below is for pages that scroll: the project detail page,
   the review flow, the list, and sign-in. They share the tokens above,
   which is the whole reason this is one file.
   ══════════════════════════════════════════════════════════════════ */

.page {
  max-width: 54rem;
  margin: 0 auto;
  padding: 0 1.25rem 5rem;
}

/* Top bar on document pages. Static, not fixed: a fixed bar on a scrolling
   page eats vertical space on every screen, and Tarly measured its own
   chrome at 32% of a phone before any content appeared. */
.topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 0;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--bd-rule);
}
.topbar .btn { padding: 6px 10px; }
.topbar-spacer { margin-left: auto; }
.back {
  font-size: 13px;
  color: var(--bd-soft);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 8px;
  border-radius: 7px;
  min-height: 36px;
}
.back:hover { background: var(--bd-rule); color: var(--bd-ink); }

h1.title {
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -.02em;
  line-height: 1.15;
  margin: 0 0 .5rem;
  text-wrap: balance;
}
.sub {
  font-size: 13.5px;
  color: var(--bd-faint);
  margin: 0 0 1.75rem;
  line-height: 1.55;
}

.panel {
  background: var(--bd-card);
  border: 1px solid var(--bd-rule);
  border-radius: 12px;
  padding: 18px 20px;
  margin-bottom: 1rem;
}
.panel > h2 {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--bd-faint);
  margin: 0 0 14px;
}
.panel-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

/* ── the pipeline ─────────────────────────────────────────────────
   Four stages as a row of steps. The exits (parked, dropped) are NOT
   in this row; they are separate actions, because they are not further
   along, they are off to the side. */
.pipe { display: flex; gap: 6px; flex-wrap: wrap; }
.pipe-step {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  padding: 9px 14px;
  min-height: 40px;
  border-radius: 8px;
  border: 1px solid var(--bd-rule);
  background: transparent;
  color: var(--bd-soft);
  cursor: pointer;
}
.pipe-step:hover { background: var(--bd-rule); color: var(--bd-ink); }
.pipe-step[aria-current="true"] {
  background: var(--bd-ink);
  border-color: var(--bd-ink);
  color: var(--bd-ground);
}
/* Stages already passed read as done without being loud about it. */
.pipe-step.past { color: var(--bd-ink); border-color: var(--bd-ink); }
.pipe-step:focus-visible { outline: 2px solid var(--bd-brass); outline-offset: 2px; }

/* ── steps list ───────────────────────────────────────────────────
   Thin on purpose. No assignee, no priority, no drag handles. */
.step-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 0;
  border-top: 1px solid var(--bd-rule);
}
.step-row:first-child { border-top: none; }
.step-check {
  appearance: none;
  width: 19px;
  height: 19px;
  flex: none;
  border: 1.5px solid var(--bd-faint);
  border-radius: 5px;
  cursor: pointer;
  position: relative;
  background: transparent;
}
.step-check:checked { background: var(--bd-patina); border-color: var(--bd-patina); }
.step-check:checked::after {
  content: "";
  position: absolute;
  left: 6px; top: 2px;
  width: 5px; height: 10px;
  border: solid var(--bd-card);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
/* The wrapping label is the 44px target, not the 19px box. A checkbox
   stretched to 44px square looks broken; a label that size does not. */
.step-label {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 44px;
  flex: 1;
  cursor: pointer;
  font-size: 14px;
  line-height: 1.45;
}
.step-label.done { color: var(--bd-faint); text-decoration: line-through; }
.step-kill {
  font: inherit;
  font-size: 15px;
  line-height: 1;
  color: var(--bd-faint);
  background: transparent;
  border: 0;
  border-radius: 7px;
  cursor: pointer;
  min-width: 34px;
  min-height: 34px;
}
.step-kill:hover { background: var(--bd-rule); color: var(--bd-ink); }

.inline-form { display: flex; gap: 8px; margin-top: 10px; }
.inline-form input {
  flex: 1;
  font: inherit;
  font-size: 16px;      /* 16px or iOS zooms on focus */
  min-height: 44px;
  color: var(--bd-ink);
  background: var(--bd-ground);
  border: 1px solid var(--bd-rule);
  border-radius: 8px;
  padding: 9px 11px;
}
.inline-form input:focus { outline: 2px solid var(--bd-brass); outline-offset: -1px; }

/* ── the timeline ─────────────────────────────────────────────────
   The honest history. Events that were not progress are dimmed rather
   than hidden: "I reviewed this and left it parked" is a real thing to
   have done and deserves to be visible. */
.event {
  display: flex;
  gap: 12px;
  padding: 8px 0;
  font-size: 13.5px;
  line-height: 1.5;
  border-top: 1px solid var(--bd-rule);
}
.event:first-child { border-top: none; }
.event.quiet { color: var(--bd-faint); }
.event-when {
  flex: none;
  width: 5.5rem;
  color: var(--bd-faint);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  padding-top: 1px;
}
.event-what { flex: 1; }
.event-kind { font-weight: 600; }

/* ── list / portfolio ─────────────────────────────────────────────── */
.rows { display: flex; flex-direction: column; }
.row-item {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 14px;
  align-items: center;
  padding: 13px 0;
  border-top: 1px solid var(--bd-rule);
  text-decoration: none;
  color: inherit;
}
.row-item:first-child { border-top: none; }
.row-item:hover .row-name { color: var(--bd-brass); }
.row-main { min-width: 0; }
/* display:block matters. These are <span>s inside a <span>, so without it
   they render INLINE and the project name runs straight into its subtitle
   with no break — which is exactly what shipped on /list and /patterns
   before anyone looked at the page. check-classes proves a class resolves,
   not that the layout is right. */
.row-name {
  display: block;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.008em;
}
.row-next {
  display: block;
  font-size: 12.5px;
  color: var(--bd-soft);
  margin-top: 2px;
}
.row-next.none { color: var(--bd-faint); font-style: italic; }
.row-stage {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--bd-faint);
}
.row-days {
  font-size: 12.5px;
  color: var(--bd-faint);
  font-variant-numeric: tabular-nums;
  min-width: 3.6rem;
  text-align: right;
}
/* Dropped projects are kept and readable, never hidden and never red.
   The graveyard is a feature; the decision is the value. */
.row-item.gone .row-name { color: var(--bd-faint); }

/* ── review ───────────────────────────────────────────────────────
   Three doors, equal weight. Dropping must never look like losing:
   a tool that shames you is a tool you stop opening, and the streak
   research is explicit that perfectionism is what makes people quit. */
.doors { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: 10px; }
.door {
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: left;
  font: inherit;
  padding: 15px 16px;
  border: 1px solid var(--bd-rule);
  border-radius: 10px;
  background: var(--bd-card);
  color: inherit;
  cursor: pointer;
}
.door:hover { border-color: var(--bd-ink); }
.door:focus-visible { outline: 2px solid var(--bd-brass); outline-offset: 2px; }
.door-title { font-size: 14.5px; font-weight: 600; }
.door-blurb { font-size: 12.5px; color: var(--bd-soft); line-height: 1.45; }

.empty {
  text-align: center;
  padding: 4rem 1rem;
  color: var(--bd-faint);
}
.empty h2 { font-size: 17px; font-weight: 600; color: var(--bd-soft); margin: 0 0 .5rem; }
.empty p { font-size: 13.5px; margin: 0 auto; max-width: 28rem; line-height: 1.55; }

.flash {
  background: var(--bd-card);
  border: 1px solid var(--bd-rule);
  border-left: 3px solid var(--bd-brass);
  border-radius: 8px;
  padding: 11px 14px;
  font-size: 13.5px;
  margin-bottom: 1rem;
}

.danger { color: var(--bd-clay); }

@media (max-width: 640px) {
  .page { padding: 0 1rem 4rem; }
  h1.title { font-size: 22px; }
  .row-item { grid-template-columns: 1fr auto; }
  .row-stage { display: none; }
  .event-when { width: 4.2rem; }
}

/* ── share links ──────────────────────────────────────────────────── */
.token {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  word-break: break-all;
  background: var(--bd-ground);
  border: 1px solid var(--bd-rule);
  border-radius: 7px;
  padding: 9px 11px;
  margin: 9px 0;
  user-select: all;          /* one click selects the whole link */
}
.check-line {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  font-size: 14px;
  cursor: pointer;
}
.ro-note {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--bd-faint);
  padding: 0 8px;
}
/* A read-only card must not look draggable. */
.card.ro { cursor: default; }

/* ── sign in with Google ──────────────────────────────────────────── */
.google-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  border: 1px solid var(--bd-rule);
  background: var(--bd-card);
  font-size: 14.5px;
  font-weight: 600;
  text-decoration: none;
  color: var(--bd-ink);
}
.google-btn:hover { border-color: var(--bd-ink); background: var(--bd-card); }
.or {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--bd-faint);
}
.or::before, .or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--bd-rule);
}

/* ── security page ────────────────────────────────────────────────── */
.qr-holder {
  display: flex;
  justify-content: center;
  padding: 14px;
  background: #fff;              /* a scanner needs light behind the code */
  border-radius: 10px;
}
.codes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 8px;
}
.code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 14px;
  letter-spacing: .06em;
  background: var(--bd-ground);
  border: 1px solid var(--bd-rule);
  border-radius: 7px;
  padding: 9px 11px;
  text-align: center;
  user-select: all;
}
.inline-input {
  flex: 1;
  min-width: 12rem;
  font: inherit;
  font-size: 16px;               /* 16px or iOS zooms on focus */
  min-height: 44px;
  color: var(--bd-ink);
  background: var(--bd-ground);
  border: 1px solid var(--bd-rule);
  border-radius: 8px;
  padding: 9px 11px;
}
.inline-input:focus { outline: 2px solid var(--bd-brass); outline-offset: -1px; }

/* ── patterns ─────────────────────────────────────────────────────── */
.pattern-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.pattern-head h2 {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
  text-transform: none;   /* panel h2 is an uppercase label; this is a heading */
  color: var(--bd-ink);
}
.pattern-count {
  font-size: 12.5px;
  color: var(--bd-faint);
  font-variant-numeric: tabular-nums;
  flex: none;
}
/* Deliberately the accent, never a warning colour. This is a record of your
   own decisions, not an alarm about them. */
.bar-track {
  height: 6px;
  border-radius: 3px;
  background: var(--bd-rule);
  overflow: hidden;
}
.bar-track > i {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--bd-brass);
  opacity: .8;
}

/* ── resize grip ──────────────────────────────────────────────────── */
.grip {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 18px;
  height: 18px;
  cursor: nwse-resize;
  opacity: 0;
  transition: opacity .15s ease;
  /* Two hairlines suggesting a corner, drawn rather than an icon file. */
  background:
    linear-gradient(135deg, transparent 45%, var(--bd-faint) 45%, var(--bd-faint) 55%, transparent 55%),
    linear-gradient(135deg, transparent 70%, var(--bd-faint) 70%, var(--bd-faint) 80%, transparent 80%);
}
/* Revealed on hover only. A grip on every card at all times turns a calm
   canvas into a fidgety one, which is the opposite of the brief. */
.card:hover .grip { opacity: .9; }
.card.is-resizing { opacity: 1; }
.card.is-resizing .grip { opacity: 1; }
/* A read-only viewer must not be shown a control they cannot use. */
.card.ro .grip { display: none; }
