| ... | @@ -17,16 +17,6 @@ const screenshot = await getImage({ | ... | @@ -17,16 +17,6 @@ const screenshot = await getImage({ |
| 17 | quality: 90, | 17 | quality: 90, |
| 18 | }); | 18 | }); |
| 19 | | 19 | |
| 20 | const desktopEndorsementRows = [ | | |
| 21 | tweets.filter((_, index) => index % 2 === 0), | | |
| 22 | tweets.filter((_, index) => index % 2 === 1), | | |
| 23 | ]; | | |
| 24 | | | |
| 25 | const mobileEndorsementRows = [ | | |
| 26 | tweets.filter((_, index) => index % 3 === 0), | | |
| 27 | tweets.filter((_, index) => index % 3 === 1), | | |
| 28 | tweets.filter((_, index) => index % 3 === 2), | | |
| 29 | ]; | | |
| 30 | --- | 20 | --- |
| 31 | | 21 | |
| 32 | <Layout> | 22 | <Layout> |
| ... | @@ -121,80 +111,29 @@ const mobileEndorsementRows = [ | ... | @@ -121,80 +111,29 @@ const mobileEndorsementRows = [ |
| 121 | </div> | 111 | </div> |
| 122 | </div> | 112 | </div> |
| 123 | | 113 | |
| 124 | <div class="endorsement-carousel endorsement-carousel--desktop" aria-label="Developer endorsements"> | 114 | <div class="endorsement-carousel" role="region" aria-label="Developer endorsements" tabindex="0"> |
| 125 | { | 115 | {tweets.map((tweet) => ( |
| 126 | desktopEndorsementRows.map((row, rowIndex) => ( | 116 | <a |
| 127 | <div class:list={["endorsement-row", rowIndex === 1 && "is-reverse"]}> | 117 | class="endorsement-card" |
| 128 | <div class="endorsement-track"> | 118 | href={tweet.link} |
| 129 | {[0, 1].map((groupIndex) => ( | 119 | target="_blank" |
| 130 | <div class="endorsement-track-group" aria-hidden={groupIndex === 1 ? "true" : undefined}> | 120 | rel="noopener noreferrer" |
| 131 | {row.map((tweet) => ( | 121 | aria-label={`Read ${tweet.handle}'s post on X`} |
| 132 | <a | 122 | > |
| 133 | class="endorsement-card" | 123 | <div class="endorsement-author"> |
| 134 | href={tweet.link} | 124 | <img |
| 135 | target="_blank" | 125 | src={`/pfps/${tweet.handle}.webp`} |
| 136 | rel="noopener noreferrer" | 126 | alt="" |
| 137 | aria-label={`Read ${tweet.handle}'s post on X`} | 127 | width="28" |
| 138 | tabindex={groupIndex === 1 ? "-1" : undefined} | 128 | height="28" |
| 139 | > | 129 | loading="lazy" |
| 140 | <div class="endorsement-author"> | 130 | decoding="async" |
| 141 | <img | 131 | /> |
| 142 | src={`/pfps/${tweet.handle}.webp`} | 132 | <div class="endorsement-handle">@{tweet.handle}</div> |
| 143 | alt="" | 133 | </div> |
| 144 | width="28" | 134 | <p>{tweet.excerpt ?? tweet.content}</p> |
| 145 | height="28" | 135 | </a> |
| 146 | loading="lazy" | 136 | ))} |
| 147 | decoding="async" | | |
| 148 | /> | | |
| 149 | <div class="endorsement-handle">@{tweet.handle}</div> | | |
| 150 | </div> | | |
| 151 | <p>{tweet.excerpt ?? tweet.content}</p> | | |
| 152 | </a> | | |
| 153 | ))} | | |
| 154 | </div> | | |
| 155 | ))} | | |
| 156 | </div> | | |
| 157 | </div> | | |
| 158 | )) | | |
| 159 | } | | |
| 160 | </div> | | |
| 161 | | | |
| 162 | <div class="endorsement-carousel endorsement-carousel--mobile" aria-label="Developer endorsements"> | | |
| 163 | { | | |
| 164 | mobileEndorsementRows.map((row, rowIndex) => ( | | |
| 165 | <div class:list={["endorsement-row", rowIndex === 1 && "is-reverse"]}> | | |
| 166 | <div class="endorsement-track"> | | |
| 167 | {[0, 1].map((groupIndex) => ( | | |
| 168 | <div class="endorsement-track-group" aria-hidden={groupIndex === 1 ? "true" : undefined}> | | |
| 169 | {row.map((tweet) => ( | | |
| 170 | <a | | |
| 171 | class="endorsement-card" | | |
| 172 | href={tweet.link} | | |
| 173 | target="_blank" | | |
| 174 | rel="noopener noreferrer" | | |
| 175 | aria-label={`Read ${tweet.handle}'s post on X`} | | |
| 176 | tabindex={groupIndex === 1 ? "-1" : undefined} | | |
| 177 | > | | |
| 178 | <div class="endorsement-author"> | | |
| 179 | <img | | |
| 180 | src={`/pfps/${tweet.handle}.webp`} | | |
| 181 | alt="" | | |
| 182 | width="28" | | |
| 183 | height="28" | | |
| 184 | loading="lazy" | | |
| 185 | decoding="async" | | |
| 186 | /> | | |
| 187 | <div class="endorsement-handle">@{tweet.handle}</div> | | |
| 188 | </div> | | |
| 189 | <p>{tweet.excerpt ?? tweet.content}</p> | | |
| 190 | </a> | | |
| 191 | ))} | | |
| 192 | </div> | | |
| 193 | ))} | | |
| 194 | </div> | | |
| 195 | </div> | | |
| 196 | )) | | |
| 197 | } | | |
| 198 | </div> | 137 | </div> |
| 199 | </section> | 138 | </section> |
| 200 | | 139 | |
| ... | @@ -480,28 +419,6 @@ const mobileEndorsementRows = [ | ... | @@ -480,28 +419,6 @@ const mobileEndorsementRows = [ |
| 480 | } | 419 | } |
| 481 | | 420 | |
| 482 | init(); | 421 | init(); |
| 483 | | | |
| 484 | // Marks lean a few pixels toward the pointer, each by its own depth. | | |
| 485 | const hero = document.querySelector<HTMLElement>(".hero"); | | |
| 486 | const field = document.querySelector<HTMLElement>(".hero-float"); | | |
| 487 | const fine = window.matchMedia("(pointer: fine) and (prefers-reduced-motion: no-preference)").matches; | | |
| 488 | if (hero && field && fine) { | | |
| 489 | let raf = 0; | | |
| 490 | let nx = 0; | | |
| 491 | let ny = 0; | | |
| 492 | const apply = () => { | | |
| 493 | raf = 0; | | |
| 494 | field.style.setProperty("--px", `${(nx * 18).toFixed(1)}px`); | | |
| 495 | field.style.setProperty("--py", `${(ny * 14).toFixed(1)}px`); | | |
| 496 | }; | | |
| 497 | hero.addEventListener("pointermove", (e) => { | | |
| 498 | const r = hero.getBoundingClientRect(); | | |
| 499 | nx = ((e.clientX - r.left) / r.width - 0.5) * 2; | | |
| 500 | ny = ((e.clientY - r.top) / r.height - 0.5) * 2; | | |
| 501 | if (!raf) raf = requestAnimationFrame(apply); | | |
| 502 | }); | | |
| 503 | hero.addEventListener("pointerleave", () => { nx = 0; ny = 0; if (!raf) raf = requestAnimationFrame(apply); }); | | |
| 504 | } | | |
| 505 | </script> | 422 | </script> |
| 506 | | 423 | |
| 507 | <style> | 424 | <style> |
| ... | @@ -673,18 +590,12 @@ const mobileEndorsementRows = [ | ... | @@ -673,18 +590,12 @@ const mobileEndorsementRows = [ |
| 673 | :global([data-platform="linux"]) .dl-icon--linux { display: block; } | 590 | :global([data-platform="linux"]) .dl-icon--linux { display: block; } |
| 674 | :global(html:not([data-platform])) .dl-icon--apple { display: block; } | 591 | :global(html:not([data-platform])) .dl-icon--apple { display: block; } |
| 675 | | 592 | |
| 676 | /* Floating harness marks. Six points on an ellipse around the hero copy: | 593 | /* Harness marks stay in their final positions after the entry animation. */ |
| 677 | two small at the top, two high and wide, two low and close. Symmetric, | | |
| 678 | and none of them touch the screenshot. Each mark flies in from its own | | |
| 679 | side on load, then drifts on its own period, and the whole set leans a | | |
| 680 | few pixels toward the pointer. */ | | |
| 681 | .hero-float { | 594 | .hero-float { |
| 682 | position: absolute; | 595 | position: absolute; |
| 683 | inset: 0; | 596 | inset: 0; |
| 684 | pointer-events: none; | 597 | pointer-events: none; |
| 685 | z-index: 1; | 598 | z-index: 1; |
| 686 | --px: 0px; | | |
| 687 | --py: 0px; | | |
| 688 | } | 599 | } |
| 689 | | 600 | |
| 690 | .hero-float-mark { | 601 | .hero-float-mark { |
| ... | @@ -694,8 +605,6 @@ const mobileEndorsementRows = [ | ... | @@ -694,8 +605,6 @@ const mobileEndorsementRows = [ |
| 694 | width: var(--s); | 605 | width: var(--s); |
| 695 | height: var(--s); | 606 | height: var(--s); |
| 696 | margin-left: calc(var(--s) / -2); | 607 | margin-left: calc(var(--s) / -2); |
| 697 | translate: calc(var(--px) * var(--depth)) calc(var(--py) * var(--depth)); | | |
| 698 | transition: translate 0.7s cubic-bezier(0.2, 0.7, 0.2, 1); | | |
| 699 | } | 608 | } |
| 700 | | 609 | |
| 701 | .hero-float-card { | 610 | .hero-float-card { |
| ... | @@ -709,9 +618,7 @@ const mobileEndorsementRows = [ | ... | @@ -709,9 +618,7 @@ const mobileEndorsementRows = [ |
| 709 | box-shadow: | 618 | box-shadow: |
| 710 | 0 20px 48px -16px rgba(0, 0, 0, 0.65), | 619 | 0 20px 48px -16px rgba(0, 0, 0, 0.65), |
| 711 | 0 2px 0 0 rgba(255, 255, 255, 0.04) inset; | 620 | 0 2px 0 0 rgba(255, 255, 255, 0.04) inset; |
| 712 | animation: | 621 | animation: mark-in 1s cubic-bezier(0.2, 0.7, 0.2, 1) var(--d) both; |
| 713 | mark-in 1s cubic-bezier(0.2, 0.7, 0.2, 1) var(--d) both, | | |
| 714 | mark-drift var(--dur) ease-in-out calc(var(--d) + 1s) infinite; | | |
| 715 | } | 622 | } |
| 716 | | 623 | |
| 717 | .hero-float-card img { | 624 | .hero-float-card img { |
| ... | @@ -730,21 +637,16 @@ const mobileEndorsementRows = [ | ... | @@ -730,21 +637,16 @@ const mobileEndorsementRows = [ |
| 730 | from { opacity: 0; transform: translate(var(--fx), var(--fy)) rotate(calc(var(--rot) + 24deg)) scale(0.6); } | 637 | from { opacity: 0; transform: translate(var(--fx), var(--fy)) rotate(calc(var(--rot) + 24deg)) scale(0.6); } |
| 731 | to { opacity: 1; transform: translate(0, 0) rotate(var(--rot)) scale(1); } | 638 | to { opacity: 1; transform: translate(0, 0) rotate(var(--rot)) scale(1); } |
| 732 | } | 639 | } |
| 733 | @keyframes mark-drift { | | |
| 734 | 0%, 100% { transform: translate(0, 0) rotate(var(--rot)); } | | |
| 735 | 50% { transform: translate(var(--dx), var(--dy)) rotate(calc(var(--rot) + var(--wob))); } | | |
| 736 | } | | |
| 737 | | 640 | |
| 738 | .hf-opencode { --x: max(-300px, -19vw); --y: 20px; --s: 64px; --rot: 6deg; --depth: 0.6; --d: 620ms; --dur: 8.5s; --dx: 2px; --dy: -8px; --wob: 3deg; --fx: -40px; --fy: -80px; } | 641 | .hf-opencode { --x: max(-300px, -19vw); --y: 20px; --s: 64px; --rot: 6deg; --d: 620ms; --fx: -40px; --fy: -80px; } |
| 739 | .hf-antigravity { --x: min(300px, 19vw); --y: 20px; --s: 64px; --rot: -5deg; --depth: 0.6; --d: 580ms; --dur: 9.8s; --dx: -2px; --dy: -9px; --wob: -3deg; --fx: 40px; --fy: -80px; } | 642 | .hf-antigravity { --x: min(300px, 19vw); --y: 20px; --s: 64px; --rot: -5deg; --d: 580ms; --fx: 40px; --fy: -80px; } |
| 740 | .hf-claude { --x: max(-560px, -38vw); --y: 128px; --s: 96px; --rot: -8deg; --depth: 1.2; --d: 380ms; --dur: 10.5s; --dx: 4px; --dy: -12px; --wob: -2deg; --fx: -160px; --fy: -40px; --tint: rgba(217, 119, 87, 0.22); } | 643 | .hf-claude { --x: max(-560px, -38vw); --y: 128px; --s: 96px; --rot: -8deg; --d: 380ms; --fx: -160px; --fy: -40px; --tint: rgba(217, 119, 87, 0.22); } |
| 741 | .hf-codex { --x: min(560px, 38vw); --y: 128px; --s: 96px; --rot: 7deg; --depth: 1.2; --d: 460ms; --dur: 9.2s; --dx: -4px; --dy: -10px; --wob: 2deg; --fx: 160px; --fy: -40px; } | 644 | .hf-codex { --x: min(560px, 38vw); --y: 128px; --s: 96px; --rot: 7deg; --d: 460ms; --fx: 160px; --fy: -40px; } |
| 742 | .hf-grok { --x: max(-440px, -30vw); --y: 440px; --s: 80px; --rot: 4deg; --depth: 0.9; --d: 540ms; --dur: 11.5s; --dx: 3px; --dy: -9px; --wob: -3deg; --fx: -120px; --fy: 80px; } | 645 | .hf-grok { --x: max(-440px, -30vw); --y: 440px; --s: 80px; --rot: 4deg; --d: 540ms; --fx: -120px; --fy: 80px; } |
| 743 | .hf-cursor { --x: min(440px, 30vw); --y: 440px; --s: 80px; --rot: -5deg; --depth: 0.9; --d: 700ms; --dur: 7.8s; --dx: -3px; --dy: -11px; --wob: 3deg; --fx: 120px; --fy: 80px; } | 646 | .hf-cursor { --x: min(440px, 30vw); --y: 440px; --s: 80px; --rot: -5deg; --d: 700ms; --fx: 120px; --fy: 80px; } |
| 744 | | 647 | |
| 745 | @media (prefers-reduced-motion: reduce) { | 648 | @media (prefers-reduced-motion: reduce) { |
| 746 | .hero-float-card { animation: none; transform: rotate(var(--rot)); } | 649 | .hero-float-card { animation: none; transform: rotate(var(--rot)); } |
| 747 | .hero-float-mark { transition: none; } | | |
| 748 | } | 650 | } |
| 749 | | 651 | |
| 750 | @media (max-width: 1180px) { | 652 | @media (max-width: 1180px) { |
| ... | @@ -767,12 +669,12 @@ const mobileEndorsementRows = [ | ... | @@ -767,12 +669,12 @@ const mobileEndorsementRows = [ |
| 767 | } | 669 | } |
| 768 | | 670 | |
| 769 | /* Four above the headline, two beside the CTA. Keeps the copy clear. */ | 671 | /* Four above the headline, two beside the CTA. Keeps the copy clear. */ |
| 770 | .hf-claude { --x: -37vw; --y: 44px; --s: 64px; --rot: -10deg; } | 672 | .hf-claude { --x: -37vw; --y: 44px; --s: 64px; --rot: -10deg; } |
| 771 | .hf-opencode { --x: -12vw; --y: 52px; --s: 56px; --rot: -4deg; } | 673 | .hf-opencode { --x: -12vw; --y: 52px; --s: 56px; --rot: -4deg; } |
| 772 | .hf-antigravity { --x: 12vw; --y: 52px; --s: 56px; --rot: 5deg; } | 674 | .hf-antigravity { --x: 12vw; --y: 52px; --s: 56px; --rot: 5deg; } |
| 773 | .hf-codex { --x: 37vw; --y: 44px; --s: 64px; --rot: 8deg; } | 675 | .hf-codex { --x: 37vw; --y: 44px; --s: 64px; --rot: 8deg; } |
| 774 | .hf-grok { --x: -36vw; --y: 474px; --s: 64px; --rot: -6deg; } | 676 | .hf-grok { --x: -36vw; --y: 474px; --s: 64px; --rot: -6deg; } |
| 775 | .hf-cursor { --x: 36vw; --y: 474px; --s: 64px; --rot: 7deg; } | 677 | .hf-cursor { --x: 36vw; --y: 474px; --s: 64px; --rot: 7deg; } |
| 776 | } | 678 | } |
| 777 | | 679 | |
| 778 | @media (max-width: 340px) { | 680 | @media (max-width: 340px) { |
| ... | @@ -896,67 +798,19 @@ const mobileEndorsementRows = [ | ... | @@ -896,67 +798,19 @@ const mobileEndorsementRows = [ |
| 896 | } | 798 | } |
| 897 | | 799 | |
| 898 | .endorsement-carousel { | 800 | .endorsement-carousel { |
| 899 | position: relative; | 801 | display: grid; |
| 900 | display: flex; | 802 | grid-auto-flow: column; |
| 901 | flex-direction: column; | 803 | grid-template-rows: repeat(2, 132px); |
| 902 | gap: 12px; | 804 | grid-auto-columns: clamp(264px, 22vw, 308px); |
| 903 | } | | |
| 904 | | | |
| 905 | .endorsement-carousel--mobile { | | |
| 906 | display: none; | | |
| 907 | } | | |
| 908 | | | |
| 909 | .endorsement-carousel::before, | | |
| 910 | .endorsement-carousel::after { | | |
| 911 | content: ""; | | |
| 912 | position: absolute; | | |
| 913 | top: 0; | | |
| 914 | bottom: 0; | | |
| 915 | z-index: 2; | | |
| 916 | width: max(48px, calc((100vw - 1240px) / 2 + 56px)); | | |
| 917 | pointer-events: none; | | |
| 918 | } | | |
| 919 | | | |
| 920 | .endorsement-carousel::before { | | |
| 921 | left: 0; | | |
| 922 | background: linear-gradient(90deg, var(--bg), transparent); | | |
| 923 | } | | |
| 924 | | | |
| 925 | .endorsement-carousel::after { | | |
| 926 | right: 0; | | |
| 927 | background: linear-gradient(270deg, var(--bg), transparent); | | |
| 928 | } | | |
| 929 | | | |
| 930 | .endorsement-row { | | |
| 931 | overflow: hidden; | | |
| 932 | } | | |
| 933 | | | |
| 934 | .endorsement-track { | | |
| 935 | width: max-content; | | |
| 936 | display: flex; | | |
| 937 | animation: endorsementMarquee 76s linear infinite; | | |
| 938 | will-change: transform; | | |
| 939 | } | | |
| 940 | | | |
| 941 | .endorsement-track-group { | | |
| 942 | display: flex; | | |
| 943 | gap: 12px; | 805 | gap: 12px; |
| 944 | padding-right: 12px; | 806 | overflow-x: auto; |
| 945 | } | 807 | overscroll-behavior-x: contain; |
| 946 | | 808 | padding: 4px max(32px, calc((100vw - 1240px) / 2 + 32px)); |
| 947 | .endorsement-row.is-reverse .endorsement-track { | 809 | scroll-padding-inline: max(32px, calc((100vw - 1240px) / 2 + 32px)); |
| 948 | animation-direction: reverse; | | |
| 949 | animation-duration: 84s; | | |
| 950 | } | | |
| 951 | | | |
| 952 | .endorsement-carousel:hover .endorsement-track, | | |
| 953 | .endorsement-carousel:focus-within .endorsement-track { | | |
| 954 | animation-play-state: paused; | | |
| 955 | } | 810 | } |
| 956 | | 811 | |
| 957 | .endorsement-card { | 812 | .endorsement-card { |
| 958 | position: relative; | 813 | position: relative; |
| 959 | width: clamp(264px, 22vw, 308px); | | |
| 960 | height: 132px; | 814 | height: 132px; |
| 961 | padding: 14px 16px; | 815 | padding: 14px 16px; |
| 962 | display: flex; | 816 | display: flex; |
| ... | @@ -1134,7 +988,6 @@ const mobileEndorsementRows = [ | ... | @@ -1134,7 +988,6 @@ const mobileEndorsementRows = [ |
| 1134 | width: 7px; height: 14px; | 988 | width: 7px; height: 14px; |
| 1135 | background: var(--fg); | 989 | background: var(--fg); |
| 1136 | margin-left: 4px; | 990 | margin-left: 4px; |
| 1137 | animation: blink 1s steps(2) infinite; | | |
| 1138 | } | 991 | } |
| 1139 | | 992 | |
| 1140 | .open-pitch { | 993 | .open-pitch { |
| ... | @@ -1252,19 +1105,6 @@ const mobileEndorsementRows = [ | ... | @@ -1252,19 +1105,6 @@ const mobileEndorsementRows = [ |
| 1252 | margin-bottom: 24px; | 1105 | margin-bottom: 24px; |
| 1253 | } | 1106 | } |
| 1254 | | 1107 | |
| 1255 | @keyframes blink { | | |
| 1256 | 50% { opacity: 0; } | | |
| 1257 | } | | |
| 1258 | | | |
| 1259 | @keyframes endorsementMarquee { | | |
| 1260 | from { | | |
| 1261 | transform: translateX(0); | | |
| 1262 | } | | |
| 1263 | to { | | |
| 1264 | transform: translateX(-50%); | | |
| 1265 | } | | |
| 1266 | } | | |
| 1267 | | | |
| 1268 | /* ── Responsive ────────────────────────────────────────── */ | 1108 | /* ── Responsive ────────────────────────────────────────── */ |
| 1269 | @media (max-width: 960px) { | 1109 | @media (max-width: 960px) { |
| 1270 | .hero-preview { | 1110 | .hero-preview { |
| ... | @@ -1291,25 +1131,13 @@ const mobileEndorsementRows = [ | ... | @@ -1291,25 +1131,13 @@ const mobileEndorsementRows = [ |
| 1291 | .endorsements-head { | 1131 | .endorsements-head { |
| 1292 | margin-bottom: 40px; | 1132 | margin-bottom: 40px; |
| 1293 | } | 1133 | } |
| 1294 | .endorsement-carousel--desktop { | 1134 | .endorsement-carousel { |
| 1295 | display: none; | 1135 | grid-template-rows: repeat(3, 132px); |
| 1296 | } | 1136 | grid-auto-columns: 268px; |
| 1297 | .endorsement-carousel--mobile { | 1137 | padding-inline: 20px; |
| 1298 | display: flex; | 1138 | scroll-padding-inline: 20px; |
| 1299 | } | | |
| 1300 | .endorsement-carousel::before, | | |
| 1301 | .endorsement-carousel::after { | | |
| 1302 | width: 28px; | | |
| 1303 | } | | |
| 1304 | .endorsement-track { | | |
| 1305 | animation-duration: 68s; | | |
| 1306 | } | | |
| 1307 | .endorsement-row.is-reverse .endorsement-track { | | |
| 1308 | animation-duration: 76s; | | |
| 1309 | } | 1139 | } |
| 1310 | .endorsement-card { | 1140 | .endorsement-card { |
| 1311 | width: 268px; | | |
| 1312 | height: 132px; | | |
| 1313 | padding: 14px; | 1141 | padding: 14px; |
| 1314 | } | 1142 | } |
| 1315 | .endorsement-card p { | 1143 | .endorsement-card p { |