/* ============================================================================
   AAPKA LUCK — the design system.
   ----------------------------------------------------------------------------
   One stylesheet, no build step, no framework. Everything below is driven by
   the tokens in :root — change those and the whole product follows.

   THE IDEA. This is a warm night sky, not a cold one. The ground is aubergine
   rather than blue-black, the text is ivory rather than white, and the accent
   runs violet → rose → gold instead of violet → teal. That warmth is the whole
   difference between "premium guidance app" and "crypto dashboard", and it is
   why nearly every colour here carries a little red in it.

   THE SECOND IDEA. Rhythm beats containment. A screen made of twelve bordered
   rectangles reads as an admin panel however nicely the rectangles are drawn,
   so most content sits in open SECTIONS separated by space and hairlines, and
   a card is used only where a card genuinely earns its edges.

   Sections:
     1  tokens          8  cards            15  sheets & dialogs
     2  reset           9  metrics          16  navigation
     3  typography     10  score visuals    17  pages
     4  layout         11  lists            18  share cards
     5  shell          12  states           19  responsive
     6  buttons        13  chips & badges   20  accessibility
     7  forms          14  premium          21  motion
   ==========================================================================*/

/* ==========================================================  1. tokens === */

:root {
  /* ---- ground and surfaces.
     Four steps, each a real mix rather than an opacity of white, so a surface
     stacked on a surface never turns grey. */
  --bg:          #0b0711;
  --bg-deep:     #070410;
  --surface-1:   #15101f;
  --surface-2:   #1d1729;
  --surface-3:   #271f36;

  /* ---- ink. Warm ivory, then two steps of lavender.
     Pure white on this ground reads clinical; the ivory is what makes it feel
     like paper under lamplight. */
  --text:        #f8f2e9;
  --text-muted:  #c2b3d3;
  --text-soft:   #8b7ea1;
  --text-invert: #17101f;
  /* Ink that sits ON an accent, which is not the same as the inverted page
     ink: gold needs near-black in the dark theme and white in the light one. */
  --on-accent: #ffffff;
  --on-gold:   #2a1c02;

  /* ---- accents */
  --primary:       #8a5cff;
  --primary-hover: #9d78ff;
  --primary-soft:  rgba(138, 92, 255, 0.14);
  --primary-line:  rgba(138, 92, 255, 0.38);

  --accent-gold:   #f0b849;
  --gold-soft:     rgba(240, 184, 73, 0.14);
  --gold-line:     rgba(240, 184, 73, 0.36);

  --accent-love:   #ff6e9c;
  --love-soft:     rgba(255, 110, 156, 0.14);

  --positive:      #45d6a0;
  --positive-soft: rgba(69, 214, 160, 0.14);
  --warning:       #f5a73c;
  --warning-soft:  rgba(245, 167, 60, 0.14);
  --danger:        #ff6b63;
  --danger-soft:   rgba(255, 107, 99, 0.14);

  /* Score bands. Named separately from the semantic colours above because a
     LOW day is not an error and must never be painted like one. */
  --band-high: var(--positive);
  --band-mid:  var(--accent-gold);
  --band-low:  #b98fd8;
  --band-high-soft: var(--positive-soft);
  --band-mid-soft:  var(--gold-soft);
  --band-low-soft:  rgba(185, 143, 216, 0.16);

  /* ---- lines */
  --border:        rgba(248, 242, 233, 0.09);
  --border-strong: rgba(248, 242, 233, 0.2);

  /* ---- elevation. Warm shadows: a neutral black shadow on an aubergine
     ground looks like a hole rather than a lift. */
  --shadow-sm:   0 1px 2px rgba(6, 3, 12, 0.4);
  --shadow-md:   0 12px 34px rgba(6, 3, 12, 0.5);
  --shadow-lg:   0 30px 70px rgba(6, 3, 12, 0.6);
  --shadow-glow: 0 0 60px rgba(138, 92, 255, 0.28);

  /* ---- shape */
  --radius-sm: 10px;
  --radius-md: 16px;
  --radius-lg: 22px;
  --radius-xl: 30px;
  --radius-pill: 999px;

  /* ---- type */
  --font-display: 'Instrument Serif', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
  --font:         'Manrope', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono:    ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, monospace;

  /* A fifth-based scale, clamped so a phone and a desktop read the same shape
     at different sizes. Nothing below 13px exists in this product. */
  --t-2xs: 0.75rem;
  --t-xs:  0.8125rem;
  --t-sm:  0.9063rem;
  --t-base: 1rem;
  --t-md:  1.0938rem;
  --t-lg:  clamp(1.25rem, 1.12rem + 0.6vw, 1.5rem);
  --t-xl:  clamp(1.6rem,  1.4rem  + 1vw,   2.1rem);
  --t-2xl: clamp(2.1rem,  1.75rem + 1.8vw, 3rem);
  --t-3xl: clamp(3.2rem,  2.4rem  + 4vw,   5.4rem);
  --t-hero: clamp(4.4rem, 3rem    + 8vw,   8rem);

  /* ---- space. A 4px base, skipping where nothing ever lands. */
  --s-1: 0.25rem;  --s-2: 0.5rem;   --s-3: 0.75rem;  --s-4: 1rem;
  --s-5: 1.5rem;   --s-6: 2rem;     --s-7: 2.75rem;  --s-8: 3.5rem;
  --s-9: 5rem;     --s-10: 7rem;

  /* ---- layout */
  --w-page:   1200px;
  --w-read:   680px;
  --w-narrow: 460px;
  --gutter:   clamp(1rem, 0.6rem + 2vw, 2rem);
  --nav-h:    4.75rem;
  --header-h: 4rem;
  --safe-b:   env(safe-area-inset-bottom, 0px);

  /* ---- motion */
  --ease:      cubic-bezier(0.22, 0.9, 0.28, 1);
  --ease-out:  cubic-bezier(0.16, 1, 0.3, 1);
  --fast: 140ms; --mid: 260ms; --slow: 520ms;

  /* ---- depth */
  --z-nav: 60; --z-header: 55; --z-sheet: 90; --z-toast: 100;

  color-scheme: dark;
}

/* A light theme that is its own design, not an inversion. The ground is warm
   paper because the dark one is warm night — the product should feel like the
   same object seen in daylight. */
