/* ─────────────────────────────────────────────────────────────────────────
   Maison Sillage — stylesheet
   Vellum + aura design system in light mode. Cream parchment ground, single
   warm light source top-left, paper-ticket chips, vellum card with aura halo.
   Typography is Switzer throughout — no second face.
   ───────────────────────────────────────────────────────────────────── */

/* ─── local webfonts (Switzer only) ─────────────────────────────────── */
@font-face {
  font-family: 'Switzer';
  src: url('./fonts/Switzer-Variable.ttf') format('truetype-variations'),
       url('./fonts/Switzer-Variable.ttf') format('truetype');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Switzer';
  src: url('./fonts/Switzer-VariableItalic.ttf') format('truetype-variations'),
       url('./fonts/Switzer-VariableItalic.ttf') format('truetype');
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

/* CS Maison Sezanne — used ONLY for the perfume name on la carte. */
@font-face {
  font-family: 'Maison Sezanne';
  src: url('./fonts/CS-MaisonSezanne-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ─── tokens ─────────────────────────────────────────────────────────── */
:root {
  /* Ground — warm cream parchment */
  --ground-light:  oklch(0.97 0.014 82);
  --ground:        oklch(0.945 0.018 80);
  --ground-mid:    oklch(0.91 0.024 76);
  --ground-deep:   oklch(0.85 0.032 70);
  --halo:          oklch(0.99 0.028 88);

  /* Ink */
  --ink:           oklch(0.22 0.050 26);
  --ink-deep:      oklch(0.28 0.080 22);
  --ink-soft:      oklch(0.38 0.040 30);
  --ink-mute:      oklch(0.50 0.030 35);
  --ink-quiet:     oklch(0.60 0.022 40);
  --ink-faint:     oklch(0.72 0.018 45);

  /* Vellum — the card */
  --vellum-a:      oklch(0.955 0.022 78);
  --vellum-b:      oklch(0.91 0.030 72);
  --vellum-edge:   oklch(0.72 0.030 60 / 0.45);

  /* Sheets — frosted panels on cream */
  --sheet:         oklch(1 0 0 / 0.42);
  --sheet-edge:    oklch(0.60 0.025 55 / 0.18);

  /* Aura cores (center → outer) */
  --aura-floral-a:   oklch(0.78 0.18 18);
  --aura-floral-b:   oklch(0.62 0.18 6);
  --aura-oriental-a: oklch(0.62 0.16 32);
  --aura-oriental-b: oklch(0.40 0.14 18);
  --aura-marine-a:   oklch(0.80 0.10 210);
  --aura-marine-b:   oklch(0.55 0.10 220);
  --aura-woody-a:    oklch(0.70 0.12 70);
  --aura-woody-b:    oklch(0.46 0.10 50);
  --aura-citrus-a:   oklch(0.88 0.14 95);
  --aura-citrus-b:   oklch(0.66 0.14 75);
  --aura-gourmand-a: oklch(0.82 0.10 60);
  --aura-gourmand-b: oklch(0.56 0.09 35);

  /* Type — Switzer only */
  --font-sans:     'Switzer', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  /* Legacy alias: anything previously asking for the script face gets Switzer. */
  --font-script:   var(--font-sans);

  /* Motion */
  --ease:          cubic-bezier(0.16, 0.84, 0.30, 1);
  --t-fast:        220ms;
  --t-med:         360ms;
  --t-slow:        560ms;

  /* Legacy color aliases (so app.js's --accent override still resolves
     cleanly against the new vocabulary) */
  --paper:         var(--ground);
  --paper-card:    var(--vellum-a);
  --ink-soft:      var(--ink-soft);
  --hairline:      oklch(0.45 0.04 35 / 0.30);
  --muted:         var(--ink-mute);
  --muted-soft:    var(--ink-faint);
  --accent:        var(--ink-deep);

  --shadow-card:
      inset 0 1px 0 rgba(255, 250, 240, 0.7),
      inset 0 -1px 0 oklch(0.65 0.04 50 / 0.20),
      0 1px 0 oklch(0.30 0.06 30 / 0.10),
      0 40px 90px -28px oklch(0.30 0.08 30 / 0.30),
      0 12px 30px -10px oklch(0.30 0.05 30 / 0.18);
}

/* ─── base ───────────────────────────────────────────────────────────── */
html, body { height: 100%; }

body {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--ground);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow: hidden;
}

/* ─── atmosphere ─────────────────────────────────────────────────────── */
/* Persistent ground that sits behind every screen. The atmosphere stays
   put across navigation — only the aura behind it shifts color. */
.app {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--ground);
}

.atmosphere {
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    radial-gradient(70% 60% at 18% -8%, var(--halo) 0%, var(--ground-light) 32%, var(--ground) 62%, var(--ground-mid) 95%),
    var(--ground);
}

.atmosphere::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    radial-gradient(50% 40% at 100% 100%, oklch(0.78 0.04 50 / 0.20), transparent 70%),
    radial-gradient(40% 30% at 0% 100%, oklch(0.72 0.05 30 / 0.10), transparent 70%);
}

