/* Orbit Axis :: Tarot.
 *
 * The card is the only object on this surface. Everything around it — the
 * sections, the form, the meaning text — is interface, and interface in this
 * design system is flat: elevation is a surface change, not a shadow, and the
 * system has exactly ONE drop shadow, which belongs to objects resting on a
 * surface. The chart wheel and the Moon already carry it. A tarot card is the
 * same kind of thing, so `.o-object` is reused rather than a second shadow
 * being invented for it.
 *
 * No second palette. Tarot is the surface most likely to attract gold foil,
 * deep purples, and star fields; it gets the same Orbit Violet accent as
 * everything else, and the same rule that colour only ever means "you can act
 * on this". The card faces are typographic because there is no artwork — and
 * inventing decorative symbolism would imply an authored deck that does not
 * exist yet.
 */

/* ── Layout ────────────────────────────────────────────────────────────────
   Phone first: the card and its meaning stack. From 768px they sit side by
   side, because the card is the object and the text is what it means — the
   pairing reads better across than down once there is room for both. */
.tarot-layout {
  display: grid;
  gap: var(--space-lg);
  align-items: start;
}

@media (min-width: 768px) {
  .tarot-layout {
    grid-template-columns: minmax(0, 200px) minmax(0, 1fr);
    gap: var(--space-xl);
  }
}

.tarot-card-slot { min-width: 0; }
.tarot-reading { min-width: 0; }

/* ── The card face ─────────────────────────────────────────────────────────
   A real 2:3, the proportion every physical deck uses. `aspect-ratio` rather
   than a fixed height so it scales with the column without ever going oval. */
.tarot-card {
  /* 7:12 — the proportion of an actual tarot card (70x120mm). The frame
     was 2:3 before there was artwork to put in it; the scans disagreed. */
  aspect-ratio: 7 / 12;
  width: 100%;
  max-width: 200px;
  /* The curve of a printed card, not the curve of a UI surface.
     18px is the radius of a CARD in this design system — an interface tile —
     and it made the tarot card read as a tile with a picture in it. Square was
     the other extreme: the 1909 plates have their own rounded corners printed
     in, so a square frame left four white square corners standing proud of the
     artwork's curve.
     A physical tarot card is about 3.2mm radius on a 70mm width — roughly 4.5%
     — which at this card's 200px is 9px. The frame now follows the plate. */
  --tarot-radius: 9px;
  border-radius: var(--tarot-radius);
  border: var(--border-width) solid var(--color-border);
  background: var(--color-surface);
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--space-xs);
  padding: var(--space-md);
  text-align: center;
  /* The one object shadow. .o-object already applies it; naming it here would
     be a second definition of the same fact. */
}

.tarot-card__rank {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--color-muted);
}

.tarot-card__name {
  align-self: center;
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 1.6vw + 0.7rem, 1.4rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
  text-wrap: balance;
}

.tarot-card__suit {
  font-size: 0.8rem;
  color: var(--color-muted);
  letter-spacing: 0.04em;
}

/* The back is one local Orbit asset: it appears before every front, so it must
   never wait on Storage. Its instrument pattern is 180-degree symmetric — a
   reversed draw cannot be identified before the card is turned. */
@keyframes tarot-card-back-breathe {
  0%, 100% { background-size: 100% 100%; background-position: 50% 50%; }
  50% { background-size: 101.6% 101.6%; background-position: 50% 49.7%; }
}

@keyframes tarot-card-back-light {
  0%, 18% { opacity: 0; transform: translateX(-28%); }
  38% { opacity: 0.12; }
  58% { opacity: 0; transform: translateX(28%); }
  100% { opacity: 0; transform: translateX(28%); }
}

.tarot-card--down {
  display: grid;
  place-items: center;
  position: relative;
  overflow: hidden;
  /* It is a <button> now, so the UA's own chrome has to go before the card
     reads as a card. Everything else about it — the border, the 2:3, the one
     object shadow — is unchanged. */
  appearance: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  /* The gesture is a horizontal swipe, so the browser must not claim it for
     panning. pan-y keeps vertical scrolling, which is what a reader needs to
     get past the card. */
  touch-action: pan-y;
  background: #080a12 url("/brand/orbit-tarot-card-back.svg") center / cover no-repeat;
  animation: tarot-card-back-breathe 18s ease-in-out infinite;
}