:root[data-theme='light'] {
  --bg:        #fbf7f2;
  --bg-deep:   #f4eee6;
  --surface-1: #ffffff;
  --surface-2: #f7f2ec;
  --surface-3: #efe8df;

  --text:        #1b1327;
  --text-muted:  #5c5070;
  --text-soft:   #857a96;
  --text-invert: #fdfaf6;
  --on-accent: #ffffff;
  --on-gold:   #ffffff;

  --primary:       #6a35e8;
  --primary-hover: #5a27d6;
  --primary-soft:  rgba(106, 53, 232, 0.09);
  --primary-line:  rgba(106, 53, 232, 0.28);

  --accent-gold: #b6791a;
  --gold-soft:   rgba(182, 121, 26, 0.12);
  --gold-line:   rgba(182, 121, 26, 0.3);

  --accent-love: #d94578;
  --love-soft:   rgba(217, 69, 120, 0.1);

  --positive:      #10906a;
  --positive-soft: rgba(16, 144, 106, 0.12);
  --warning:       #a86c12;
  --warning-soft:  rgba(168, 108, 18, 0.12);
  --danger:        #c93a35;
  --danger-soft:   rgba(201, 58, 53, 0.1);

  --band-high: var(--positive);
  --band-mid:  var(--accent-gold);
  --band-low:  #7d55a8;
  --band-high-soft: var(--positive-soft);
  --band-mid-soft:  var(--gold-soft);
  --band-low-soft:  rgba(125, 85, 168, 0.12);

  --border:        rgba(27, 19, 39, 0.1);
  --border-strong: rgba(27, 19, 39, 0.22);

  --shadow-sm:   0 1px 2px rgba(50, 35, 70, 0.06);
  --shadow-md:   0 10px 30px rgba(50, 35, 70, 0.08);
  --shadow-lg:   0 26px 60px rgba(50, 35, 70, 0.12);
  --shadow-glow: 0 0 50px rgba(106, 53, 232, 0.16);

  color-scheme: light;
}

/* ===========================================================  2. reset === */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  min-height: 100svh;
  font-family: var(--font);
  font-size: var(--t-base);
  font-weight: 400;
  line-height: 1.62;
  letter-spacing: -0.006em;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* Form controls do NOT inherit type or colour from the page — a browser paints
   them with its own `buttontext`, which is black. Any element styled as a
   surface but built as a <button> came out as black text on a dark card. */
button, input, select, textarea { font-family: inherit; color: inherit; }

img, svg, video { max-width: 100%; height: auto; display: block; }
hr { border: 0; border-top: 1px solid var(--border); margin: var(--s-6) 0; }

/* ======================================================  3. typography === */

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
h1 { font-family: var(--font-display); font-size: var(--t-2xl); }
h2 { font-family: var(--font-display); font-size: var(--t-xl); }
h3 { font-size: var(--t-lg); font-weight: 600; letter-spacing: -0.015em; }
h4 { font-size: var(--t-md); font-weight: 600; }

p { margin: 0; }

/* Running prose. Capped near 66 characters, which is where reading speed
   stops improving and starts falling off. */
.prose {
  color: var(--text-muted);
  font-size: var(--t-md);
  line-height: 1.72;
  max-width: 60ch;
  text-wrap: pretty;
}
.prose + .prose { margin-top: var(--s-4); }
.prose strong { color: var(--text); font-weight: 600; }

.lede {
  font-family: var(--font-display);
  font-size: var(--t-xl);
  line-height: 1.42;
  letter-spacing: -0.01em;
  color: var(--text);
  max-width: 34ch;
  text-wrap: pretty;
}

/* The eyebrow. Small, wide, quiet — it labels a section without competing
   with the thing it labels. */
.section-label, .eyebrow {
  font-size: var(--t-2xs);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-soft);
  margin: 0;
}

.hint  { color: var(--text-soft); font-size: var(--t-xs); line-height: 1.55; }
.muted { color: var(--text-muted); }
.mono  { font-family: var(--font-mono); font-size: 0.92em; }

/* Every figure in this product lines up in a column somewhere. */
.num { font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }

.display { font-family: var(--font-display); font-weight: 500; letter-spacing: -0.02em; }
.italic  { font-style: italic; }

a { color: var(--primary); text-decoration: none; }
a:hover { color: var(--primary-hover); }

/* ==========================================================  4. layout === */

.stack   { display: flex; flex-direction: column; gap: var(--s-4); }
.stack-2 { display: flex; flex-direction: column; gap: var(--s-2); }
.stack-3 { display: flex; flex-direction: column; gap: var(--s-3); }
.stack-6 { display: flex; flex-direction: column; gap: var(--s-6); }
.stack-8 { display: flex; flex-direction: column; gap: var(--s-8); }

.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); }
.row.wrap, .wrap { flex-wrap: wrap; }
/* A modifier, and also usable alone — reading `class="row-top"` as a row
   that happens to top-align is the obvious mistake, so make it true. */
.row-top { display: flex; align-items: flex-start; gap: var(--s-3); }
.grow { flex: 1 1 auto; min-width: 0; }
.center { text-align: center; }
.right  { text-align: right; }

.grid   { display: grid; gap: var(--s-4); }
/* Two columns where there is room for two, one where there is not. A fixed
   two-column grid at 360px gives every card a 150px measure, which turns
   "Check a date" into two lines and a card title into a poem. */
.grid-2 {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.grid-3 { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); }
.grid-4 { display: grid; gap: var(--s-3); grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr)); }

.divider { height: 1px; background: var(--border); border: 0; margin: 0; }

/* A SECTION is the main structural unit, not a card. Open, separated by space,
   with an optional hairline above it. This is what keeps the screens from
   turning into a wall of boxes. */
.section { display: flex; flex-direction: column; gap: var(--s-4); }
.section + .section { margin-top: var(--s-8); }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-4); }
.section-head h2, .section-head h3 { margin: 0; }
.section-rule { border-top: 1px solid var(--border); padding-top: var(--s-6); }

.read   { max-width: var(--w-read);   margin-inline: auto; width: 100%; }
.narrow { max-width: var(--w-narrow); margin-inline: auto; width: 100%; }

/* Full-bleed out of the page gutter, for heroes and share cards. */
.bleed { margin-inline: calc(var(--gutter) * -1); }

.hide { display: none !important; }

/* ===========================================================  5. shell === */

/* THE SKY. Three fixed layers: a warm depth field, a slow aurora, and a
   starfield drawn with elements rather than an image, so it costs no request.
   All three are decorative and all three stop under reduced motion. */
.sky { position: fixed; inset: 0; z-index: -2; overflow: hidden; pointer-events: none; }
.sky::before {
  content: ''; position: absolute; inset: -25%;
  background:
    radial-gradient(ellipse 65% 45% at 12% -5%,  rgba(138, 92, 255, 0.26), transparent 62%),
    radial-gradient(ellipse 55% 40% at 88% 8%,   rgba(255, 110, 156, 0.16), transparent 64%),
    radial-gradient(ellipse 70% 50% at 50% 104%, rgba(240, 184, 73, 0.10),  transparent 66%),
    var(--bg);
  animation: drift 42s var(--ease) infinite alternate;
}
:root[data-theme='light'] .sky::before {
  background:
    radial-gradient(ellipse 65% 45% at 12% -5%,  rgba(106, 53, 232, 0.10), transparent 62%),
    radial-gradient(ellipse 55% 40% at 88% 8%,   rgba(217, 69, 120, 0.08), transparent 64%),
    radial-gradient(ellipse 70% 50% at 50% 104%, rgba(182, 121, 26, 0.07), transparent 66%),
    var(--bg);
}
@keyframes drift {
  from { transform: translate3d(-1.5%, -1%, 0) scale(1.03); }
  to   { transform: translate3d(1.5%, 1.5%, 0) scale(1.09); }
}

