:root {
  --bg: #fdf9f0;
  --surface: #f5edda;
  --surface-raised: #ecdfc2;
  --ink: #1c1410;
  --ink-muted: #7a6b52;
  --gold: #dc931a;
  --gold-ink: #241704;
  --red: #c1001f;
  --red-ink: #fdf8ee;
  --border: #e3d3ac;
  --font-display: 'Baloo 2', sans-serif;
  --font-body: 'Work Sans', sans-serif;
}

* { box-sizing: border-box; }

html { background: var(--bg); }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  max-width: 880px;
  margin: 0 auto;
  padding: 2.5rem 1.5rem 3rem;
}

@media (min-width: 701px) {
  body { padding-left: 7.5rem; }
}
@media (max-width: 700px) {
  body { padding-bottom: 6.5rem; }
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.2;
  margin: 0 0 0.5rem;
}
h1 { font-size: 2.1rem; }
h2 { font-size: 1.5rem; margin-top: 2.5rem; }
h3 { font-size: 1.2rem; }
h4 { font-size: 1.05rem; }


p { color: var(--ink-muted); max-width: 60ch; }

a { color: var(--gold); font-weight: 600; }

.series-index {
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--gold);
  font-size: 0.9rem;
  letter-spacing: 0.02em;
  margin: 0 0 0.25rem;
}

/* Dock navigation: quiet by default. Hover only lifts an item; gold is
   reserved for the page you're on, so the two never look alike. */
#nav { position: fixed; z-index: 50; }
@media (min-width: 701px) {
  #nav { left: 18px; top: 50%; transform: translateY(-50%); }
}
@media (max-width: 700px) {
  #nav { left: 50%; bottom: 14px; transform: translateX(-50%); }
}

.dock {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px;
  background: rgba(255, 253, 248, 0.82);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  border: 1px solid rgba(28, 20, 16, 0.08);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(28, 20, 16, 0.06), 0 12px 32px rgba(28, 20, 16, 0.12);
}
@media (max-width: 700px) {
  .dock { flex-direction: row; }
}

.dock .dock-item {
  width: 42px;
  height: 42px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 12px;
  background: transparent;
  color: var(--ink-muted);
  text-decoration: none;
  position: relative;
  transition: transform 140ms ease-out, background 140ms ease-out, color 140ms ease-out;
}
.dock .dock-item svg {
  width: 20px;
  height: 20px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.dock .dock-item:hover,
.dock .dock-item:focus-visible {
  background: rgba(28, 20, 16, 0.06);
  color: var(--ink);
  transform: scale(1.08);
  filter: none;
}
.dock .dock-item.is-active {
  background: var(--gold);
  color: var(--gold-ink);
}
.dock .dock-item.is-active:hover { background: var(--gold); }

.dock-divider {
  width: 22px;
  height: 1px;
  margin: 4px 0;
  background: rgba(28, 20, 16, 0.12);
}
@media (max-width: 700px) {
  .dock-divider { width: 1px; height: 22px; margin: 0 4px; }
}

/* Tooltips */
.dock .dock-item[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  left: calc(100% + 14px);
  top: 50%;
  transform: translate(-4px, -50%);
  background: var(--ink);
  color: var(--bg);
  padding: 4px 9px;
  border-radius: 6px;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease, transform 120ms ease;
}
.dock .dock-item:hover[data-tip]::after,
.dock .dock-item:focus-visible[data-tip]::after {
  opacity: 1;
  transform: translate(0, -50%);
}
@media (max-width: 700px) {
  .dock .dock-item[data-tip]::after { display: none; }
}

/* Account: avatar + small menu */
.dock-account { position: relative; }
.dock .dock-avatar {
  border-radius: 50%;
  width: 36px;
  height: 36px;
  margin: 3px;
  background: var(--ink);
  color: var(--bg);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
}
.dock .dock-avatar:hover,
.dock .dock-avatar[aria-expanded="true"] {
  background: var(--ink);
  color: var(--bg);
  box-shadow: 0 0 0 3px rgba(220, 147, 26, 0.45);
}
.account-menu {
  position: absolute;
  left: calc(100% + 16px);
  bottom: -8px;
  min-width: 210px;
  padding: 0.85rem;
  background: #fffdf8;
  border: 1px solid rgba(28, 20, 16, 0.08);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(28, 20, 16, 0.16);
}
.account-menu[hidden] { display: none; }
.account-menu p { margin: 0; max-width: none; }
.account-name { font-weight: 600; color: var(--ink); font-size: 0.92rem; }
.account-email { font-size: 0.8rem; margin-bottom: 0.75rem !important; overflow: hidden; text-overflow: ellipsis; }
.account-link {
  display: block;
  margin-bottom: 0.5rem;
  padding: 0.4rem 0;
  font-size: 0.85rem;
  color: var(--ink);
  text-decoration: none;
}
.account-link:hover { color: var(--gold-ink, var(--ink)); text-decoration: underline; }
.account-menu .account-logout {
  width: 100%;
  height: auto;
  padding: 0.45rem 0.8rem;
  font-size: 0.85rem;
}
@media (max-width: 700px) {
  .account-menu { left: auto; right: -8px; bottom: calc(100% + 18px); }
}
@media (prefers-reduced-motion: reduce) {
  .dock .dock-item, .dock .dock-item[data-tip]::after { transition: none; }
}

/* Catalog grid */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 1.1rem;
  margin-top: 1.5rem;
}
.grid-3 {
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 700px) {
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
}
.card {
  display: block;
  color: inherit;
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.1rem;
  transition: border-color 150ms, transform 150ms;
}
.card:hover {
  border-color: var(--gold);
  transform: translateY(-2px);
}
.card img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 10px;
  margin-bottom: 0.75rem;
  background: var(--surface-raised);
}
.card .spec-no {
  display: block;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--gold);
  margin-bottom: 0.3rem;
}
.card h3, .card h4 { margin: 0; }
.card p { margin: 0.35rem 0 0; font-size: 0.92rem; }

/* Candle detail photo */
.candle-photo { position: relative; max-width: 320px; margin: 1rem 0; }
.candle-photo img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
}

/* Collection grid tiles: hover crossfade from neutral placeholder to the
   candle's own vasetto photo; owned tiles just show the filled photo. */
