The source code for our eny.social landing page, which is mirrored in a different repository as part of the CI setup. eny.social
social-network eny local-first
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'feature/Enysocial-MVP-Tasks#CU-86c8yzvp4' into mirror/main

+93 -24
+92 -22
app/components/Hero.tsx
··· 58 58 currentIndex === index 59 59 ? "translateY(0)" 60 60 : currentIndex > index 61 - ? "translateY(-20px)" 62 - : "translateY(20px)", 61 + ? "translateY(-20px)" 62 + : "translateY(20px)", 63 63 }} 64 64 > 65 - {slide.word} 65 + <span 66 + className={ 67 + slide.word === "communities" 68 + ? "inline-block min-[1024px]:max-[1400px]:translate-x-[12%]" 69 + : undefined 70 + } 71 + > 72 + {slide.word} 73 + </span> 66 74 </span> 67 75 ))} 68 76 {/* Invisible placeholder for sizing */} 69 77 <span className="stage-heading-2 invisible"> 70 - communities 78 + <span className="inline-block min-[1024px]:max-[1400px]:translate-x-[12%]"> 79 + communities 80 + </span> 71 81 </span> 72 82 </span> 73 83 </FadeIn> 74 84 </h1> 75 85 </div> 76 86 87 + {/* Mobile-only blob between heading and sub-text */} 88 + <div className="relative mt-6 mb-2 lg:hidden"> 89 + <svg className="absolute h-0 w-0" aria-hidden="true"> 90 + <defs> 91 + <clipPath 92 + id="hero-blob-mobile" 93 + clipPathUnits="objectBoundingBox" 94 + > 95 + <path 96 + transform="translate(-0.01 0.09) scale(0.94 0.82)" 97 + d="M0.9502,0.5487 C1.0731,0.7445,0.9515,1.0075,0.7345,0.9473 C0.6670,0.9286,0.4922,0.9118,0.4336,0.9537 C0.2466,1.0875,0.0064,0.9066,0.0696,0.6794 C0.0894,0.6084,0.0786,0.5322,0.0403,0.4712 C-0.0826,0.2754,0.0950,0.0193,0.3119,0.0794 C0.3795,0.0982,0.4523,0.0850,0.5108,0.0431 C0.6977,-0.0907,0.9993,0.1093,0.9361,0.3364 C0.9163,0.4074,0.9119,0.4876,0.9502,0.5487Z" 98 + > 99 + <animate 100 + attributeName="d" 101 + dur="12s" 102 + repeatCount="indefinite" 103 + values=" 104 + M0.9502,0.5487 C1.0731,0.7445,0.9515,1.0075,0.7345,0.9473 C0.6670,0.9286,0.4922,0.9118,0.4336,0.9537 C0.2466,1.0875,0.0064,0.9066,0.0696,0.6794 C0.0894,0.6084,0.0786,0.5322,0.0403,0.4712 C-0.0826,0.2754,0.0950,0.0193,0.3119,0.0794 C0.3795,0.0982,0.4523,0.0850,0.5108,0.0431 C0.6977,-0.0907,0.9993,0.1093,0.9361,0.3364 C0.9163,0.4074,0.9119,0.4876,0.9502,0.5487Z; 105 + M0.9402,0.5587 C1.0631,0.7545,0.9615,1.0175,0.7445,0.9373 C0.6570,0.9186,0.5022,0.9218,0.4436,0.9437 C0.2566,1.0775,0.0164,0.9166,0.0596,0.6894 C0.0794,0.5984,0.0886,0.5422,0.0503,0.4612 C-0.0726,0.2854,0.1050,0.0293,0.3219,0.0694 C0.3895,0.0882,0.4423,0.0950,0.5208,0.0531 C0.7077,-0.0807,0.9893,0.1193,0.9261,0.3464 C0.9063,0.4174,0.9219,0.4776,0.9402,0.5587Z; 106 + M0.9602,0.5387 C1.0831,0.7345,0.9415,0.9975,0.7245,0.9573 C0.6770,0.9386,0.4822,0.9018,0.4236,0.9637 C0.2366,1.0975,-0.0036,0.8966,0.0796,0.6694 C0.0994,0.6184,0.0686,0.5222,0.0303,0.4812 C-0.0926,0.2654,0.0850,0.0093,0.3019,0.0894 C0.3695,0.1082,0.4623,0.0750,0.5008,0.0331 C0.6877,-0.1007,1.0093,0.0993,0.9461,0.3264 C0.9263,0.3974,0.9019,0.4976,0.9602,0.5387Z; 107 + M0.9502,0.5487 C1.0731,0.7445,0.9515,1.0075,0.7345,0.9473 C0.6670,0.9286,0.4922,0.9118,0.4336,0.9537 C0.2466,1.0875,0.0064,0.9066,0.0696,0.6794 C0.0894,0.6084,0.0786,0.5322,0.0403,0.4712 C-0.0826,0.2754,0.0950,0.0193,0.3119,0.0794 C0.3795,0.0982,0.4523,0.0850,0.5108,0.0431 C0.6977,-0.0907,0.9993,0.1093,0.9361,0.3364 C0.9163,0.4074,0.9119,0.4876,0.9502,0.5487Z 108 + " 109 + /> 110 + </path> 111 + </clipPath> 112 + </defs> 113 + </svg> 114 + <div 115 + className="relative ml-auto w-[130%] aspect-square overflow-visible min-[640px]:translate-x-[20%] lg:translate-x-0" 116 + style={{ clipPath: "url(#hero-blob-mobile)" }} 117 + > 118 + {slides.map((slide, index) => ( 119 + <Image 120 + key={`${slide.word}-mobile`} 121 + src={slide.image} 122 + alt={`People ${slide.word}`} 123 + width={1200} 124 + height={1200} 125 + priority={index === 0} 126 + className="absolute inset-[-12%] h-[124%] w-[124%] object-cover object-[42%_center]" 127 + style={{ 128 + transition: 129 + "opacity 500ms ease-out, transform 3000ms ease-out", 130 + opacity: currentIndex === index ? 1 : 0, 131 + transform: 132 + currentIndex === index ? "scale(1)" : "scale(1.06)", 133 + }} 134 + /> 135 + ))} 136 + </div> 137 + </div> 138 + 77 139 {/* Menu + sub-text side by side */} 78 140 <div className="mt-12 grid lg:grid-cols-6 gap-4"> 79 141 {/* Menu links */} 80 - <NavMenu 81 - items={[ 82 - { label: "What", href: "#what" }, 83 - { label: "Values", href: "#values" }, 84 - { label: "Offenbach", href: "/offenbach" }, 85 - { label: "Waitlist", href: "#waitlist" }, 86 - ]} 87 - className="hidden lg:col-span-1 lg:flex lg:flex-col lg:items-end" 88 - itemClassName="self-end text-right" 89 - baseDelay={400} 90 - stagger={80} 91 - /> 142 + <div className="hidden lg:col-span-1 lg:flex lg:flex-col lg:items-end lg:translate-x-[10%]"> 143 + <NavMenu 144 + items={[ 145 + { label: "What", href: "#what" }, 146 + { label: "Values", href: "#values" }, 147 + { label: "Offenbach", href: "/offenbach" }, 148 + { label: "Waitlist", href: "#waitlist" }, 149 + ]} 150 + className="" 151 + itemClassName="self-end text-right" 152 + baseDelay={400} 153 + stagger={80} 154 + /> 155 + </div> 92 156 93 157 {/* Sub-heading + body */} 94 158 <div className="lg:col-span-5 lg:pl-[calc(100%/5)]"> ··· 112 176 </div> 113 177 114 178 {/* Image (6 cols) — bleeds top + right, blob visible on left + bottom */} 115 - <div className="relative lg:col-span-6 lg:overflow-visible"> 179 + <div className="relative hidden lg:block lg:col-span-6 lg:overflow-visible"> 116 180 {/* SVG clip-path with SMIL morph animation */} 117 181 <svg className="absolute h-0 w-0" aria-hidden="true"> 118 182 <defs> 119 - <clipPath id="hero-blob" clipPathUnits="objectBoundingBox"> 120 - <path d="M0.9502,0.5487 C1.0731,0.7445,0.9515,1.0075,0.7345,0.9473 C0.6670,0.9286,0.4922,0.9118,0.4336,0.9537 C0.2466,1.0875,0.0064,0.9066,0.0696,0.6794 C0.0894,0.6084,0.0786,0.5322,0.0403,0.4712 C-0.0826,0.2754,0.0950,0.0193,0.3119,0.0794 C0.3795,0.0982,0.4523,0.0850,0.5108,0.0431 C0.6977,-0.0907,0.9993,0.1093,0.9361,0.3364 C0.9163,0.4074,0.9119,0.4876,0.9502,0.5487Z"> 183 + <clipPath 184 + id="hero-blob-desktop" 185 + clipPathUnits="objectBoundingBox" 186 + > 187 + <path 188 + transform="translate(-0.01 0.09) scale(0.94 0.82)" 189 + d="M0.9502,0.5487 C1.0731,0.7445,0.9515,1.0075,0.7345,0.9473 C0.6670,0.9286,0.4922,0.9118,0.4336,0.9537 C0.2466,1.0875,0.0064,0.9066,0.0696,0.6794 C0.0894,0.6084,0.0786,0.5322,0.0403,0.4712 C-0.0826,0.2754,0.0950,0.0193,0.3119,0.0794 C0.3795,0.0982,0.4523,0.0850,0.5108,0.0431 C0.6977,-0.0907,0.9993,0.1093,0.9361,0.3364 C0.9163,0.4074,0.9119,0.4876,0.9502,0.5487Z" 190 + > 121 191 <animate 122 192 attributeName="d" 123 193 dur="12s" ··· 136 206 137 207 {/* Full-size blob-masked photo */} 138 208 <div 139 - className="relative w-[130%] -mr-[30%] lg:w-[170%] aspect-square lg:-mr-[70%] lg:[transform:translate(2%,-20%)] overflow-hidden" 140 - style={{ clipPath: "url(#hero-blob)" }} 209 + className="relative w-[130%] -mr-[30%] lg:w-[170%] aspect-square lg:-mr-[70%] lg:[transform:translate(2%,-20%)] overflow-visible" 210 + style={{ clipPath: "url(#hero-blob-desktop)" }} 141 211 > 142 212 {slides.map((slide, index) => ( 143 213 <Image ··· 147 217 width={1200} 148 218 height={1200} 149 219 priority={index === 0} 150 - className="absolute inset-[-12%] h-[124%] w-[124%] object-cover" 220 + className="absolute inset-[-5%] h-[124%] w-[124%] object-cover" 151 221 style={{ 152 222 transition: 153 223 "opacity 500ms ease-out, transform 3000ms ease-out",
+1 -2
app/globals.css
··· 115 115 } 116 116 117 117 /* Responsive scaling for stage headings */ 118 - @media (min-width: 1024px) { 118 + @media (min-width: 1401px) { 119 119 .stage-heading-1 { 120 120 font-size: 130px; 121 121 } ··· 255 255 transform: scale(1) translate(4%, 3%); 256 256 } 257 257 } 258 - 259 258 260 259 /* Horizontal scroll for value cards */ 261 260 .hide-scrollbar {