@rizom/site-rizom-work 0.2.0-alpha.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +42 -0
- package/src/index.ts +1 -0
- package/src/layout.tsx +82 -0
- package/src/link-targets.tsx +25 -0
- package/src/routes.ts +61 -0
- package/src/sections/bridge/layout.tsx +31 -0
- package/src/sections/bridge/schema.ts +11 -0
- package/src/sections/closer/layout.tsx +50 -0
- package/src/sections/closer/schema.ts +13 -0
- package/src/sections/credibility/layout.tsx +45 -0
- package/src/sections/credibility/schema.ts +10 -0
- package/src/sections/hero/layout.tsx +170 -0
- package/src/sections/hero/schema.ts +20 -0
- package/src/sections/ownership/layout.tsx +76 -0
- package/src/sections/ownership/schema.ts +11 -0
- package/src/sections/personas/layout.tsx +45 -0
- package/src/sections/personas/schema.ts +16 -0
- package/src/sections/problem/layout.tsx +30 -0
- package/src/sections/problem/schema.ts +10 -0
- package/src/sections/proof/layout.tsx +124 -0
- package/src/sections/proof/schema.ts +14 -0
- package/src/sections/styles.ts +16 -0
- package/src/sections/workshop/layout.tsx +66 -0
- package/src/sections/workshop/schema.ts +20 -0
- package/src/site-content.ts +227 -0
- package/src/site.ts +19 -0
- package/src/theme.css +152 -0
- package/src/types.d.ts +4 -0
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rizom/site-rizom-work",
|
|
3
|
+
"version": "0.2.0-alpha.142",
|
|
4
|
+
"description": "Rizom Work site package for hosted Rover deployments",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.ts"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"test": "bun test",
|
|
15
|
+
"lint": "eslint . --max-warnings 0",
|
|
16
|
+
"lint:fix": "eslint . --max-warnings 0 --fix",
|
|
17
|
+
"postpack": "publish-manifest restore",
|
|
18
|
+
"prepack": "publish-manifest prepare"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@rizom/site": "0.2.0-alpha.142",
|
|
22
|
+
"@rizom/site-rizom": "0.2.0-alpha.142",
|
|
23
|
+
"preact": "^10.27.2"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/rizom-ai/brains.git",
|
|
31
|
+
"directory": "sites/rizom-work"
|
|
32
|
+
},
|
|
33
|
+
"license": "Apache-2.0",
|
|
34
|
+
"author": "Yeehaa <yeehaa@rizom.ai> (https://rizom.ai)",
|
|
35
|
+
"homepage": "https://github.com/rizom-ai/brains/tree/main/sites/rizom-work#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/rizom-ai/brains/issues"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"bun": ">=1.3.3"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { rizomWorkSite, rizomWorkSite as default } from "./site";
|
package/src/layout.tsx
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX, ComponentChildren } from "preact";
|
|
3
|
+
import type { RizomLink, SiteLayoutInfo } from "@rizom/site";
|
|
4
|
+
import {
|
|
5
|
+
Footer,
|
|
6
|
+
Header,
|
|
7
|
+
RizomFrame,
|
|
8
|
+
SideNav,
|
|
9
|
+
socialLinksToRizomLinks,
|
|
10
|
+
} from "@rizom/site-rizom";
|
|
11
|
+
import { isNewTabHref, BOOKING_HREF, QUIZ_HREF } from "./link-targets";
|
|
12
|
+
|
|
13
|
+
interface WorkLayoutProps {
|
|
14
|
+
sections: ComponentChildren;
|
|
15
|
+
siteInfo: Pick<SiteLayoutInfo, "socialLinks"> & {
|
|
16
|
+
copyright?: string;
|
|
17
|
+
cta?: {
|
|
18
|
+
buttonLink: string;
|
|
19
|
+
buttonText: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const NAV_LINKS: RizomLink[] = [
|
|
25
|
+
{ href: "#workshop", label: "Workshop" },
|
|
26
|
+
{ href: "#cta", label: "Contact" },
|
|
27
|
+
{ href: "#about", label: "About" },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const PRIMARY_CTA: RizomLink = {
|
|
31
|
+
href: QUIZ_HREF,
|
|
32
|
+
label: "Take the quiz",
|
|
33
|
+
external: true,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const FOOTER_LINKS: RizomLink[] = [
|
|
37
|
+
{ href: "#workshop", label: "The workshops" },
|
|
38
|
+
{ href: "https://rizom.foundation", label: "The research" },
|
|
39
|
+
{ href: "https://rizom.ai", label: "The platform" },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const SIDE_NAV_ITEMS = [
|
|
43
|
+
{ href: "#hero", label: "Intro" },
|
|
44
|
+
{ href: "#problem", label: "Problem" },
|
|
45
|
+
{ href: "#workshop", label: "Workshop" },
|
|
46
|
+
{ href: "#methodology", label: "Methodology" },
|
|
47
|
+
{ href: "#personas", label: "Fit" },
|
|
48
|
+
{ href: "#proof", label: "Proof" },
|
|
49
|
+
{ href: "#about", label: "About" },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export const WorkLayout = ({
|
|
53
|
+
sections,
|
|
54
|
+
siteInfo,
|
|
55
|
+
}: WorkLayoutProps): JSX.Element => (
|
|
56
|
+
<RizomFrame>
|
|
57
|
+
<Header
|
|
58
|
+
brandSuffix="work"
|
|
59
|
+
navLinks={NAV_LINKS}
|
|
60
|
+
primaryCta={
|
|
61
|
+
siteInfo.cta
|
|
62
|
+
? {
|
|
63
|
+
href: siteInfo.cta.buttonLink,
|
|
64
|
+
label: siteInfo.cta.buttonText,
|
|
65
|
+
external: isNewTabHref(siteInfo.cta.buttonLink),
|
|
66
|
+
}
|
|
67
|
+
: PRIMARY_CTA
|
|
68
|
+
}
|
|
69
|
+
/>
|
|
70
|
+
<SideNav items={SIDE_NAV_ITEMS} />
|
|
71
|
+
<main>{sections}</main>
|
|
72
|
+
<Footer
|
|
73
|
+
brandSuffix="work"
|
|
74
|
+
metaLabel={siteInfo.copyright ?? ""}
|
|
75
|
+
links={[
|
|
76
|
+
...FOOTER_LINKS,
|
|
77
|
+
...socialLinksToRizomLinks(siteInfo, ["linkedin"]),
|
|
78
|
+
{ href: BOOKING_HREF, label: "Email", external: true },
|
|
79
|
+
]}
|
|
80
|
+
/>
|
|
81
|
+
</RizomFrame>
|
|
82
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX } from "preact";
|
|
3
|
+
import { Button, type ButtonProps } from "@rizom/site-rizom";
|
|
4
|
+
|
|
5
|
+
export const QUIZ_HREF = "https://form.typeform.com/to/NGqo9Fnf";
|
|
6
|
+
export const BOOKING_HREF = "mailto:contact@rizom.ai";
|
|
7
|
+
|
|
8
|
+
const NEW_TAB_HREFS = new Set([QUIZ_HREF, BOOKING_HREF]);
|
|
9
|
+
|
|
10
|
+
export function isNewTabHref(href: string): boolean {
|
|
11
|
+
return NEW_TAB_HREFS.has(href);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function newTabProps(href: string): { target?: string; rel?: string } {
|
|
15
|
+
return isNewTabHref(href)
|
|
16
|
+
? { target: "_blank", rel: "noopener noreferrer" }
|
|
17
|
+
: {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type WorkButtonVariant = NonNullable<ButtonProps["variant"]>;
|
|
21
|
+
export type WorkButtonSize = NonNullable<ButtonProps["size"]>;
|
|
22
|
+
|
|
23
|
+
export const WorkButton = (props: ButtonProps): JSX.Element => (
|
|
24
|
+
<Button {...newTabProps(props.href)} {...props} />
|
|
25
|
+
);
|
package/src/routes.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { RouteDefinitionInput } from "@rizom/site";
|
|
2
|
+
|
|
3
|
+
export const workRoutes: RouteDefinitionInput[] = [
|
|
4
|
+
{
|
|
5
|
+
id: "home",
|
|
6
|
+
path: "/",
|
|
7
|
+
layout: "default",
|
|
8
|
+
navigation: {
|
|
9
|
+
show: false,
|
|
10
|
+
slot: "secondary",
|
|
11
|
+
priority: 10,
|
|
12
|
+
},
|
|
13
|
+
sections: [
|
|
14
|
+
{
|
|
15
|
+
id: "hero",
|
|
16
|
+
template: "landing-page:hero",
|
|
17
|
+
content: {},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "problem",
|
|
21
|
+
template: "landing-page:problem",
|
|
22
|
+
content: {},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "workshop",
|
|
26
|
+
template: "landing-page:workshop",
|
|
27
|
+
content: {},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "credibility",
|
|
31
|
+
template: "landing-page:credibility",
|
|
32
|
+
content: {},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "personas",
|
|
36
|
+
template: "landing-page:personas",
|
|
37
|
+
content: {},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "proof",
|
|
41
|
+
template: "landing-page:proof",
|
|
42
|
+
content: {},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "ownership",
|
|
46
|
+
template: "landing-page:ownership",
|
|
47
|
+
content: {},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "mission",
|
|
51
|
+
template: "landing-page:closer",
|
|
52
|
+
content: {},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "ecosystem",
|
|
56
|
+
template: "landing-page:ecosystem",
|
|
57
|
+
content: {},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX } from "preact";
|
|
3
|
+
import { Section } from "@rizom/site-rizom";
|
|
4
|
+
import { WORK_KICKER, WORK_RULE } from "../styles";
|
|
5
|
+
import type { BridgeContent } from "./schema";
|
|
6
|
+
|
|
7
|
+
export const BridgeLayout = ({
|
|
8
|
+
kicker,
|
|
9
|
+
body,
|
|
10
|
+
linkLabel,
|
|
11
|
+
linkHref,
|
|
12
|
+
}: BridgeContent): JSX.Element => {
|
|
13
|
+
return (
|
|
14
|
+
<Section
|
|
15
|
+
id="bridge"
|
|
16
|
+
className="reveal pt-20 pb-24 text-center max-[768px]:pt-14 max-[768px]:pb-[72px]"
|
|
17
|
+
>
|
|
18
|
+
<div className={`${WORK_RULE} mb-12`} />
|
|
19
|
+
<span className={`${WORK_KICKER} mb-4 justify-center`}>{kicker}</span>
|
|
20
|
+
<p className="mx-auto mb-7 max-w-[760px] font-display text-[clamp(22px,2.6vw,32px)] italic leading-[1.4] text-theme max-[768px]:text-[19px] max-[768px]:leading-[1.42]">
|
|
21
|
+
{body}
|
|
22
|
+
</p>
|
|
23
|
+
<a
|
|
24
|
+
href={linkHref}
|
|
25
|
+
className="inline-flex border-b border-accent pb-[3px] font-body text-[15px] font-medium text-accent transition-all hover:pb-[5px]"
|
|
26
|
+
>
|
|
27
|
+
{linkLabel}
|
|
28
|
+
</a>
|
|
29
|
+
</Section>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime schema/formatter for bridge now live in `src/site-content.ts`.
|
|
3
|
+
* Keep the props type local to the layout so app code does not depend on
|
|
4
|
+
* low-level template authoring primitives here.
|
|
5
|
+
*/
|
|
6
|
+
export interface BridgeContent {
|
|
7
|
+
kicker: string;
|
|
8
|
+
body: string;
|
|
9
|
+
linkLabel: string;
|
|
10
|
+
linkHref: string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX } from "preact";
|
|
3
|
+
import type { CloserContent } from "./schema";
|
|
4
|
+
import { Section, renderHighlightedText } from "@rizom/site-rizom";
|
|
5
|
+
import { WorkButton } from "../../link-targets";
|
|
6
|
+
import { WORK_RULE } from "../styles";
|
|
7
|
+
|
|
8
|
+
const HIGHLIGHT_CLS = "italic text-accent font-normal";
|
|
9
|
+
|
|
10
|
+
export const CloserLayout = ({
|
|
11
|
+
preamble,
|
|
12
|
+
headline,
|
|
13
|
+
primaryCtaLabel,
|
|
14
|
+
primaryCtaHref,
|
|
15
|
+
secondaryCtaLabel,
|
|
16
|
+
secondaryCtaHref,
|
|
17
|
+
}: CloserContent): JSX.Element => {
|
|
18
|
+
return (
|
|
19
|
+
<Section
|
|
20
|
+
id="cta"
|
|
21
|
+
className="reveal pt-[128px] pb-[144px] text-center max-[768px]:pt-20 max-[768px]:pb-24"
|
|
22
|
+
>
|
|
23
|
+
<div className={`${WORK_RULE} mb-12 max-[768px]:mb-9`} />
|
|
24
|
+
{preamble && (
|
|
25
|
+
<p className="mx-auto mb-10 max-w-[560px] font-body text-[18px] italic leading-[1.7] text-theme-light max-[768px]:mb-7 max-[768px]:text-[15px]">
|
|
26
|
+
{preamble}
|
|
27
|
+
</p>
|
|
28
|
+
)}
|
|
29
|
+
<h2 className="font-display text-[clamp(40px,6vw,80px)] font-[520] leading-[0.98] tracking-[-2px] text-theme max-[768px]:text-[35px] max-[768px]:leading-[1.04] max-[768px]:tracking-[-1px]">
|
|
30
|
+
{renderHighlightedText(headline, HIGHLIGHT_CLS)}
|
|
31
|
+
</h2>
|
|
32
|
+
<div className="mt-14 flex flex-wrap justify-center gap-5 max-[768px]:mt-10 max-[768px]:flex-col max-[768px]:items-stretch max-[768px]:gap-3 max-[768px]:[&_.rizom-btn]:w-full max-[768px]:[&_.rizom-btn]:justify-center max-[768px]:[&_.rizom-btn]:px-6 max-[768px]:[&_.rizom-btn]:py-4 max-[768px]:[&_.rizom-btn]:text-[16px]">
|
|
33
|
+
<WorkButton
|
|
34
|
+
href={primaryCtaHref}
|
|
35
|
+
variant="primary"
|
|
36
|
+
className="text-[18px] md:px-11 md:py-5"
|
|
37
|
+
>
|
|
38
|
+
{primaryCtaLabel}
|
|
39
|
+
</WorkButton>
|
|
40
|
+
<WorkButton
|
|
41
|
+
href={secondaryCtaHref}
|
|
42
|
+
variant="secondary"
|
|
43
|
+
className="text-[18px] md:px-11 md:py-5"
|
|
44
|
+
>
|
|
45
|
+
{secondaryCtaLabel}
|
|
46
|
+
</WorkButton>
|
|
47
|
+
</div>
|
|
48
|
+
</Section>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime schema/formatter for closer now live in `src/site-content.ts`.
|
|
3
|
+
* Keep the props type local to the layout so app code does not depend on
|
|
4
|
+
* low-level template authoring primitives here.
|
|
5
|
+
*/
|
|
6
|
+
export interface CloserContent {
|
|
7
|
+
preamble: string;
|
|
8
|
+
headline: string;
|
|
9
|
+
primaryCtaLabel: string;
|
|
10
|
+
primaryCtaHref: string;
|
|
11
|
+
secondaryCtaLabel: string;
|
|
12
|
+
secondaryCtaHref: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX } from "preact";
|
|
3
|
+
import { Section } from "@rizom/site-rizom";
|
|
4
|
+
import { WORK_KICKER, WORK_RULE } from "../styles";
|
|
5
|
+
import type { CredibilityContent } from "./schema";
|
|
6
|
+
|
|
7
|
+
export const CredibilityLayout = ({
|
|
8
|
+
kicker,
|
|
9
|
+
headline,
|
|
10
|
+
cards,
|
|
11
|
+
}: CredibilityContent): JSX.Element => {
|
|
12
|
+
return (
|
|
13
|
+
<Section id="methodology" className="reveal py-[128px] max-[768px]:py-20">
|
|
14
|
+
<div
|
|
15
|
+
className={`${WORK_RULE} mb-16 -mt-16 max-[768px]:-mt-10 max-[768px]:mb-12`}
|
|
16
|
+
/>
|
|
17
|
+
|
|
18
|
+
<div className="mx-auto mb-20 max-w-[760px] text-center max-[768px]:mb-14">
|
|
19
|
+
<span className={`${WORK_KICKER} mb-3`}>{kicker}</span>
|
|
20
|
+
<h2 className="mt-3 font-display text-[clamp(34px,4.8vw,60px)] font-[520] leading-[1.04] tracking-[-1.5px] text-theme max-[768px]:text-[31px] max-[768px]:tracking-[-1px]">
|
|
21
|
+
{headline}
|
|
22
|
+
</h2>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div className="mx-auto grid max-w-[1120px] grid-cols-2 gap-6 max-[768px]:grid-cols-1 max-[768px]:gap-4">
|
|
26
|
+
{cards.map((card, index) => (
|
|
27
|
+
<article
|
|
28
|
+
key={card.title}
|
|
29
|
+
className={`reveal reveal-delay-${Math.min(index + 1, 3)} rounded-2xl border border-[var(--color-work-divider-soft)] bg-white/[0.025] p-7 max-[768px]:rounded-xl max-[768px]:p-5`}
|
|
30
|
+
>
|
|
31
|
+
<div className="mb-4 font-label text-[11px] font-semibold uppercase tracking-[2.5px] text-accent">
|
|
32
|
+
{String(index + 1).padStart(2, "0")}
|
|
33
|
+
</div>
|
|
34
|
+
<h3 className="mb-3 font-display text-[24px] font-[520] leading-[1.15] tracking-[-0.5px] text-theme max-[768px]:text-[20px]">
|
|
35
|
+
{card.title}
|
|
36
|
+
</h3>
|
|
37
|
+
<p className="font-body text-[16px] leading-[1.72] text-theme-muted max-[768px]:text-[14.5px] max-[768px]:leading-[1.68]">
|
|
38
|
+
{card.body}
|
|
39
|
+
</p>
|
|
40
|
+
</article>
|
|
41
|
+
))}
|
|
42
|
+
</div>
|
|
43
|
+
</Section>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX } from "preact";
|
|
3
|
+
import type { WorkHeroContent } from "./schema";
|
|
4
|
+
import { Section, renderHighlightedText } from "@rizom/site-rizom";
|
|
5
|
+
import { WorkButton, newTabProps } from "../../link-targets";
|
|
6
|
+
|
|
7
|
+
const HIGHLIGHT_CLS = "italic text-accent";
|
|
8
|
+
const RADAR_OUTER_RING_CLASS = "fill-none stroke-white/26 [stroke-width:1.5]";
|
|
9
|
+
const RADAR_SHAPE_CLASS =
|
|
10
|
+
"fill-accent/20 stroke-accent [stroke-width:2.5] [stroke-linejoin:round]";
|
|
11
|
+
const RADAR_NODE_CLASS = "fill-white stroke-[var(--color-bg)] [stroke-width:2]";
|
|
12
|
+
const RADAR_LABEL_CLASS =
|
|
13
|
+
"font-mono text-[10px] font-semibold uppercase tracking-[1.6px] fill-white/64";
|
|
14
|
+
|
|
15
|
+
const RADAR_OUTER_POINTS = "180,50 266.60,200 93.40,200";
|
|
16
|
+
const RADAR_DATA_POINTS = "180,95 233.69,181 150.56,167";
|
|
17
|
+
|
|
18
|
+
const RADAR_NODES = [
|
|
19
|
+
{ cx: "180", cy: "95" },
|
|
20
|
+
{ cx: "233.69", cy: "181" },
|
|
21
|
+
{ cx: "150.56", cy: "167" },
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
const RADAR_LABELS = [
|
|
25
|
+
{ x: "180", y: "34", textAnchor: "middle", name: "Specialization" },
|
|
26
|
+
{ x: "252", y: "222", textAnchor: "start", name: "Credibility" },
|
|
27
|
+
{ x: "108", y: "222", textAnchor: "end", name: "Coordination" },
|
|
28
|
+
] as const;
|
|
29
|
+
|
|
30
|
+
export const WorkHeroLayout = ({
|
|
31
|
+
kicker,
|
|
32
|
+
headline,
|
|
33
|
+
subtitle,
|
|
34
|
+
primaryCtaLabel,
|
|
35
|
+
primaryCtaHref,
|
|
36
|
+
secondaryCtaLabel,
|
|
37
|
+
secondaryCtaHref,
|
|
38
|
+
verdictLabel,
|
|
39
|
+
verdictValue,
|
|
40
|
+
findingsLabel,
|
|
41
|
+
findings,
|
|
42
|
+
diagnosticCtaLabel,
|
|
43
|
+
diagnosticCtaHref,
|
|
44
|
+
}: WorkHeroContent): JSX.Element => {
|
|
45
|
+
return (
|
|
46
|
+
<Section
|
|
47
|
+
id="hero"
|
|
48
|
+
className="relative overflow-hidden lg:flex lg:min-h-[100dvh] lg:items-center lg:pt-[120px] lg:pb-16"
|
|
49
|
+
>
|
|
50
|
+
<div className="grid w-full gap-7 lg:grid-cols-[0.95fr_1.05fr] lg:grid-rows-[auto_auto] lg:items-center lg:gap-x-16 lg:gap-y-0">
|
|
51
|
+
<div className="flex min-h-[100dvh] flex-col pt-[104px] pb-10 lg:contents">
|
|
52
|
+
<div className="flex flex-1 flex-col justify-center gap-7 lg:contents">
|
|
53
|
+
<div className="relative z-[2] lg:col-start-1 lg:row-start-1">
|
|
54
|
+
<div className="mb-5 inline-flex items-center gap-[14px] font-label text-[11px] font-semibold uppercase text-accent opacity-0 animate-hero-rise [animation-delay:0.1s] [letter-spacing:2.8px] before:block before:h-px before:w-8 before:bg-accent before:opacity-80 before:content-[''] max-[768px]:gap-3 max-[768px]:text-[10px] max-[768px]:tracking-[2.4px] max-[768px]:before:w-6 md:mb-6">
|
|
55
|
+
<span>{kicker}</span>
|
|
56
|
+
</div>
|
|
57
|
+
<h1 className="mb-6 max-w-full font-display text-[34px] font-[520] leading-[1.04] tracking-[-0.9px] text-theme opacity-0 animate-hero-rise [animation-delay:0.2s] min-[420px]:text-[36px] md:mb-7 md:text-[clamp(36px,5.2vw,76px)] md:leading-none md:tracking-[-2px] lg:max-w-[14ch]">
|
|
58
|
+
{renderHighlightedText(headline, HIGHLIGHT_CLS)}
|
|
59
|
+
</h1>
|
|
60
|
+
<p className="max-w-[500px] font-body text-[15.5px] leading-[1.68] text-theme-muted opacity-0 animate-hero-rise [animation-delay:0.4s] md:mb-10 md:text-[18px] md:leading-[1.65]">
|
|
61
|
+
{subtitle}
|
|
62
|
+
</p>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div className="flex flex-col gap-3 opacity-0 animate-hero-rise [animation-delay:0.6s] md:flex-row md:flex-wrap md:gap-[14px] max-[768px]:[&_.rizom-btn]:w-full max-[768px]:[&_.rizom-btn]:justify-center max-[768px]:[&_.rizom-btn]:px-6 max-[768px]:[&_.rizom-btn]:py-[15px] lg:col-start-1 lg:row-start-2">
|
|
66
|
+
<WorkButton href={primaryCtaHref} variant="primary" block>
|
|
67
|
+
{primaryCtaLabel}
|
|
68
|
+
</WorkButton>
|
|
69
|
+
<WorkButton href={secondaryCtaHref} variant="secondary" block>
|
|
70
|
+
{secondaryCtaLabel}
|
|
71
|
+
</WorkButton>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div
|
|
77
|
+
id="hero-diagnostic"
|
|
78
|
+
className="rizom-diagnostic-panel relative z-[2] w-full max-w-[500px] rounded-[14px] border border-accent/30 px-[18px] py-5 opacity-0 animate-hero-rise [animation-delay:0.75s] md:rounded-2xl md:px-[30px] md:pt-5 md:pb-[26px] max-[1100px]:mx-auto max-[1100px]:max-w-[480px] max-[768px]:mb-14 max-[380px]:px-4 lg:ml-auto lg:col-start-2 lg:row-start-1 lg:row-span-2"
|
|
79
|
+
>
|
|
80
|
+
<div className="rizom-diagnostic-panel-bar absolute inset-x-0 top-0 h-[2px] rounded-t-2xl" />
|
|
81
|
+
|
|
82
|
+
<div className="relative pb-2 md:pb-3">
|
|
83
|
+
<svg
|
|
84
|
+
viewBox="0 4 360 248"
|
|
85
|
+
className="block h-auto w-full"
|
|
86
|
+
role="img"
|
|
87
|
+
aria-label="Three-axis TMS radar — Specialization, Credibility, Coordination"
|
|
88
|
+
>
|
|
89
|
+
<polygon
|
|
90
|
+
className={RADAR_OUTER_RING_CLASS}
|
|
91
|
+
points={RADAR_OUTER_POINTS}
|
|
92
|
+
/>
|
|
93
|
+
<polygon
|
|
94
|
+
className={RADAR_SHAPE_CLASS}
|
|
95
|
+
points={RADAR_DATA_POINTS}
|
|
96
|
+
/>
|
|
97
|
+
{RADAR_NODES.map(({ cx, cy }) => (
|
|
98
|
+
<circle
|
|
99
|
+
key={`${cx}-${cy}`}
|
|
100
|
+
className={RADAR_NODE_CLASS}
|
|
101
|
+
cx={cx}
|
|
102
|
+
cy={cy}
|
|
103
|
+
r="4"
|
|
104
|
+
/>
|
|
105
|
+
))}
|
|
106
|
+
{RADAR_LABELS.map(({ x, y, textAnchor, name }) => (
|
|
107
|
+
<text
|
|
108
|
+
key={name}
|
|
109
|
+
className={RADAR_LABEL_CLASS}
|
|
110
|
+
x={x}
|
|
111
|
+
y={y}
|
|
112
|
+
textAnchor={textAnchor}
|
|
113
|
+
>
|
|
114
|
+
{name}
|
|
115
|
+
</text>
|
|
116
|
+
))}
|
|
117
|
+
</svg>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div className="flex items-baseline justify-between gap-4 border-t border-white/12 py-[18px] max-[420px]:flex-col max-[420px]:items-start max-[420px]:gap-1.5 max-[420px]:py-4">
|
|
121
|
+
<span className="whitespace-nowrap font-mono text-[10px] uppercase tracking-[0.18em] text-theme-light">
|
|
122
|
+
{verdictLabel}
|
|
123
|
+
</span>
|
|
124
|
+
<span className="whitespace-nowrap text-right font-display italic text-[22px] font-medium leading-[1.05] tracking-[-0.015em] text-theme [font-variation-settings:'opsz'_96] max-[420px]:text-left max-[420px]:text-[20px] max-[360px]:text-[18px]">
|
|
125
|
+
{verdictValue}
|
|
126
|
+
</span>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div className="border-t border-white/12 pt-2.5 md:pt-5">
|
|
130
|
+
<div className="flex items-baseline justify-between gap-3">
|
|
131
|
+
<span className="font-mono text-[8px] uppercase tracking-[0.22em] text-theme-light md:text-[9.5px] md:tracking-[0.2em]">
|
|
132
|
+
{findingsLabel}
|
|
133
|
+
</span>
|
|
134
|
+
<span className="flex items-baseline gap-1.5 font-mono text-[8px] uppercase tracking-[0.18em] text-theme-light md:text-[9.5px]">
|
|
135
|
+
<span aria-hidden className="text-theme-light/60">
|
|
136
|
+
→
|
|
137
|
+
</span>
|
|
138
|
+
<span className="text-accent/85">Coordination</span>
|
|
139
|
+
<span className="tabular-nums text-accent/85">
|
|
140
|
+
34<span className="opacity-60">/100</span>
|
|
141
|
+
</span>
|
|
142
|
+
</span>
|
|
143
|
+
</div>
|
|
144
|
+
<ul className="mt-3 flex flex-col gap-2 md:mt-4 md:gap-3">
|
|
145
|
+
{findings.map((finding, index) => (
|
|
146
|
+
<li
|
|
147
|
+
key={finding}
|
|
148
|
+
className="flex items-baseline gap-3 text-[11.5px] leading-[1.38] text-white/84 md:text-body-xs md:leading-[1.5]"
|
|
149
|
+
>
|
|
150
|
+
<span className="min-w-[26px] font-mono text-[9px] font-semibold uppercase tracking-[0.1em] text-accent/80 md:text-[10px]">
|
|
151
|
+
{`[${String(index + 1).padStart(2, "0")}]`}
|
|
152
|
+
</span>
|
|
153
|
+
<span>{finding}</span>
|
|
154
|
+
</li>
|
|
155
|
+
))}
|
|
156
|
+
</ul>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<a
|
|
160
|
+
href={diagnosticCtaHref}
|
|
161
|
+
{...newTabProps(diagnosticCtaHref)}
|
|
162
|
+
className="mt-3 inline-flex w-full items-center justify-center rounded-[10px] border border-white/18 px-3 py-2.5 font-mono text-[8.5px] uppercase tracking-[0.18em] text-white/88 transition-colors hover:border-accent/60 hover:bg-accent/8 hover:text-theme md:mt-5 md:px-4 md:py-3 md:text-[10.5px]"
|
|
163
|
+
>
|
|
164
|
+
{diagnosticCtaLabel}
|
|
165
|
+
</a>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</Section>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime schema/formatter for hero now live in `src/site-content.ts`.
|
|
3
|
+
* Keep the props type local to the layout so app code does not depend on
|
|
4
|
+
* low-level template authoring primitives here.
|
|
5
|
+
*/
|
|
6
|
+
export interface WorkHeroContent {
|
|
7
|
+
kicker: string;
|
|
8
|
+
headline: string;
|
|
9
|
+
subtitle: string;
|
|
10
|
+
primaryCtaLabel: string;
|
|
11
|
+
primaryCtaHref: string;
|
|
12
|
+
secondaryCtaLabel: string;
|
|
13
|
+
secondaryCtaHref: string;
|
|
14
|
+
verdictLabel: string;
|
|
15
|
+
verdictValue: string;
|
|
16
|
+
findingsLabel: string;
|
|
17
|
+
findings: string[];
|
|
18
|
+
diagnosticCtaLabel: string;
|
|
19
|
+
diagnosticCtaHref: string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import type { JSX } from "preact";
|
|
3
|
+
import { Section } from "@rizom/site-rizom";
|
|
4
|
+
import { WORK_KICKER, WORK_RULE } from "../styles";
|
|
5
|
+
import type { OwnershipContent } from "./schema";
|
|
6
|
+
|
|
7
|
+
const renderBody = (body: string): JSX.Element => {
|
|
8
|
+
const marker = "rizom.foundation";
|
|
9
|
+
const index = body.indexOf(marker);
|
|
10
|
+
|
|
11
|
+
if (index === -1) return <>{body}</>;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
{body.slice(0, index)}
|
|
16
|
+
<a
|
|
17
|
+
href="https://rizom.foundation"
|
|
18
|
+
className="border-b border-transparent text-accent transition-colors hover:border-accent"
|
|
19
|
+
>
|
|
20
|
+
{marker}
|
|
21
|
+
</a>
|
|
22
|
+
{body.slice(index + marker.length)}
|
|
23
|
+
</>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const OwnershipLayout = ({
|
|
28
|
+
kicker,
|
|
29
|
+
headline,
|
|
30
|
+
cards,
|
|
31
|
+
}: OwnershipContent): JSX.Element => {
|
|
32
|
+
return (
|
|
33
|
+
<Section id="about" className="reveal py-[128px] max-[768px]:py-20">
|
|
34
|
+
<div
|
|
35
|
+
className={`${WORK_RULE} mb-16 -mt-16 max-[768px]:-mt-10 max-[768px]:mb-12`}
|
|
36
|
+
/>
|
|
37
|
+
|
|
38
|
+
<div className="mx-auto flex max-w-[1160px] items-start gap-[88px] max-[1100px]:gap-12 max-[768px]:flex-col max-[768px]:gap-10">
|
|
39
|
+
<div className="w-[42%] max-[768px]:w-full">
|
|
40
|
+
<span className={`${WORK_KICKER} mb-4`}>{kicker}</span>
|
|
41
|
+
<h2 className="mt-4 font-display text-[clamp(32px,4.2vw,52px)] font-[520] leading-[1.05] tracking-[-1.4px] text-theme max-[768px]:mt-3 max-[768px]:text-[30px]">
|
|
42
|
+
{headline}
|
|
43
|
+
</h2>
|
|
44
|
+
</div>
|
|
45
|
+
<div className="flex w-[58%] flex-col gap-10 pt-16 max-[768px]:w-full max-[768px]:gap-8 max-[768px]:pt-0">
|
|
46
|
+
{cards.map((card, index) => {
|
|
47
|
+
const badge =
|
|
48
|
+
card.badge.trim() ||
|
|
49
|
+
(card.title.startsWith("A network") ? "+" : "");
|
|
50
|
+
const body = card.body.trim();
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
key={card.title}
|
|
54
|
+
className={`reveal reveal-delay-${index + 1} flex items-start gap-[22px] max-[768px]:gap-[18px]`}
|
|
55
|
+
>
|
|
56
|
+
<div className="flex h-12 min-w-12 items-center justify-center rounded-lg border border-accent px-3 font-display text-[16px] font-semibold tracking-[-0.3px] text-accent max-[768px]:h-11 max-[768px]:min-w-11 max-[768px]:text-[15px]">
|
|
57
|
+
{badge}
|
|
58
|
+
</div>
|
|
59
|
+
<div>
|
|
60
|
+
<div className="mb-1.5 font-display text-[20px] font-[520] text-theme max-[768px]:text-[18px]">
|
|
61
|
+
{card.title}
|
|
62
|
+
</div>
|
|
63
|
+
{body ? (
|
|
64
|
+
<p className="font-body text-[15px] leading-[1.72] text-theme-muted max-[768px]:text-[14px] max-[768px]:leading-[1.68]">
|
|
65
|
+
{renderBody(body)}
|
|
66
|
+
</p>
|
|
67
|
+
) : null}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
})}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</Section>
|
|
75
|
+
);
|
|
76
|
+
};
|