.tile-photo-stack {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface-raised);
  margin-bottom: 0.75rem;
}
.tile-photo-stack .tile-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.tile-photo-reveal {
  opacity: 0;
  transition: opacity 220ms ease;
}
.tile:hover .tile-photo-reveal,
.tile:focus-visible .tile-photo-reveal,
.tile.is-active .tile-photo-reveal {
  opacity: 1;
}
.tile-badge {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--ink-muted);
  background: var(--surface-raised);
  border-radius: 999px;
  padding: 0.15rem 0.55rem;
  margin-top: 0.4rem;
}
.tile-badge.is-owned {
  color: var(--gold-ink);
  background: var(--gold);
}

/* Scent smoke (see scentFx in app.js). The SVG's bottom-centre sits on the
   wick of the filled-jar photo: all filled-jar shots share one framing
   (862x744, wick at 37.4% / 25.3%), which inside a square object-fit:contain
   box lands at 37.4% / 28.7%. Strands rise as long dashes travelling up each
   path, fading in at the wick and out as they climb. */
.scent-fx {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
  color: var(--scent-color);
}
.smoke {
  position: absolute;
  left: 37.4%;
  bottom: 71.3%;
  width: 26%;
  height: auto;
  aspect-ratio: 1 / 2;
  transform: translateX(-50%);
  overflow: visible;
  -webkit-mask-image: linear-gradient(to top, transparent 0%, #000 12%, #000 38%, transparent 90%);
  mask-image: linear-gradient(to top, transparent 0%, #000 12%, #000 38%, transparent 90%);
}
.strand {
  stroke-dasharray: 90 200;
  stroke-dashoffset: 90;
  opacity: 0.6;
  animation: strand-rise 6.5s linear infinite;
}
.strand.s1 { opacity: 0.5; animation-duration: 7.8s; animation-delay: -2.6s; }
.strand.s2 { opacity: 0.45; animation-duration: 9s; animation-delay: -5.1s; }
.strand--haze {
  stroke-dasharray: 140 200;
  opacity: 0.12;
  animation-duration: 11s;
  animation-delay: -4s;
}
/* The modal photo is bigger and sits close to the top of a scrolling box,
   so a proportionally shorter column keeps the smoke inside it. */
.modal-photo .smoke { width: 16%; }

@keyframes strand-rise {
  from { stroke-dashoffset: 90; }
  to   { stroke-dashoffset: -200; }
}
@media (prefers-reduced-motion: reduce) {
  .tile-photo-reveal { transition: none; }
  .strand { animation: none; stroke-dasharray: none; }
}

/* Homepage catalog: numbered archive specimens, floating on the page with
   no card chrome. Photos are cut out (transparent PNG) so nothing may draw
   a background behind or under them — not the stack, not the <img> itself
   (the shared ".card img" rule above does, which is why it's overridden
   again here down to the image level). */
#catalog .card {
  background: none;
  border: none;
  padding: 0;
  text-align: left;
}
#catalog .card:hover {
  border-color: transparent;
  transform: none;
}
#catalog .grid-3 {
  gap: 3rem 1.75rem;
}
#catalog .tile-photo-stack {
  background: none;
  overflow: visible;
  margin-bottom: 0.9rem;
}
/* Hover: the jar floats up a little and its shadow grows in from beneath.
   The shadow is its own element (not a filter on the photo) so it is never
   clipped and stays on the "floor" while the jar rises. */
#catalog .tile-photo-stack .tile-photo,
#catalog .tile-photo-stack .scent-fx,
#catalog .tile-photo-stack .jar-glow,
#catalog .tile-photo-stack .jar-shine {
  transition: translate 480ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 360ms ease;
}
#catalog .tile-photo-stack .tile-photo {
  object-fit: contain;
  background: none;
}
#catalog .tile:hover .tile-photo-stack > :not(.tile-shadow),
#catalog .tile:focus-visible .tile-photo-stack > :not(.tile-shadow),
#catalog .tile.is-active .tile-photo-stack > :not(.tile-shadow) {
  translate: 0 -10px;
}
.tile-shadow {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -2;
  overflow: visible;
  fill: rgb(28, 20, 16);
  filter: blur(6px);
  opacity: 0.32;
  translate: 0 2%;
  transform-origin: 50% 78%;
  transition: opacity 480ms ease, transform 480ms cubic-bezier(0.2, 0.8, 0.2, 1);
  pointer-events: none;
}
/* The popup photo is bigger: same footprint, a little more blur. */
.modal-photo .tile-shadow { filter: blur(9px); }
/* As the jar lifts its shadow spreads a little and deepens beneath it. */
#catalog .tile:hover .tile-shadow,
#catalog .tile:focus-visible .tile-shadow,
#catalog .tile.is-active .tile-shadow {
  opacity: 0.5;
  transform: scale(1.06);
}
/* Empty jars read a touch smaller than the filled ones (owned tiles keep
   full size: the smoke origin is calibrated to that framing). */
#catalog .tile-photo-base,
#catalog .tile-photo-reveal {
  transform: scale(0.9);
}
#catalog .tile-photo-reveal { opacity: 0; }
#catalog .tile:hover .tile-photo-reveal,
#catalog .tile:focus-visible .tile-photo-reveal,
#catalog .tile.is-active .tile-photo-reveal {
  opacity: 1;
}
#catalog .spec-no {
  font-size: 0.85rem;
  letter-spacing: 0.03em;
  margin-bottom: 0.15rem;
}
#catalog h4 {
  font-size: 1.15rem;
}
@media (prefers-reduced-motion: reduce) {
  #catalog .tile-photo-stack > * { transition: none; }
}

/* Intro video. The overlay is the page's own cream and the video is
   multiplied onto it: its pure-white areas become exactly the background, so
   the logo looks drawn on the page rather than a clip pasted in a box. On
   leaving, the logo recedes and the homepage rises in underneath. */
.intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: opacity 650ms ease 350ms;
}
.intro-overlay video {
  width: auto;
  height: auto;
  max-width: min(100vw, 620px);
  max-height: 88vh;
  max-height: 88dvh;
  mix-blend-mode: multiply;
  transition: transform 900ms cubic-bezier(0.5, 0, 0.2, 1), opacity 650ms ease, filter 650ms ease;
}
.intro-overlay.is-leaving { opacity: 0; pointer-events: none; }
.intro-overlay.is-leaving video {
  transform: scale(0.82) translateY(-4%);
  opacity: 0;
  filter: blur(6px);
}
.intro-skip {
  position: absolute;
  right: max(20px, env(safe-area-inset-right));
  bottom: max(20px, env(safe-area-inset-bottom));
  background: transparent;
  color: var(--ink-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.4rem 1rem;
  opacity: 0;
  animation: intro-skip-in 500ms ease 1.2s forwards;
}
.intro-skip:hover { color: var(--ink); border-color: var(--gold); filter: none; }
.intro-overlay.is-leaving .intro-skip { opacity: 0; animation: none; }
@keyframes intro-skip-in { to { opacity: 1; } }

/* Homepage content held back while the intro plays, then risen in. */
body.intro-playing { overflow: hidden; }
body.intro-playing .home-hero,
body.intro-playing #catalog,
body.intro-playing #nav { opacity: 0; }
body.intro-done .home-hero,
body.intro-done #catalog { animation: page-rise 900ms cubic-bezier(0.2, 0.8, 0.2, 1) both; }
body.intro-done .home-hero { animation-delay: 380ms; }
body.intro-done #catalog { animation-delay: 520ms; }
body.intro-done #nav { animation: page-fade 700ms ease 700ms both; }
@keyframes page-rise {
  from { opacity: 0; translate: 0 18px; }
  to { opacity: 1; translate: 0 0; }
}
@keyframes page-fade { from { opacity: 0; } to { opacity: 1; } }

/* Candle modal — opens in place of navigating to candle.html. Kept in the
   DOM at all times (not [hidden]) so opacity/transform can transition on
   the way in and out; .is-open is what actually shows it. */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(28, 20, 16, 0);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  z-index: 100;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 260ms ease, background-color 260ms ease, visibility 0s linear 260ms;
}
.modal-overlay.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  background: rgba(28, 20, 16, 0.55);
  transition: opacity 260ms ease, background-color 260ms ease, visibility 0s linear 0s;
}
.modal-box {
  position: relative;
  background: #fdf9f0;
  border-radius: 18px;
  width: 100%;
  max-width: 780px;
  max-height: 85vh;
  overflow: auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
  padding: 2rem;
  box-shadow: 0 24px 60px rgba(28, 20, 16, 0.32);
  transform: translateY(28px) scale(0.96);
  transition: transform 340ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.modal-overlay.is-open .modal-box {
  transform: translateY(0) scale(1);
}
@media (prefers-reduced-motion: reduce) {
  .modal-overlay, .modal-box { transition: none; }
}
@media (max-width: 700px) {
  .modal-box { grid-template-columns: 1fr; max-height: 90vh; padding: 1.5rem; }
}
.modal-photo {
  position: relative;
}
.modal-photo img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  background: none;
}
.modal-info {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}
.modal-info h2 { margin-top: 0.15rem; }
#modal-collection-actions {
  margin-top: auto;
  padding-top: 1.25rem;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.6rem;
}
#modal-collection-actions button {
  padding: 0.55rem 1.3rem;
  font-size: 0.88rem;
  border-radius: 999px;
}
#modal-collection-actions p {
  margin: 0;
  text-align: right;
}
#modal-collection-actions.is-reward { align-items: flex-start; }
#modal-collection-actions.is-reward p { text-align: left; }
.modal-close {
  position: absolute;
  z-index: 5;
  top: 1rem;
  right: 1rem;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 50%;
  background: var(--surface-raised);
  color: var(--ink);
  border: none;
  cursor: pointer;
}
.modal-close svg {
  width: 16px;
  height: 16px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
}
.modal-close:hover { background: var(--gold); color: var(--gold-ink); }

/* Homepage candle modal = a two-sided card. The entrance moves from the box
   to .modal-flip, which also holds the perspective; the faces stack in one
   grid cell and the back (the box label) is laid over the front's height
   and scrolls on its own if the label is longer. */
.modal-flip {
  position: relative;
  width: 100%;
  max-width: 780px;
  perspective: 2000px;
  transform: translateY(28px) scale(0.96);
  transition: transform 340ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.modal-overlay.is-open .modal-flip { transform: translateY(0) scale(1); }
.modal-card {
  position: relative;
  display: grid;
  transform-style: preserve-3d;
  transition: transform 780ms cubic-bezier(0.45, 0.05, 0.2, 1);
}
.modal-card.is-flipped { transform: rotateY(180deg); }
.modal-card.no-anim { transition: none; }
.modal-card.is-settled { transform: none; transition: none; }
.modal-card.is-settled .modal-back { transform: none; }
.modal-card.is-settled .modal-front { visibility: hidden; }
/* The front can be stretched to the label's height: keep the jar and its
   shadow together, centred. */
.modal-front .modal-photo { align-self: center; }
.modal-box.modal-front, .modal-overlay.is-open .modal-box.modal-front { transform: none; transition: none; }
.modal-front, .modal-back {
  grid-area: 1 / 1;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
.modal-back {
  max-height: 85vh;
  transform: rotateY(180deg);
  background: #f3ead6;
  border-radius: 18px;
  box-shadow: 0 24px 60px rgba(28, 20, 16, 0.32);
  overflow: auto;
  padding: 2rem 1.75rem 1.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
}
.modal-back .modal-close { background: #fff; }
@media (prefers-reduced-motion: reduce) {
  .modal-flip, .modal-card { transition: none; }
}
@media (max-width: 700px) {
  .modal-back { padding: 1.5rem 1rem 1.25rem; max-height: 90vh; }
}
@media (min-width: 701px) {
  .modal-back { padding: 1.5rem 1.75rem 1.25rem; gap: 0.9rem; }
  .label-sheet { margin-top: 2.75rem; }
}

/* The label itself: a paper sticker, like the one on the box. */
.label-sheet {
  width: 100%;
  max-width: 560px;
  margin-top: 0.75rem;
  background:
    radial-gradient(circle at 20% 10%, rgba(255, 255, 255, 0.9), transparent 60%),
    #fffdf8;
  color: #1a1a1a;
  border-radius: 6px;
  padding: 1.2rem 1.35rem 1.1rem;
  box-shadow: 0 1px 0 rgba(28, 20, 16, 0.06), 0 10px 26px rgba(28, 20, 16, 0.16);
  outline: 1px solid rgba(28, 20, 16, 0.08);
  outline-offset: -7px;
  font-size: 0.8rem;
  line-height: 1.5;
}
.label-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding-bottom: 0.65rem;
  margin-bottom: 0.7rem;
  border-bottom: 1px dashed rgba(28, 20, 16, 0.25);
}
.label-head img { width: 30px; height: auto; }
.label-head div { display: flex; flex-direction: column; line-height: 1.2; }
.label-head strong { font: 700 1.05rem/1.2 var(--font-display); }
.label-sheet h3 {
  margin: 0 0 0.45rem;
  font: 600 0.78rem/1.3 var(--font-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.label-text p { margin: 0 0 0.6rem; color: inherit; }
@media (min-width: 701px) {
  .label-sheet { max-width: none; }
  .label-text { display: grid; grid-template-columns: 1fr 1fr; column-gap: 1.6rem; }
}
.label-text b { font-weight: 600; }
.label-icons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding-top: 0.6rem;
  border-top: 1px dashed rgba(28, 20, 16, 0.25);
}
.label-icon { width: 38px; height: 38px; }
.label-icon svg { width: 100%; height: 100%; display: block; }

.label-turn {
  align-self: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}
.label-turn svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.modal-back .label-turn {
  padding: 0.55rem 1.3rem;
  font-size: 0.88rem;
  border-radius: 999px;
  flex-shrink: 0;
}

/* Forms */
form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  max-width: 380px;
  margin-top: 1.5rem;
}
label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.9rem;
  color: var(--ink-muted);
}
input, textarea, select {
  font-family: var(--font-body);
  font-size: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink);
  border-radius: 8px;
  padding: 0.55rem 0.7rem;
}
input:focus, textarea:focus, select:focus, button:focus-visible, .dock-item:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}
textarea { min-height: 4.5rem; resize: vertical; }

