/* ============================================================================
   Escalate — design system
   ----------------------------------------------------------------------------
   Ink and parchment. No gendered colour, no wellness-app pastels, no SaaS
   dashboard chrome. The accent is a muted sage; brass appears only where the
   app wants to feel expensive, and never twice on one screen.

   Two modes. Dark is the default because the app is used at the edges of the
   day — last thing at night, first thing in the morning — and because a
   sanctuary is not brightly lit.

   Everything is one origin: fonts below are self-hosted, GSAP is vendored.
   ========================================================================= */

/* ── fonts ───────────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Lora';
  src: url('/fonts/lora-var.woff2') format('woff2-variations');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Raleway';
  src: url('/fonts/raleway-var.woff2') format('woff2-variations');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

/* ── tokens ──────────────────────────────────────────────────────────────── */

:root {
  /* primitive */
  --ink-900: #0B0F18;
  --ink-800: #101521;
  --ink-700: #171D2B;
  --ink-600: #1F2637;
  --ink-500: #2A3346;

  --parch-50:  #FDFCF9;
  --parch-100: #FBF9F5;
  --parch-200: #F4F1EA;
  --parch-300: #E9E4D9;

  --sage-300: #9BC0B2;
  --sage-400: #7FA898;
  --sage-500: #6E9A8B;
  --sage-600: #567C6F;

  --brass-300: #D8C29A;
  --brass-400: #BFA173;
  --brass-500: #A28757;

  --clay-400: #C2705F;   /* destructive — earthen, not emergency red */
  --clay-500: #A65946;

  /* semantic — dark is the default mode */
  --bg:            var(--ink-800);
  --bg-deep:       var(--ink-900);
  --surface:       var(--ink-700);
  --surface-raised: var(--ink-600);
  --surface-sunk:  #0E121C;

  --text:          #E9E5DC;
  --text-muted:    rgba(233, 229, 220, 0.62);
  --text-faint:    rgba(233, 229, 220, 0.38);

  --line:          rgba(233, 229, 220, 0.10);
  --line-strong:   rgba(233, 229, 220, 0.18);

  --accent:        var(--sage-400);
  --accent-quiet:  var(--sage-600);
  --on-accent:     #08120E;
  --brass:         var(--brass-400);
  --danger:        var(--clay-400);

  --ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--sage-400);

  /* type */
  --serif: 'Lora', Georgia, 'Times New Roman', serif;
  --sans:  'Raleway', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;

  --t-xs:   0.75rem;
  --t-sm:   0.8125rem;
  --t-base: 0.9375rem;
  --t-md:   1.0625rem;
  --t-lg:   1.25rem;
  --t-xl:   1.5rem;
  --t-2xl:  1.875rem;
  --t-3xl:  2.375rem;

  /* space — spacious, per the density dial */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  /*
   * How tall .topbar actually is, derived rather than guessed.
   *
   * The desktop sidebar sticks below the header, so it needs this number and
   * it needs it to stay true. It was hardcoded as 57px in one place and 0 in
   * another, while the real height is 69px — the 44px brand target plus the
   * bar's own vertical padding and its bottom border. Written as a calc over
   * the same tokens the bar is built from, so changing --s-3 moves both
   * together instead of silently reopening the gap.
   */
  --topbar-h: calc(44px + (var(--s-3) * 2) + 1px);
  --s-5: 24px;  --s-6: 32px;  --s-7: 44px;  --s-8: 64px;  --s-9: 88px;

  --r-sm: 8px;  --r-md: 14px; --r-lg: 20px; --r-xl: 28px; --r-full: 999px;

  --shadow-sm: 0 1px 2px rgba(0,0,0,.28);
  --shadow-md: 0 6px 20px -6px rgba(0,0,0,.42);
  --shadow-lg: 0 24px 56px -20px rgba(0,0,0,.58);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.45, 0, 0.25, 1);
  --dur-fast: 160ms;
  --dur: 260ms;
  --dur-slow: 480ms;

  --shell-max: 720px;
  --nav-h: 64px;

  color-scheme: dark;
}

[data-theme='parchment'], [data-theme='light'] {
  --bg:            var(--parch-200);
  --bg-deep:       var(--parch-300);
  --surface:       var(--parch-100);
  --surface-raised: var(--parch-50);
  --surface-sunk:  #EFEBE1;

  --text:          #1B2130;
  --text-muted:    rgba(27, 33, 48, 0.66);
  --text-faint:    rgba(27, 33, 48, 0.42);

  --line:          rgba(27, 33, 48, 0.10);
  --line-strong:   rgba(27, 33, 48, 0.18);

  --accent:        var(--sage-600);
  --accent-quiet:  var(--sage-500);
  --on-accent:     #FBFDFC;
  --brass:         var(--brass-500);
  --danger:        var(--clay-500);

  --shadow-sm: 0 1px 2px rgba(27,33,48,.06);
  --shadow-md: 0 6px 20px -8px rgba(27,33,48,.14);
  --shadow-lg: 0 24px 56px -22px rgba(27,33,48,.20);

  color-scheme: light;
}