.paper-grain {
  position: absolute; inset: 0;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='260' height='260'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.45  0 0 0 0 0.36  0 0 0 0 0.25  0 0 0 0.4 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  background-size: 260px 260px;
  opacity: 0.10;
  mix-blend-mode: multiply;
}

/* The ambient aura paints three independent radial spots — one each
   for the head, heart, and base selections — plus a soft warm default
   bloom so the room isn't blank before any selection has been made.
   Each section's colour is set in JS through --aura-head / --aura-heart
   / --aura-base; they persist independently, so once a section's chip
   is chosen its colour stays in the aura even as other sections change. */
.ambient-aura {
  position: absolute;
  inset: -10%;
  width: auto; height: auto;
  border-radius: 0;
  filter: blur(110px) saturate(0.9);
  opacity: 0.45;
  pointer-events: none;
  background:
    radial-gradient(32% 28% at 22% 28%, var(--aura-head,  transparent) 0%, transparent 70%),
    radial-gradient(30% 28% at 78% 26%, var(--aura-heart, transparent) 0%, transparent 70%),
    radial-gradient(42% 36% at 50% 80%, var(--aura-base,  transparent) 0%, transparent 68%),
    radial-gradient(60% 50% at 50% 100%, oklch(0.86 0.06 65 / 0.55) 0%, transparent 70%);
  transition: opacity 1200ms var(--ease), background 1500ms var(--ease);
  animation: aura-drift 22s ease-in-out infinite alternate;
}

@keyframes aura-drift {
  0%   { transform: translate(0%, 0%)  scale(1); }
  50%  { transform: translate(2%, -2%) scale(1.04); }
  100% { transform: translate(-2%, 2%) scale(0.97); }
}

/* Aura family backgrounds — driven by JS via the .aura-* class. */
.aura-floral   { background: radial-gradient(circle at 50% 45%, var(--aura-floral-a)   0%, var(--aura-floral-b)   40%, transparent 72%); }
.aura-oriental { background: radial-gradient(circle at 50% 45%, var(--aura-oriental-a) 0%, var(--aura-oriental-b) 40%, transparent 72%); }
.aura-marine   { background: radial-gradient(circle at 50% 45%, var(--aura-marine-a)   0%, var(--aura-marine-b)   40%, transparent 72%); }
.aura-woody    { background: radial-gradient(circle at 50% 45%, var(--aura-woody-a)    0%, var(--aura-woody-b)    40%, transparent 72%); }
.aura-citrus   { background: radial-gradient(circle at 50% 45%, var(--aura-citrus-a)   0%, var(--aura-citrus-b)   40%, transparent 72%); }
.aura-gourmand { background: radial-gradient(circle at 50% 45%, var(--aura-gourmand-a) 0%, var(--aura-gourmand-b) 40%, transparent 72%); }

/* ─── screens ────────────────────────────────────────────────────────── */
.screen {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 80px 36px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 900ms var(--ease);
  overflow-y: auto;
  z-index: 1;
}

.screen.active {
  opacity: 1;
  pointer-events: auto;
}

/* Subtle back link anchored to the top-left of each step screen. Hidden
   when there's nothing to go back to (toggled by JS via the `is-hidden`
   class) so the chrome stays quiet on the first visible screen. */
.screen-back {
  position: absolute;
  top: 28px;
  left: 28px;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 10.5px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding: 8px 10px;
  opacity: 0.65;
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
  z-index: 5;
}
.screen-back:hover {
  opacity: 1;
  transform: translateX(-2px);
}
.screen-back.is-hidden {
  visibility: hidden;
  pointer-events: none;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 24px;
  width: 100%;
  max-width: 560px;
}

.stack--center {
  align-items: center;
  text-align: center;
}

.stack--wide {
  max-width: 1040px;
}

