/**
 * mod_lwmemory — Laserworld product memory game
 *
 * Everything is scoped under .lwmemory and every custom property is namespaced
 * --lwm-*, so the module can drop into any template without colliding with it.
 * Bootstrap 5 is used for the product grid only (.row / .row-cols-* / .col),
 * which every Joomla 5 and 6 template already ships.
 *
 * @license GNU General Public License version 2 or later
 */

/* Registering the aim angle as a real <angle> is what lets it be animated by
   CSS at all — an unregistered custom property jumps between keyframes instead
   of interpolating. The script writes the same property directly. */
@property --lwm-aim {
    syntax: "<angle>";
    inherits: true;
    initial-value: 0deg;
}

/* ── Tokens ──────────────────────────────────────────────────────────── */

.lwmemory {
    --lwm-max: 1500px;
    --lwm-cols: 4;
    --lwm-gap: clamp(0.4rem, 0.9vw, 0.9rem);
    --lwm-radius: 14px;
    --lwm-radius-sm: 10px;

    --lwm-accent: #ff2d9b;
    --lwm-accent-2: #22d3ee;
    --lwm-accent-3: #7c5cff;

    --lwm-bg: #0b0d1a;
    --lwm-bg-2: #141834;
    --lwm-panel: rgba(255, 255, 255, 0.05);
    --lwm-panel-line: rgba(255, 255, 255, 0.12);
    --lwm-text: #f2f4ff;
    --lwm-muted: rgba(242, 244, 255, 0.62);
    --lwm-tile: #ffffff;
    --lwm-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.9);

    max-width: var(--lwm-max);
    margin-inline: auto;
    color: var(--lwm-text);
    container-type: inline-size;
}

.lwmemory--light {
    --lwm-bg: #f4f6fb;
    --lwm-bg-2: #e6ebf7;
    --lwm-panel: rgba(12, 16, 40, 0.04);
    --lwm-panel-line: rgba(12, 16, 40, 0.12);
    --lwm-text: #101427;
    --lwm-muted: rgba(16, 20, 39, 0.6);
    --lwm-tile: #ffffff;
    --lwm-shadow: 0 16px 36px -20px rgba(16, 20, 39, 0.5);
}

@media (prefers-color-scheme: light) {
    .lwmemory--auto {
        --lwm-bg: #f4f6fb;
        --lwm-bg-2: #e6ebf7;
        --lwm-panel: rgba(12, 16, 40, 0.04);
        --lwm-panel-line: rgba(12, 16, 40, 0.12);
        --lwm-text: #101427;
        --lwm-muted: rgba(16, 20, 39, 0.6);
        --lwm-shadow: 0 16px 36px -20px rgba(16, 20, 39, 0.5);
    }
}

/* ── Shell ───────────────────────────────────────────────────────────── */

.lwmemory__inner {
    position: relative;
    padding: clamp(0.9rem, 2.2vw, 1.75rem);
    border-radius: calc(var(--lwm-radius) + 6px);
    border: 1px solid var(--lwm-panel-line);
    background:
        radial-gradient(90% 120% at 12% -10%, rgba(255, 45, 155, 0.22), transparent 60%),
        radial-gradient(80% 110% at 92% 0%, rgba(34, 211, 238, 0.18), transparent 58%),
        linear-gradient(170deg, var(--lwm-bg-2), var(--lwm-bg));
    box-shadow: var(--lwm-shadow);
    overflow: hidden;
}

.lwmemory__inner::after {
    /* A single hairline of light along the top edge — the only decoration that
       is not a gradient, and the thing that makes the panel read as glass. */
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
    pointer-events: none;
}

/* ── Top bar ─────────────────────────────────────────────────────────── */

.lwmemory__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1.25rem;
    margin-bottom: clamp(0.8rem, 1.8vw, 1.35rem);
}

.lwmemory__heading {
    flex: 1 1 100%;
    margin: 0;
    font-size: clamp(1.15rem, 2.4vw, 1.6rem);
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.2;
}

.lwmemory__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
}

.lwmemory__field {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
}

.lwmemory__field-label {
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--lwm-muted);
}

.lwmemory__select {
    appearance: none;
    padding: 0.45rem 2rem 0.45rem 0.75rem;
    border-radius: 999px;
    border: 1px solid var(--lwm-panel-line);
    background-color: var(--lwm-panel);
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: calc(100% - 15px) 52%, calc(100% - 10px) 52%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    color: inherit;
    font: inherit;
    font-size: 0.9rem;
    line-height: 1.3;
    cursor: pointer;
}

.lwmemory__select option {
    color: #101427;
    background: #fff;
}