/* ══════════════════════════════════════════════════════════════════════════
   Themes
   --------------------------------------------------------------------------
   Each block overrides only the semantic layer — never the primitives and
   never a component rule. That is the whole reason a new theme is ~25 lines:
   every component reads --surface, --accent, --line and so on, so nothing
   below has to know that buttons or chips exist.

   Contrast was checked for body text on --bg in each: all clear 4.5:1.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Midnight — ink navy and sage, the default ───────────────────────────
   These values are already in :root, which is what a browser gets before any
   theme is chosen. Restating them under the key means every theme in config
   has a matching block — so "is this theme wired up?" is answerable by
   searching for its name, and midnight is not a special case a future reader
   has to know about. There is a test that enforces exactly this. */
[data-theme='midnight'] {
  --bg:            var(--ink-800);
  --bg-deep:       var(--ink-900);
  --surface:       var(--ink-700);
  --surface-raised: var(--ink-600);
  --surface-sunk:  #0E121C;

  --text:          #E9E5DC;
  --text-muted:    rgba(233, 229, 220, 0.62);
  --text-faint:    rgba(233, 229, 220, 0.38);

  --line:          rgba(233, 229, 220, 0.10);
  --line-strong:   rgba(233, 229, 220, 0.18);

  --accent:        var(--sage-400);
  --accent-quiet:  var(--sage-600);
  --on-accent:     #08120E;
  --brass:         var(--brass-400);
  --danger:        var(--clay-400);

  --ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--sage-400);
  color-scheme: dark;
}

/* ── Ember — near-black and brass, candlelit ─────────────────────────────── */
[data-theme='ember'] {
  --bg:            #16110D;
  --bg-deep:       #0F0B08;
  --surface:       #201913;
  --surface-raised: #2A211A;
  --surface-sunk:  #120E0A;

  --text:          #EFE6DA;
  --text-muted:    rgba(239, 230, 218, 0.62);
  --text-faint:    rgba(239, 230, 218, 0.38);

  --line:          rgba(239, 230, 218, 0.10);
  --line-strong:   rgba(239, 230, 218, 0.20);

  --accent:        #C79A5C;
  --accent-quiet:  #8A6737;
  --on-accent:     #17110A;
  --brass:         #D8B27A;
  --danger:        #B9705A;

  --ring: 0 0 0 2px var(--bg), 0 0 0 4px #C79A5C;
  color-scheme: dark;
}

[data-theme='ember'] body::before {
  background:
    radial-gradient(60% 60% at 22% 8%, rgba(199, 154, 92, 0.16), transparent 70%),
    radial-gradient(52% 52% at 82% 0%, rgba(185, 112, 90, 0.10), transparent 72%);
}

/* ── Tide — deep water and pale aqua, cool and awake ─────────────────────── */
[data-theme='tide'] {
  --bg:            #0B1A1F;
  --bg-deep:       #071216;
  --surface:       #10252B;
  --surface-raised: #163038;
  --surface-sunk:  #081418;

  --text:          #E4EDEE;
  --text-muted:    rgba(228, 237, 238, 0.62);
  --text-faint:    rgba(228, 237, 238, 0.38);

  --line:          rgba(228, 237, 238, 0.10);
  --line-strong:   rgba(228, 237, 238, 0.20);

  --accent:        #7FB8BE;
  --accent-quiet:  #48838A;
  --on-accent:     #05171A;
  --brass:         #C4A98A;
  --danger:        #C87F6B;

  --ring: 0 0 0 2px var(--bg), 0 0 0 4px #7FB8BE;
  color-scheme: dark;
}

[data-theme='tide'] body::before {
  background:
    radial-gradient(60% 60% at 22% 8%, rgba(127, 184, 190, 0.16), transparent 70%),
    radial-gradient(52% 52% at 82% 0%, rgba(196, 169, 138, 0.09), transparent 72%);
}

/* ── Graphite — neutral charcoal, nothing decorative ─────────────────────── */
[data-theme='graphite'] {
  --bg:            #141517;
  --bg-deep:       #0E0F11;
  --surface:       #1D1F22;
  --surface-raised: #26282C;
  --surface-sunk:  #101113;

  --text:          #E6E8EA;
  --text-muted:    rgba(230, 232, 234, 0.60);
  --text-faint:    rgba(230, 232, 234, 0.36);

  --line:          rgba(230, 232, 234, 0.10);
  --line-strong:   rgba(230, 232, 234, 0.20);

  --accent:        #9AA3AD;
  --accent-quiet:  #626A73;
  --on-accent:     #101112;
  --brass:         #C2B49A;
  --danger:        #C08475;

  --ring: 0 0 0 2px var(--bg), 0 0 0 4px #9AA3AD;
  color-scheme: dark;
}