.stars { position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; }
.stars i {
  position: absolute; border-radius: 50%; background: var(--text);
  opacity: 0.16; animation: twinkle var(--dur, 4s) ease-in-out var(--delay, 0s) infinite;
}
:root[data-theme='light'] .stars { opacity: 0.35; }
@keyframes twinkle { 0%, 100% { opacity: 0.06; } 50% { opacity: 0.42; } }

.app {
  position: relative;
  min-height: 100svh;
  display: flex; flex-direction: column;
}

.header {
  position: sticky; top: 0; z-index: var(--z-header);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4);
  height: var(--header-h);
  padding-inline: var(--gutter);
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: border-color var(--mid) var(--ease);
}
/* The glass lives on a pseudo-element, NOT on .header itself. `backdrop-filter`
   makes an element the containing block for every fixed descendant, and the
   mobile bottom bar is a fixed descendant — with the filter on .header it
   anchored to the header instead of to the viewport. */
.header::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
}
.header[data-scrolled] { border-bottom-color: var(--border); }

.main {
  flex: 1 1 auto;
  width: 100%;
  max-width: var(--w-page);
  margin-inline: auto;
  padding: var(--s-6) var(--gutter) calc(var(--nav-h) + var(--safe-b) + var(--s-8));
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  border: 0; font: inherit; cursor: pointer;
  padding: var(--s-3) var(--s-4); border-radius: 0 0 var(--radius-md) 0;
  background: var(--primary); color: var(--on-accent); font-weight: 600;
}
.skip:focus { left: 0; }

/* ---- brand */
.brand { display: flex; align-items: center; gap: var(--s-3); color: var(--text); }
.brand:hover { color: var(--text); }
.brand-mark {
  width: 1.85rem; height: 1.85rem; flex: none; border-radius: 50%;
  background: conic-gradient(from 210deg,
    var(--primary), var(--accent-love), var(--accent-gold), var(--primary));
  box-shadow: 0 0 18px rgba(138, 92, 255, 0.5);
  position: relative;
}
.brand-mark::after {
  content: ''; position: absolute; inset: 4px; border-radius: 50%;
  background: var(--bg);
}
.brand-name {
  font-family: var(--font-display); font-size: 1.32rem; font-weight: 500;
  letter-spacing: -0.015em; line-height: 1;
}
.brand-name span { color: var(--text-muted); }

/* =========================================================  6. buttons === */

.btn {
  --btn-bg: transparent; --btn-fg: var(--text); --btn-line: var(--border-strong);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 3rem; padding: 0 var(--s-5);
  border-radius: var(--radius-pill);
  border: 1px solid var(--btn-line);
  background: var(--btn-bg); color: var(--btn-fg);
  font: inherit; font-size: var(--t-sm); font-weight: 700;
  letter-spacing: -0.005em;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: transform var(--fast) var(--ease), filter var(--fast) var(--ease),
              background-color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.btn:hover  { color: var(--btn-fg); filter: brightness(1.08); }
.btn:active { transform: translateY(1px) scale(0.99); }
.btn[disabled], .btn:disabled { opacity: 0.42; cursor: not-allowed; transform: none; filter: none; }

.btn-primary {
  --btn-bg: linear-gradient(135deg, var(--primary), color-mix(in srgb, var(--primary) 55%, var(--accent-love)));
  --btn-fg: var(--on-accent); --btn-line: transparent;
  box-shadow: 0 6px 24px rgba(138, 92, 255, 0.34);
}
.btn-gold {
  --btn-bg: linear-gradient(135deg, var(--accent-gold), color-mix(in srgb, var(--accent-gold) 70%, var(--accent-love)));
  --btn-fg: var(--on-gold); --btn-line: transparent;
  box-shadow: 0 6px 24px rgba(240, 184, 73, 0.28);
}

.btn-ghost { --btn-bg: var(--surface-2); --btn-line: var(--border); }
.btn-quiet { --btn-bg: transparent; --btn-line: transparent; --btn-fg: var(--text-muted); }
.btn-quiet:hover { --btn-fg: var(--text); background: var(--surface-2); }
.btn-danger { --btn-bg: var(--danger-soft); --btn-fg: var(--danger); --btn-line: transparent; }

.btn-sm { min-height: 2.35rem; padding: 0 var(--s-4); font-size: var(--t-xs); }
.btn-lg { min-height: 3.5rem; padding: 0 var(--s-6); font-size: var(--t-md); }
.btn-block { width: 100%; }
.btn-icon { width: 2.6rem; min-height: 2.6rem; padding: 0; border-radius: 50%; }

/* A link that behaves as a button but reads as text. */
.btn-link {
  background: none; border: 0; padding: 0; min-height: 0;
  color: var(--primary); font-weight: 700; font-size: var(--t-sm); cursor: pointer;
}
.btn-link:hover { color: var(--primary-hover); text-decoration: underline; }

.spinner {
  width: 1.05em; height: 1.05em; flex: none; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ===========================================================  7. forms === */

.field { display: flex; flex-direction: column; gap: var(--s-2); }
.label { font-size: var(--t-xs); font-weight: 700; color: var(--text-muted);
  letter-spacing: 0.01em; }
.label .opt { color: var(--text-soft); font-weight: 500; text-transform: none; letter-spacing: 0; }

.input, .select, .textarea {
  width: 100%;
  padding: 0.9rem var(--s-4);
  min-height: 3.1rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--surface-1);
  color: var(--text);
  font: inherit; font-size: var(--t-base);
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--text-soft); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-soft);
}
.textarea { min-height: 7rem; resize: vertical; line-height: 1.6; }
.select {
  appearance: none;
  padding-right: 2.6rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1.15rem center, right 0.8rem center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
.input[aria-invalid='true'], .select[aria-invalid='true'], .textarea[aria-invalid='true'] {
  border-color: var(--danger);
}
.field-error { color: var(--danger); font-size: var(--t-xs); font-weight: 600; }

.checkbox { display: flex; align-items: flex-start; gap: var(--s-3); cursor: pointer;
  font-size: var(--t-sm); color: var(--text-muted); }
.checkbox input { width: 1.15rem; height: 1.15rem; accent-color: var(--primary); flex: none;
  margin-top: 0.2rem; }

/* One-time code. Six boxes that behave like one field. */
.otp-row { display: flex; gap: var(--s-2); justify-content: center; }
.otp {
  width: 3rem; height: 3.6rem; text-align: center;
  font-family: var(--font-display); font-size: 1.7rem; font-weight: 500;
  border-radius: var(--radius-md); border: 1px solid var(--border-strong);
  background: var(--surface-1); color: var(--text);
}
.otp:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }

