/* ============================================================
   FEEDBACK — messages, empty states, loading, Yuna's voice
   ============================================================ */

/* --- Messages ----------------------------------------------- */

.messages {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}

.message {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-sm);
    background: var(--bg-surface);
    border: var(--border-width) solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius-md);
}

.message--success { border-left-color: var(--success); }
.message--error   { border-left-color: var(--danger); }
.message--warning { border-left-color: var(--warning); }
.message--info    { border-left-color: var(--info); }

/* --- Empty state --------------------------------------------- */

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-7) var(--space-4);
    text-align: center;
}

.empty-state__avatar {
    width: 112px;
    height: 112px;
    object-fit: contain;
    opacity: 0.9;
}

.empty-state__title {
    font-size: var(--text-lg);
    margin: 0;
}

.empty-state__text {
    max-width: 40ch;
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin: 0;
}

/* --- Loading -------------------------------------------------
   Yuna while she works: she bobs, a ring of moonlight turns
   behind her and four stars come and go. Every piece animates
   `transform` and `opacity` only, so a long wait costs nothing.
   -------------------------------------------------------------- */

.loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-7) var(--space-4);
    text-align: center;
}

/* Over the whole window, for the wait after a form is sent. */
.loading--overlay {
    position: fixed;
    inset: 0;
    /* Above the navbar. Below it, the menu stayed clickable while Yuna
       shuffled and a second navigation could start on top of the request
       already in flight. */
    z-index: var(--z-overlay);
    justify-content: center;
    background: color-mix(in srgb, var(--bg-page) 86%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    animation: loading-appear var(--duration-base) var(--ease-out) both;
}

@keyframes loading-appear {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.loading__stage {
    position: relative;
    display: grid;
    place-items: center;
    width: 150px;
    height: 150px;
}

.loading__avatar {
    position: relative;
    z-index: 1;
    width: 96px;
    height: 96px;
    object-fit: contain;
    filter: drop-shadow(0 6px 14px color-mix(in srgb, var(--secondary) 45%, transparent));
    animation: loading-bob 2.8s var(--ease-in-out) infinite;
}

/* She floats rather than sits: a small rise and fall, nothing acrobatic. */
@keyframes loading-bob {
    0%, 100% { transform: translateY(0) rotate(-1.5deg); }
    50%      { transform: translateY(-9px) rotate(1.5deg); }
}

/* A ring of moonlight, open at one side so the turn is visible. */
.loading__ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid color-mix(in srgb, var(--gold) 38%, transparent);
    border-right-color: transparent;
    border-bottom-color: transparent;
    animation: loading-turn 3.2s linear infinite;
}

.loading__ring::after {
    content: "";
    position: absolute;
    inset: 14px;
    border-radius: 50%;
    border: 1px solid color-mix(in srgb, var(--secondary) 32%, transparent);
    border-left-color: transparent;
    border-top-color: transparent;
    /* The inner ring turns the other way, which reads as mechanism. */
    animation: loading-turn 4.6s linear infinite reverse;
}

@keyframes loading-turn {
    to { transform: rotate(360deg); }
}

/* Four stars around her, breathing out of step with the bob. */
.loading__sparks {
    position: absolute;
    inset: -6px;
    pointer-events: none;
    background-color: var(--gold-bright);
    mask-image: var(--sparkle-star), var(--sparkle-star),
                var(--sparkle-star), var(--sparkle-star);
    -webkit-mask-image: var(--sparkle-star), var(--sparkle-star),
                        var(--sparkle-star), var(--sparkle-star);
    mask-repeat: no-repeat;
    -webkit-mask-repeat: no-repeat;
    mask-position: 6% 20%, 92% 30%, 16% 84%, 86% 78%;
    -webkit-mask-position: 6% 20%, 92% 30%, 16% 84%, 86% 78%;
    mask-size: 15px, 11px, 10px, 13px;
    -webkit-mask-size: 15px, 11px, 10px, 13px;
    animation: loading-twinkle 2.1s var(--ease-in-out) infinite;
}

@keyframes loading-twinkle {
    0%, 100% { opacity: 0.35; transform: scale(0.92) rotate(-5deg); }
    50%      { opacity: 1;    transform: scale(1.05) rotate(5deg); }
}

.loading__text {
    max-width: 30ch;
    font-size: var(--text-base);
    font-family: var(--font-display);
    color: var(--text-secondary);
}

.loading__dots::after {
    content: "";
    animation: dots 1.4s steps(4, end) infinite;
}

/* El punto medio va escrito tal cual, no como escape CSS. Escrito como
   escape, la barra invertida se perdio al generar el fichero y en su
   lugar quedo un byte nulo dentro del CSS: el navegador pintaba un
   rombo con una interrogacion detras del texto de carga, y grep daba
   el fichero por binario. En UTF-8 el caracter no necesita escaparse. */
@keyframes dots {
    0%   { content: ""; }
    25%  { content: "·"; }
    50%  { content: "··"; }
    75%  { content: "···"; }
}

/* Nothing disappears; it simply holds still. */
@media (prefers-reduced-motion: reduce) {
    .loading__avatar,
    .loading__ring,
    .loading__ring::after,
    .loading__sparks,
    .loading--overlay {
        animation: none;
    }

    .loading__dots::after {
        content: "...";
    }
}


/* ============================================================
   YUNA'S VOICE — the interpretation block
   ============================================================ */

.yuna-speech {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
    padding: var(--space-5);
    background: var(--bg-surface);
    border: var(--border-width) solid var(--border-accent);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
}

@media (min-width: 640px) {
    .yuna-speech {
        grid-template-columns: auto 1fr;
        align-items: start;
        gap: var(--space-5);
    }
}

.yuna-speech__avatar {
    width: 80px;
    height: 80px;
    object-fit: contain;
    justify-self: center;
    filter: drop-shadow(var(--glow-moon));
}

.yuna-speech__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
}