button {
  font-family: var(--font-body);
  font-size: 0.86rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  cursor: pointer;
  background: var(--gold);
  color: var(--gold-ink);
  border: 1px solid transparent;
  border-radius: 7px;
  padding: 0.45rem 0.85rem;
  align-self: flex-start;
  transition: filter 120ms, border-color 120ms, color 120ms, background 120ms;
}
button:hover { filter: brightness(1.05); }

/* Secondary / ghost — for actions that shouldn't shout as loud as the
   primary gold button: table row actions, cancel, reject, remove. */
.btn-ghost {
  background: transparent;
  color: var(--ink-muted);
  border-color: var(--border);
  font-weight: 500;
}
.btn-ghost:hover {
  border-color: var(--gold);
  color: var(--ink);
  filter: none;
}
.btn-danger.btn-ghost:hover {
  border-color: var(--red);
  color: var(--red);
}

.error { color: var(--red); font-size: 0.9rem; }

.shipping-fields {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin: 0.4rem 0 0;
  padding: 0;
  border: 0;
  min-width: 0;
}
.shipping-fields legend {
  padding: 0;
  margin-bottom: 0.2rem;
  font-weight: 600;
  color: var(--ink);
}
.shipping-fields .optional { font-weight: 400; font-size: 0.8rem; color: var(--ink-muted); }
.shipping-fields .hint { margin: 0; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
.field-row > label { min-width: 0; }
.field-row input { min-width: 0; width: 100%; box-sizing: border-box; }

.hint {
  font-size: 0.9rem;
  color: var(--ink-muted);
  margin-top: 0.75rem;
}

section { margin-top: 2.5rem; }
section:first-of-type { margin-top: 1.5rem; }

/* ---------- Homepage ---------- */
body.home-page { max-width: 1080px; }

.home-hero {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 0.5rem 0 2rem;
  border-bottom: 1px solid rgba(28, 20, 16, 0.08);
}
.home-logo { margin: 0; line-height: 0; flex-shrink: 0; }
.home-logo img { width: 88px; height: auto; }
.home-intro p { margin: 0; }
.home-tagline {
  font-family: var(--font-display);
  font-size: 1.55rem;
  font-weight: 500;
  line-height: 1.25;
  color: var(--ink);
  max-width: 22ch;
}
.home-status { margin-top: 0.4rem !important; font-size: 0.92rem; }
.home-status strong { color: var(--ink); font-weight: 600; }
.home-status a { font-weight: 500; }
.unlock-notice {
  margin: 0.8rem 0 0;
  padding: 0.6rem 0.85rem;
  border-left: 3px solid var(--gold);
  background: rgba(220, 147, 26, 0.08);
  border-radius: 0 8px 8px 0;
  font-size: 0.9rem;
  max-width: 34rem;
}
.unlock-card {
  display: flex;
  gap: 0.85rem;
  align-items: flex-start;
  max-width: 380px;
  margin: 1rem 0 0;
  padding: 0.9rem 1rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: linear-gradient(135deg, #fff6e2, var(--surface));
}
.unlock-card[hidden] { display: none; }
.unlock-card strong { display: block; font-family: var(--font-display); font-size: 1.1rem; line-height: 1.2; }
.unlock-card p { margin: 0.3rem 0 0; font-size: 0.9rem; color: var(--ink-muted); }
.unlock-card a { font-weight: 600; }
.unlock-card-icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  margin-top: 0.1rem;
  fill: var(--gold);
  filter: drop-shadow(0 0 6px rgba(220, 147, 26, 0.45));
}

.series-block { margin-top: 2.75rem; }
.series-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
}
.series-head h2 { margin: 0.1rem 0 0; font-size: 1.6rem; }
.series-no {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--gold);
  letter-spacing: 0.02em;
}
.series-desc { margin: 0.5rem 0 0; }
.series-total { font-size: 0.85rem; color: var(--ink-muted); padding-bottom: 0.3rem; }
.series-progress {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  padding-bottom: 0.35rem;
}
.series-bar {
  position: relative;
  width: 96px;
  height: 4px;
  border-radius: 999px;
  background: rgba(28, 20, 16, 0.1);
  overflow: hidden;
}
.series-bar > span {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--gold);
  border-radius: inherit;
}

/* Modal essences */
.modal-essences { margin-top: 0.75rem; }
.meta-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--ink-muted);
  margin-bottom: 0.35rem;
}
.essence-tags { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.essence-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.82rem;
  padding: 0.2rem 0.65rem;
  border-radius: 999px;
  background: rgba(28, 20, 16, 0.06);
  color: var(--ink);
}
.essence-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(28, 20, 16, 0.15);
}

@media (max-width: 700px) {
  .home-hero { gap: 1rem; padding-bottom: 1.5rem; }
  .home-logo img { width: 64px; }
  .home-tagline { font-size: 1.2rem; }
  .series-head h2 { font-size: 1.3rem; }
  #catalog .grid-3 { gap: 1.75rem 1rem; }
  #catalog h4 { font-size: 0.98rem; }
}