/* ─── monogram ───────────────────────────────────────────────────────── */
.ms-monogram {
  width: 72px;
  height: auto;
  line-height: 0;
  color: var(--ink);
  user-select: none;
  opacity: 0.78;
}

.ms-monogram--large {
  width: 56px;
  opacity: 1;
}

/* The landing wordmark uses a wider, more elaborate mark
   (maison-soba.svg, ~344×224), so it gets its own size class rather
   than reusing --large. */
.ms-monogram--home {
  width: 320px;
  opacity: 1;
}

.ms-monogram__mark {
  display: block;
  width: 100%;
  height: auto;
}

/* ─── typography (Switzer throughout) ────────────────────────────────── */
.display {
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 52px;
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.display--lg {
  font-size: 78px;
  letter-spacing: -0.03em;
}

.body {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1.2rem;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 480px;
}

.body--lead {
  font-size: 1.2rem;
  line-height: 1.6;
  max-width: 520px;
}

/* ─── buttons + links ────────────────────────────────────────────────── */
.btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  height: 48px;
  padding: 0 28px;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 11.5px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink);
  background: oklch(1 0 0 / 0.5);
  border: 1px solid oklch(0.40 0.05 30 / 0.35);
  border-radius: 8px;
  cursor: pointer;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.5);
  transition: all 280ms var(--ease);
}

.btn:hover:not(:disabled) {
  background: var(--ink);
  color: var(--ground-light);
  border-color: var(--ink);
  letter-spacing: 0.38em;
}

.btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.btn--small {
  height: 40px;
  padding: 0 20px;
  font-size: 10.5px;
  letter-spacing: 0.30em;
}

.btn__arrow {
  display: inline-block;
  font-size: 14px;
  letter-spacing: 0;
  opacity: 0.85;
  transition: transform var(--t-fast) var(--ease);
}

.btn:hover:not(:disabled) .btn__arrow {
  transform: translateX(2px);
}

.btn:hover:not(:disabled) .btn__arrow--down {
  transform: translateY(2px);
}

.link {
  font-family: var(--font-sans);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-mute);
  text-decoration: none;
  cursor: pointer;
  background: none; border: none; padding: 0;
  transition: color var(--t-fast) var(--ease);
}

.link:hover {
  color: var(--ink);
}

.link--quiet {
  color: var(--ink-quiet);
}

/* ─── form inputs ────────────────────────────────────────────────────── */
.form-confidence {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  max-width: 520px;
  align-items: stretch;
}

.input {
  width: 100%;
  display: block;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 400;
  color: var(--ink);
  background: var(--sheet);
  border: 1px solid var(--sheet-edge);
  border-radius: 6px;
  padding: 16px 18px;
  outline: none;
  resize: none;
  transition: border-color 200ms, background 200ms, box-shadow 200ms;
}

.input::placeholder {
  color: var(--ink-faint);
  font-style: italic;
}

.input:focus {
  border-color: oklch(0.40 0.05 30 / 0.6);
  background: oklch(1 0 0 / 0.65);
  box-shadow: 0 0 0 4px oklch(0.40 0.05 30 / 0.06);
}

.input--memory {
  font-style: italic;
  min-height: 100px;
}

.form-help {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-soft);
  opacity: 0.85;
  text-align: center;
  margin: 4px 0 8px;
}

.form-confidence .btn {
  align-self: center;
  margin-top: 14px;
}

/* ─── composition: three frosted sheets ──────────────────────────────── */
.form-composition {
  display: flex;
  flex-direction: column;
  gap: 28px;
  width: 100%;
  align-items: center;
  margin-top: 40px;
}

.composition-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
  width: 100%;
}

/* The fieldset is a transparent two-row stack: legend sits outside the
   chip container; the .tag-row beneath it is the visible frosted sheet.
   `<legend>` is special-cased by browsers and doesn't always participate
   in the parent's flex flow, so the spacing below the legend is enforced
   with an explicit margin-bottom rather than a flex `gap`. */
