CSS
@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@300;400;500;600&display=swap');
.scd-hovr, .scd-hovr *, .scd-hovr *::before, .scd-hovr *::after { box-sizing: border-box; margin: 0; padding: 0; }
.scd-hovr {
position: relative;
min-height: 460px;
display: grid;
place-items: center;
background: #05060a;
font-family: 'Geist', sans-serif;
}
.scd-hovr::before {
content: '';
position: absolute; inset: -30%;
background:
radial-gradient(40% 40% at 20% 30%, rgba(99,102,241,.35), transparent 70%),
radial-gradient(45% 45% at 80% 25%, rgba(236,72,153,.3), transparent 70%),
radial-gradient(50% 50% at 50% 85%, rgba(45,212,191,.28), transparent 70%);
filter: blur(20px);
animation: scd-hovr-drift 18s ease-in-out infinite alternate;
pointer-events: none;
}
@keyframes scd-hovr-drift {
0% { transform: translate(-3%,-2%) scale(1); }
100% { transform: translate(4%,3%) scale(1.12); }
}
.scd-hovr::after {
content: '';
position: absolute; inset: 0;
background: repeating-linear-gradient(0deg, rgba(255,255,255,.022) 0 1px, transparent 1px 3px);
mix-blend-mode: overlay;
pointer-events: none;
}
.scd-hovr__stage { position: relative; width: 300px; height: 400px; perspective: 1300px; z-index: 1; }
.scd-hovr__deck { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; transition: transform .25s ease-out; }
.scd-hovr__card {
position: absolute; inset: 0;
border-radius: 24px;
transform-style: preserve-3d;
transition: transform .8s cubic-bezier(.16,1,.3,1), box-shadow .6s, filter .6s;
cursor: pointer;
overflow: hidden;
box-shadow: 0 30px 60px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.08) inset;
}
.scd-hovr__surface { position: absolute; inset: 0; backdrop-filter: blur(8px); }
.scd-hovr__grad {
position: absolute; inset: 0;
opacity: .9;
background-size: 200% 200%;
animation: scd-hovr-flow 8s ease infinite;
}
@keyframes scd-hovr-flow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.scd-hovr__glow {
position: absolute; inset: 0;
background: radial-gradient(280px circle at var(--mx,50%) var(--my,40%), rgba(255,255,255,.4), transparent 60%);
mix-blend-mode: soft-light;
opacity: 0;
transition: opacity .5s;
}
.scd-hovr__card:hover .scd-hovr__glow { opacity: 1; }
.scd-hovr__sheen {
position: absolute; top: -60%; left: -30%;
width: 60%; height: 220%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.5), transparent);
transform: rotate(18deg) translateX(-220px);
transition: transform .9s cubic-bezier(.16,1,.3,1);
}
.scd-hovr__card:hover .scd-hovr__sheen { transform: rotate(18deg) translateX(560px); }
.scd-hovr__content {
position: absolute; inset: 0;
padding: 28px;
display: flex; flex-direction: column; justify-content: space-between;
transform: translateZ(50px);
}
.scd-hovr__meta { display: flex; align-items: center; gap: 10px; }
.scd-hovr__chip {
font-size: .62rem; letter-spacing: .22em; text-transform: uppercase;
padding: 6px 12px; border-radius: 999px;
background: rgba(255,255,255,.16); backdrop-filter: blur(6px);
color: #fff; font-weight: 500;
border: 1px solid rgba(255,255,255,.22);
}
.scd-hovr__idx {
margin-left: auto;
font-family: 'Instrument Serif', serif; font-style: italic;
font-size: 1.3rem; color: rgba(255,255,255,.7);
}
.scd-hovr__title {
font-family: 'Instrument Serif', serif;
font-size: 3rem; line-height: .92;
color: #fff; letter-spacing: -.01em;
text-shadow: 0 2px 20px rgba(0,0,0,.3);
}
.scd-hovr__title em { font-style: italic; }
.scd-hovr__desc { font-size: .8rem; color: rgba(255,255,255,.78); line-height: 1.5; max-width: 24ch; }
.scd-hovr__row { display: flex; align-items: center; justify-content: space-between; }
.scd-hovr__cta {
font-size: .78rem; color: #fff;
display: inline-flex; align-items: center; gap: 6px;
opacity: 0; transform: translateY(8px);
transition: opacity .5s .1s, transform .5s .1s;
}
.scd-hovr__card:hover .scd-hovr__cta { opacity: 1; transform: translateY(0); }
.scd-hovr__arrow {
width: 22px; height: 22px;
border-radius: 50%;
background: rgba(255,255,255,.2);
display: grid; place-items: center;
font-size: .8rem;
}
.scd-hovr__card--c1 .scd-hovr__grad { background: linear-gradient(135deg,#1e1b4b,#4338ca 40%,#6d28d9); }
.scd-hovr__card--c2 .scd-hovr__grad { background: linear-gradient(135deg,#831843,#be185d 45%,#f43f5e); }
.scd-hovr__card--c3 .scd-hovr__grad { background: linear-gradient(135deg,#064e3b,#0d9488 50%,#2dd4bf); }
.scd-hovr__card--c4 .scd-hovr__grad { background: linear-gradient(135deg,#7c2d12,#ea580c 45%,#f59e0b); }
.scd-hovr__card--c1 { transform: translateZ(0) translateY(0) rotate(-3deg); z-index: 4; }
.scd-hovr__card--c2 { transform: translateZ(-40px) translateY(10px) rotate(2deg); z-index: 3; filter: brightness(.85); }
.scd-hovr__card--c3 { transform: translateZ(-80px) translateY(20px) rotate(-2deg); z-index: 2; filter: brightness(.72); }
.scd-hovr__card--c4 { transform: translateZ(-120px) translateY(30px) rotate(3deg); z-index: 1; filter: brightness(.6); }
/* Stage-spread fan — only when no individual card is being hovered.
Without the :not(:has(...:hover)) guard, hovering any single card
fires BOTH this stage spread AND the per-card lift below. The two
transforms fight on the same .8s cubic-bezier transition, sliding
the card out from under the cursor mid-animation → cursor leaves
the card → spread snaps back → cursor re-enters → infinite flicker.
Restricting the spread to "stage hover AND nothing inside being
hovered" lets the spread fan cleanly when you first enter the
stage area, then freezes at the moment your cursor lands on a card. */
.scd-hovr__stage:hover:not(:has(.scd-hovr__card:hover)) .scd-hovr__card--c1 { transform: translate(-165px,-30px) translateZ(40px) rotate(-12deg); filter: brightness(1); }
.scd-hovr__stage:hover:not(:has(.scd-hovr__card:hover)) .scd-hovr__card--c2 { transform: translate(-55px,18px) translateZ(20px) rotate(-4deg); filter: brightness(1); }
.scd-hovr__stage:hover:not(:has(.scd-hovr__card:hover)) .scd-hovr__card--c3 { transform: translate(55px,18px) translateZ(20px) rotate(4deg); filter: brightness(1); }
.scd-hovr__stage:hover:not(:has(.scd-hovr__card:hover)) .scd-hovr__card--c4 { transform: translate(165px,-30px) translateZ(40px) rotate(12deg); filter: brightness(1); }
/* When a card IS being hovered, write each card's hovered transform
explicitly — keeping its fanned x/y position AND adding the lift
(translateZ + scale). This way the transform doesn't snap the card
back to (0,0) which is what caused the flicker. */
.scd-hovr__stage:hover .scd-hovr__card--c1:hover { transform: translate(-165px,-56px) translateZ(90px) rotate(-12deg) scale(1.06); filter: brightness(1.05); z-index: 9; }
.scd-hovr__stage:hover .scd-hovr__card--c2:hover { transform: translate(-55px,-8px) translateZ(90px) rotate(-4deg) scale(1.06); filter: brightness(1.05); z-index: 9; }
.scd-hovr__stage:hover .scd-hovr__card--c3:hover { transform: translate(55px,-8px) translateZ(90px) rotate(4deg) scale(1.06); filter: brightness(1.05); z-index: 9; }
.scd-hovr__stage:hover .scd-hovr__card--c4:hover { transform: translate(165px,-56px) translateZ(90px) rotate(12deg) scale(1.06); filter: brightness(1.05); z-index: 9; }
.scd-hovr__card:hover { box-shadow: 0 50px 90px rgba(0,0,0,.65), 0 0 0 1px rgba(255,255,255,.18) inset, 0 0 40px rgba(255,255,255,.12); }
.scd-hovr__floor {
position: absolute; bottom: -70px; left: 50%;
width: 240px; height: 40px;
background: radial-gradient(ellipse, rgba(0,0,0,.6), transparent 70%);
transform: translateX(-50%);
filter: blur(8px);
}
@media (prefers-reduced-motion: reduce) {
.scd-hovr::before,
.scd-hovr__grad,
.scd-hovr__sheen,
.scd-hovr__deck,
.scd-hovr__card,
.scd-hovr__cta { animation: none !important; transition: none !important; }
}