.lwmemory__button {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.48rem 1.05rem;
    border-radius: 999px;
    border: 1px solid var(--lwm-panel-line);
    background: var(--lwm-panel);
    color: inherit;
    font: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    line-height: 1.3;
    cursor: pointer;
    transition: transform 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.lwmemory__button:hover {
    border-color: color-mix(in srgb, var(--lwm-accent) 55%, transparent);
    transform: translateY(-1px);
}

.lwmemory__button:active {
    transform: translateY(0);
}

.lwmemory__button--primary {
    border-color: transparent;
    background: linear-gradient(120deg, var(--lwm-accent), var(--lwm-accent-3) 60%, var(--lwm-accent-2));
    color: #fff;
    padding-inline: 1.5rem;
}

.lwmemory__button-icon {
    width: 1em;
    height: 1em;
    flex: none;
}

.lwmemory__stats {
    display: flex;
    gap: clamp(0.75rem, 2vw, 1.75rem);
    margin: 0 0 0 auto;
    padding: 0;
}

.lwmemory__stat {
    text-align: right;
}

.lwmemory__stat dt {
    margin: 0;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--lwm-muted);
}

.lwmemory__stat dd {
    margin: 0;
    font-size: clamp(1.05rem, 2vw, 1.35rem);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.15;
}

/* ── Board ───────────────────────────────────────────────────────────── */

.lwmemory__boardwrap {
    position: relative;
}

.lwmemory__board {
    display: grid;
    grid-template-columns: repeat(var(--lwm-cols), minmax(0, 1fr));
    gap: var(--lwm-gap);
    margin: 0;
    padding: 0;
    list-style: none;
}

.lwmemory__cell {
    margin: 0;
    padding: 0;
    list-style: none;
}

.lwmemory__card {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    padding: 0;
    border: 0;
    border-radius: var(--lwm-radius);
    background: transparent;
    cursor: pointer;
    transform-style: preserve-3d;
    transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.3, 1);
    -webkit-tap-highlight-color: transparent;
}

.lwmemory__card:focus-visible {
    outline: 2px solid var(--lwm-accent-2);
    outline-offset: 3px;
}

.lwmemory__card.is-flipped,
.lwmemory__card.is-matched {
    transform: rotateY(180deg);
}

.lwmemory__face {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--lwm-radius);
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    overflow: hidden;
}

.lwmemory__face--back {
    /* Dark first, colour second. A board is sixteen of these at once, so the
       card back has to survive being repeated: the atmosphere lives here, in
       the background, and never on the beams themselves. */
    background:
        radial-gradient(120% 95% at 50% 118%, rgba(255, 45, 155, 0.5), transparent 62%),
        radial-gradient(95% 80% at 8% -10%, rgba(124, 92, 255, 0.45), transparent 60%),
        radial-gradient(85% 75% at 100% 0%, rgba(34, 211, 238, 0.22), transparent 58%),
        linear-gradient(165deg, #241a4d, #150f2e 55%, #0d0a1e);
    border: 1px solid rgba(255, 255, 255, 0.14);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.22),
        inset 0 0 0 5px rgba(255, 255, 255, 0.05),
        inset 0 0 0 6px rgba(255, 255, 255, 0.07);
}

.lwmemory__face--back::before {
    /* Fine diagonal rules, the way a printed card back is guilloched. */
    content: "";
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(
        115deg,
        rgba(255, 255, 255, 0.045) 0 1px,
        transparent 1px 8px
    );
}

/* ── Beams ───────────────────────────────────────────────────────────────
   Every face-down card is a little projector aimed at the pointer. The fan
   is four crisp hairlines pivoting from one aperture near the bottom of the
   card — hard-edged on purpose: a real beam in air is a line, and softening
   it reads as fog rather than laser. The angle arrives as --lwm-aim, set on
   the card by the script; CSS derives each beam's own angle from it, so one
   property write moves the whole fan.
   ------------------------------------------------------------------------ */

.lwmemory__beams {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    overflow: hidden;
    pointer-events: none;
}

.lwmemory__beam {
    position: absolute;
    left: 50%;
    bottom: 13%;
    width: 1.25px;
    height: 155%;
    transform-origin: 50% 100%;
    transform: translateX(-50%) rotate(calc(var(--lwm-aim, 0deg) + var(--lwm-fan, 0deg)));
    background: linear-gradient(
        0deg,
        rgba(255, 255, 255, 0.95) 0%,
        rgba(255, 255, 255, 0.38) 24%,
        rgba(255, 255, 255, 0.1) 48%,
        rgba(255, 255, 255, 0) 74%
    );
}

.lwmemory__beam:nth-child(1) { --lwm-fan: -20deg; opacity: 0.38; }
.lwmemory__beam:nth-child(2) { --lwm-fan: -7deg;  opacity: 0.72; }
.lwmemory__beam:nth-child(3) { --lwm-fan: 7deg;   opacity: 0.72; }
.lwmemory__beam:nth-child(4) { --lwm-fan: 20deg;  opacity: 0.38; }

