diff --git a/apps/web/public/images/partners/global/orca-security.svg b/apps/web/public/images/partners/global/orca-security.svg new file mode 100644 index 000000000..f1de20455 --- /dev/null +++ b/apps/web/public/images/partners/global/orca-security.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/components/sections/home/HeroHeading.tsx b/apps/web/src/components/sections/home/HeroHeading.tsx index f5d18ed5c..12f88ba0b 100644 --- a/apps/web/src/components/sections/home/HeroHeading.tsx +++ b/apps/web/src/components/sections/home/HeroHeading.tsx @@ -23,7 +23,7 @@ export function HeroHeading() { return (

`region-tab-${region.replace(/\s+/g, "-").toLowerCase()}`; +const ALLIANCES: Partner[] = [ + { name: "Nutanix", country: "San Jose, USA", logo: "/images/partners/global/nutanix.jpg" }, + { name: "Sysdig", country: "San Francisco, USA", logo: "/images/partners/global/sysdig.png" }, + { + name: "Orca Security", + country: "Portland, USA", + logo: "/images/partners/global/orca-security.svg", + }, + { + name: "Zensar", + country: "San Jose, USA", + logo: "/images/partners/global/zensar.svg", + invertOnLight: true, + }, +]; -const PARTNERS: Record = { - Global: [ - { name: "Nutanix", country: "San Jose, USA", logo: "/images/partners/global/nutanix.jpg" }, - { name: "Sysdig", country: "San Francisco, USA", logo: "/images/partners/global/sysdig.png" }, - { - name: "Zensar", - country: "San Jose, USA", - logo: "/images/partners/global/zensar.svg", - invertOnLight: true, - }, - ], - "Asia Pacific": [ - { name: "Hitachi Systems", country: "India", logo: "/images/partners/global/hitachi.webp" }, - { name: "Citius Cloud", country: "India", logo: "/images/partners/global/citius.webp" }, - { name: "CyberNx", country: "India", logo: "/images/partners/global/cybernx.webp" }, - { name: "eCaps", country: "India", logo: "/images/partners/global/ecaps.webp" }, - { name: "SEESEC", country: "India", logo: "/images/partners/global/seesec.webp" }, - { name: "Imperium", country: "Singapore", logo: "/images/partners/global/imperium.webp" }, - { name: "R-Tech", country: "Indonesia", logo: "/images/partners/global/rtech.webp" }, - { name: "eSec Forte", country: "India", logo: "/images/partners/global/sec-forte.webp" }, - { name: "Raksha Technologies", country: "India", logo: "/images/partners/global/raksha.webp" }, - { name: "Flying Stars", country: "India", logo: "/images/partners/global/flying-stars.webp" }, - { name: "Softcell", country: "India", logo: "/images/partners/global/softcell.png" }, - ], - Europe: [ - { name: "NGIT", country: "Nordics", logo: "/images/partners/global/ngit.webp" }, - { - name: "Mactech Consultants", - country: "London, UK", - logo: "/images/partners/global/MactechProperties_logo.png", - }, - ], - "Middle East": [ - { - name: "Surakshate", - country: "UAE", - logo: "/images/partners/global/surakshate.webp", - invertOnLight: true, - }, - { - name: "Help AG", - country: "Dubai, UAE", - logo: "/images/partners/global/help-ag-logo-2048x603.png", - }, - { - name: "Paramount Assure", - country: "Dubai, UAE", - logo: "/images/partners/global/paramount.png", - }, - { - name: "Gulf IT", - country: "Dubai, UAE", - logo: "/images/partners/global/gulf-it.svg", - }, - { - name: "Sechpoint Tech", - country: "Dubai, UAE", - logo: "/images/partners/global/sechpoint.svg", - }, - { - name: "Sandbox Security", - country: "Dubai, UAE", - logo: "/images/partners/global/sandbox-security.png", - }, - { - name: "TecCentric", - country: "Doha, Qatar", - logo: "/images/partners/global/teccentric.svg", - }, - ], - "North America": [ - { - name: "PacGenesis", - country: "USA", - logo: "/images/partners/global/pacgenesis.png", - }, - { - name: "Fortifire", - country: "North America", - logo: "/images/partners/global/fortifire-icon.webp", - wordmark: "FORTIFIRE", - }, - { - name: "Zensar", - country: "San Jose, USA", - logo: "/images/partners/global/zensar.svg", - invertOnLight: true, - }, - ], -}; +const CHANNEL: RegionGroup[] = [ + { + region: "Asia Pacific", + partners: [ + { name: "Hitachi Systems", country: "India", logo: "/images/partners/global/hitachi.webp" }, + { name: "Citius Cloud", country: "India", logo: "/images/partners/global/citius.webp" }, + { name: "CyberNx", country: "India", logo: "/images/partners/global/cybernx.webp" }, + { name: "eCaps", country: "India", logo: "/images/partners/global/ecaps.webp" }, + { name: "SEESEC", country: "India", logo: "/images/partners/global/seesec.webp" }, + { name: "eSec Forte", country: "India", logo: "/images/partners/global/sec-forte.webp" }, + { + name: "Raksha Technologies", + country: "India", + logo: "/images/partners/global/raksha.webp", + }, + { + name: "Flying Stars", + country: "India", + logo: "/images/partners/global/flying-stars.webp", + }, + { name: "Softcell", country: "India", logo: "/images/partners/global/softcell.png" }, + { name: "Imperium", country: "Singapore", logo: "/images/partners/global/imperium.webp" }, + { name: "R-Tech", country: "Indonesia", logo: "/images/partners/global/rtech.webp" }, + ], + }, + { + region: "Middle East & Africa", + partners: [ + { + name: "Help AG", + country: "Dubai, UAE", + logo: "/images/partners/global/help-ag-logo-2048x603.png", + }, + { + name: "Paramount Assure", + country: "Dubai, UAE", + logo: "/images/partners/global/paramount.png", + }, + { name: "Gulf IT", country: "Dubai, UAE", logo: "/images/partners/global/gulf-it.svg" }, + { + name: "Sandbox Security", + country: "Dubai, UAE", + logo: "/images/partners/global/sandbox-security.png", + }, + { + name: "Surakshate", + country: "UAE", + logo: "/images/partners/global/surakshate.webp", + invertOnLight: true, + }, + { name: "TecCentric", country: "Doha, Qatar", logo: "/images/partners/global/teccentric.svg" }, + { + name: "Sechpoint Tech", + country: "Nairobi, Kenya", + logo: "/images/partners/global/sechpoint.svg", + }, + ], + }, + { + region: "Europe", + partners: [ + { name: "NGIT", country: "Nordics", logo: "/images/partners/global/ngit.webp" }, + { + name: "Mactech Consultants", + country: "London, UK", + logo: "/images/partners/global/MactechProperties_logo.png", + }, + ], + }, + { + region: "North America", + partners: [ + { name: "PacGenesis", country: "USA", logo: "/images/partners/global/pacgenesis.png" }, + { + name: "Fortifire", + country: "North America", + logo: "/images/partners/global/fortifire-icon.webp", + wordmark: "FORTIFIRE", + }, + ], + }, +]; export function PartnersNetwork(): React.ReactElement { const reduce = useReducedMotion(); - const [active, setActive] = useState("Global"); - const [expanded, setExpanded] = useState(false); - const partners = PARTNERS[active]; - const basePartners = partners.slice(0, INITIAL_VISIBLE); - const extraPartners = partners.slice(INITIAL_VISIBLE); - - // The tab bar is a single horizontal row that scrolls (no wrap) when it overflows a - // narrow viewport. Auto-center the active tab so the selection is never stuck off-screen; - // the first tab ("Global") rests at the left edge so the bar reads from the start. - const scrollerRef = useRef(null); + const [active, setActive] = useState("technology-alliances"); + const showAlliances = active === "technology-alliances"; + const activeIndex = showAlliances ? 0 : 1; + // The panel fades only when the reader switches tracks. On first paint the section's own + // FadeUp wrapper owns the entrance, so a second fade here would double up. + const mounted = useRef(false); useEffect(() => { - const scroller = scrollerRef.current; - if (!scroller) return; - if (active === "Global") { - scroller.scrollTo({ left: 0, behavior: "smooth" }); - return; - } - const btn = scroller.querySelector(`#${tabId(active)}`); - if (!btn) return; - const target = btn.offsetLeft - (scroller.clientWidth - btn.offsetWidth) / 2; - scroller.scrollTo({ left: Math.max(0, target), behavior: "smooth" }); - }, [active]); - - const selectRegion = (region: Region): void => { - setActive(region); - setExpanded(false); - }; + mounted.current = true; + }, []); return (
+ {/* Two equal grid columns let the sliding indicator be exactly 50% wide with no + measurement: translateX(100%) lands it on the second column. */}
-
- {REGIONS.map((region) => { - const isActive = region === active; - return ( - - ); - })} -
+ /> + {TRACKS.map((track) => { + const isActive = track.id === active; + return ( + + ); + })}
-
- {partners.length === 0 ? ( -
- We're actively expanding in this region. Check back soon. + {/* Swapping the panel changes the section height by ~1500px (4 cards vs 22). Doing + that in one frame, with an opacity-only fade and no exit phase, means the reader + sees a single layout change instead of a blank gap followed by a jump. */} + + {showAlliances ? ( +
+ {ALLIANCES.map((partner) => ( + + ))}
) : ( - basePartners.map((p) => ) +
+ {CHANNEL.map((group) => ( +
+
+

+ {group.region} +

+ +
+
+ {group.partners.map((partner) => ( + + ))} +
+
+ ))} +
)} - {/* Mobile/tablet: show every partner up front — no View More toggle below lg. */} -
- {extraPartners.map((p) => ( - - ))} -
- {/* Desktop (lg+): reveal the remaining partners via View More, with the spring entrance. */} -
- - {expanded && - extraPartners.map((p, i) => ( - - - - ))} - -
-
- - {partners.length > INITIAL_VISIBLE && ( -
- -
- )} +
); } -function PartnerCard({ partner }: { partner: Partner }): React.ReactElement { +function PartnerCard({ + partner, + headingLevel: NameTag, +}: { + partner: Partner; + headingLevel: "h3" | "h4"; +}): React.ReactElement { return (
-
+
{partner.logo.endsWith(".svg") ? ( // SVG logos render via plain — next/image needs dangerouslyAllowSVG, and // vectors gain nothing from the optimizer. @@ -334,7 +333,7 @@ function PartnerCard({ partner }: { partner: Partner }): React.ReactElement { height={36} loading="lazy" decoding="async" - className="h-full w-auto object-contain object-left" + className="h-full w-auto max-w-full object-contain object-left" style={partner.invertOnLight ? { filter: "brightness(0)" } : undefined} /> ) : ( @@ -344,7 +343,7 @@ function PartnerCard({ partner }: { partner: Partner }): React.ReactElement { width={partner.wordmark ? 36 : 160} height={36} sizes={partner.wordmark ? "36px" : "160px"} - className="h-full w-auto object-contain object-left" + className="h-full w-auto max-w-full object-contain object-left" style={partner.invertOnLight ? { filter: "brightness(0)" } : undefined} /> )} @@ -358,12 +357,12 @@ function PartnerCard({ partner }: { partner: Partner }): React.ReactElement { ) : null}
-

{partner.name} -

+

{partner.country}