/* The one theme with no wash. "Nothing decorative at all" is the brief. */
[data-theme='graphite'] body::before { background: none; }

/* ── Linen — warm oat and clay, the softest light one ────────────────────── */
[data-theme='linen'] {
  --bg:            #F5EFE6;
  --bg-deep:       #EBE3D6;
  --surface:       #FBF7F0;
  --surface-raised: #FFFDF9;
  --surface-sunk:  #EFE8DC;

  --text:          #2C2520;
  --text-muted:    rgba(44, 37, 32, 0.66);
  --text-faint:    rgba(44, 37, 32, 0.42);

  --line:          rgba(44, 37, 32, 0.10);
  --line-strong:   rgba(44, 37, 32, 0.20);

  --accent:        #9A6A55;
  --accent-quiet:  #B08975;
  --on-accent:     #FFFAF5;
  --brass:         #7C7A63;
  --danger:        #A65946;

  --ring: 0 0 0 2px var(--bg), 0 0 0 4px #9A6A55;

  --shadow-sm: 0 1px 2px rgba(44,37,32,.06);
  --shadow-md: 0 6px 20px -8px rgba(44,37,32,.14);
  --shadow-lg: 0 24px 56px -22px rgba(44,37,32,.20);

  color-scheme: light;
}

/* ── theme picker swatches ───────────────────────────────────────────────── */

.swatch {
  display: inline-flex;
  flex: 0 0 auto;
  gap: 0;
  border-radius: var(--r-full);
  overflow: hidden;
  border: 1px solid var(--line-strong);
}

.swatch i {
  display: block;
  width: 16px;
  height: 28px;
}

/* ══════════════════════════════════════════════════════════════════════════ */

/* ── reset ───────────────────────────────────────────────────────────────── */

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

/* The `hidden` attribute is only a default `display: none`, so any element
   carrying an inline or class-based `display` silently ignores it. That bites
   hardest where it matters most — a hidden loading state stacked above the
   finished content it was meant to be replaced by. Make hidden mean hidden. */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font: 400 var(--t-base)/1.65 var(--sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-wrap: break-word;
}

/* A slow atmospheric wash — the one gradient in the whole app. 14s, barely
   perceptible, and gone entirely under reduced-motion. */
body::before {
  content: '';
  position: fixed;
  inset: -20% -20% auto -20%;
  height: 70vh;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(60% 60% at 22% 8%, rgba(110, 154, 139, 0.16), transparent 70%),
    radial-gradient(52% 52% at 82% 0%, rgba(191, 161, 115, 0.10), transparent 72%);
  animation: drift 22s var(--ease-in-out) infinite alternate;
}

[data-theme='parchment'] body::before, [data-theme='linen'] body::before, [data-theme='light'] body::before {
  background:
    radial-gradient(60% 60% at 22% 8%, rgba(110, 154, 139, 0.20), transparent 70%),
    radial-gradient(52% 52% at 82% 0%, rgba(191, 161, 115, 0.16), transparent 72%);
}

@keyframes drift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(0, 2.5%, 0) scale(1.06); }
}

img, svg, video, canvas { max-width: 100%; height: auto; display: block; }

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.18;
  letter-spacing: -0.011em;
  margin: 0;
  text-wrap: balance;
}

h1 { font-size: clamp(1.75rem, 5vw, var(--t-3xl)); }
h2 { font-size: clamp(1.35rem, 4vw, var(--t-2xl)); }
h3 { font-size: var(--t-lg); }
h4 { font-size: var(--t-md); }

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--text); }

hr { border: 0; border-top: 1px solid var(--line); margin: var(--s-6) 0; }

/* One focus style, everywhere, never removed. */
:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--r-sm);
}

/* The .option and .chip patterns hide their real input with opacity:0 and
   style the label instead. The ring above was therefore being painted on an
   invisible element — a keyboard user tabbing through My World had no idea
   where they were. Lift the ring onto the visible container. */
.option:has(input:focus-visible),
.chip:has(input:focus-visible) {
  box-shadow: var(--ring);
}

::selection { background: var(--accent-quiet); color: var(--text); }

/* ── utilities ───────────────────────────────────────────────────────────── */

.eyebrow {
  font-size: var(--t-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--brass);
  font-weight: 500;
  margin: 0 0 var(--s-3);
}

