File size: 6,596 Bytes
c1a683f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | /* =========================================================================
scene.css — the 6-layer parallax cloudscape
.scene is fixed behind content; each .layer translates on scroll + mouse.
Depths (back → front):
0 sky (static gradient, lives on body)
1 far clouds (data-speed slow)
2 moon (fades in on scroll)
3 mid clouds (data-speed medium)
3 whale (owned by whale.js — scroll-mapped)
4 bubbles (data-speed fast, ambient rise)
5 content (normal document flow, z-index 1)
========================================================================= */
.scene {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none; /* never block content clicks */
overflow: hidden;
transform: translateZ(0);
will-change: transform;
/* NO gradient here anymore. The sky now lives ONLY on <body> (see
base.css). Having two gradients — a fixed .scene one AND a scrolling
body one — caused the strip: they're identical at the top of the
page but diverge as you scroll (fixed stays locked, body shifts to
peach), so any momentary gap in .scene revealed a MISMATCHED body
colour = a visible band. One sky on <body> (which scrolls with the
content and cannot gap) eliminates the mismatch entirely. .scene is
now just a transparent holder for the parallax clouds/moon. */
background: transparent;
}
.layer {
position: absolute;
inset: 0;
will-change: transform; /* promote to its own GPU layer */
}
/* Top layers float ABOVE the content. They are children of <body>, not
.scene, so they escape the scene's stacking context (z-index 0) and
render above <main> (z-index 1). Whale = 2, bubbles = 3 (above all). */
.layer-top {
position: fixed;
inset: 0;
z-index: 2;
pointer-events: none;
overflow: hidden;
will-change: transform;
}
.layer-top.layer-bubbles { z-index: 3; } /* bubbles above the whale */
/* Each layer seeded taller than the viewport so parallax never reveals
an empty edge. The JS reads data-speed and data-mouse to translate. */
.layer-far-clouds { height: 200%; top: -50%; }
.layer-moon { height: 160%; top: -30%; }
.layer-mid-clouds { height: 180%; top: -40%; }
/* --- Far clouds (layer 1) --------------------------------------------- */
.cloud-far {
position: absolute;
width: 340px;
opacity: 0.5;
filter: blur(6px);
color: var(--cloud); /* SVG fill uses currentColor */
}
.cloud-far.c1 { top: 12%; left: 8%; }
.cloud-far.c2 { top: 6%; left: 62%; transform: scale(1.3); }
.cloud-far.c3 { top: 38%; left: 78%; transform: scale(0.85); }
.cloud-far.c4 { top: 24%; left: 40%; transform: scale(1.1); opacity: 0.4; }
/* slow horizontal drift */
@keyframes drift-far {
0%, 100% { transform: translateX(0) scale(var(--s, 1)); }
50% { transform: translateX(40px) scale(var(--s, 1)); }
}
.cloud-far { animation: drift-far 18s var(--ease-gentle) infinite; }
.cloud-far.c2 { --s: 1.3; animation-duration: 24s; }
.cloud-far.c3 { --s: 0.85; animation-duration: 20s; animation-delay: -6s; }
.cloud-far.c4 { --s: 1.1; animation-duration: 22s; animation-delay: -10s; }
/* --- Moon (layer 2) --------------------------------------------------- */
/* A dreamy crescent moon: no glow, a translucent body. The crescent is
carved with a CSS mask for the clean silhouette. */
.moon {
position: absolute;
top: 8%;
right: 9%;
width: 96px;
height: 96px;
border-radius: 50%;
opacity: 0; /* fades in as user scrolls */
transition: opacity 1.2s var(--ease-soft);
}
.moon.is-visible { opacity: 0.9; }
.moon-disc {
position: absolute;
inset: 0;
border-radius: 50%;
/* warm translucent body — a bit less see-through than before */
background: radial-gradient(circle at 34% 30%,
rgba(255, 251, 234, 0.78) 0%,
rgba(255, 233, 77, 0.66) 50%,
rgba(245, 178, 46, 0.58) 100%);
/* crescent flipped top↔bottom: bite now sits upper-left, opening down */
-webkit-mask: radial-gradient(circle 50px at 30% 28%, transparent 96%, #000 100%);
mask: radial-gradient(circle 50px at 30% 28%, transparent 96%, #000 100%);
}
/* --- Mid clouds (layer 3) --------------------------------------------- */
.cloud-mid {
position: absolute;
width: 260px;
opacity: 0.78;
filter: blur(2px);
color: var(--cloud);
}
.cloud-mid.m1 { top: 22%; left: -10%; animation: drift-mid 38s linear infinite; }
.cloud-mid.m2 { top: 58%; left: 70%; animation: drift-mid 52s linear infinite; animation-delay: -18s; }
.cloud-mid.m3 { top: 44%; left: 30%; animation: drift-mid 46s linear infinite; animation-delay: -30s; transform: scale(0.7); }
@keyframes drift-mid {
from { transform: translateX(-30vw); }
to { transform: translateX(130vw); }
}
/* --- Bubbles (layer 4) ------------------------------------------------ */
.bubbles {
/* container; individual bubbles injected by main.js */
}
.bubble {
position: absolute;
bottom: -110px; /* start further down (bigger now) */
border-radius: 50%;
/* glass body: mostly transparent, faint wash toward the light source */
background: radial-gradient(circle at 30% 28%,
rgba(255, 255, 255, 0.5) 0%,
rgba(255, 255, 255, 0.12) 42%,
rgba(255, 143, 171, 0.06) 72%,
rgba(255, 143, 171, 0.02) 100%);
/* the bright soap-film rim + inner spherical depth */
border: 1px solid rgba(255, 255, 255, 0.42);
box-shadow:
inset -5px -7px 12px rgba(157, 170, 242, 0.22), /* bottom-inner shade */
inset 4px 5px 10px rgba(255, 255, 255, 0.45), /* top-inner glow */
0 2px 10px rgba(157, 170, 242, 0.1); /* soft contact shadow*/
animation: rise linear infinite;
will-change: transform, opacity;
}
/* the crisp glassy highlight on each bubble */
.bubble::after {
content: "";
position: absolute;
top: 15%;
left: 19%;
width: 32%;
height: 32%;
border-radius: 50%;
background: radial-gradient(circle, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.25) 55%, transparent 75%);
}
@keyframes rise {
0% { transform: translate(0, 0) rotate(0deg); opacity: 0; }
8% { opacity: 0.75; }
92% { opacity: 0.75; }
100% { transform: translate(var(--sway, 24px), -115vh) rotate(var(--spin, 40deg)); opacity: 0; }
}
/* --- Content sits above the whole scene --- */
main {
position: relative;
z-index: 1;
}
|