/* ---------- Candle details: cipher + first-time reveal (reveal.js) ---------- */
.candle-details { margin-top: 0.75rem; }
.candle-description { margin: 0.9rem 0 0; }

/* A cipher character: the real/stand-in character keeps the layout but is
   invisible; the glyph in data-g is drawn centred over its slot. */
.cc {
  position: relative;
  color: transparent;
}
.cc::after {
  content: attr(data-g);
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  color: var(--ink-muted);
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 0.86em;
  line-height: inherit;
}
.is-locked .cc::after {
  opacity: 0.55;
  filter: blur(0.6px);
}
.is-locked .essence-tag { background: rgba(28, 20, 16, 0.045); }
.is-locked .essence-dot { background: rgba(28, 20, 16, 0.14); }

.lock-note {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 1rem 0 0;
  font-size: 0.82rem;
  color: var(--ink-muted);
}
.lock-note svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  stroke: var(--gold);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.essence-dot { background: var(--dot, rgba(28, 20, 16, 0.14)); }

/* While revealing: cipher glyphs glow gold, each character lands with a
   brief flash, each essence dot floods with the candle's colour. */
.is-revealing .cc::after {
  color: var(--gold);
  text-shadow: 0 0 6px rgba(220, 147, 26, 0.55);
}
.cc-set { animation: cc-land 700ms ease-out; }
@keyframes cc-land {
  0%   { color: var(--gold); text-shadow: 0 0 10px rgba(220, 147, 26, 0.9); }
  100% { color: inherit; text-shadow: none; }
}
.is-revealing .essence-tag {
  opacity: 0.35;
  transform: translateY(4px) scale(0.96);
  transition: opacity 320ms ease, transform 420ms cubic-bezier(0.2, 1.4, 0.4, 1);
}
.is-revealing .essence-tag .essence-dot {
  background: rgba(28, 20, 16, 0.14);
  transition: background 500ms ease 120ms;
}
.is-revealing .essence-tag.is-lit { opacity: 1; transform: none; }
.is-revealing .essence-tag.is-lit .essence-dot {
  background: var(--dot);
  animation: dot-pulse 900ms ease-out 120ms;
}
@keyframes dot-pulse {
  0%   { box-shadow: 0 0 0 0 var(--dot); }
  100% { box-shadow: 0 0 0 9px transparent; }
}

/* Newly owned jar (never opened): a warm glow breathes behind it and a band
   of light sweeps across it. The sweep is masked with the jar photo itself,
   so it only lights the glass and wax, never the empty space around. */