.tag-group {
  position: relative;
  display: block;
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0;
  margin: 0;
  box-shadow: none;
  width: auto;
  max-width: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.tag-group__legend {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0 0 28px;
  padding: 0;
  text-align: center;
  align-items: center;
  width: 100%;
  float: none;
}

.tag-group__title {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.30em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.tag-group__sub {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-soft);
  opacity: 0.65;
}

/* The chip container is the visible frosted sheet — fits the chips it
   holds (no forced height, no separator line). */
.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  width: 100%;
  justify-content: center;
  align-content: flex-start;
  padding: 22px 20px;
  background: var(--sheet);
  backdrop-filter: blur(10px) saturate(1.1);
  -webkit-backdrop-filter: blur(10px) saturate(1.1);
  border: 1px solid var(--sheet-edge);
  border-radius: 10px;
  box-shadow:
    inset 0 1px 0 oklch(1 0 0 / 0.5),
    0 1px 0 oklch(0 0 0 / 0.04),
    0 18px 50px -20px oklch(0.30 0.05 30 / 0.22);
}

/* ─── chips (paper tickets) ──────────────────────────────────────────── */
.tag {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 36px;
  padding: 0 16px;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: oklch(1 0 0 / 0.42);
  border: 1px solid oklch(0.60 0.025 55 / 0.20);
  border-radius: 7px;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.5);
  transition: all 280ms var(--ease);
}

.tag::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--ink-faint);
  flex: 0 0 auto;
  transition: all 300ms var(--ease);
}

.tag:hover {
  background: oklch(1 0 0 / 0.7);
  border-color: oklch(0.40 0.05 30 / 0.32);
  color: var(--ink);
}

.tag[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--ground-light);
  box-shadow:
    inset 0 1px 0 oklch(1 0 0 / 0.08),
    0 4px 14px -4px oklch(0 0 0 / 0.25),
    0 0 28px -4px var(--chip-glow, oklch(0.7 0.15 30 / 0.45));
}

.tag[aria-pressed="true"]::before {
  background: var(--chip-glow, oklch(0.85 0.15 60));
  box-shadow: 0 0 8px var(--chip-glow, oklch(0.85 0.15 60));
}

/* ─── method list ────────────────────────────────────────────────────── */
.method-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  text-align: left;
  width: 100%;
  max-width: 480px;
}

.method-list__item {
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: center;
  padding: 22px 0 24px;
  border-bottom: 1px solid oklch(0.50 0.04 35 / 0.12);
}

.method-list__item:first-child { padding-top: 6px; }
.method-list__item:last-child  { border-bottom: 0; padding-bottom: 6px; }

.method-list__title {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 22px;
  line-height: 1.1;
  letter-spacing: -0.005em;
  color: var(--ink-deep);
  margin: 0;
}

.method-list__body {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-soft);
}

.method-list__body em { font-style: italic; }

/* ─── anchoring (loading) ────────────────────────────────────────────── */
.bottle-line {
  width: 110px;
  height: auto;
  color: var(--ink-deep);
  opacity: 0.78;
}

.bottle-line--reveal {
  width: 160px;
  opacity: 1;
  filter: drop-shadow(0 4px 10px oklch(0.30 0.06 30 / 0.10));
}

/* Hover shine — a diagonal white band sweeps once across the bottle.
   The bottle itself stays put; only the band moves. The animation is
   keyed to :hover so re-entering re-triggers a fresh sweep. */
.bottle-shine__beam {
  opacity: 0;
  transform: translateX(-110px);
}

.bottle-line--reveal:hover .bottle-shine__beam {
  animation: bottle-shine-sweep 1.1s var(--ease);
}

@keyframes bottle-shine-sweep {
  0%   { opacity: 0; transform: translateX(-110px); }
  18%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { opacity: 0; transform: translateX(110px); }
}

@media (prefers-reduced-motion: reduce) {
  .bottle-line--reveal:hover .bottle-shine__beam {
    animation: none;
  }
}

/* La carte reveal: the bottle's liquid rises from empty up to ~80% of
   the bottle interior when the screen activates. Once full, the rect
   rests in place — the SMIL animateTransform inside #liquid-grad keeps
   the colour bands swirling continuously, and the bubbles SMIL keeps
   ascending. The rect's resting bottom sits past the bottle clip at
   y≈200 so the blurred bottom edge gets cropped sharp by the bottle
   silhouette (only the top meniscus stays dreamy). The HTML rect's
   default y/height are the resting state, so reduced-motion users see
   a filled bottle without any rise animation. */
.screen--carte.active #reveal-liquid {
  animation: reveal-liquid-fill 1.6s var(--ease) backwards;
  animation-delay: 360ms;
}

@keyframes reveal-liquid-fill {
  from { y: 200px; height: 0px; }
  to   { y: 76px;  height: 124px; }
}

/* Bubbles fade in only after the liquid has finished rising, so they
   don't appear in mid-air against the empty bottle during the reveal.
   The SMIL animations on each circle run continuously underneath; the
   group's opacity is what gates visibility. */