.lede    { font-size: var(--t-md); color: var(--text-muted); line-height: 1.7; }
.muted   { color: var(--text-muted); }
.faint   { color: var(--text-faint); }
.small   { font-size: var(--t-sm); }
.serif   { font-family: var(--serif); }
.center  { text-align: center; }
.stack > * + * { margin-top: var(--s-4); }
.stack-lg > * + * { margin-top: var(--s-6); }
.row { display: flex; align-items: center; gap: var(--s-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.wrap { flex-wrap: wrap; }
.grow { flex: 1 1 auto; min-width: 0; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ── app shell ───────────────────────────────────────────────────────────── */

/*
 * The bottom padding reserves room for the fixed tab bar. It has to include
 * the home-indicator inset, because .tabbar adds that same inset to its own
 * padding and is therefore TALLER than --nav-h on every iPhone since the X.
 * Without it the last card on any screen sits behind the tab bar and cannot
 * be scrolled clear of it.
 */
.shell {
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: var(--s-5) var(--s-4)
           calc(var(--nav-h) + var(--s-8) + env(safe-area-inset-bottom));
  padding-left: max(var(--s-4), env(safe-area-inset-left));
  padding-right: max(var(--s-4), env(safe-area-inset-right));
}

/*
 * Both layouts set `viewport-fit=cover`, which is what lets the background
 * run edge to edge behind the notch — and also what puts the top of the page
 * underneath the status bar and Dynamic Island. Every inset here is paying
 * that back: without the top one the wordmark and the theme button sit under
 * the clock on any iPhone since the X, and in landscape the whole bar runs
 * under the notch.
 *
 * -webkit- prefix because Safari needed it until version 18. Modern Safari
 * honours the unprefixed property (verified in WebKit 26.5), but iOS 17 and
 * earlier do not, and there the bar is transparent rather than frosted —
 * text scrolling visibly through it, because the background is deliberately
 * only 88% opaque.
 */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: calc(var(--s-3) + env(safe-area-inset-top)) var(--s-4) var(--s-3);
  padding-left: max(var(--s-4), env(safe-area-inset-left));
  padding-right: max(var(--s-4), env(safe-area-inset-right));
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--line);
}

/* The wordmark is also the link home, so it needs a real target rather than
   the 28px height of its own text. Padding only — it does not move. */
.topbar .brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-right: var(--s-2);
  font-family: var(--serif);
  font-size: var(--t-md);
  letter-spacing: 0.04em;
  color: var(--text);
  text-decoration: none;
}

.topbar .brand span { color: var(--brass); }

.page-head { margin-bottom: var(--s-6); }
.page-head h1 { margin-bottom: var(--s-2); }

/* ── bottom navigation (mobile-first, the primary nav) ───────────────────── */

.tabbar {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 30;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: stretch;
  min-height: var(--nav-h);
  padding-bottom: env(safe-area-inset-bottom);
  background: color-mix(in srgb, var(--bg-deep) 92%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  backdrop-filter: blur(18px) saturate(150%);
  border-top: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}

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

.tabbar a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-width: 60px;
  min-height: 44px;
  padding: var(--s-2) var(--s-1);
  color: var(--text-faint);
  text-decoration: none;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  transition: color var(--dur-fast) var(--ease);
  position: relative;
  cursor: pointer;
}

.tabbar a svg { width: 21px; height: 21px; stroke-width: 1.5; flex: 0 0 auto; }
.tabbar a span { white-space: nowrap; }
.tabbar .tab-full { display: none; }
.tabbar a:hover { color: var(--text-muted); }
.tabbar a[aria-current='page'] { color: var(--text); }

.tabbar a[aria-current='page']::before {
  content: '';
  position: absolute;
  top: 0; left: 50%;
  width: 22px; height: 2px;
  transform: translateX(-50%);
  background: var(--brass);
  border-radius: 0 0 2px 2px;
}

/*
 * Narrow phones: let the seven tabs share the width instead of overflowing.
 *
 * `min-width: 60px` on seven tabs demands 420px. An iPhone 12/13/14 is 390.
 * The grid overflowed, `overflow-x: auto` turned the bar into a scrollable
 * strip, and My World sat off the right edge — permanently unreachable,
 * because nobody thinks to swipe a fixed bottom bar sideways. It looked like
 * a six-tab app with a clipped seventh icon.
 *
 * 390 / 7 = 55px per tab, still comfortably over the 44px minimum touch
 * target. The type has to come down slightly for "Gratitude" to fit on one
 * line at that width; the icons carry the recognition anyway.
 */