/* The aperture: a wash of accent light on the housing, and the emitter itself. */
.lwmemory__beams::before,
.lwmemory__beams::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 13%;
}

.lwmemory__beams::before {
    width: 62%;
    aspect-ratio: 5 / 2;
    transform: translate(-50%, 42%);
    background: radial-gradient(
        closest-side,
        color-mix(in srgb, var(--lwm-accent-2) 55%, #fff) 0%,
        rgba(255, 255, 255, 0) 72%
    );
    opacity: 0.5;
}

.lwmemory__beams::after {
    width: 5px;
    height: 5px;
    margin: 0 0 -2px;
    border-radius: 50%;
    transform: translate(-50%, 50%);
    background: #fff;
    box-shadow: 0 0 9px 2px rgba(255, 255, 255, 0.55);
}

/* A hovered card leans in. */
.lwmemory__card:not(.is-matched):not(.is-flipped):hover .lwmemory__beam {
    opacity: 1;
}

.lwmemory__card:not(.is-matched):not(.is-flipped):hover .lwmemory__beams::before {
    opacity: 0.85;
}

/* Touch screens have no cursor to follow, so the fans sweep on their own.
   This is pure CSS on purpose: it costs nothing per frame on a phone, where
   running a script loop over thirty-six cards would be felt. The script adds
   .lwmemory--sweep when it decides the pointer is not a fine one. */
@keyframes lwm-sweep {
    0%, 100% { --lwm-aim: -21deg; }
    50%      { --lwm-aim: 21deg; }
}

.lwmemory--sweep .lwmemory__card {
    animation: lwm-sweep 7s ease-in-out infinite;
}

/* A small stagger so the sweep travels across the board rather than moving as
   one block. Four phases is enough to read as a wave at any board size. */
.lwmemory--sweep .lwmemory__cell:nth-child(4n + 2) .lwmemory__card { animation-delay: -0.85s; }
.lwmemory--sweep .lwmemory__cell:nth-child(4n + 3) .lwmemory__card { animation-delay: -1.7s; }
.lwmemory--sweep .lwmemory__cell:nth-child(4n + 4) .lwmemory__card { animation-delay: -2.55s; }

/* Small cards get the two inner beams only — four hairlines on a 36-card
   board is clutter rather than a fan. */
.lwmemory__cell {
    container-type: inline-size;
}

@container (max-width: 150px) {
    .lwmemory__beam:nth-child(1),
    .lwmemory__beam:nth-child(4) {
        display: none;
    }

    .lwmemory__beam:nth-child(2) { --lwm-fan: -5deg; }
    .lwmemory__beam:nth-child(3) { --lwm-fan: 5deg; }
}

.lwmemory__face--front {
    flex-direction: column;
    gap: 0.25rem;
    padding: 6% 6% 4%;
    background: var(--lwm-tile);
    border: 1px solid rgba(12, 16, 40, 0.1);
    transform: rotateY(180deg);
}

.lwmemory__card-img {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    object-fit: contain;
    user-select: none;
}

.lwmemory__card-name {
    flex: none;
    max-width: 100%;
    /* cqw is the cell's width — the card's own container — so the label grows
       with the card instead of with the module. */
    font-size: clamp(0.5rem, 4.2cqw, 0.78rem);
    font-weight: 600;
    line-height: 1.15;
    text-align: center;
    color: #35406b;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    line-clamp: 1;
    -webkit-box-orient: vertical;
}

/* Cards that are still face down invite a click; matched ones stop. */
.lwmemory__card.is-matched {
    cursor: default;
}

/* A settled pair steps back: the ring says "found", the fade keeps the eye on
   the cards still in play. */
.lwmemory__card.is-matched .lwmemory__face--front {
    border-color: transparent;
    opacity: 0.68;
    box-shadow:
        0 0 0 2px color-mix(in srgb, var(--lwm-accent-2) 85%, transparent),
        0 0 26px -6px var(--lwm-accent-2);
    transition: opacity 0.35s ease 0.15s;
}

.lwmemory__card.is-matched .lwmemory__card-name {
    color: #4b5a8c;
}

.lwmemory__card.is-miss .lwmemory__face--front {
    animation: lwm-shake 0.4s ease;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--lwm-accent) 70%, transparent);
}

@keyframes lwm-shake {
    0%, 100% { transform: rotateY(180deg) translateX(0); }
    25%      { transform: rotateY(180deg) translateX(-4px); }
    75%      { transform: rotateY(180deg) translateX(4px); }
}

.lwmemory__board.is-locked .lwmemory__card:not(.is-matched):not(.is-flipped) {
    pointer-events: none;
}

/* ── Win panel ───────────────────────────────────────────────────────── */