.tarot-card--down::after {
  content: "";
  position: absolute;
  inset: -20% -45%;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(110deg,
    transparent 39%,
    rgba(185, 167, 255, 0.1) 48%,
    rgba(244, 242, 250, 0.08) 51%,
    transparent 61%);
  opacity: 0;
  animation: tarot-card-back-light 18s ease-in-out infinite;
}
.tarot-card__back {
  display: none;
  width: 58%;
  aspect-ratio: 1;
  border-radius: 50%;
  border: var(--border-width) solid var(--color-border-strong, var(--color-border));
  /* One line across the circle — the minimal celestial mark the brief allows,
     drawn in CSS so it needs no asset and inherits the theme. */
  background:
    linear-gradient(to bottom, transparent calc(50% - 0.5px),
      var(--color-border-strong, var(--color-border)) calc(50% - 0.5px),
      var(--color-border-strong, var(--color-border)) calc(50% + 0.5px),
      transparent calc(50% + 0.5px));
}

.tarot-card-loader {
  aspect-ratio: 7 / 12;
  width: 100%;
  max-width: 200px;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: 9px;
  background:
    radial-gradient(circle at 50% 45%, rgba(74, 40, 184, 0.3), transparent 58%),
    #080a12;
  border: 1px solid rgba(185, 167, 255, 0.22);
}

.tarot-card-loader__mark {
  display: block;
  width: min(62%, 112px);
  height: auto;
}

/* ── The reveal ────────────────────────────────────────────────────────────
   A short fade-and-lift. The card is replaced rather than flipped, because a
   real flip needs both faces in the DOM at once and a face-down card that
   already contains today's card in its markup is not face down in any sense a
   reader would accept. */
@keyframes tarot-reveal {
  from { opacity: 0; transform: translateY(6px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

.tarot-card--up {
  animation: tarot-reveal var(--duration-slow, 320ms) var(--ease-standard, ease-out);
}

/* Reduced motion shows the final state immediately — the card is still
   revealed, the status line still announces it, and nothing about the
   feedback is lost. Only the movement goes. */
@media (prefers-reduced-motion: reduce) {
  .tarot-card--up,
  .tarot-card--down,
  .tarot-card--down::after { animation: none; }
  .tarot-card--down::after { display: none; }
}

:root[data-motion="reduced"] .tarot-card--down,
:root[data-motion="reduced"] .tarot-card--down::after { animation: none; }
:root[data-motion="reduced"] .tarot-card--down::after { display: none; }

/* ── Meaning ───────────────────────────────────────────────────────────── */
.tarot-meaning { display: grid; gap: var(--space-xs); }

.tarot-meaning__position {
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 0;
}

.tarot-meaning__name {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2vw + 0.8rem, 1.6rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0;
}
/* The heading takes focus after a reveal, so it needs a focus ring of its own
   — it is not otherwise interactive. */
.tarot-meaning__name:focus-visible { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

.tarot-meaning__kind { margin: 0; }
.tarot-meaning__body { margin: 0; }

.tarot-meaning__prompt {
  margin: var(--space-xs) 0 0;
  padding-top: var(--space-sm);
  border-top: var(--border-width) solid var(--color-border);
}
.tarot-meaning__prompt-label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-muted);
}

/* ── States ───────────────────────────────────────────────────────────────
   Face-down, empty, and error are all flat blocks, not cards. A bordered box
   around "the deck is not ready" would be a card containing an apology. */
.tarot-facedown,
.tarot-empty,
.tarot-error { display: grid; gap: var(--space-sm); justify-items: start; }

.tarot-facedown h3,
.tarot-empty h3,
.tarot-error h3 {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  margin: 0;
}
.tarot-facedown p, .tarot-empty p, .tarot-error p { margin: 0; }

.tarot-status:empty { display: none; }
.tarot-status { margin: 0 0 var(--space-md); color: var(--color-secondary); }

.tarot-actions { margin-top: var(--space-md); display: grid; gap: var(--space-xs); justify-items: start; }
.tarot-saved { margin: 0; color: var(--color-secondary); }

/* ── The form ─────────────────────────────────────────────────────────────
   One column at every width, per the phone-form rule. The two draw buttons
   sit side by side where there is room and stack below 420px rather than
   shrinking under the 44px minimum. */
.tarot-form { display: grid; gap: var(--space-md); margin-bottom: var(--space-lg); }
.tarot-form__field { display: grid; gap: var(--space-xxs); }
.tarot-form__label { font-weight: 600; }

.tarot-form__actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
.tarot-form__actions .o-btn { min-height: 44px; }

@media (max-width: 420px) {
  .tarot-form__actions { display: grid; }
  .tarot-form__actions .o-btn { width: 100%; }
}

/* ── Spreads ──────────────────────────────────────────────────────────────
   Three cards are a vertical reading sequence, always. Compressing three full
   faces into one narrow row is what the brief rules out, and it is also just
   unreadable: at 375px each card would be under a hundred pixels wide. */
.tarot-spread__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-xl);
}
.tarot-spread__item { min-width: 0; }
.tarot-spread--three .tarot-spread__item + .tarot-spread__item {
  padding-top: var(--space-xl);
  border-top: var(--border-width) solid var(--color-border);
}