@media (max-width: 440px) {
  .tabbar a {
    min-width: 0;
    padding-inline: 2px;
    font-size: 9px;
    letter-spacing: 0.01em;
  }

  .tabbar a svg { width: 20px; height: 20px; }

  /* Belt and braces: if a translation ever makes a label longer than its
     column, clip it rather than reintroducing the overflow. */
  .tabbar a span {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

@media (min-width: 900px) {
  /*
   * Scoped to .app-shell, and that scope is the whole point.
   *
   * This was a bare `body` selector, so it also caught the auth pages — which
   * set `display:grid; place-items:center` inline but say nothing about
   * columns, so `grid-template-columns: 236px 1fr` from here applied to them
   * too. The sign-in form, max-width 26rem, was laid into the 236px sidebar
   * column and centred inside it: a 236px sliver pinned to the left edge of a
   * 1440px screen with the other 1200px empty. It is the first screen anyone
   * sees, and on desktop it looked broken because it was.
   */
  body.app-shell { display: grid; grid-template-columns: 236px 1fr; }
  .topbar { grid-column: 1 / -1; }
  .tabbar {
    position: sticky;
    /*
     * Below the header, not behind it.
     *
     * This said `top: 0` while .topbar is also `position: sticky; top: 0` — so
     * on any page long enough to scroll, the sidebar rode up to the viewport
     * top and collided with the header. And because the mobile rule leaves it
     * at z-index 30 against the header's 20, the sidebar won: the nav links
     * painted OVER the wordmark and the theme button.
     *
     * The height was wrong by the same amount for the same reason — 57px
     * against a real 69px — which left the sidebar overflowing the bottom of
     * the viewport by 12px.
     */
    top: var(--topbar-h);
    z-index: 10;
    align-self: start;
    height: calc(100dvh - var(--topbar-h));
    grid-auto-flow: row;
    grid-auto-columns: auto;
    align-content: start;
    gap: var(--s-1);
    padding: var(--s-5) var(--s-3);
    border-top: 0;
    border-right: 1px solid var(--line);
    background: transparent;
    backdrop-filter: none;
  }
  .tabbar a {
    flex-direction: row;
    justify-content: flex-start;
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
    border-radius: var(--r-md);
    font-size: var(--t-sm);
    letter-spacing: 0.02em;
    text-transform: none;
  }
  .tabbar .tab-short { display: none; }
  .tabbar .tab-full { display: inline; }
  .tabbar a[aria-current='page'] { background: var(--surface); }
  .tabbar a[aria-current='page']::before {
    top: 50%; left: 0;
    width: 2px; height: 18px;
    transform: translateY(-50%);
    border-radius: 0 2px 2px 0;
  }
  .shell { padding-bottom: var(--s-9); }
}

/* ── card ────────────────────────────────────────────────────────────────── */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  box-shadow: var(--shadow-sm);
}

.card-quiet { background: transparent; box-shadow: none; }
.card-raised { background: var(--surface-raised); box-shadow: var(--shadow-md); }
.card + .card { margin-top: var(--s-4); }

a.card, button.card {
  display: block;
  width: 100%;
  text-align: left;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

a.card:hover, button.card:hover {
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
}

a.card:active, button.card:active { transform: translateY(0); }

/* ── buttons ─────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 46px;
  padding: 0 var(--s-5);
  border: 1px solid transparent;
  border-radius: var(--r-full);
  background: var(--accent);
  color: var(--on-accent);
  font: 500 var(--t-sm)/1 var(--sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
}

.btn:hover  { background: var(--sage-300); color: var(--on-accent); }
.btn:active { transform: scale(0.985); }
.btn[disabled], .btn[aria-disabled='true'] { opacity: 0.5; cursor: not-allowed; transform: none; }

.btn-ghost {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--text);
}
.btn-ghost:hover { background: var(--surface-raised); color: var(--text); border-color: var(--text-faint); }

.btn-quiet {
  background: transparent;
  border-color: transparent;
  color: var(--text-muted);
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--t-base);
  padding: 0 var(--s-3);
}
.btn-quiet:hover { background: var(--surface); color: var(--text); }

.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { background: var(--clay-500); color: #fff; }

.btn-sm { min-height: 38px; padding: 0 var(--s-4); font-size: var(--t-xs); }
.btn-full { width: 100%; }

.btn-icon {
  min-height: 44px;
  min-width: 44px;
  padding: 0;
  border-radius: var(--r-full);
  background: transparent;
  border-color: var(--line-strong);
  color: var(--text-muted);
}
.btn-icon:hover { background: var(--surface-raised); color: var(--text); }
.btn-icon svg { width: 20px; height: 20px; stroke-width: 1.6; }

/* ── forms ───────────────────────────────────────────────────────────────── */

.field { display: block; margin-bottom: var(--s-5); }

.field > label,
.label {
  display: block;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--text);
  margin-bottom: var(--s-2);
}

.hint {
  display: block;
  font-size: var(--t-sm);
  color: var(--text-muted);
  margin: calc(var(--s-1) * -1) 0 var(--s-3);
  line-height: 1.55;
}

.input, .textarea, .select {
  width: 100%;
  min-height: 48px;
  padding: var(--s-3) var(--s-4);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  color: var(--text);
  font: 400 var(--t-base)/1.6 var(--sans);
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.textarea { min-height: 132px; resize: vertical; font-family: var(--serif); line-height: 1.75; }

.input::placeholder, .textarea::placeholder { color: var(--text-faint); }
.input:hover, .textarea:hover, .select:hover { border-color: var(--line-strong); }
.input:focus, .textarea:focus, .select:focus {
  border-color: var(--accent);
  background: var(--surface);
  box-shadow: var(--ring);
  outline: none;
}

.select {
  appearance: none;
  padding-right: var(--s-7);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237FA898' stroke-width='1.6'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-4) center;
  background-size: 18px;
  cursor: pointer;
}

.error {
  display: block;
  font-size: var(--t-sm);
  color: var(--danger);
  margin-top: var(--s-2);
}

.input[aria-invalid='true'], .textarea[aria-invalid='true'] { border-color: var(--danger); }

/*
 * 16px on touch, and not for legibility.
 *
 * iOS Safari zooms the entire page in when a text field smaller than 16px
 * receives focus, and it does not zoom back out when the field is blurred.
 * --t-base is 15px, so every field in the app did it: tap "What do you want?"
 * and the layout lurches, the tab bar slides off-screen, and you are left
 * pinching your way back. On a journal the user is meant to sink into, that
 * is the single most disruptive thing the interface can do.
 *
 * Scoped to coarse pointers so the desktop typography is untouched, and the
 * one-pixel difference is invisible on a phone anyway.
 */
@media (pointer: coarse) {
  .input, .textarea, .select { font-size: 16px; }
}

/*
 * Standalone "back" links.
 *
 * These sit alone on a line and are the only way out of a screen, but they
 * were bare anchors — 15px tall, the height of their own text, and a real
 * miss on a phone. An inline link inside a sentence is fine at text height;
 * a navigation control on its own line is not.
 */
.link-back {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  padding-right: var(--s-2);
  color: var(--text-muted);
  text-decoration: none;
  font-size: var(--t-sm);
}

.link-back::before { content: '←'; opacity: 0.6; }
.link-back:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }

.counter { font-size: var(--t-xs); color: var(--text-faint); text-align: right; margin-top: var(--s-1); }

/* option grid — the radio/checkbox pattern used all through the wizard */

.options { display: grid; gap: var(--s-2); }
.options-2 { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

/* A grid of free-text fields — the same shape as .options-2, deliberately a
   different class. .options carries a meaning: "these are the choices". Using
   it for text inputs made My World's values look like six buttons that ignored
   every tap. */
.field-grid {
  display: grid;
  gap: var(--s-2);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.option {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  width: 100%;
  min-height: 52px;
  padding: var(--s-3) var(--s-4);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  color: var(--text);
  font: 400 var(--t-base)/1.5 var(--sans);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}

.option:hover { border-color: var(--line-strong); background: var(--surface); }
.option:active { transform: scale(0.995); }

.option.is-on,
.option:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
}

.option input { position: absolute; opacity: 0; pointer-events: none; }
.option .option-body { flex: 1; min-width: 0; }
.option .option-label { font-weight: 500; display: block; }
.option small { display: block; color: var(--text-muted); font-size: var(--t-sm); margin-top: 2px; line-height: 1.5; }

.option .tick {
  flex: 0 0 auto;
  width: 20px; height: 20px;
  margin-top: 2px;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-full);
  position: relative;
  transition: border-color var(--dur-fast) var(--ease);
}

.option:has(input:checked) .tick { border-color: var(--accent); }
.option:has(input:checked) .tick::after {
  content: '';
  position: absolute;
  inset: 4px;
  border-radius: var(--r-full);
  background: var(--accent);
}
.option:has(input[type='checkbox']) .tick { border-radius: 6px; }
.option:has(input[type='checkbox']:checked) .tick::after { border-radius: 3px; }

/* ── week dots (gratitude streak) ────────────────────────────────────────
   Deliberately low-contrast for days not written. A missed day should read as
   "nothing here", not as a failure — this is a reflection app, not a habit
   tracker with a guilt mechanic. */

.week-dot {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-full);
  border: 1px solid var(--line);
  font-size: var(--t-xs);
  color: var(--text-faint);
  background: var(--surface-sunk);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.week-dot.is-written {
  background: color-mix(in srgb, var(--accent) 24%, var(--surface));
  border-color: var(--accent-quiet);
  color: var(--text);
}

.week-dot.is-today {
  border-color: var(--brass);
  color: var(--text);
}

.week-dot.is-future { opacity: .45; }

/* ── chips & pills ───────────────────────────────────────────────────────── */

.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 36px;
  padding: 0 var(--s-4);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  color: var(--text-muted);
  font-size: var(--t-sm);
  text-decoration: none;
  cursor: pointer;
  transition: all var(--dur-fast) var(--ease);
}