/* Segmented control — the way this app asks a small either/or question. */
.segmented {
  display: inline-flex; padding: 4px; gap: 2px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
.segmented button {
  border: 0; background: none; cursor: pointer;
  padding: 0.5rem var(--s-4); border-radius: var(--radius-pill);
  font-size: var(--t-xs); font-weight: 700; color: var(--text-muted);
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.segmented button:hover { color: var(--text); }
.segmented button[aria-pressed='true'] {
  background: var(--surface-3); color: var(--text); box-shadow: var(--shadow-sm);
}

/* ===========================================================  8. cards === */

.card {
  position: relative;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--s-5);
  display: flex; flex-direction: column; gap: var(--s-4);
}
.card-tight { padding: var(--s-4); gap: var(--s-3); }
.card-lg    { padding: var(--s-6); gap: var(--s-5); border-radius: var(--radius-xl); }
.card-flat  { background: transparent; border-color: var(--border); }
.card-quiet { background: var(--surface-2); border-color: transparent; }

/* Interactive cards get a real affordance, not just a cursor. */
a.card, button.card { text-align: left; cursor: pointer; color: inherit;
  transition: transform var(--mid) var(--ease), border-color var(--mid) var(--ease),
              background-color var(--mid) var(--ease); }
a.card:hover, button.card:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  background: var(--surface-2);
  color: inherit;
}
a.card:active, button.card:active { transform: translateY(0); }
[aria-pressed='true'].card, .card[data-selected] {
  border-color: var(--primary-line); background: var(--primary-soft);
}

.card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-4); }
.card-title { font-size: var(--t-md); font-weight: 700; letter-spacing: -0.012em; }
.card-sub   { color: var(--text-muted); font-size: var(--t-sm); line-height: 1.55; }

/* THE HERO. One per screen at most. The aura behind it is the product's
   signature: a warm bloom that says "this number is the point of the page". */
.card-hero {
  position: relative; overflow: hidden;
  border-radius: var(--radius-xl);
  padding: var(--s-6) var(--s-5);
  background:
    radial-gradient(ellipse 120% 80% at 50% -30%, var(--primary-soft), transparent 70%),
    linear-gradient(170deg, var(--surface-2), var(--surface-1));
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; gap: var(--s-5);
}
/* The orbit: one thin ellipse, rotated, bleeding off the corner. Drawn rather
   than imported, so it costs nothing and recolours with the theme. */
.card-hero::before {
  content: ''; position: absolute;
  width: 130%; aspect-ratio: 1; right: -45%; top: -55%;
  border-radius: 50%;
  border: 1px solid var(--border);
  pointer-events: none;
}
.card-hero::after {
  content: ''; position: absolute;
  width: 90%; aspect-ratio: 1; right: -30%; top: -30%;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--primary) 26%, transparent);
  pointer-events: none;
}
.card-hero > * { position: relative; z-index: 1; }

/* On a phone the hero is a centred column — the ring, then the theme, then the
   line. Left-aligning a 150px circle in a 340px column leaves a hole beside it
   that no amount of copy fills. From 620px the ring moves beside the words. */
.hero-split {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--s-5); text-align: center;
}
.hero-copy {
  min-width: 0; display: flex; flex-direction: column; gap: var(--s-3);
  align-items: center;
}
.hero-split .prose, .hero-split .lede { margin-inline: auto; }

@media (min-width: 620px) {
  .hero-split { flex-direction: row; align-items: center; gap: var(--s-7); text-align: left; }
  .hero-copy  { flex: 1 1 16rem; align-items: flex-start; }
  .hero-split .prose, .hero-split .lede { margin-inline: 0; }
}

/* =========================================================  9. metrics === */

/* A tile is a single fact: a label, a value, and an optional note. Used in
   rows of two to four. Deliberately quieter than a card. */
.tile {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--s-4);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  border: 1px solid transparent;
  min-width: 0;
}
.tile-label {
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-soft);
}
.tile-value {
  font-family: var(--font-display); font-size: 1.55rem; font-weight: 500;
  line-height: 1.2; letter-spacing: -0.02em;
  display: flex; align-items: center; gap: var(--s-2);
  overflow-wrap: anywhere;
}
.tile-note {
  font-size: var(--t-xs); color: var(--text-soft); line-height: 1.5;
  /* Engine notes run long. Two lines, then an ellipsis — the full sentence is
     always available in the "why" panel, and a tile that grows to six lines
     breaks the grid it sits in. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

.swatch {
  width: 0.95em; height: 0.95em; flex: none; border-radius: 50%;
  box-shadow: 0 0 0 1px var(--border-strong), 0 0 14px currentColor;
}

/* A meter is a score with a bar. The number carries the meaning; the bar is
   there for shape, which is why the number is never removed on small screens. */
.meter { display: flex; flex-direction: column; gap: var(--s-2); width: 100%; }
.meter-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); }
.meter-label { font-size: var(--t-sm); font-weight: 600; color: var(--text); }
.meter-value { font-family: var(--font-display); font-size: 1.28rem; font-weight: 500;
  font-variant-numeric: tabular-nums; }
.meter-track {
  height: 7px; border-radius: var(--radius-pill);
  background: var(--surface-3); overflow: hidden;
}
.meter-fill {
  height: 100%; border-radius: inherit;
  background: linear-gradient(90deg, color-mix(in srgb, currentColor 55%, transparent), currentColor);
  transform-origin: left center;
  animation: grow var(--slow) var(--ease-out) both;
}
@keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

button.meter { border: 0; background: none; padding: var(--s-2); margin: calc(var(--s-2) * -1);
  border-radius: var(--radius-md); cursor: pointer; text-align: left; }
button.meter:hover { background: var(--surface-2); }

.band-high { color: var(--band-high); }
.band-mid  { color: var(--band-mid); }
.band-low  { color: var(--band-low); }

/* ==================================================  10. score visuals === */

/* THE RING. An SVG-free ring built from a conic gradient, with the track
   underneath and a warm bloom behind. The value is always printed inside it —
   a ring alone is not readable and is not accessible. */