.tile-photo-stack, .modal-photo { isolation: isolate; }
.jar-glow {
  position: absolute;
  inset: 4%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 220, 150, 0.95), rgba(220, 147, 26, 0.45) 42%, transparent 70%);
  pointer-events: none;
  animation: glow-breathe 2.6s ease-in-out infinite;
}
@keyframes glow-breathe {
  0%, 100% { opacity: 0.55; transform: scale(0.92); }
  50%      { opacity: 1;    transform: scale(1.08); }
}
.jar-shine {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, 0.85) 48%, rgba(255, 236, 190, 0.9) 52%, transparent 62%) no-repeat;
  background-size: 300% 100%;
  -webkit-mask: var(--jar) center / contain no-repeat;
  mask: var(--jar) center / contain no-repeat;
  mix-blend-mode: soft-light;
  animation: jar-sweep 3.2s ease-in-out infinite;
}
@keyframes jar-sweep {
  0%   { background-position: 120% 0; }
  55%, 100% { background-position: -20% 0; }
}
.modal-photo.is-new img {
  animation: jar-lift 2.6s ease-in-out infinite;
}
#catalog .tile.is-new .tile-photo {
  filter: drop-shadow(0 0 14px rgba(255, 196, 90, 0.75));
}
@keyframes jar-lift {
  0%, 100% { filter: brightness(1) drop-shadow(0 0 0 rgba(255, 196, 90, 0)); }
  50%      { filter: brightness(1.12) drop-shadow(0 0 22px rgba(255, 196, 90, 0.8)); }
}
.tile-badge.is-new {
  color: var(--gold-ink);
  background: var(--gold);
  box-shadow: 0 0 0 0 rgba(220, 147, 26, 0.6);
  animation: badge-ping 2.6s ease-out infinite;
}
@keyframes badge-ping {
  0%   { box-shadow: 0 0 0 0 rgba(220, 147, 26, 0.6); }
  70%, 100% { box-shadow: 0 0 0 8px rgba(220, 147, 26, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .cc-set, .jar-glow, .jar-shine, .modal-photo.is-new img, .tile-badge.is-new { animation: none; }
  .jar-shine { display: none; }
}

/* ---------- Special editions (specialOf() in app.js) ---------- */
/* A label beside the number in the candle's special colour (--sp), with a
   slow light glint running across it. */
.special-label {
  display: inline-block;
  margin-left: 0.35rem;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: 0.08em;
  --sp-deep: color-mix(in srgb, var(--sp, #d9441f) 82%, #000);
  color: var(--sp-ink, #fff8ec);
  background: linear-gradient(110deg, var(--sp-deep) 0%, var(--sp, #d9441f) 40%,
    color-mix(in srgb, var(--sp, #d9441f) 45%, #f6dc8a) 50%, var(--sp, #d9441f) 60%, var(--sp-deep) 100%);
  background-size: 250% 100%;
  animation: special-glint 4.5s ease-in-out infinite;
}
@keyframes special-glint {
  0%, 35% { background-position: 100% 0; }
  70%, 100% { background-position: 0 0; }
}
/* The special jar keeps a faint halo in its colour even at rest. */
#catalog .tile.is-special .tile-photo {
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--sp, #d9441f) 18%, transparent));
}

/* Snow: flakes fall over the jar while the tile is hovered, and always in
   the popup. Pale blue so they read on the cream background. */
.snow-fx {
  position: absolute;
  inset: -8% 0 12%;
  pointer-events: none;
  z-index: 2;
}
.snowflake {
  position: absolute;
  top: 0;
  aspect-ratio: 1;
  color: #9dbbd6;
  opacity: 0;
  filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.9));
}
.snowflake svg { display: block; width: 100%; height: 100%; overflow: visible; }
#catalog .tile:hover .snowflake,
#catalog .tile:focus-visible .snowflake,
#catalog .tile.is-active .snowflake,
.modal-photo .snowflake {
  animation: snow-fall var(--dur) linear var(--delay) infinite;
}
@keyframes snow-fall {
  0%   { opacity: 0; transform: translate(0, 0) rotate(0deg); }
  12%  { opacity: 0.95; }
  50%  { transform: translate(var(--drift), 45cqh) rotate(90deg); }
  80%  { opacity: 0.85; }
  100% { opacity: 0; transform: translate(0, 90cqh) rotate(180deg); }
}
.snow-fx { container-type: size; }
@media (prefers-reduced-motion: reduce) {
  .special-label { animation: none; }
  .snow-fx { display: none; }
}

/* ---------- Unlock "lighting" scene (unlock-fx.js) ----------
   Cumulative states on .unlock-fx, in order: is-dark → is-spark-1 (fails) →
   is-spark-2 (catches) → is-bead → is-lit (flame full) → is-rim (light
   catches the jar's rim) → is-glow (whole candle out of the dark) → is-text
   (number + name, ciphered) → [tap] is-decoding → is-sub → [tap] is-flying.
   is-hint shows the "Tap to …" suggestion; is-skipped = opening cut short by
   a tap. --flick (≈0.8–1.1) is set every frame by JS so the light breathes
   with the flame: never transition a property that uses it. */
@property --ufx-r {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 0%;
}
body.unlock-fx-open { overflow: hidden; }
.unlock-fx {
  --flick: 1;
  --jar-w: min(78vw, 50vh, 440px);
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: 16px;
  isolation: isolate;
  opacity: 0;
  transition: opacity 1100ms ease;
  color: #f6ead2;
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}
/* The dark is its own layer so it can lift while the jar flies home. */
.ufx-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(ellipse at 50% 40%, #1a120b 0%, #0a0704 72%);
  transition: opacity 1100ms ease 150ms;
}
.unlock-fx.is-dark { opacity: 1; }
.unlock-fx.is-leaving { opacity: 0; transition-duration: 900ms; pointer-events: none; }

.ufx-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.4rem, 2vh, 1.2rem);
  /* Slow push-in as the candle comes out of the dark. */
  scale: 0.93;
  transition: scale 6500ms cubic-bezier(0.25, 0.6, 0.2, 1), translate 900ms ease;
}
.is-rim .ufx-stage { scale: 1; }
.unlock-fx.is-leaving .ufx-stage { scale: 1.05; transition-duration: 900ms; }

.ufx-jar {
  position: relative;
  width: var(--jar-w);
  aspect-ratio: 1;
}

/* Warm light thrown by the flame onto the room; breathes with --flick. */
.ufx-light {
  position: absolute;
  left: var(--wick-x);
  top: var(--wick-y);
  width: 270%;
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 184, 92, 0.32) 0%, rgba(232, 128, 40, 0.13) 28%, rgba(120, 60, 20, 0) 60%);
  scale: 0;
  opacity: 0;
  filter: brightness(var(--flick));
  transition: scale 4200ms cubic-bezier(0.2, 0.7, 0.2, 1), opacity 1800ms ease;
  pointer-events: none;
}
.is-lit .ufx-light { scale: 0.22; opacity: 0.8; }
.is-rim .ufx-light { scale: 0.42; opacity: 0.9; }
.is-glow .ufx-light { scale: 1; opacity: 1; }

/* A pool of light where the jar stands, once it is fully lit. */
.ufx-floor {
  position: absolute;
  left: 50%;
  bottom: 6%;
  width: 90%;
  height: 16%;
  translate: -50% 0;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(255, 176, 90, 0.22), transparent 70%);
  opacity: 0;
  filter: brightness(var(--flick));
  transition: opacity 3000ms ease 1200ms;
}
.is-glow .ufx-floor { opacity: 1; }

/* The jar comes out of the dark from the flame outwards: first only the rim
   near the flame, dim and warm; then a slow widening circle of light while it
   warms from silhouette to fully lit. The wrapper carries the flicker (no
   transition, it changes every frame); the photo carries the slow states. */
.ufx-photo-wrap {
  position: absolute;
  inset: 0;
  filter: brightness(var(--flick));
}
.ufx-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  --ufx-r: 0%;
  -webkit-mask-image: radial-gradient(circle at var(--wick-x) var(--wick-y), #000 calc(var(--ufx-r) * 0.45), rgba(0, 0, 0, 0.4) calc(var(--ufx-r) * 0.75), transparent var(--ufx-r));
  mask-image: radial-gradient(circle at var(--wick-x) var(--wick-y), #000 calc(var(--ufx-r) * 0.45), rgba(0, 0, 0, 0.4) calc(var(--ufx-r) * 0.75), transparent var(--ufx-r));
  filter: brightness(0.25) sepia(0.7) saturate(1.5);
  transition: --ufx-r 2000ms cubic-bezier(0.3, 0.7, 0.2, 1), filter 2000ms ease;
}
.is-lit .ufx-photo { --ufx-r: 12%; filter: brightness(0.32) sepia(0.7) saturate(1.5); }
.is-rim .ufx-photo {
  --ufx-r: 30%;
  filter: brightness(0.5) sepia(0.55) saturate(1.4);
}
.is-glow .ufx-photo {
  --ufx-r: 170%;
  filter: brightness(1.02) sepia(0.12) saturate(1.05);
  transition: --ufx-r 4200ms cubic-bezier(0.45, 0.05, 0.25, 1), filter 4000ms ease;
}

.ufx-smoke {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 3000ms ease 1500ms;
  pointer-events: none;
}
.is-glow .ufx-smoke { opacity: 1; }
/* On the dark scene the candle-coloured smoke needs lifting to read. */
.ufx-smoke .scent-fx { filter: brightness(1.7) saturate(0.8); }

/* The wick sits inside the jar, behind the front glass: a faint copy of the
   photo, feathered around the strip between the front rim and the wood,
   lies over the flame's base so the rim and the glass haze pass in front. */
.ufx-glass {
  opacity: 0.45;
  -webkit-mask-image: radial-gradient(ellipse 4% 2.4% at var(--wick-x) 27.6%, #000 45%, transparent 100%);
  mask-image: radial-gradient(ellipse 4% 2.4% at var(--wick-x) 27.6%, #000 45%, transparent 100%);
}

/* Flame anchored on the wick: its base sits on the wick point. */
.ufx-wick {
  position: absolute;
  left: var(--wick-x);
  top: var(--wick-y);
  width: 0;
  height: 0;
}
.ufx-flame {
  position: absolute;
  left: 0;
  /* The flame's base (y≈124 of its 140 viewBox) rests on the wood. */
  bottom: calc(var(--jar-w) * -0.02);
  width: calc(var(--jar-w) * 0.075);
  height: auto;
  overflow: visible;
  translate: -50% 0;
  transform-origin: 50% 88%;
  scale: 0 0;
  opacity: 0;
  filter: drop-shadow(0 0 calc(var(--jar-w) * 0.018) rgba(255, 190, 100, 0.85));
  transition: scale 500ms ease-out, opacity 300ms ease;
}
.ufx-flame-sway { transform-origin: 30px 122px; transform-box: view-box; }
/* Born as a small blue bead, then it draws itself up into a full flame. */
.is-bead .ufx-flame { scale: 0.32 0.18; opacity: 1; }
.is-lit .ufx-flame {
  scale: 1 1;
  transition: scale 1600ms cubic-bezier(0.22, 1.2, 0.36, 1);
}

.ufx-halo {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(var(--jar-w) * 0.34);
  aspect-ratio: 1;
  translate: -50% -60%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 214, 140, 0.5) 0%, rgba(255, 160, 60, 0.16) 38%, transparent 70%);
  scale: 0;
  opacity: 0;
  filter: brightness(var(--flick));
  transition: scale 1600ms ease-out, opacity 700ms ease;
}
.is-bead .ufx-halo { scale: 0.35; opacity: 0.7; }
.is-lit .ufx-halo { scale: 1; opacity: 1; }

/* Strikes: embers burst from the wick. The first one is weak and dies with
   a brief flash; the second is bigger and leaves the flame behind. */
.ufx-sparks { position: absolute; left: 0; top: 0; }
.ufx-spark {
  position: absolute;
  left: -1.5px;
  top: -1.5px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #ffe2a0;
  box-shadow: 0 0 6px 1px rgba(255, 180, 80, 0.9);
  opacity: 0;
}
.ufx-sparks-1 .ufx-spark { width: 2px; height: 2px; }
.is-spark-1 .ufx-sparks-1 .ufx-spark { animation: ufx-spark 520ms cubic-bezier(0.1, 0.7, 0.3, 1) var(--delay) forwards; }
.is-spark-2 .ufx-sparks-2 .ufx-spark { animation: ufx-spark 900ms cubic-bezier(0.1, 0.7, 0.3, 1) var(--delay) forwards; }
@keyframes ufx-spark {
  0% { opacity: 1; transform: rotate(var(--a)) translateX(0) scale(1.5); }
  60% { opacity: 0.9; }
  100% { opacity: 0; transform: rotate(var(--a)) translateX(var(--d)) translateY(14px) scale(0.3); }
}
.ufx-wick::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: calc(var(--jar-w) * 0.12);
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 226, 160, 0.9), rgba(255, 160, 60, 0.25) 40%, transparent 70%);
  opacity: 0;
}
.is-spark-1 .ufx-wick::before { animation: ufx-flash 420ms ease-out; }
.is-spark-2 .ufx-wick::before { animation: ufx-flash 700ms ease-out 0ms; }
@keyframes ufx-flash {
  0% { opacity: 0; scale: 0.4; }
  18% { opacity: 1; scale: 1; }
  100% { opacity: 0; scale: 1.4; }
}