.chip:hover { border-color: var(--line-strong); color: var(--text); }
.chip.is-on { background: color-mix(in srgb, var(--accent) 16%, var(--surface)); border-color: var(--accent); color: var(--text); }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px var(--s-3);
  border-radius: var(--r-full);
  font-size: var(--t-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
  border: 1px solid var(--line-strong);
  color: var(--text-muted);
  white-space: nowrap;
}

.pill::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: var(--r-full);
  background: currentColor;
}

/* Status colours are deliberately close together — this is an archive, not a
   traffic light. Difference in hue, not in loudness. */
.pill-desired    { color: var(--sage-300); border-color: color-mix(in srgb, var(--sage-300) 34%, transparent); }
.pill-unfolding  { color: var(--brass-300); border-color: color-mix(in srgb, var(--brass-300) 34%, transparent); }
.pill-manifested { color: #A8CFA0; border-color: rgba(168,207,160,.34); }
.pill-answered   { color: #C9BBE0; border-color: rgba(201,187,224,.34); }
.pill-achieved   { color: #9FC6D8; border-color: rgba(159,198,216,.34); }
.pill-redirected { color: #D8B79F; border-color: rgba(216,183,159,.34); }
.pill-evolved    { color: #BFD0A0; border-color: rgba(191,208,160,.34); }
.pill-released   { color: var(--text-faint); border-color: var(--line-strong); }
.pill-paused     { color: var(--text-faint); border-color: var(--line-strong); }

/* ── prose (stories, rewinds, anything read rather than scanned) ──────────── */

.prose {
  font-family: var(--serif);
  font-size: var(--t-md);
  line-height: 1.85;
  color: var(--text);
}

.prose p { margin: 0 0 1.15em; }
.prose p:last-child { margin-bottom: 0; }
.prose-lg { font-size: var(--t-lg); line-height: 1.8; }

/* ── empty states ────────────────────────────────────────────────────────── */

.empty {
  text-align: center;
  padding: var(--s-8) var(--s-4);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg);
}

.empty svg { width: 34px; height: 34px; margin: 0 auto var(--s-4); color: var(--text-faint); stroke-width: 1.2; }
.empty h3 { margin-bottom: var(--s-2); }
.empty p { color: var(--text-muted); max-width: 34ch; margin: 0 auto var(--s-5); }

/* ── notice / flash ──────────────────────────────────────────────────────── */

.notice {
  display: flex;
  gap: var(--s-3);
  padding: var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  font-size: var(--t-sm);
  margin-bottom: var(--s-5);
}

.notice svg { flex: 0 0 auto; width: 18px; height: 18px; margin-top: 1px; stroke-width: 1.6; }
.notice-ok    { border-color: color-mix(in srgb, var(--accent) 40%, transparent); color: var(--sage-300); }
.notice-warn  { border-color: color-mix(in srgb, var(--brass) 44%, transparent); color: var(--brass-300); }
.notice-error { border-color: color-mix(in srgb, var(--danger) 44%, transparent); color: var(--clay-400); }

[data-theme='parchment'] .notice-ok, [data-theme='linen'] .notice-ok { color: var(--sage-600); }
[data-theme='parchment'] .notice-warn, [data-theme='linen'] .notice-warn { color: var(--brass-500); }
[data-theme='parchment'] .notice-error, [data-theme='linen'] .notice-error { color: var(--clay-500); }

/* ── toast ───────────────────────────────────────────────────────────────── */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--nav-h) + var(--s-4) + env(safe-area-inset-bottom));
  z-index: 60;
  transform: translate(-50%, 14px);
  max-width: min(92vw, 30rem);
  padding: var(--s-3) var(--s-5);
  background: var(--surface-raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-full);
  box-shadow: var(--shadow-lg);
  font-size: var(--t-sm);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.toast.is-shown { opacity: 1; transform: translate(-50%, 0); }

@media (min-width: 900px) { .toast { bottom: var(--s-5); } }

/* ── divider with a word in it ───────────────────────────────────────────── */

.rule {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  margin: var(--s-6) 0;
  color: var(--text-faint);
  font-size: var(--t-xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.rule::before, .rule::after { content: ''; flex: 1; height: 1px; background: var(--line); }

/* ── progress ────────────────────────────────────────────────────────────── */

.progress {
  height: 2px;
  background: var(--line);
  border-radius: var(--r-full);
  overflow: hidden;
}

.progress > i {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--accent-quiet), var(--brass));
  border-radius: inherit;
  transition: width var(--dur-slow) var(--ease);
}

/* ── reduced motion ──────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  body::before { animation: none; }
}

/* ── print: a reading should be printable, it belongs to the user ────────── */

/*
 * Touch targets, last in the file on purpose.
 *
 * 44px is the smallest reliably hittable target on a phone. Chips were 36 and
 * some buttons measured 38 — the size where a thumb lands beside the thing it
 * aimed at.
 *
 * These live at the end because an earlier attempt put them beside the form
 * rules, above `.chip`'s own `min-height: 36px`. A media query adds no
 * specificity, so the later plain rule simply won and the chips stayed 36px
 * while everything else in the same block worked. Anything overriding a
 * component belongs after that component.
 */
@media (pointer: coarse) {
  .chip { min-height: 44px; }
  .btn  { min-height: 48px; }
}


/* ── the signed-out pages ────────────────────────────────────────────────
   One column on a phone: what the screen is, then the form, in that order.
   Two columns from 1000px, because a 26rem strip centred in 1440px of empty
   dark is not a design — it is a mobile layout that nobody looked at on a
   laptop. The left column carries the words, the right the work. */

.auth-page {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--s-6) var(--s-4);
}

.auth-main { width: 100%; max-width: 26rem; }
.auth-say h1 { margin-bottom: var(--s-3); }
.auth-say .lede { margin-bottom: var(--s-6); }

/* Reserved for the second column; there is nowhere to put it on a phone. */
.auth-aside { display: none; }

@media (min-width: 1000px) {
  .auth-page { padding: var(--s-8) var(--s-7); }

  .auth-main {
    max-width: 62rem;
    display: grid;
    grid-template-columns: 1fr 26rem;
    gap: var(--s-9);
    align-items: center;
  }

  .auth-say { max-width: 30rem; }
  .auth-say h1 { font-size: var(--t-3xl); line-height: 1.1; }

  .auth-aside {
    display: block;
    max-width: 26rem;
    padding-top: var(--s-5);
    border-top: 1px solid var(--line);
    line-height: 1.7;
  }

  /* The form column keeps its own width so the fields stay a comfortable
     measure — a 40rem-wide email input looks like a mistake. */
  .auth-do { width: 100%; }
}

/* ── My Circle rows ──────────────────────────────────────────────────────
   Each person is a bordered group so it is obvious which details belong to
   whom once there are more than a couple of them — with five fixed rows that
   was never in doubt, and with twenty it would be. */
.circle-person {
  display: grid;
  gap: var(--s-2);
  padding: var(--s-4);
  margin-bottom: var(--s-3);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.circle-person [data-notes] { display: grid; gap: var(--s-2); }
.circle-person [data-add-note] { justify-self: start; }


/* ── My Journey ──────────────────────────────────────────────────────────
   A timeline, so the vertical rule matters more than the rows: it is the
   thing that turns a list of events into a length of time. */

.journey-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s-3);
  margin-bottom: var(--s-7);
}