.yuna-speech__name {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    color: var(--text-primary);
    margin: 0;
}

.yuna-speech__text {
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    color: var(--text-secondary);
    margin: 0;
    text-wrap: pretty;
    /* Preserves the model's paragraph breaks without rendering its markup. */
    white-space: pre-line;
}

/* reading.js appends the card paragraphs here; without this they ran together
   with no space between. */
.yuna-speech__paragraphs {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* --- Gold-ruled quote ---------------------------------------------
   One pull-quote shared by the reading's highlights and the
   horoscope's advice: a gold rule, not a filled slab. The cream block
   dominated the panel and left a gap between its label and its text.
   ------------------------------------------------------------------ */

/* ------------------------------------------------------------
   LA CITA DORADA
   «Lectura conjunta» y «Consejo de Yuna»: lo que cierra una
   lectura, y lo unico que mucha gente se lleva de ella.

   Antes era un recuadro con una barra a la izquierda y un arco
   de circulo asomando por la esquina superior derecha. El arco
   estaba recortado por el `overflow` del propio bloque, asi que
   no se leia como adorno sino como una linea que sobraba. Fuera.

   Ahora el bloque se distingue por tres cosas que si dicen algo:
   el borde de oro a la izquierda, una comilla enorme de fondo
   que lo marca como cita, y un titulo con icono.
   ------------------------------------------------------------ */

.quote-gold {
    position: relative;
    overflow: hidden;
    margin: 0;
    padding: var(--space-5);
    background:
        linear-gradient(
            100deg,
            color-mix(in srgb, var(--gold) 7%, transparent),
            transparent 46%
        ),
        var(--surface-celestial);
    border: var(--border-width) solid var(--border);
    /* El oro entra por la izquierda y se apaga hacia abajo, en vez de ser
       una barra plana de tres pixeles. */
    border-left: 3px solid transparent;
    border-image: linear-gradient(
        180deg,
        var(--gold-bright),
        color-mix(in srgb, var(--gold) 35%, transparent)
    ) 1 / 0 0 0 3px;
    border-radius: var(--radius-md);
    box-shadow: var(--ring-gold), var(--shadow-sm);
}

/* La comilla de fondo. Decorativa pura, asi que va en un pseudoelemento
   y no en el texto: un lector de pantalla no tiene por que leerla. */
.quote-gold::before {
    content: "\201C";
    position: absolute;
    /* Dentro del bloque, no asomando por arriba: el `overflow: hidden` que
       redondea la esquina recortaba la comilla y solo dejaba ver las dos
       puntas, que parecian suciedad. */
    top: 0.1rem;
    right: 0.4rem;
    font-family: var(--font-display);
    font-size: 5.5rem;
    line-height: 1;
    color: color-mix(in srgb, var(--gold) 12%, transparent);
    pointer-events: none;
    user-select: none;
}

@media (max-width: 559px) {
    .quote-gold {
        padding: var(--space-4);
    }

    .quote-gold::before {
        font-size: 5rem;
    }
}

.quote-gold__label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    position: relative;
    margin-bottom: var(--space-2);
    padding-bottom: var(--space-2);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--accent-ink);
    border-bottom: 1px solid color-mix(in srgb, var(--gold) 22%, transparent);
}