/* Number and name decrypt in gold under the jar. */
.ufx-text {
  text-align: center;
  opacity: 0;
  translate: 0 12px;
  transition: opacity 900ms ease, translate 1200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.is-text .ufx-text { opacity: 1; translate: 0 0; }
.ufx-no {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1rem, 3.6vw, 1.2rem);
  letter-spacing: 0.12em;
  color: var(--gold);
}
.ufx-name {
  margin: 0.1rem 0 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.8rem, 7.5vw, 2.8rem);
  line-height: 1.1;
  color: #fbf1dc;
  text-shadow: 0 0 28px rgba(255, 170, 80, 0.35);
}
.ufx-c { position: relative; color: transparent; }
.ufx-c::after {
  content: attr(data-g);
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 0.8em;
  font-weight: 400;
  color: var(--gold);
  text-shadow: 0 0 8px rgba(220, 147, 26, 0.8);
  opacity: 0.85;
}
.ufx-c.is-set { color: inherit; animation: ufx-land 1000ms ease-out; }
.ufx-c.is-set::after { content: none; }
@keyframes ufx-land {
  0% { color: #ffd27a; text-shadow: 0 0 18px rgba(255, 190, 90, 1); }
  100% { text-shadow: inherit; }
}
.ufx-sub {
  margin: 0.7rem 0 0;
  font-size: 0.78rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(246, 234, 210, 0.7);
  opacity: 0;
  transition: opacity 1200ms ease;
}
.is-sub .ufx-sub { opacity: 1; }

/* "Tap to …" hint, laid over the bottom of the scene. It only shows up if
   the person hasn't tapped by themselves after a while. */
.ufx-hint {
  position: absolute;
  left: 50%;
  bottom: calc(2.2rem + env(safe-area-inset-bottom));
  translate: -50% 8px;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: 0;
  white-space: nowrap;
  font-size: 0.74rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(251, 231, 194, 0.78);
  opacity: 0;
  pointer-events: none;
  transition: opacity 900ms ease, translate 900ms ease;
}
.is-hint .ufx-hint { opacity: 1; translate: -50% 0; }
.ufx-hint-ring {
  position: relative;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 10px rgba(220, 147, 26, 0.8);
}
.ufx-hint-ring::after {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 50%;
  border: 1.5px solid var(--gold);
  animation: ufx-ping 1.8s ease-out infinite;
}
@keyframes ufx-ping {
  0% { scale: 1; opacity: 0.9; }
  100% { scale: 3.2; opacity: 0; }
}

/* Tap 2: the dark lifts, the flame goes out, the text fades, and the jar
   (moved by JS) flies into its tile. */
.is-flying .ufx-bg { opacity: 0; }
.is-flying .ufx-text { opacity: 0 !important; transition: opacity 250ms ease !important; }
.is-flying .ufx-light,
.is-flying .ufx-floor,
.is-flying .ufx-smoke,
.is-flying .ufx-halo {
  opacity: 0 !important;
  transition: opacity 500ms ease !important;
}
.is-flying .ufx-flame {
  scale: 0.2 0 !important;
  opacity: 0 !important;
  transition: scale 450ms ease-in, opacity 450ms ease-in !important;
}
.is-flying .ufx-photo-wrap { filter: none; }
.is-flying .ufx-photo { filter: none; transition: filter 900ms ease; }
.unlock-fx.is-flying { cursor: default; pointer-events: none; }

/* Where it lands: a short pop and a gold ring in the list. */
#catalog .tile.just-added .tile-photo-stack { animation: ufx-landed 1400ms cubic-bezier(0.2, 0.8, 0.2, 1); }
@keyframes ufx-landed {
  0% { scale: 1; filter: drop-shadow(0 0 0 rgba(220, 147, 26, 0)); }
  25% { scale: 1.06; filter: drop-shadow(0 0 18px rgba(220, 147, 26, 0.75)); }
  100% { scale: 1; filter: drop-shadow(0 0 0 rgba(220, 147, 26, 0)); }
}

/* No photo of the filled jar: the flame and its light carry the moment. */
.ufx-jar.is-empty { aspect-ratio: 2 / 1; --wick-y: 75%; --wick-x: 50%; }

/* Tapped mid-way: everything settles quickly into the final scene. */
.unlock-fx.is-skipped *,
.unlock-fx.is-skipped .ufx-photo { transition-duration: 700ms !important; transition-delay: 0ms !important; }
.unlock-fx.is-skipped .ufx-spark,
.unlock-fx.is-skipped .ufx-wick::before { animation: none; opacity: 0; }

.unlock-fx.is-still,
.unlock-fx.is-still * { transition: none !important; animation: none !important; }

/* Coming soon: a dimmed, locked jar with only its number. Not a link and
   nothing happens on hover or touch. */
#catalog .tile.is-soon { cursor: default; }
#catalog .tile.is-soon .tile-photo {
  filter: grayscale(1) brightness(1.04);
  opacity: 0.38;
}
#catalog .tile.is-soon .tile-photo-stack > :not(.tile-shadow) { translate: none; }
#catalog .tile.is-soon .tile-shadow { opacity: 0.12; transform: none; }
#catalog .tile.is-soon h4 { color: var(--ink-muted); font-weight: 500; }
#catalog .tile.is-soon .spec-no { color: var(--ink-muted); }
.soon-lock {
  width: 0.95em;
  height: 0.95em;
  margin-right: 0.35em;
  vertical-align: -0.1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}