.score-ring {
  --size: var(--ring-size, 9.5rem); --thickness: 8px; --pct: 0;
  position: relative; flex: none;
  width: var(--size); height: var(--size); border-radius: 50%;
  display: grid; place-items: center;
  background:
    conic-gradient(currentColor calc(var(--pct) * 1%), transparent 0),
    radial-gradient(circle at 50% 50%, transparent 61%, var(--surface-3) 61.5%);
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent calc(50% - var(--thickness)), #000 calc(50% - var(--thickness) + 1px));
          mask: radial-gradient(circle at 50% 50%, transparent calc(50% - var(--thickness)), #000 calc(50% - var(--thickness) + 1px));
  animation: sweep var(--slow) var(--ease-out) both;
}
@keyframes sweep { from { --pct: 0; } }
@property --pct { syntax: '<number>'; inherits: false; initial-value: 0; }

/* The ring is masked, so its label lives in a sibling positioned over it. */
.score-shell { position: relative; display: grid; place-items: center; flex: none; }
.score-shell::before {
  content: ''; position: absolute; width: 78%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle, currentColor, transparent 68%);
  opacity: 0.16; filter: blur(18px);
}
.score-inner {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; text-align: center;
}
.score-value {
  font-family: var(--font-display); font-weight: 500;
  font-size: 3rem; line-height: 1; letter-spacing: -0.03em;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.score-band {
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: currentColor;
  max-width: 6.5em; line-height: 1.25;
}
.score-cap {
  font-size: var(--t-2xs); color: var(--text-soft); letter-spacing: 0.08em;
  text-transform: uppercase; font-weight: 600;
  /* Wide enough for "out of 100" on one line. A caption longer than that is
     truncated rather than allowed to run into the stroke — captions here are
     written short on purpose, and the score itself carries the meaning. */
  max-width: 8.5em; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The big number with no ring — used on share cards and result screens where
   the figure IS the design. */
.score-huge {
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--t-3xl); line-height: 0.9; letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  background: linear-gradient(160deg, var(--text) 30%, currentColor);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ===========================================================  11. lists === */

.list { display: flex; flex-direction: column; }
.list-item {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-4) 0;
  border-bottom: 1px solid var(--border);
  text-align: left; color: inherit;
}
.list-item:last-child { border-bottom: 0; }
a.list-item, button.list-item {
  background: none; border-left: 0; border-right: 0; border-top: 0;
  width: 100%; cursor: pointer; font: inherit;
  padding-inline: var(--s-3); margin-inline: calc(var(--s-3) * -1);
  border-radius: var(--radius-sm);
  transition: background-color var(--fast) var(--ease);
}
a.list-item:hover, button.list-item:hover { background: var(--surface-2); color: inherit; }
.list-item[data-static] { cursor: default; }

.list-title { font-weight: 650; font-size: var(--t-sm); }
.list-sub   { color: var(--text-soft); font-size: var(--t-xs); }

/* The circular mark that opens a list row or a tool card. */
.mark {
  width: 2.75rem; height: 2.75rem; flex: none;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--primary-soft); border: 1px solid var(--primary-line);
  color: var(--primary);
  font-family: var(--font-display); font-size: 1.15rem; font-weight: 500;
}
.mark-gold  { background: var(--gold-soft);  border-color: var(--gold-line);  color: var(--accent-gold); }
.mark-love  { background: var(--love-soft);  border-color: transparent;       color: var(--accent-love); }
.mark-green { background: var(--positive-soft); border-color: transparent;    color: var(--positive); }
.mark-lg    { width: 3.4rem; height: 3.4rem; font-size: 1.4rem; }

/* Avatar for a saved person: initials, coloured from their own name. */
.avatar {
  width: 2.9rem; height: 2.9rem; flex: none; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--font-display); font-size: 1.15rem; letter-spacing: 0;
  color: var(--text);
  background: linear-gradient(150deg, var(--surface-3), var(--surface-2));
  border: 1px solid var(--border-strong);
}

/* DO / AVOID. The most scanned block on the Today screen, so it is a list of
   sentences with a mark — never a card each. */
.advice { display: flex; align-items: flex-start; gap: var(--s-3); padding: var(--s-3) 0; }
.advice + .advice { border-top: 1px solid var(--border); }
.advice-icon {
  width: 1.55rem; height: 1.55rem; flex: none; margin-top: 0.15rem;
  border-radius: 50%; display: grid; place-items: center;
}
.advice-do    .advice-icon { background: var(--positive-soft); color: var(--positive); }
.advice-avoid .advice-icon { background: var(--danger-soft);   color: var(--danger); }
.advice-text  { font-size: var(--t-md); line-height: 1.55; }
.advice-domain {
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-soft); margin-top: 2px;
}

/* ==========================================================  12. states === */

/* Four states, one shape. Every screen in this app answers loading, error,
   empty and locked, and they must look like the same product answering. */

.skeleton {
  border-radius: var(--radius-sm);
  background: linear-gradient(100deg,
    var(--surface-2) 30%, var(--surface-3) 50%, var(--surface-2) 70%);
  background-size: 220% 100%;
  animation: shimmer 1.5s linear infinite;
}
@keyframes shimmer { from { background-position: 180% 0; } to { background-position: -60% 0; } }
.sk-line { height: 0.85rem; }
.sk-ring { width: 9.5rem; height: 9.5rem; border-radius: 50%; flex: none; }
.sk-card { height: 8rem; border-radius: var(--radius-lg); }

.empty, .error-state {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
  text-align: center;
  padding: var(--s-8) var(--s-4);
}
.empty-icon {
  width: 3.75rem; height: 3.75rem; border-radius: 50%;
  display: grid; place-items: center; margin-bottom: var(--s-2);
  background: var(--surface-2); color: var(--text-soft);
  border: 1px solid var(--border);
}
.error-state .empty-icon { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
.empty-title { font-family: var(--font-display); font-size: var(--t-lg); font-weight: 500; }
.empty-text  { color: var(--text-muted); font-size: var(--t-sm); max-width: 34ch; line-height: 1.6; }

/* Toasts. Bottom on mobile because that is where the thumb is, and above the
   navigation because covering the navigation with a message is hostile. */
.toast-wrap {
  position: fixed; z-index: var(--z-toast);
  left: 50%; transform: translateX(-50%);
  bottom: calc(var(--nav-h) + var(--safe-b) + var(--s-4));
  display: flex; flex-direction: column; gap: var(--s-2);
  width: min(30rem, calc(100vw - 2rem));
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius-md);
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg);
  font-size: var(--t-sm); font-weight: 600;
  animation: toast-in var(--mid) var(--ease-out) both;
}
.toast-good { border-left: 3px solid var(--positive); }
.toast-warn { border-left: 3px solid var(--warning); }
.toast-bad  { border-left: 3px solid var(--danger); }
@keyframes toast-in { from { opacity: 0; transform: translateY(14px); } }

/* ==================================================  13. chips & badges === */

.chip {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 0.3rem 0.75rem;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: 0.02em;
  white-space: nowrap;
}
.chip-good { background: var(--positive-soft); border-color: transparent; color: var(--positive); }
.chip-mid  { background: var(--gold-soft);     border-color: transparent; color: var(--accent-gold); }
.chip-low  { background: var(--band-low-soft); border-color: transparent; color: var(--band-low); }
.chip-love { background: var(--love-soft);     border-color: transparent; color: var(--accent-love); }
.chip-gold { background: var(--gold-soft);     border-color: var(--gold-line); color: var(--accent-gold); }

.chip-btn {
  cursor: pointer; min-height: 2.6rem; padding-inline: var(--s-4);
  font-size: var(--t-sm);
  /* A chip that is a CONTROL carries a real label, and a real label can be
     longer than a phone is wide ("Situation, obstacle, what would help · 3").
     A static chip stays on one line; a pressable one is allowed to wrap
     rather than push the page sideways. */
  white-space: normal; text-align: left; max-width: 100%;
  padding-block: 0.45rem;
  transition: background-color var(--fast) var(--ease), border-color var(--fast) var(--ease),
              color var(--fast) var(--ease);
}
.chip-btn:hover { border-color: var(--border-strong); color: var(--text); }
.chip-btn[aria-pressed='true'] {
  background: var(--primary-soft); border-color: var(--primary-line); color: var(--text);
}