.tarot-question-echo {
  margin: 0 0 var(--space-lg);
  padding-bottom: var(--space-sm);
  border-bottom: var(--border-width) solid var(--color-border);
}
.tarot-question-echo .u-meta { display: block; }

.tarot-manual-result { margin-top: var(--space-lg); }
.tarot-save { margin-top: var(--space-lg); }

/* ── Forced colors ────────────────────────────────────────────────────────
   In forced-colors mode every background and shadow is discarded, so the card
   would dissolve into the page. A real border keeps it an object, and the
   accent-coloured position label keeps its meaning through weight rather than
   through a colour that no longer exists. */
@media (forced-colors: active) {
  .tarot-card {
    border: 1px solid CanvasText;
    forced-color-adjust: none;
    background: Canvas;
    color: CanvasText;
  }
  .tarot-card__back { display: block; border-color: CanvasText; background: none; }
  .tarot-meaning__position { color: CanvasText; }
}

/* ── Tarot history ────────────────────────────────────────────────────────
   Divided rows, like every other collection in the app. A saved reflection is
   a record, not an object, so it gets no card treatment and no shadow. */
.tarot-history__row { padding: var(--space-md) 0; display: grid; gap: var(--space-xxs); }
.tarot-history__head {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  align-items: baseline;
  justify-content: space-between;
}
.tarot-history__kind { font-weight: 600; }
.tarot-history__question { margin: 0; color: var(--color-secondary); }
.tarot-history__cards { margin: 0; }


/* The face-down card is interactive, so it needs the states an interactive
   thing has. Hover and active are deliberately small: it is a card being
   turned, not a button being pressed. */
.tarot-card--down:hover { border-color: var(--color-border-strong, var(--color-border)); }
.tarot-card--down:active { transform: scale(0.99); }
.tarot-card--down:focus-visible { box-shadow: var(--focus-ring); }

@media (prefers-reduced-motion: reduce) {
  .tarot-card--down:active { transform: none; }
}

.tarot-hint { margin: 0 0 var(--space-sm); color: var(--color-secondary); }

/* ── The three-card carousel ──────────────────────────────────────────────
   One card at a time, the next partially visible, swiped horizontally.

   Scroll-snap rather than a gesture handler: the browser owns the physics,
   momentum and rubber-banding, and there is no drag threshold to tune or drag
   state to leave stuck. It is also keyboard-scrollable and screen-reader
   navigable for free, which a JS drag implementation is not.

   The list stays an <ol> underneath, because the order is the meaning. */
.tarot-spread__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-md);
}
.tarot-spread__label { margin: 0; font-weight: 600; }
.tarot-spread__loading { display: grid; gap: var(--space-md); grid-auto-flow: column; overflow: hidden; }