.bubbles {
  opacity: 0;
}

.bubble {
  filter: blur(0.6px);
}

.screen--carte.active .bubbles {
  animation: bubbles-appear 800ms var(--ease) forwards;
  animation-delay: 2s;
}

@keyframes bubbles-appear {
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .bubbles { display: none; }
}

.anchoring-liquid {
  animation: liquid-fill 2.4s ease-in-out infinite;
}

@keyframes liquid-fill {
  0%, 100% { y: 172px; height: 0px; }
  50%      { y: 44px;  height: 128px; }
}

.loading-line {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 15px;
  font-weight: 400;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  opacity: 0.85;
  transition: opacity var(--t-slow) var(--ease);
  min-height: 1.6em;
}

/* Loading screen aura — three spots, one per section selection
   (head/heart/base), the same colour vocabulary the user chose on
   step 2. The orb rotates a full 360° over its drift loop so the
   three colours swirl around the bottle while the AI composes. */
.anchoring-orb {
  position: absolute;
  inset: -8%;
  width: auto;
  height: auto;
  margin: 0;
  pointer-events: none;
  border-radius: 0;
  filter: blur(130px) saturate(0.78);
  opacity: 0.5;
  z-index: 0;
  background:
    radial-gradient(36% 32% at 25% 28%, var(--orb-head,  var(--aura-gourmand-a)) 0%, transparent 70%),
    radial-gradient(36% 32% at 75% 28%, var(--orb-heart, var(--aura-gourmand-a)) 0%, transparent 70%),
    radial-gradient(44% 38% at 50% 78%, var(--orb-base,  var(--aura-gourmand-b)) 0%, transparent 68%);
  animation: anchoring-orb-drift 16s linear infinite;
  will-change: transform, opacity;
}

/* Inner wash rotates the opposite direction so the two layers swirl
   against each other — gives the room a slow vortex feel. */
.anchoring-orb::after {
  content: '';
  position: absolute;
  inset: 6%;
  background:
    radial-gradient(28% 24% at 50% 18%, var(--orb-head,  var(--aura-gourmand-a)) 0%, transparent 78%),
    radial-gradient(26% 22% at 18% 62%, var(--orb-heart, var(--aura-gourmand-a)) 0%, transparent 80%),
    radial-gradient(30% 26% at 78% 70%, var(--orb-base,  var(--aura-gourmand-b)) 0%, transparent 78%);
  filter: blur(50px) saturate(0.78);
  opacity: 0.6;
  animation: anchoring-orb-inner 12s linear infinite;
}

@keyframes anchoring-orb-drift {
  0%   { transform: translate(-3%, -2%) rotate(0deg)   scale(1.00); opacity: 0.42; }
  25%  { transform: translate( 4%, -5%) rotate(90deg)  scale(1.06); opacity: 0.55; }
  50%  { transform: translate( 5%,  4%) rotate(180deg) scale(0.96); opacity: 0.48; }
  75%  { transform: translate(-4%,  5%) rotate(270deg) scale(1.04); opacity: 0.58; }
  100% { transform: translate(-3%, -2%) rotate(360deg) scale(1.00); opacity: 0.42; }
}

@keyframes anchoring-orb-inner {
  0%   { transform: translate(-5%,  3%) rotate(0deg)    scale(1.00); opacity: 0.45; }
  25%  { transform: translate( 6%, -5%) rotate(-90deg)  scale(0.95); opacity: 0.65; }
  50%  { transform: translate( 4%,  6%) rotate(-180deg) scale(1.05); opacity: 0.55; }
  75%  { transform: translate(-6%, -4%) rotate(-270deg) scale(0.94); opacity: 0.70; }
  100% { transform: translate(-5%,  3%) rotate(-360deg) scale(1.00); opacity: 0.45; }
}

/* Keep the bottle/text/monogram above the orb. */
.screen--anchoring .stack {
  position: relative;
  z-index: 1;
}

/* Default .ms-monogram is 78% opacity, which reads as a smaller visual
   weight against the anchoring screen's vivid aura than it does against
   the cream parchment of the other flow screens. Restore full opacity
   here so the monogram matches the perceived size on every other page. */
.screen--anchoring .ms-monogram {
  opacity: 1;
}

/* ─── la carte: layout shell ─────────────────────────────────────────── */
.screen--carte {
  overflow: hidden;
  padding: 60px 36px;
}