.lock-badge {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.24rem 0.6rem; border-radius: var(--radius-pill);
  background: var(--gold-soft); color: var(--accent-gold);
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* =========================================================  14. premium === */

/* A locked section shows its SHAPE and hides its content. Nothing behind the
   veil was fetched — the server declined, and this is that refusal drawn. */
/* Both layers share one grid cell, so whichever is TALLER sets the height of
   the card. Absolutely positioning the veil over a shorter skeleton clipped
   the paywall's own heading and button — the two things it exists to show. */
.locked {
  position: relative; display: grid; overflow: hidden;
  border-radius: var(--radius-lg);
}
.locked > * { grid-area: 1 / 1; }
.locked-veil {
  z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-3); text-align: center; padding: var(--s-6) var(--s-4);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--bg) 55%, transparent),
    color-mix(in srgb, var(--bg) 88%, transparent));
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
}
.locked-body { opacity: 0.3; pointer-events: none; user-select: none; filter: saturate(0.5); }

.plan {
  position: relative; overflow: hidden;
  container-type: inline-size;
  display: flex; flex-direction: column; gap: var(--s-4);
  padding: var(--s-6);
  border-radius: var(--radius-xl);
  background: var(--surface-1);
  border: 1px solid var(--border);
}
.plan-featured {
  border-color: var(--gold-line);
  background:
    radial-gradient(ellipse 120% 70% at 50% -20%, var(--gold-soft), transparent 70%),
    var(--surface-1);
  box-shadow: var(--shadow-md);
}
.plan-price {
  /* The price is the one thing on this screen that must stay legible, so it
     scales with the CARD rather than with the page: at 1280 with four plans a
     card is 272px wide, and "AED 1,290.00" at --t-2xl measured 273px and ran
     straight out of it. `cqi` sizes against the card; the clamp keeps it from
     going small enough to lose its weight. */
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.5rem, 11cqi, 2.4rem);
  line-height: 1.05; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
  display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap;
  overflow-wrap: anywhere;
}
.plan-per { font-family: var(--font); font-size: var(--t-sm); font-weight: 600; color: var(--text-soft); }
.plan-feat { display: flex; align-items: flex-start; gap: var(--s-3); font-size: var(--t-sm);
  color: var(--text-muted); }
.plan-feat svg { color: var(--positive); flex: none; margin-top: 0.22rem; }

/* ================================================  15. sheets & dialogs === */

dialog {
  border: 0; padding: 0; background: none; color: var(--text);
  max-width: 100%; max-height: 100%; width: 100%;
  margin: 0; margin-block-start: auto;   /* bottom sheet on mobile */
}
dialog::backdrop { background: rgba(4, 2, 9, 0.62); backdrop-filter: blur(4px); }

.sheet {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-bottom: 0;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: var(--s-3) var(--gutter) calc(var(--s-6) + var(--safe-b));
  max-height: 88svh; overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--s-4);
  box-shadow: var(--shadow-lg);
  animation: sheet-up var(--mid) var(--ease-out) both;
}
@keyframes sheet-up { from { transform: translateY(18px); opacity: 0; } }

/* The grab handle. Purely a signal that this thing came from the bottom and
   can go back there. */
.sheet-grip {
  width: 2.5rem; height: 4px; border-radius: var(--radius-pill);
  background: var(--border-strong); margin: 0 auto var(--s-2);
  flex: none;
}
.sheet-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); }
.sheet-title { font-family: var(--font-display); font-size: var(--t-lg); font-weight: 500; }
.sheet-actions { display: flex; gap: var(--s-3); margin-top: var(--s-2); }

@media (min-width: 720px) {
  dialog { margin: auto; width: min(34rem, calc(100vw - 3rem)); }
  .sheet {
    border-radius: var(--radius-xl); border-bottom: 1px solid var(--border);
    padding-bottom: var(--s-6); max-height: 82svh;
  }
  .sheet-grip { display: none; }
  @keyframes sheet-up { from { transform: translateY(8px) scale(0.985); opacity: 0; } }
}

/* ======================================================  16. navigation === */

/* Mobile: a bottom bar. Four destinations, never five — Premium is reached
   from where a limit is actually met, not from a permanent tab. */
.nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-nav);
  display: flex; justify-content: space-around; align-items: stretch;
  padding: var(--s-2) var(--s-2) calc(var(--s-2) + var(--safe-b));
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  border-top: 1px solid var(--border);
}
/* Hidden for a signed-out visitor (app.js buildNav). `display: flex` above
   beats the `hidden` attribute's own UA rule, so it has to be said here — and
   the reserved bottom gutter goes with it, or the landing pages carry 4.75rem
   of empty space under the footer for a bar that is not there. */
.nav[hidden] { display: none; }
body:has(.nav[hidden]) { --nav-h: 0rem; }

.nav-item {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: var(--s-2) var(--s-1);
  min-height: 3.25rem; border-radius: var(--radius-md);
  color: var(--text-soft); font-size: var(--t-2xs); font-weight: 700;
  letter-spacing: 0.01em;
  transition: color var(--fast) var(--ease);
  position: relative;
}
.nav-item:hover { color: var(--text-muted); }
.nav-item svg { transition: transform var(--mid) var(--ease); }
.nav-item[aria-current='page'] { color: var(--text); }
.nav-item[aria-current='page'] svg { color: var(--primary); transform: translateY(-1px); }
/* The active dot. A filled pill under the label reads as a browser tab; a dot
   above the icon reads as a place you are. */
.nav-item[aria-current='page']::before {
  content: ''; position: absolute; top: 2px;
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--primary); box-shadow: 0 0 8px var(--primary);
}

/* Tabs inside a page (Me, Explore filters). */
.tabs {
  display: flex; gap: var(--s-2); overflow-x: auto;
  scrollbar-width: none; -ms-overflow-style: none;
  border-bottom: 1px solid var(--border);
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  flex: none; padding: var(--s-3) var(--s-1); margin-right: var(--s-4);
  background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--text-soft); font: inherit; font-size: var(--t-sm); font-weight: 700;
  cursor: pointer; white-space: nowrap;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.tab:hover { color: var(--text-muted); }
.tab[aria-current='page'] { color: var(--text); border-bottom-color: var(--primary); }

/* ===========================================================  17. pages === */

.page-head { display: flex; flex-direction: column; gap: var(--s-2); margin-bottom: var(--s-6); }
.page-head h1 { font-size: var(--t-2xl); }
.page-sub { color: var(--text-muted); font-size: var(--t-md); max-width: 46ch; }

.back-link {
  display: inline-flex; align-items: center; gap: var(--s-2);
  color: var(--text-soft); font-size: var(--t-xs); font-weight: 700;
  margin-bottom: var(--s-3);
}
.back-link:hover { color: var(--text); }

/* The greeting above Today. Date first, small; the name is the emotional bit. */
.greeting { display: flex; flex-direction: column; gap: var(--s-1); }
.greeting-date {
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--text-soft);
}
.greeting-name { font-family: var(--font-display); font-size: var(--t-xl); font-weight: 500; }