.lwmemory__win {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    padding: 1rem;
    border-radius: var(--lwm-radius);
    background: color-mix(in srgb, var(--lwm-bg) 78%, transparent);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    z-index: 3;
    animation: lwm-fade 0.3s ease;
}

.lwmemory__win[hidden] {
    display: none;
}

@keyframes lwm-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.lwmemory__win-panel {
    max-width: 30rem;
    padding: clamp(1.25rem, 3vw, 2rem);
    border-radius: var(--lwm-radius);
    border: 1px solid var(--lwm-panel-line);
    background: linear-gradient(160deg, var(--lwm-bg-2), var(--lwm-bg));
    box-shadow: var(--lwm-shadow);
    text-align: center;
}

.lwmemory__win-kicker {
    margin: 0 0 0.25rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    background: linear-gradient(90deg, var(--lwm-accent), var(--lwm-accent-2));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.lwmemory__win-title {
    margin: 0 0 1rem;
    font-size: clamp(1.3rem, 3vw, 1.8rem);
    font-weight: 800;
    letter-spacing: -0.01em;
}

.lwmemory__win-stats {
    display: flex;
    justify-content: center;
    gap: clamp(1rem, 4vw, 2.25rem);
    margin: 0 0 1.5rem;
    padding: 0;
    list-style: none;
}

.lwmemory__win-stats li {
    display: flex;
    flex-direction: column-reverse;
    gap: 0.15rem;
}

.lwmemory__win-stats b {
    font-size: clamp(1.15rem, 2.6vw, 1.5rem);
    font-variant-numeric: tabular-nums;
}

.lwmemory__win-stats span {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--lwm-muted);
}

.lwmemory__win-stats .is-record {
    color: var(--lwm-accent-2);
}

/* ── Product grid ────────────────────────────────────────────────────── */

.lwmemory__products {
    margin-top: clamp(1.1rem, 2.6vw, 1.9rem);
    padding-top: clamp(0.9rem, 2vw, 1.4rem);
    border-top: 1px solid var(--lwm-panel-line);
}

.lwmemory__products-title {
    margin: 0 0 0.85rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--lwm-muted);
}

.lwmemory__product {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    height: 100%;
    padding: 0.4rem;
    border-radius: var(--lwm-radius-sm);
    border: 1px solid transparent;
    color: inherit;
    text-decoration: none;
    transition: border-color 0.15s ease, background-color 0.15s ease, transform 0.15s ease;
}

a.lwmemory__product:hover,
a.lwmemory__product:focus-visible {
    border-color: color-mix(in srgb, var(--lwm-accent-2) 45%, transparent);
    background: var(--lwm-panel);
    transform: translateY(-2px);
    text-decoration: none;
    color: inherit;
}

a.lwmemory__product:focus-visible {
    outline: 2px solid var(--lwm-accent-2);
    outline-offset: 2px;
}

.lwmemory__product-shot {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 3;
    border-radius: calc(var(--lwm-radius-sm) - 3px);
    background: var(--lwm-tile);
    overflow: hidden;
}

.lwmemory__product-shot img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 6%;
}

.lwmemory__product-title {
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.25;
    text-align: center;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
}

/* ── Utility ─────────────────────────────────────────────────────────── */

.lwmemory__sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.lwmemory__noscript {
    margin: 0.85rem 0 0;
    padding: 0.6rem 0.9rem;
    border-radius: var(--lwm-radius-sm);
    border: 1px solid var(--lwm-panel-line);
    background: var(--lwm-panel);
    font-size: 0.85rem;
    color: var(--lwm-muted);
}

.lwmemory__debug {
    margin-top: 1.25rem;
    padding: 0.75rem 1rem;
    border-radius: var(--lwm-radius-sm);
    border: 1px dashed var(--lwm-panel-line);
    font-size: 0.78rem;
    color: var(--lwm-muted);
}

.lwmemory__debug ul {
    margin: 0.5rem 0 0;
    padding-left: 1.1rem;
}

/* ── Responsive ──────────────────────────────────────────────────────── */

@media (max-width: 767.98px) {
    .lwmemory__board {
        grid-template-columns: repeat(min(var(--lwm-cols), 4), minmax(0, 1fr));
    }

    .lwmemory__stats {
        margin-left: 0;
        width: 100%;
        justify-content: space-between;
    }

    .lwmemory__stat {
        text-align: left;
    }
}

@media (max-width: 479.98px) {
    .lwmemory__board {
        grid-template-columns: repeat(min(var(--lwm-cols), 3), minmax(0, 1fr));
    }

    .lwmemory__card-name {
        display: none;
    }
}

/* ── Motion ──────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
    .lwmemory__card,
    .lwmemory__button,
    .lwmemory__product {
        transition-duration: 0.01ms;
    }

    .lwmemory__win,
    .lwmemory--sweep .lwmemory__card,
    .lwmemory__card.is-miss .lwmemory__face--front {
        animation: none;
    }
}