.carte-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 80px;
  align-items: center;
  justify-items: center;
  width: 100%;
  max-width: 1180px;
  height: 100%;
  perspective: 1400px;
}

.carte-left {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  text-align: center;
  height: 100%;
}

.carte-header {
  text-align: center;
}

.carte-header .display {
  font-size: 42px;
}

.carte-header .body {
  margin: 10px auto 0;
  color: var(--ink-soft);
  font-size: 14.5px;
  max-width: 320px;
}

.carte-bottle-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
}

.archive-actions {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: center;
}

/* ─── la carte: vellum card with aura halo ───────────────────────────── */
.card {
  --rx: 0deg;
  --ry: 0deg;
  --lift: 0px;
  --glow-x: 50%;
  --glow-y: 50%;
  --glow-opacity: 0;

  position: relative;
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - 120px);
  padding: 38px 36px 30px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  font-size: 12px;
  overflow: hidden;
  background:
    linear-gradient(168deg,
      oklch(0.975 0.014 80 / 0.48) 0%,
      oklch(0.945 0.020 74 / 0.44) 55%,
      oklch(0.915 0.026 68 / 0.42) 100%);
  backdrop-filter: blur(28px) saturate(1.18);
  -webkit-backdrop-filter: blur(28px) saturate(1.18);
  border: 1px solid oklch(0.82 0.025 70 / 0.55);
  border-radius: 8px;
  box-shadow: var(--shadow-card);
  transform-style: preserve-3d;
  transform:
    perspective(1400px)
    translateY(var(--lift))
    rotateX(var(--rx))
    rotateY(var(--ry));
  transition:
    transform 360ms var(--ease),
    box-shadow 360ms var(--ease);
  will-change: transform;
}

/* Vellum paper grain — fine multiply noise + long parchment fibers. The
   second layer uses anisotropic turbulence (low X / high Y baseFrequency)
   to stretch the noise into faint vertical fibers, the way real vellum
   reads when held up to light. */
.card::before {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='480' height='480'><filter id='f'><feTurbulence type='fractalNoise' baseFrequency='0.012 0.55' numOctaves='2' seed='4' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.40  0 0 0 0 0.32  0 0 0 0 0.22  0 0 0 0.45 0'/></filter><rect width='100%' height='100%' filter='url(%23f)'/></svg>"),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.45  0 0 0 0 0.36  0 0 0 0 0.25  0 0 0 0.55 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  background-size: 480px 480px, 220px 220px;
  background-repeat: repeat, repeat;
  opacity: 0.20;
  mix-blend-mode: multiply;
}

/* Vellum edge darkening + faint horizontal ruling. The cursor-tracked
   highlight rides on top of this layer via the .is-hovering filter
   below. */
.card::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    radial-gradient(
      260px circle at var(--glow-x) var(--glow-y),
      rgba(255, 255, 255, calc(0.55 * var(--glow-opacity, 0))),
      rgba(255, 255, 255, 0) 65%
    ),
    radial-gradient(130% 90% at 50% 50%,
      transparent 50%,
      oklch(0.55 0.05 45 / 0.18) 92%,
      oklch(0.40 0.06 35 / 0.30) 100%),
    repeating-linear-gradient(92deg,
      transparent 0,
      transparent 3px,
      oklch(0.70 0.03 60 / 0.04) 3px,
      oklch(0.70 0.03 60 / 0.04) 4px);
  transition: opacity 360ms var(--ease);
  mix-blend-mode: soft-light;
}

.card > * { position: relative; z-index: 1; }

.card:hover {
  --lift: -8px;
  --glow-opacity: 1;
}

/* While the pointer is over the card, the JS handler updates --rx/--ry on
   every frame. Drop the transform transition so the tilt tracks the cursor
   instead of trailing behind it; keep the box-shadow transition for the
   soft hover lift. */
.card.is-tilting {
  transition: box-shadow 360ms var(--ease);
}

.card.is-capturing {
  transform: none !important;
  box-shadow: none !important;
  transition: none !important;
  max-height: none !important;
  overflow: visible !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  background:
    linear-gradient(168deg,
      oklch(0.975 0.016 80) 0%,
      oklch(0.945 0.022 74) 55%,
      oklch(0.915 0.028 68) 100%) !important;
}
.card.is-capturing::after {
  background:
    radial-gradient(130% 90% at 50% 50%,
      transparent 50%,
      oklch(0.55 0.05 45 / 0.18) 92%,
      oklch(0.40 0.06 35 / 0.30) 100%),
    repeating-linear-gradient(92deg,
      transparent 0,
      transparent 3px,
      oklch(0.70 0.03 60 / 0.04) 3px,
      oklch(0.70 0.03 60 / 0.04) 4px);
}