/* The body font, matching the paragraphs it sits among: the display serif at
   --text-lg was built for a one-line pull quote, and the joint reading runs to
   three lines of prose, so the panel read as two different voices. The gold
   rule and the label are what set the quote apart. */
.quote-gold__label .material-symbols-rounded {
    font-size: 1.1rem;
}

.quote-gold__text {
    display: block;
    position: relative;
    margin: 0;
    font-size: var(--text-base);
    line-height: var(--leading-normal);
    color: var(--text-primary);
    text-wrap: pretty;
}

/* Shown when the offline interpreter produced the text. */
.yuna-speech__notice {
    font-size: var(--text-xs);
    color: var(--text-muted);
    font-style: italic;
}

/* --- Disclaimer ---------------------------------------------- */

.disclaimer {
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-xs);
    color: var(--text-muted);
    background: var(--bg-surface-sunken);
    border-radius: var(--radius-md);
    text-align: center;
}

/* ============================================================
   COOKIE CONSENT
   A band at the foot of the window, not a blocking overlay: it
   asks without taking the page hostage.
   ============================================================ */

.cookie-consent {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: var(--z-nav);
    padding: var(--space-3) var(--page-gutter);
    background: var(--surface-celestial-strong);
    border-top: var(--border-width) solid var(--border-accent);
    box-shadow: var(--ring-gold-strong), var(--shadow-lg);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    animation: consent-rise var(--duration-slow) var(--ease-out) both;
}

@keyframes consent-rise {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .cookie-consent {
        animation: none;
    }
}

.cookie-consent__inner {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    align-items: center;
    width: 100%;
    max-width: var(--content-max);
    margin-inline: auto;
    text-align: center;
}

@media (min-width: 768px) {
    .cookie-consent__inner {
        flex-direction: row;
        justify-content: space-between;
        text-align: left;
    }
}

.cookie-consent__text {
    margin: 0;
    max-width: 68ch;
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    color: var(--text-secondary);
    text-wrap: pretty;
}

.cookie-consent__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    justify-content: center;
    flex-shrink: 0;
}

/* ============================================================
   SHUFFLING
   Five backs out of a stack: each lifts, arcs across and drops
   back in, a fifth of a cycle apart. The stack never empties,
   so it reads as one deck rather than five cards.
   ============================================================ */

.shuffle {
    position: relative;
    display: grid;
    place-items: center;
    width: 160px;
    height: 170px;
}

.shuffle__card {
    position: absolute;
    width: 90px;
    height: auto;
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    transform-origin: 50% 120%;
    animation: shuffle-riffle 2s var(--ease-in-out) infinite;
    /* A fifth of the cycle between each, so one is always in the air. */
    animation-delay: calc(var(--card-index) * -0.4s);
}

@keyframes shuffle-riffle {
    0%   { transform: translate3d(0, 0, 0) rotate(0deg); }
    25%  { transform: translate3d(42px, -16px, 0) rotate(9deg); }
    50%  { transform: translate3d(0, -26px, 0) rotate(0deg); }
    75%  { transform: translate3d(-42px, -16px, 0) rotate(-9deg); }
    100% { transform: translate3d(0, 0, 0) rotate(0deg); }
}

/* Held still, the stack still reads as a deck. */
@media (prefers-reduced-motion: reduce) {
    .shuffle__card {
        animation: none;
    }

    .shuffle__card:nth-child(2) { transform: translate3d(10px, -4px, 0) rotate(4deg); }
    .shuffle__card:nth-child(3) { transform: translate3d(-10px, -8px, 0) rotate(-4deg); }
    .shuffle__card:nth-child(4) { transform: translate3d(16px, -12px, 0) rotate(7deg); }
    .shuffle__card:nth-child(5) { transform: translate3d(-16px, -16px, 0) rotate(-7deg); }
}
