/**
 * THE rgba(0,0,0,...) GRADIENT STOPS ARE LOAD-BEARING (2026-09-14).
 *
 * The img-grad-* overlays are SCRIMS: their job is to move luminance toward an
 * extreme so text can sit on them, and Plexa_Ground_Tone::scrimmed() models a
 * scrim as compositing toward pure 0 or pure 255. That is the model every
 * overlay floor in fixed-compat.json and ground-compat.json was measured with.
 * Painting --color-neutral here instead would make the paint disagree with the
 * arithmetic and silently invalidate both catalogues — the same finding that
 * kept .overlay-dark and .overlay-light literal in backgrounds.css. Declared
 * family_independent in image-compat.json.
 *
 * Carrying the family's colour is a different job, and img-grad-warm-vignette
 * and -golden-hour do it with tokens.
 *
 * Image Effects System
 *
 * Self-contained CSS class library for image filter presets, hover effects,
 * gradient overlays, blend modes, and Ken Burns animations.
 *
 * Class targets:
 * - .img-effect (frame div): hover transforms, overlays, gradients, animation
 * - .img-effect__img (img tag): filter presets, custom filters, blend modes
 */

/* ==========================================================================
   Base Structure
   ========================================================================== */

.img-effect {
    position: relative;
    overflow: hidden;
    display: inline-block;
    width: 100%;
}

.img-effect__img {
    display: block;
    width: 100%;
    height: 100%;
    position: relative;
    z-index: 0;
}

/* ==========================================================================
   Filter Presets (16) — applied to .img-effect__img
   ========================================================================== */

.img-fx-grayscale { filter: grayscale(1); }
.img-fx-vintage { filter: sepia(0.47) contrast(1.12) brightness(0.94) saturate(0.77); }
.img-fx-faded { filter: brightness(1.13) saturate(0.6) contrast(0.87); }
.img-fx-dramatic { filter: contrast(1.44) brightness(0.89) saturate(1.11); }
.img-fx-cool { filter: hue-rotate(15deg) saturate(1.43) brightness(1.11); }
.img-fx-warm { filter: hue-rotate(-15deg) saturate(1.39) brightness(1.13); }
.img-fx-noir { filter: grayscale(1) contrast(1.3) brightness(.95); }
.img-fx-soft { filter: contrast(0.72) brightness(1.19) saturate(0.81); }
.img-fx-vivid { filter: saturate(1.5) contrast(1.2) brightness(1.05); }
.img-fx-muted { filter: saturate(0.45) brightness(1.05) contrast(0.94); }
.img-fx-cinematic { filter: contrast(1.35) brightness(0.81) saturate(1.12) sepia(0.23); }
.img-fx-editorial { filter: contrast(1.39) saturate(0.76) brightness(0.97); }
.img-fx-retro { filter: sepia(0.57) contrast(1.23) brightness(1.06) hue-rotate(-5.66deg); }
.img-fx-crisp { filter: contrast(1.47) saturate(1.16) brightness(1.03); }
.img-fx-bold { filter: saturate(1.4) contrast(1.25) brightness(1.08); }
.img-fx-glitch { filter: saturate(1.6) contrast(1.4) hue-rotate(90deg) brightness(1.1); }

/* ==========================================================================
   Custom Filter Composition — variable-driven
   ========================================================================== */

.img-fx-custom .img-effect__img {
    filter:
        blur(var(--img-fx-blur, 0px))
        brightness(var(--img-fx-brightness, 1))
        contrast(var(--img-fx-contrast, 1))
        grayscale(var(--img-fx-grayscale, 0))
        saturate(var(--img-fx-saturate, 1))
        sepia(var(--img-fx-sepia, 0))
        hue-rotate(var(--img-fx-hue, 0deg));
}

/* Blur modifiers */
.img-filter-blur-sm { --img-fx-blur: 2px; }
.img-filter-blur-md { --img-fx-blur: 4px; }
.img-filter-blur-lg { --img-fx-blur: 8px; }

/* Brightness modifiers */
.img-filter-brightness-dark { --img-fx-brightness: .7; }
.img-filter-brightness-dim { --img-fx-brightness: .85; }
.img-filter-brightness-bright { --img-fx-brightness: 1.15; }
.img-filter-brightness-vivid { --img-fx-brightness: 1.3; }

/* Contrast modifiers */
.img-filter-contrast-low { --img-fx-contrast: .8; }
.img-filter-contrast-high { --img-fx-contrast: 1.3; }
.img-filter-contrast-max { --img-fx-contrast: 1.5; }

/* Saturate modifiers */
.img-filter-saturate-off { --img-fx-saturate: 0; }
.img-filter-saturate-muted { --img-fx-saturate: .5; }
.img-filter-saturate-vibrant { --img-fx-saturate: 1.3; }
.img-filter-saturate-vivid { --img-fx-saturate: 1.6; }

/* Grayscale modifiers */
.img-filter-grayscale-partial { --img-fx-grayscale: .5; }
.img-filter-grayscale-full { --img-fx-grayscale: 1; }

/* Sepia modifiers */
.img-filter-sepia-light { --img-fx-sepia: .3; }
.img-filter-sepia-full { --img-fx-sepia: 1; }