/* The aura halo behind the card. Position is set via JS sibling sizing. */
.vellum-aura {
  position: absolute;
  border-radius: 50%;
  filter: blur(80px) saturate(0.45);
  opacity: 0.32;
  pointer-events: none;
  z-index: 0;
  animation: aura-drift-card 22s ease-in-out infinite alternate;
}

.carte-card-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

@keyframes aura-drift-card {
  0%   { transform: translate(0%, 0%) scale(1); opacity: 0.30; }
  50%  { transform: translate(3%, 1.5%) scale(1.05); opacity: 0.36; }
  100% { transform: translate(-2%, -1%) scale(0.97); opacity: 0.28; }
}

/* Card contents ------------------------------------------------------- */
.card__seal {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 0 14px;
  border-radius: 50%;
  background: oklch(1 0 0 / 0.22);
  border: 1px solid oklch(0.55 0.05 40 / 0.30);
  box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.4);
}

.card__seal-mark {
  display: block;
  width: 26px;
  height: auto;
  opacity: 0.78;
}

.card__dedication {
  font-family: var(--font-sans);
  font-size: 9px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin: 0 0 10px;
}

.card__dedication #card-dedicate-name {
  color: var(--ink-deep);
  letter-spacing: 0.5em;
}

.card__stars {
  display: block;
  width: 36px;
  height: auto;
  margin: 4px 0 2px;
  opacity: 0.45;
  filter: brightness(0.4) sepia(0.4);
  user-select: none;
}

.card__name {
  /* The card title is the one place we depart from Switzer — Maison
     Sezanne carries the signature feel of the perfume name. Colour is
     driven by --card-name-color (set from the heart's family in JS)
     so the signature reads as the soul of the composition. */
  font-family: 'Maison Sezanne', 'Snell Roundhand', 'EB Garamond', Georgia, serif;
  font-weight: 400;
  font-size: 64px;
  line-height: 1;
  letter-spacing: -0.015em;
  color: var(--card-name-color, var(--accent));
  margin: 14px 0 8px;
}

.card__tagline {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-deep);
  opacity: 0.85;
  max-width: 300px;
  margin: 0 auto 18px;
}

/* Pyramid: keep the 3-column structure of the existing markup; restyle
   as a vellum-friendly band of notes. */
.card__pyramid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
  width: 100%;
  padding: 16px 14px;
  margin: 0 0 14px;
  text-align: center;
  background:
    linear-gradient(180deg,
      oklch(0.92 0.024 70 / 0.55) 0%,
      oklch(0.88 0.030 65 / 0.50) 100%);
  border: 1px solid oklch(0.70 0.030 55 / 0.28);
  border-radius: 6px;
  box-shadow:
    inset 0 1px 0 oklch(1 0 0 / 0.35),
    inset 0 -1px 0 oklch(0.55 0.04 40 / 0.10);
}

.card__layer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.card__layer-label {
  font-family: var(--font-sans);
  font-size: 9.5px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 400;
}

.card__layer-notes {
  font-family: var(--font-sans);
  font-size: 10.5px;
  line-height: 1.55;
  color: var(--ink-deep);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.card__layer-note { display: block; }

.card__attrs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 28px;
  width: 100%;
  margin: 4px 0 14px;
  text-align: left;
  padding: 4px 4px 4px;
}

.card__attr dt {
  font-family: var(--font-sans);
  font-size: 9px;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--ink-mute);
  font-weight: 400;
  margin: 0 0 4px;
}

.card__attr dd {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-deep);
}

.card__edition {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 11.5px;
  color: var(--ink-deep);
  margin-top: 6px;
}

.card__cities {
  font-family: var(--font-sans);
  font-size: 9px;
  letter-spacing: 0.5em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin: 6px 0 8px;
}

.card__footnote {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: 9.5px;
  letter-spacing: 0.02em;
  color: var(--ink-quiet);
  opacity: 0.85;
}