@media (min-width: 560px) {
  .journey-stats { grid-template-columns: repeat(4, 1fr); }
}

.stat {
  display: grid;
  gap: var(--s-1);
  padding: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.stat-n { font-family: var(--serif); font-size: var(--t-2xl); line-height: 1; }
.stat-l { font-size: var(--t-xs); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; }

.journey-month {
  font-size: var(--t-md);
  color: var(--text-muted);
  margin: var(--s-7) 0 var(--s-4);
  padding-bottom: var(--s-2);
  border-bottom: 1px solid var(--line);
}

.timeline { list-style: none; margin: 0; padding: 0 0 0 var(--s-6); position: relative; }

/* The rule sits behind the marks, inset by half a mark so it runs through
   their centres. */
.timeline::before {
  content: '';
  position: absolute;
  left: 11px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--line);
}

.timeline-item { position: relative; display: flex; gap: var(--s-4); padding: var(--s-3) 0; }

.timeline-mark {
  position: absolute;
  left: calc(var(--s-6) * -1);
  top: var(--s-3);
  display: grid;
  place-items: center;
  width: 23px;
  height: 23px;
  border-radius: var(--r-full);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  color: var(--text-faint);
}

/* Arrival is the only event that gets colour. If every kind were tinted the
   timeline would read as decoration; one tinted mark reads as an event. */
.timeline-manifested, .timeline-achieved, .timeline-answered,
.timeline-redirected, .timeline-evolved, .timeline-released {
  border-color: var(--accent);
  color: var(--accent);
}

.timeline-body { display: grid; gap: 2px; text-decoration: none; color: inherit; min-height: 44px; }
.timeline-body:hover .timeline-title { color: var(--accent); }
.timeline-title { font-family: var(--serif); font-size: var(--t-md); line-height: 1.35; }

@media print {
  .topbar, .tabbar, .toast, .btn, body::before { display: none !important; }
  body { background: #fff; color: #000; }
  .shell { padding: 0; max-width: none; }
  .card { border: 0; box-shadow: none; padding: 0; }
}