/* Hue modifiers */
.img-filter-hue-warm { --img-fx-hue: -15deg; }
.img-filter-hue-cool { --img-fx-hue: 15deg; }

/* ==========================================================================
   Hover Effects (6) — applied to .img-effect frame
   ========================================================================== */

.img-hover-zoom .img-effect__img {
    transition: transform 0.4s ease;
}
@media (hover: hover) {
    .img-hover-zoom:hover .img-effect__img {
        transform: scale(1.08);
    }
}

.img-hover-zoom-lg .img-effect__img {
    transition: transform 0.5s ease;
}
@media (hover: hover) {
    .img-hover-zoom-lg:hover .img-effect__img {
        transform: scale(1.2);
    }
}

.img-hover-lift {
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}
@media (hover: hover) {
    .img-hover-lift:hover {
        transform: translateY(-4px);
        box-shadow:
            0 20px 25px -5px color-mix(in oklch, var(--color-neutral) 10%, transparent),
            0 8px 10px -6px color-mix(in oklch, var(--color-neutral) 10%, transparent);
    }
}

.img-hover-glow {
    transition: box-shadow 0.3s ease;
}
@media (hover: hover) {
    .img-hover-glow:hover {
        box-shadow:
            0 0 12px color-mix(in oklch, var(--color-primary) 55%, transparent),
            0 0 36px color-mix(in oklch, var(--color-primary) 30%, transparent);
    }
}

/* Color reveal: grayscale by default, full color on hover */
.img-hover-color {
    filter: grayscale(1);
    transition: filter 0.4s ease;
}
@media (hover: hover) {
    .img-hover-color:hover {
        filter: grayscale(0);
    }
}

/* Grayscale on hover: full color by default, gray on hover */
.img-hover-grayscale {
    transition: filter 0.4s ease;
}
@media (hover: hover) {
    .img-hover-grayscale:hover {
        filter: grayscale(1);
    }
}

/* ==========================================================================
   Image Blend Modes (4) — applied to .img-effect__img
   ========================================================================== */

.img-blend-multiply { mix-blend-mode: multiply; }
.img-blend-screen { mix-blend-mode: screen; }
.img-blend-overlay { mix-blend-mode: overlay; }
.img-blend-soft-light { mix-blend-mode: soft-light; }

/* ==========================================================================
   Overlay Blend Modes (6) — applied to .img-effect ::before
   ========================================================================== */

.img-effect.overlay-blend-screen::before { mix-blend-mode: screen; }
.img-effect.overlay-blend-multiply::before { mix-blend-mode: multiply; }
.img-effect.overlay-blend-overlay::before { mix-blend-mode: overlay; }
.img-effect.overlay-blend-soft-light::before { mix-blend-mode: soft-light; }
.img-effect.overlay-blend-color-dodge::before { mix-blend-mode: color-dodge; }
.img-effect.overlay-blend-exclusion::before { mix-blend-mode: exclusion; }

/* ==========================================================================
   Gradient Overlays (7) — applied to .img-effect ::after
   ========================================================================== */

.img-effect::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    opacity: 0;
}

.img-grad-bottom::after {
    opacity: 1;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.7) 0%, transparent 60%);
}

.img-grad-top::after {
    opacity: 1;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.7) 0%, transparent 60%);
}

.img-grad-vignette::after {
    opacity: 1;
    background: radial-gradient(circle, transparent 50%, rgba(0, 0, 0, 0.5) 100%);
}

.img-grad-scrim::after {
    opacity: 1;
    background: linear-gradient(
        to top,
        rgba(0, 0, 0, 0.6) 0%,
        rgba(0, 0, 0, 0.4) 20%,
        rgba(0, 0, 0, 0.2) 40%,
        rgba(0, 0, 0, 0.1) 60%,
        transparent 80%
    );
}

.img-grad-warm-vignette::after {
    opacity: 1;
    background: radial-gradient(circle, rgba(208, 186, 142, 0.15) 30%, rgba(0, 0, 0, 0.5) 100%);
}

.img-grad-cool-wash::after {
    opacity: 1;
    background: linear-gradient(to right, rgba(0, 91, 154, 0.25) 0%, transparent 60%);
}

.img-grad-golden-hour::after {
    opacity: 1;
    background: linear-gradient(to top, rgba(128, 78, 15, 0.4) 0%, rgba(100, 20, 100, 0.15) 50%, transparent 80%);
}

/* ==========================================================================
   Ken Burns Animation (3) — applied to .img-effect frame
   ========================================================================== */

@keyframes ken-burns {
    0% { transform: scale(1) translate(0, 0); }
    50% { transform: scale(1.12) translate(-1%, -1%); }
    100% { transform: scale(1) translate(0, 0); }
}

.img-anim-ken-burns .img-effect__img {
    animation: ken-burns 20s ease-in-out infinite;
}

.img-anim-ken-burns-slow .img-effect__img {
    animation: ken-burns 30s ease-in-out infinite;
}

.img-anim-ken-burns-fast .img-effect__img {
    animation: ken-burns 12s ease-in-out infinite;
}

/* ==========================================================================
   Focus States — linked images
   ========================================================================== */

a:focus-visible > .img-effect {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}