/* ─── reveal choreography ────────────────────────────────────────────── */
@keyframes reveal-rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes reveal-rise-card {
  from { opacity: 0; transform: translateY(18px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.screen--carte.active .carte-header {
  animation: reveal-rise 520ms var(--ease) both;
}

.screen--carte.active .carte-bottle-col {
  animation: reveal-rise 680ms var(--ease) both;
  animation-delay: 120ms;
}

/* `backwards` fill mode applies the FROM keyframe state during the delay
   but reverts to the rule's transform after the animation ends — which is
   essential here, because the rule's transform reads --rx/--ry from the
   pointer-tilt handler. With `both`, the keyframe's TO transform would
   permanently override the tilt and the card couldn't move on hover. */
.screen--carte.active .card {
  animation: reveal-rise-card 1100ms var(--ease) backwards;
  animation-delay: 240ms;
}

/* ─── accessibility ──────────────────────────────────────────────────── */
.btn:focus-visible,
.tag:focus-visible,
.input:focus-visible,
.link:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

@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;
  }
  .card { transform: none !important; }
  .card::after { background: none; }
  .ambient-aura, .vellum-aura, .anchoring-orb { animation: none; }
}

/* ─── toast / inline error ───────────────────────────────────────────── */
.toast {
  position: fixed;
  bottom: 32px;
  left: 50%;
  transform: translateX(-50%);
  padding: 12px 22px;
  background: var(--ink);
  color: var(--ground-light);
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.04em;
  border-radius: 6px;
  max-width: 480px;
  text-align: center;
  z-index: 100;
  opacity: 0;
  transition: opacity var(--t-med) var(--ease);
  box-shadow: 0 12px 28px -10px oklch(0.30 0.06 30 / 0.45);
}

.toast:not([hidden]) { opacity: 1; }

/* ─── responsive ─────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .ms-monogram--home { width: 240px; }
  .display--lg { font-size: 56px; }
  .display { font-size: 40px; }
  .carte-header .display { font-size: 32px; }

  .composition-grid { grid-template-columns: 1fr; gap: 24px; }

  .screen--carte { overflow-y: auto; align-items: flex-start; padding: 48px 24px; }
  .carte-layout {
    grid-template-columns: 1fr;
    column-gap: 0;
    row-gap: 36px;
    justify-items: center;
    height: auto;
  }
  .carte-left { height: auto; gap: 20px; }
  .card { max-height: none; padding: 36px 32px 28px; }
  .card__name { font-size: 56px; }
}

@media (max-width: 520px) {
  .screen { padding: 56px 20px; }
  .stack { gap: 20px; }
  .display--lg { font-size: 44px; }
  .display { font-size: 32px; }
  .ms-monogram--large { width: 44px; }
  .ms-monogram--home { width: 180px; }
  .form-confidence { gap: 12px; }
  .card { padding: 32px 24px 24px; }
  .card__name { font-size: 50px; }
  .card__pyramid { padding: 14px 8px; gap: 8px; }
  .card__attrs { gap: 14px 16px; }

  /* Monogram is reserved for landing (full wordmark) and the anchoring
     loading screen on mobile; the in-card seal stays because it's a
     different element (.card__seal). Hidden everywhere else to free up
     vertical room. */
  .screen--method .ms-monogram,
  .screen--confidence .ms-monogram,
  .screen--composition .ms-monogram,
  .carte-left .ms-monogram { display: none; }

  /* Composition: compact aggressively so head/heart/base + macerate all
     fit in one mobile viewport without scrolling. */
  .screen--composition { padding: 44px 16px 16px; }
  .screen--composition .stack { gap: 10px; }
  .screen--composition .display { font-size: 26px; }
  .screen--composition .body { font-size: 13px; }
  .screen--composition .screen-back { top: 16px; left: 16px; padding: 6px 8px; }
  .composition-grid { gap: 10px; }
  .tag-group__legend { margin: 0 0 6px; gap: 2px; }
  .tag-group__title { font-size: 10px; letter-spacing: 0.26em; }
  .tag-group__sub { font-size: 11px; }
  .tag-row { padding: 10px 10px; gap: 6px; }
  /* Force exactly three chips per row so head/heart (6 chips) stack as a
     balanced 3+3, and base (5 chips) becomes 3+2 with the orphan pair
     centred by the row's existing justify-content: center. */
  .tag {
    height: 28px;
    padding: 0 8px;
    font-size: 10px;
    letter-spacing: 0.14em;
    gap: 6px;
    flex: 0 0 calc((100% - 12px) / 3);
    min-width: 0;
    justify-content: center;
  }
  .tag::before { width: 4px; height: 4px; }
  #btn-macerate { height: 40px; font-size: 11px; padding: 0 22px; }
}