/* A calendar grid that stays square and readable at 360px. */
.cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; }
.cal-dow {
  text-align: center; font-size: var(--t-2xs); font-weight: 700;
  color: var(--text-soft); letter-spacing: 0.08em; padding-bottom: var(--s-2);
}
.cal-day {
  aspect-ratio: 1; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-1); cursor: pointer; padding: 2px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  font: inherit; color: var(--text-muted); font-size: var(--t-2xs); font-weight: 700;
  transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.cal-day:hover { transform: scale(1.06); border-color: var(--border-strong); }
.cal-day[data-band='high'] { background: var(--band-high-soft); color: var(--band-high); border-color: transparent; }
.cal-day[data-band='mid']  { background: var(--band-mid-soft);  color: var(--band-mid);  border-color: transparent; }
.cal-day[data-band='low']  { background: var(--band-low-soft);  color: var(--band-low);  border-color: transparent; }
.cal-day[data-today] { outline: 2px solid var(--primary); outline-offset: 1px; }
.cal-empty { aspect-ratio: 1; }
/* Never colour alone: every coloured day also carries a shape. */
.cal-mark { font-size: 0.6rem; line-height: 1; opacity: 0.9; }

/* Tarot. The card back is drawn, not downloaded — no artwork is scraped, and
   the pattern recolours with the theme. */
.tarot-card {
  position: relative; aspect-ratio: 0.62; border-radius: var(--radius-md);
  background: linear-gradient(160deg, var(--surface-3), var(--surface-1));
  border: 1px solid var(--border-strong);
  display: flex; flex-direction: column; justify-content: space-between;
  padding: var(--s-4); overflow: hidden;
  box-shadow: var(--shadow-md);
}
.tarot-card::before {
  content: ''; position: absolute; inset: 8px;
  border: 1px solid color-mix(in srgb, var(--accent-gold) 34%, transparent);
  border-radius: calc(var(--radius-md) - 5px);
  pointer-events: none;
}
.tarot-name {
  font-family: var(--font-display); font-size: 1.22rem; font-weight: 500;
  line-height: 1.15; letter-spacing: -0.01em;
}
.tarot-pos {
  font-size: var(--t-2xs); font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--accent-gold);
}
.tarot-reversed { transform: rotate(180deg); display: inline-block; }
.tarot-rail {
  display: grid; gap: var(--s-4);
  grid-auto-flow: column; grid-auto-columns: minmax(9.5rem, 1fr);
  overflow-x: auto; scroll-snap-type: x mandatory;
  padding-bottom: var(--s-3); margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  scrollbar-width: none;
}
.tarot-rail::-webkit-scrollbar { display: none; }
.tarot-rail > * { scroll-snap-align: center; }
@media (min-width: 720px) {
  .tarot-rail { grid-auto-flow: row; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    overflow: visible; margin-inline: 0; padding-inline: 0; }
}

/* =====================================================  18. share cards === */

/* Built to leave the app: a 4:5 portrait that lands on a story without being
   cropped. Nothing private is ever printed on one. */
.share-card {
  position: relative; overflow: hidden;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-xl);
  padding: var(--s-6);
  display: flex; flex-direction: column; justify-content: space-between;
  background:
    radial-gradient(ellipse 100% 60% at 50% 0%, var(--primary-soft), transparent 70%),
    radial-gradient(ellipse 80% 50% at 90% 100%, var(--gold-soft), transparent 70%),
    linear-gradient(165deg, var(--surface-2), var(--bg-deep));
  border: 1px solid var(--border-strong);
}
.share-brand {
  font-family: var(--font-display); font-size: var(--t-md);
  letter-spacing: -0.01em; color: var(--text-muted);
}

/* ======================================================  19. responsive === */

/* Mobile is the design; desktop is a recomposition of it, not a stretch.
   The breakpoints are the real device widths this product will meet:
   360 / 390 / 430 phones, 768 tablet, 1024 laptop, 1280+ desktop. */

/* Small phones. Nothing shrinks below readable — the gutter gives way first. */
@media (max-width: 380px) {
  :root { --gutter: 0.9rem; --s-6: 1.6rem; }
  .score-ring { --size: 8.6rem; }
  .score-value { font-size: 2.7rem; }
  .grid-2 { gap: var(--s-3); }
  .tile { padding: var(--s-3); }
  .tile-value { font-size: 1.35rem; }
}

/* Tablet and up: the bottom bar becomes a top bar, and the page gets air. */
@media (min-width: 768px) {
  :root { --nav-h: 0rem; --header-h: 4.75rem; }

  .score-ring { --size: 11.5rem; --thickness: 9px; }
  .score-value { font-size: 3.4rem; }

  .nav {
    position: static; border-top: 0; background: none; backdrop-filter: none;
    -webkit-backdrop-filter: none;
    justify-content: flex-start; gap: var(--s-1); padding: 0;
  }
  .nav-item {
    flex: none; flex-direction: row; gap: var(--s-2);
    padding: var(--s-2) var(--s-4); min-height: 2.6rem;
    font-size: var(--t-sm); border-radius: var(--radius-pill);
  }
  .nav-item::before { display: none; }
  .nav-item[aria-current='page'] { background: var(--surface-2); }
  .nav-item svg { width: 18px; height: 18px; }

  .header { display: grid; grid-template-columns: auto 1fr auto; gap: var(--s-6); }
  .header .nav { justify-content: center; }

  .main { padding-top: var(--s-8); padding-bottom: var(--s-9); }
  .toast-wrap { bottom: var(--s-6); }
}

/* Laptop: two columns where a screen genuinely has a primary and a secondary
   story. Never more than two — three columns of cards is the dashboard look
   this product is trying not to have. */
