// ============================================================================= // HOLOFOIL INTEGRATION // _holofoil-integration.scss // ============================================================================= @import "card"; // ----------------------------------------------------------------------------- // 1. WRAPPER NORMALISATION // ----------------------------------------------------------------------------- %holofoil-wrapper-base { --card-aspect: 0.718; --card-radius: 4.55% / 3.5%; --pointer-x: 50%; --pointer-y: 50%; --background-x: 50%; --background-y: 50%; --pointer-from-center: 0; --pointer-from-top: 0.5; --pointer-from-left: 0.5; --card-scale: 1; --card-opacity: 0; --grain: url('/public/holofoils/grain.webp'); --glitter: url('/public/holofoils/glitter.png'); --glittersize: 25%; --space: 5%; --angle: 133deg; --imgsize: cover; --red: #f80e35; --yellow: #eedf10; --green: #21e985; --blue: #0dbde9; --violet: #c929f1; --clip: inset(9.85% 8% 52.85% 8%); --clip-invert: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0 47.15%, 91.5% 47.15%, 91.5% 9.85%, 8% 9.85%, 8% 47.15%, 0 50%); --clip-stage: polygon(91.5% 9.85%, 57% 9.85%, 54% 12%, 17% 12%, 16% 14%, 12% 16%, 8% 16%, 8% 47.15%, 92% 47.15%); --clip-stage-invert: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0 47.15%, 91.5% 47.15%, 91.5% 9.85%, 57% 9.85%, 54% 12%, 17% 12%, 16% 14%, 12% 16%, 8% 16%, 8% 47.15%, 0 50%); --clip-trainer: inset(14.5% 8.5% 48.2% 8.5%); --clip-borders: inset(2.8% 4% round 2.55% / 1.5%); --sunpillar-clr-1: var(--sunpillar-1); --sunpillar-clr-2: var(--sunpillar-2); --sunpillar-clr-3: var(--sunpillar-3); --sunpillar-clr-4: var(--sunpillar-4); --sunpillar-clr-5: var(--sunpillar-5); --sunpillar-clr-6: var(--sunpillar-6); // NOTE: no overflow:hidden here -- that would clip the lift/scale transform // on .image-grow. Overflow is handled by the child .holo-shine/.holo-glare. position: relative; isolation: isolate; border-radius: var(--card-radius); } %holofoil-energy-glows { &[data-energy="Water"] { --card-glow: hsl(192, 97%, 60%); } &[data-energy="Fire"] { --card-glow: hsl(9, 81%, 59%); } &[data-energy="Grass"] { --card-glow: hsl(96, 81%, 65%); } &[data-energy="Lightning"] { --card-glow: hsl(54, 87%, 63%); } &[data-energy="Psychic"] { --card-glow: hsl(281, 62%, 58%); } &[data-energy="Fighting"] { --card-glow: rgb(145, 90, 39); } &[data-energy="Darkness"] { --card-glow: hsl(189, 77%, 27%); } &[data-energy="Metal"] { --card-glow: hsl(184, 20%, 70%); } &[data-energy="Dragon"] { --card-glow: hsl(51, 60%, 35%); } &[data-energy="Fairy"] { --card-glow: hsl(323, 100%, 89%); } } // ----------------------------------------------------------------------------- // 2. SHINE + GLARE CHILD DIVS // ----------------------------------------------------------------------------- %shine-base { pointer-events: none; position: absolute; inset: 0; border-radius: var(--card-radius); overflow: hidden; // clipping lives here, not on the parent z-index: 3; will-change: transform, opacity, background-image, background-size, background-position, background-blend-mode, filter; &::before, &::after { content: ''; position: absolute; inset: 0; border-radius: var(--card-radius); } } %glare-base { pointer-events: none; position: absolute; inset: 0; border-radius: var(--card-radius); z-index: 4; transform: translateZ(0); overflow: hidden; will-change: transform, opacity, background-image, background-size, background-position, background-blend-mode, filter; } // ----------------------------------------------------------------------------- // 3. MODES // ----------------------------------------------------------------------------- // -- 3a. GRID ----------------------------------------------------------------- // No idle animation. Effect is invisible until hover. .image-grow, .card-image-wrap { @extend %holofoil-wrapper-base; @extend %holofoil-energy-glows; // No effect if the image fell back to default.jpg &[data-default="true"] { .holo-shine, .holo-glare { display: none !important; } } .holo-shine { @extend %shine-base; } .holo-glare { @extend %glare-base; } } // -- 3b. GRID HOVER ----------------------------------------------------------- // The existing main.scss .image-grow:hover handles lift + scale. // We layer the holo effect on top without overriding transform or transition. .image-grow:hover, .image-grow[data-holo-active] { --card-opacity: 0.45; } // -- 3c. MODAL ---------------------------------------------------------------- // Sweeps once per minute. Peaks at 0.35. // Pointer tracking bumps opacity to 0.45 while hovering. @keyframes holo-modal-pulse { 0% { --card-opacity: 0; --pointer-x: 50%; --pointer-y: 50%; --background-x: 50%; --background-y: 50%; --pointer-from-center: 0; --pointer-from-left: 0.5; --pointer-from-top: 0.5; } 4% { --card-opacity: 0; } 8% { --card-opacity: 0.35; --pointer-x: 25%; --pointer-y: 15%; --background-x: 38%; --background-y: 28%; --pointer-from-center: 0.85; --pointer-from-left: 0.25; --pointer-from-top: 0.15; } 25% { --pointer-x: 70%; --pointer-y: 30%; --background-x: 64%; --background-y: 34%; --pointer-from-center: 0.9; --pointer-from-left: 0.70; --pointer-from-top: 0.30; } 45% { --pointer-x: 80%; --pointer-y: 70%; --background-x: 74%; --background-y: 68%; --pointer-from-center: 0.88; --pointer-from-left: 0.80; --pointer-from-top: 0.70; } 65% { --pointer-x: 35%; --pointer-y: 80%; --background-x: 38%; --background-y: 76%; --pointer-from-center: 0.8; --pointer-from-left: 0.35; --pointer-from-top: 0.80; } 85% { --card-opacity: 0.35; --pointer-x: 25%; --pointer-y: 15%; --background-x: 38%; --background-y: 28%; --pointer-from-center: 0.85; } 90% { --card-opacity: 0; } 100% { --card-opacity: 0; --pointer-x: 50%; --pointer-y: 50%; --background-x: 50%; --background-y: 50%; --pointer-from-center: 0; } } .card-image-wrap.holo-modal-mode { --card-opacity: 0; .holo-shine, .holo-glare { animation: holo-modal-pulse 60s ease-in-out infinite; animation-delay: var(--shimmer-delay, -2s); } &[data-holo-active] { --card-opacity: 0.45; .holo-shine, .holo-glare { animation-play-state: paused; } } } // ----------------------------------------------------------------------------- // 4. RARITY -> CLIP-PATH BRIDGE // ----------------------------------------------------------------------------- .image-grow, .card-image-wrap { // No effect on common/uncommon or unrecognised wrapper &[data-rarity="common"], &[data-rarity="uncommon"], &:not([data-rarity]) { .holo-shine, .holo-glare { display: none; } } // Standard holo — artwork area only &[data-rarity="rare holo"] { .holo-shine { clip-path: var(--clip); } &[data-subtypes^="stage"] .holo-shine { clip-path: var(--clip-stage); } &[data-subtypes^="supporter"] .holo-shine, &[data-subtypes^="item"] .holo-shine { clip-path: var(--clip-trainer); } } // Cosmos holo &[data-rarity="rare holo cosmos"] { .holo-shine { clip-path: var(--clip); } &[data-subtypes^="stage"] .holo-shine { clip-path: var(--clip-stage); } &[data-subtypes^="supporter"] .holo-shine { clip-path: var(--clip-trainer); } } &[data-rarity="radiant rare"] { .holo-shine { clip-path: var(--clip-borders); } } &[data-rarity="amazing rare"] { .holo-shine { clip-path: var(--clip); } } &[data-rarity="trainer gallery rare holo"], &[data-rarity="rare holo"][data-trainer-gallery="true"] { .holo-shine { clip-path: var(--clip-borders); } } &[data-rarity="rare shiny"] { .holo-shine { clip-path: var(--clip); } &[data-subtypes^="stage"] .holo-shine { clip-path: var(--clip-stage); } } // Reverse holo by rarity — borders only &[data-rarity$="reverse holo"] { .holo-shine { clip-path: var(--clip-invert); } } // Reverse Holofoil variant — borders only &[data-variant="Reverse Holofoil"] { .holo-shine { clip-path: var(--clip-invert); } } // True holofoil variants + full-bleed rarities — no clip &[data-variant="Holofoil"], &[data-variant="1st Edition Holofoil"], &[data-variant="Unlimited Holofoil"], &[data-rarity="rare ultra"], &[data-rarity="rare holo v"], &[data-rarity="rare holo vmax"], &[data-rarity="rare holo vstar"], &[data-rarity="rare shiny v"], &[data-rarity="rare shiny vmax"], &[data-rarity="rare rainbow"], &[data-rarity="rare rainbow alt"], &[data-rarity="rare secret"] { .holo-shine { clip-path: none; } } // Foil variant shine/glare — clip handled above per variant type &[data-variant="Holofoil"], &[data-variant="Reverse Holofoil"], &[data-variant="1st Edition Holofoil"], &[data-variant="Unlimited Holofoil"] { .holo-shine { background-image: radial-gradient( circle at var(--pointer-x) var(--pointer-y), #fff 5%, #000 50%, #fff 80% ), linear-gradient( var(--foil-angle, -45deg), #000 15%, #fff, #000 85% ); background-blend-mode: soft-light, difference; background-size: 120% 120%, 200% 200%; background-position: center center, calc(100% * var(--pointer-from-left)) calc(100% * var(--pointer-from-top)); filter: brightness(var(--foil-brightness, 0.4)) contrast(1.3) saturate(var(--foil-saturation, 0.5)); mix-blend-mode: color-dodge; opacity: calc((var(--card-opacity) * 0.9) - (var(--pointer-from-center) * 0.1)); } .holo-glare { opacity: calc(var(--card-opacity) * 0.5); background-image: radial-gradient( farthest-corner circle at var(--pointer-x) var(--pointer-y), hsla(0, 0%, 100%, 0.5) 10%, hsla(0, 0%, 100%, 0.25) 30%, hsla(0, 0%, 0%, 0.4) 90% ); filter: brightness(0.7) contrast(1.2); mix-blend-mode: overlay; } } } // ----------------------------------------------------------------------------- // 5. DEFAULT HOLO SHINE / GLARE // Fallback for rarities not explicitly handled above. // ----------------------------------------------------------------------------- .image-grow, .card-image-wrap { &[data-rarity]:not([data-rarity="common"]):not([data-rarity="uncommon"]) { .holo-shine { background-image: repeating-linear-gradient(110deg, var(--violet), var(--blue), var(--green), var(--yellow), var(--red), var(--violet), var(--blue), var(--green), var(--yellow), var(--red) ); background-position: calc(((50% - var(--background-x)) * 2.6) + 50%) calc(((50% - var(--background-y)) * 3.5) + 50%); background-size: 400% 400%; filter: brightness(0.7) contrast(0.9) saturate(0.8); mix-blend-mode: color-dodge; opacity: calc(var(--card-opacity) * 0.6); } .holo-glare { background-image: radial-gradient( farthest-corner circle at var(--pointer-x) var(--pointer-y), hsla(0, 0%, 100%, 0.35) 10%, hsla(0, 0%, 100%, 0.15) 30%, hsla(0, 0%, 0%, 0.35) 90% ); opacity: calc(var(--card-opacity) * 0.4); mix-blend-mode: overlay; filter: brightness(0.7) contrast(1.1); } } }