.tarot-carousel {
  list-style: none;
  margin: 0;
  padding: 0 0 var(--space-sm);
  display: grid;
  grid-auto-flow: column;
  /* 86% leaves the next card's edge showing, which is what says "there is
     more" without a row of dots having to carry that alone. */
  grid-auto-columns: 86%;
  gap: var(--space-md);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.tarot-carousel::-webkit-scrollbar { display: none; }
.tarot-carousel:focus-visible { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

.tarot-carousel__item {
  scroll-snap-align: start;
  min-width: 0;
  display: grid;
  gap: var(--space-sm);
  align-content: start;
}

.tarot-carousel__step {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--color-muted);
}

.tarot-meaning__step { font-weight: 400; color: var(--color-muted); }

/* Dots are real buttons with real names, and they are 44px targets even though
   the painted dot is small — the hit area is the control, not the ink. */
.tarot-carousel__dots {
  display: flex;
  justify-content: center;
  gap: var(--space-xxs);
  margin-top: var(--space-xs);
}
.tarot-carousel__dot {
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.tarot-carousel__dot::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-border-strong, var(--color-border));
  transition: background var(--duration-fast, 150ms) var(--ease-standard, ease);
}
.tarot-carousel__dot.is-current::before { background: var(--color-accent); }
.tarot-carousel__dot:focus-visible { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

@media (prefers-reduced-motion: reduce) {
  .tarot-carousel { scroll-behavior: auto; }
  .tarot-carousel__dot::before { transition: none; }
}

/* Above the phone width there is room for all three side by side, and a
   carousel that fits entirely on screen is just a list with extra steps. */
@media (min-width: 900px) {
  .tarot-carousel {
    grid-auto-columns: minmax(0, 1fr);
    overflow-x: visible;
    scroll-snap-type: none;
  }
  .tarot-carousel__dots { display: none; }
}

/* Forced colors: the current dot cannot rely on the accent, which is discarded. */
@media (forced-colors: active) {
  .tarot-carousel__dot::before { background: CanvasText; }
  .tarot-carousel__dot.is-current::before {
    background: Highlight;
    outline: 1px solid CanvasText;
  }
}

/* ── Card artwork ─────────────────────────────────────────────────────────
   The image fills the 2:3 frame the typographic face already defines. The
   text stays in the markup underneath it: when the image fails, onerror drops
   .tarot-card--art and the words are simply there — no swap, no second render,
   and no broken-image frame. */
.tarot-card__art {
  display: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}
.tarot-card--art { position: relative; padding: 0; }
.tarot-card--art .tarot-card__art { display: block; }
/* With artwork present the words are decoration the image already carries, so
   they are hidden visually — they remain in the DOM only as the fallback the
   onerror path reveals. Every one of them is repeated as real text beside the
   card regardless, which is what the accessibility rule actually rests on. */
.tarot-card--art .tarot-card__rank,
.tarot-card--art .tarot-card__name,
.tarot-card--art .tarot-card__suit { display: none; }

/* ── The meaning, offered rather than pushed ──────────────────────────────
   The text animates in when the reader asks for it: a short rise and fade,
   which reads as the answer arriving rather than the page redrawing. Only the
   revealed state animates — the button that requested it is already gone. */
@keyframes tarot-meaning-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

.tarot-meaning--shown .tarot-meaning__body,
.tarot-meaning--shown .tarot-meaning__prompt {
  animation: tarot-meaning-in var(--duration-slow, 320ms) var(--ease-standard, ease-out) both;
}
/* The prompt follows the meaning rather than arriving with it, so the two read
   in order instead of as one block appearing. */
.tarot-meaning--shown .tarot-meaning__prompt { animation-delay: 90ms; }

.tarot-meaning__body:focus-visible,
.tarot-meaning__prompt:focus-visible { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

.tarot-meaning__ask { margin-top: var(--space-xs); min-height: 44px; }

/* Reduced motion shows the text immediately. The state change is carried by
   the text being there and by the live region, not by the movement. */
@media (prefers-reduced-motion: reduce) {
  .tarot-meaning--shown .tarot-meaning__body,
  .tarot-meaning--shown .tarot-meaning__prompt { animation: none; }
  /* The card tilt (Dev Update 4.3) collapses here too. */
  .tarot-card { transform: none; transition: none; }
  .tarot-card--reversed { transform: rotate(180deg); }
}

/* ── Reversed ─────────────────────────────────────────────────────────────
   The card is turned, which is what a reversed card looks like on a table.
   The rotation is NEVER the only signal: "Reversed" is stated in text beside
   the card, because an upside-down illustration is not something a screen
   reader can report and not something everyone will notice. */
.tarot-card--reversed { transform: rotate(180deg); }
.tarot-meaning__reversed { font-weight: 600; color: var(--color-secondary); }

@media (forced-colors: active) {
  .tarot-meaning__reversed { color: CanvasText; }
}


/* ── Centring ─────────────────────────────────────────────────────────────
   The card is the thing on the page, so it sits in the middle of it rather
   than tucked against the left margin. The text stays left-aligned: centred
   body copy is harder to read, and the card being centred is what was asked
   for, not the paragraph under it. */
.tarot-card-slot { display: flex; justify-content: center; }
.tarot-card,
.tarot-card-loader { margin-inline: auto; }

/* Above the phone width the card and its meaning sit side by side, and there
   the card belongs beside its text rather than adrift in a wide column. */
@media (min-width: 768px) {
  .tarot-card-slot { justify-content: flex-start; }
  .tarot-card, .tarot-card-loader { margin-inline: 0; }
}

/* The carousel centres its cards too, and each item's text is centred under
   the card only for the step label — the meaning stays readable. */
.tarot-carousel__step { text-align: center; }

/* ── Card motion (Dev Update 4.3) ─────────────────────────────────────────
   The tilt lives in two custom properties the sensor code writes on the
   panel; every card composes them into its own transform. Perspective sits on
   the slot so the rotation reads as depth rather than skew. A reversed card
   keeps its 180° and tilts within it. The :active press states above still
   override while pressed, which is correct — a press is feedback, and it wins.

   Under either reduced-motion refusal the transform collapses and the
   transition goes with it. The JavaScript never attaches the sensor in those
   states either — this is the belt to that suspender. */
.tarot-card-slot { perspective: 700px; }
.tarot-card {
  transform: rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transition: transform 120ms var(--ease-standard);
}
.tarot-card--reversed {
  transform: rotate(180deg) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
}
:root[data-motion="reduced"] .tarot-card { transform: none; transition: none; }
:root[data-motion="reduced"] .tarot-card--reversed { transform: rotate(180deg); }