@media (min-width: 1024px) {
  .split {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: var(--s-7);
    align-items: start;
  }
  .split-wide { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .split > .col { display: flex; flex-direction: column; gap: var(--s-8); min-width: 0; }
  /* A column that should scroll with the page but start pinned to the top. */
  .col-sticky { position: sticky; top: calc(var(--header-h) + var(--s-5)); }

  .card-hero { padding: var(--s-8) var(--s-7); }
  .score-ring { --size: 12.5rem; --thickness: 10px; }
  .score-value { font-size: 3.8rem; }
}

@media (min-width: 1280px) {
  .grid-2-lg { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ===================================================  20. accessibility === */

/* One focus treatment everywhere. It is never removed — the ring on a
   programmatically focused heading is suppressed below because that focus is
   for a screen reader, not for the eye. */
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
:focus:not(:focus-visible) { outline: none; }
[tabindex='-1']:focus, [tabindex='-1']:focus-visible { outline: none; }

/* Every interactive target clears 44px, which is the smallest thing a thumb
   reliably hits. This used to be a comment over a rule that only set
   `touch-action` — measured, .segmented buttons were 31px and .btn-sm 38px.
   The floor is enforced on a touch pointer, where it matters; a mouse can hit
   a 38px control and the denser layout is better on a desktop. */
button, [role='button'], a.nav-item, .chip-btn, .tab {
  touch-action: manipulation;
}
@media (pointer: coarse) {
  .btn-sm, .btn-icon, .chip-btn, .segmented button, .tab { min-height: 2.75rem; }
  .btn-icon { width: 2.75rem; }
  .back-link { min-height: 2.75rem; align-items: center; }
  .list-item { min-height: 2.75rem; }
}

.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;
}

/* A high-contrast preference gets real borders rather than hairlines. */
@media (prefers-contrast: more) {
  :root { --border: rgba(248, 242, 233, 0.28); --border-strong: rgba(248, 242, 233, 0.5); }
  :root[data-theme='light'] { --border: rgba(27, 19, 39, 0.3); --border-strong: rgba(27, 19, 39, 0.55); }
  .prose, .card-sub, .hint { color: var(--text); }
}

/* ==========================================================  21. motion === */

.view-enter { animation: view-in var(--mid) var(--ease-out) both; }
@keyframes view-in { from { opacity: 0; transform: translateY(10px); } }

/* A short, staggered reveal for the first few blocks of a screen. Applied by
   the shell, capped at six children so a long list never crawls in. */
.reveal > * { animation: view-in var(--mid) var(--ease-out) both; }
.reveal > *:nth-child(1) { animation-delay: 0ms; }
.reveal > *:nth-child(2) { animation-delay: 45ms; }
.reveal > *:nth-child(3) { animation-delay: 90ms; }
.reveal > *:nth-child(4) { animation-delay: 135ms; }
.reveal > *:nth-child(5) { animation-delay: 180ms; }
.reveal > *:nth-child(n+6) { animation-delay: 215ms; }

/* The check-in confirmation. One sparkle, once, and then it is gone. */
@keyframes pop { 0% { transform: scale(0.7); opacity: 0; } 60% { transform: scale(1.12); } 100% { transform: scale(1); opacity: 1; } }
.pop { animation: pop 380ms var(--ease-out) both; }


/* ===========================================================================
   THE ACQUISITION PAGES
   Only used by web/js/views/micro.js. Kept together and named for the screen
   rather than scattered as utilities, because these are the only pages a
   stranger sees and they get changed as a set.
   ========================================================================= */

.stack-4 { display: flex; flex-direction: column; gap: var(--s-4); }

/* A quiet mark for a locked line. The gold one shouts, and a list of eight
   things you cannot see yet should not shout eight times. */
.mark-quiet {
  background: var(--surface-3);
  border-color: transparent;
  color: var(--text-soft);
  width: 1.75rem;
  height: 1.75rem;
}

/* The frequently-asked block. A native <details>, so it works with no
   JavaScript, is findable by the browser's own in-page search once opened,
   and announces its expanded state without any ARIA of ours. */
.faq {
  border-bottom: 1px solid var(--border);
  padding: var(--s-3) 0;
}
.faq:last-child { border-bottom: 0; }
.faq-q {
  cursor: pointer;
  font-weight: 600;
  font-size: var(--t-md);
  line-height: 1.45;
  list-style: none;
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding-right: var(--s-2);
}
/* Safari still paints its own triangle without this. */
.faq-q::-webkit-details-marker { display: none; }
.faq-q::after {
  content: '+';
  margin-left: auto;
  font-size: 1.15rem;
  line-height: 1;
  color: var(--text-soft);
  transition: transform 160ms ease;
}
.faq[open] .faq-q::after { content: '\2212'; }
.faq-q:focus-visible { outline: 2px solid var(--accent-gold); outline-offset: 3px; border-radius: var(--radius-sm); }
.faq-a {
  color: var(--text-soft);
  font-size: var(--t-sm);
  line-height: 1.65;
  margin-top: var(--s-3);
  max-width: 62ch;
}

/* One of the other landing pages, as a tappable row. */
.tile-link {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: inherit;
  text-decoration: none;
  transition: border-color 160ms ease, transform 160ms ease;
}
.tile-link:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.tile-link:focus-visible { outline: 2px solid var(--accent-gold); outline-offset: 2px; }

/* ---------------------------------------------------------------------------
   THE TAROT CARD PLACEHOLDER
   The final deck illustrations do not exist yet. This is a drawn placeholder
   at the real card ratio, in the suit's own colour, carrying the card's name —
   not a stock photograph and not somebody else's deck. It exists so the layout
   is honest now and the artwork drops in without moving anything.
   docs/IMAGE-ASSET-MAP.md is the list of what replaces it.
   ------------------------------------------------------------------------- */
.tarot-card {
  /* The standard tarot ratio, near enough that real artwork will not reflow
     the page when it arrives. */
  aspect-ratio: 5 / 8.6;
  width: min(12rem, 58vw);
  border-radius: var(--radius-md);
  border: 1px solid var(--suit-line, var(--border-strong));
  background:
    radial-gradient(120% 80% at 50% 0%, var(--suit-glow, transparent), transparent 68%),
    linear-gradient(168deg, var(--surface-3), var(--surface-1));
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: var(--s-4) var(--s-3);
  text-align: center;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.28);
  color: var(--suit-ink, var(--text));
  position: relative;
  isolation: isolate;
}
/* The inner rule a real card face has. Without it the placeholder reads as an
   empty panel with a caption rather than as a card. */
.tarot-card::before {
  content: '';
  position: absolute;
  inset: var(--s-2);
  border: 1px solid var(--suit-line, var(--border));
  border-radius: calc(var(--radius-md) - 6px);
  opacity: 0.45;
  pointer-events: none;
}
.tarot-name {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-wrap: balance;
  color: var(--text);
}
.tarot-corner { opacity: 0.55; }
/* The lower mark is the upper one turned over, the way a real card is
   symmetrical about its centre. */
.tarot-corner-end { transform: rotate(180deg); }
/* A reversed card is shown reversed. The NAME is not flipped with it — an
   upside-down label is a puzzle, not a design. */
.tarot-card.is-reversed .tarot-corner { transform: rotate(180deg); }
.tarot-card.is-reversed .tarot-corner-end { transform: none; }
.tarot-card.is-reversed { border-style: dashed; }

.tarot-major    { --suit-ink: var(--accent-gold); --suit-line: var(--gold-line);
                  --suit-glow: var(--gold-soft); }
.tarot-cups     { --suit-ink: var(--accent-love); --suit-glow: var(--love-soft); }
.tarot-wands    { --suit-ink: var(--accent-gold); --suit-glow: var(--gold-soft); }
.tarot-swords   { --suit-ink: var(--text-soft);   --suit-glow: rgba(139, 126, 161, 0.16); }
.tarot-pentacles{ --suit-ink: var(--positive);    --suit-glow: var(--positive-soft); }

/* Everything decorative stops. Content still animates in, but instantly —
   removing the transition entirely makes state changes hard to follow. */
@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;
  }
  .sky::before { animation: none; }
  .stars { display: none; }
  .meter-fill { animation: none; }
}

/* Printing a reading should produce something legible, not a black rectangle. */
@media print {
  .sky, .stars, .nav, .header, .toast-wrap { display: none !important; }
  body { background: #fff; color: #000; }
  .card, .card-hero { border: 1px solid #ccc; background: #fff; box-shadow: none; }
}