/* Invite a friend, in the account menu. */
.account-invite {
  margin: 0.25rem 0 0.75rem;
  padding: 0.7rem 0 0;
  border-top: 1px solid rgba(28, 20, 16, 0.08);
  max-width: 240px;
}
.account-menu .account-invite-title { font-weight: 600; color: var(--ink); font-size: 0.85rem; }
.account-menu .account-invite-text { font-size: 0.78rem; line-height: 1.4; margin: 0.2rem 0 0.5rem; }
.account-invite-url {
  display: block;
  padding: 0.35rem 0.5rem;
  margin-bottom: 0.45rem;
  font-size: 0.78rem;
  background: rgba(220, 147, 26, 0.1);
  border-radius: 6px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  user-select: all;
}
.account-menu .account-invite-share {
  width: 100%;
  padding: 0.45rem 0.8rem;
  font-size: 0.85rem;
}
.account-menu .account-invite-stats { font-size: 0.75rem; margin-top: 0.4rem; color: var(--ink-muted); }
.account-menu .account-invite-stats:empty { display: none; }

/* ---------- Request a candle ---------- */
.series-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.55rem;
}
.request-btn {
  align-self: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.45rem 1.05rem;
  border-radius: 999px;
  font-size: 0.84rem;
  white-space: nowrap;
}
.request-btn.is-asked {
  background: transparent;
  color: var(--ink-muted);
  border-color: var(--border);
}
.request-btn svg { width: 15px; height: 15px; fill: none; stroke: var(--gold); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }

#request-modal { z-index: 110; }
.modal-box.request-box {
  display: block;
  max-width: 480px;
  padding: 1.75rem 1.75rem 1.5rem;
}
.request-box h2 { margin: 0.15rem 0 0.9rem; }
.request-box .series-index { margin: 0; }
.request-info { font-size: 0.86rem; color: var(--ink-muted); margin: 0 0 1rem; }
.request-info.is-box {
  display: flex;
  gap: 0.65rem;
  align-items: flex-start;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.75rem 0.9rem;
  color: var(--ink);
}
.request-info.is-box p { margin: 0; }
.request-info svg { flex: 0 0 18px; width: 18px; height: 18px; margin-top: 0.1rem; fill: none; stroke: var(--gold); stroke-width: 2; stroke-linecap: round; }
.request-form { gap: 0.75rem; max-width: none; margin-top: 1.1rem; }
.request-form input, .request-form textarea { width: 100%; max-width: none; box-sizing: border-box; }
#modal-collection-actions button { align-self: auto; }
.request-form .optional { font-weight: 400; color: var(--ink-muted); font-size: 0.8rem; margin-left: 0.25rem; }
.request-form .error:empty { display: none; }
.request-lead { margin: 0 0 0.5rem; }
.request-address {
  margin: 0 0 0.75rem;
  padding: 0.7rem 0.9rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 10px;
  line-height: 1.45;
}
.request-note { margin: 0 0 0.75rem; font-style: italic; color: var(--ink-muted); }
.request-actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 0.25rem; }
.request-actions button { align-self: auto; padding: 0.55rem 1.3rem; border-radius: 999px; font-size: 0.88rem; }
@media (max-width: 700px) {
  .series-head { align-items: flex-start; flex-direction: column; gap: 0.6rem; }
  .series-side { flex-direction: row; align-items: center; justify-content: space-between; width: 100%; }
  .modal-box.request-box { padding: 1.4rem 1.1rem 1.2rem; }
}

/* Pointed out when arriving from the homepage's "Request a candle". */
.request-btn.is-hinted { animation: request-hint 1.2s ease-in-out 3; }
@keyframes request-hint { 50% { box-shadow: 0 0 0 6px rgba(220, 147, 26, 0.35); } }
@media (prefers-reduced-motion: reduce) { .request-btn.is-hinted { animation: none; box-shadow: 0 0 0 3px rgba(220, 147, 26, 0.45); } }
