@rizom/site-rizom-ai 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.
@@ -0,0 +1,56 @@
1
+ /** @jsxImportSource preact */
2
+ import type { JSX } from "preact";
3
+ import type { ProblemContent } from "./schema";
4
+ import { Section } from "@rizom/site-rizom";
5
+
6
+ /**
7
+ * Three-up grid of problem statements with outlined numerals.
8
+ * Content-driven via the ProblemContent schema (cards: 3 items).
9
+ */
10
+ export const ProblemLayout = ({ cards }: ProblemContent): JSX.Element => {
11
+ return (
12
+ <Section id="problem" className="reveal py-section">
13
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-16 md:gap-[60px]">
14
+ {cards.map((card, i) => (
15
+ <div key={card.num} className={`reveal reveal-delay-${i + 1}`}>
16
+ <div className="block font-display font-normal text-[64px] md:text-display-2xl mb-2 md:mb-3 leading-[1] text-transparent [-webkit-text-stroke:1.2px_var(--color-accent)] md:[-webkit-text-stroke:1.5px_var(--color-accent)]">
17
+ {card.num}
18
+ </div>
19
+ <div className="font-nav text-heading-sm md:text-heading-md font-bold mb-2.5 md:mb-3">
20
+ {card.title}
21
+ </div>
22
+ <p className="text-body-xs md:text-body-sm text-theme-muted">
23
+ {card.body}
24
+ </p>
25
+ </div>
26
+ ))}
27
+ </div>
28
+ <svg
29
+ className="hidden md:block absolute bottom-5 left-20 right-20 h-[60px] pointer-events-none"
30
+ viewBox="0 0 1280 60"
31
+ preserveAspectRatio="none"
32
+ >
33
+ <path
34
+ d="M180,30 C380,25 580,22 780,25 C980,28 1080,32 1100,30"
35
+ className="stroke-accent opacity-10"
36
+ strokeWidth="1"
37
+ fill="none"
38
+ />
39
+ <path
40
+ d="M180,35 C420,30 660,28 900,32 C1020,34 1080,35 1100,34"
41
+ className="stroke-[var(--palette-amber-light)] opacity-[0.07]"
42
+ strokeWidth="0.6"
43
+ fill="none"
44
+ />
45
+ <circle cx="180" cy="30" r="2" className="fill-accent opacity-20" />
46
+ <circle
47
+ cx="640"
48
+ cy="25"
49
+ r="1.5"
50
+ className="fill-[var(--palette-amber-light)] opacity-[0.15]"
51
+ />
52
+ <circle cx="1100" cy="30" r="2" className="fill-accent opacity-20" />
53
+ </svg>
54
+ </Section>
55
+ );
56
+ };
@@ -0,0 +1,9 @@
1
+ export interface ProblemCard {
2
+ num: string;
3
+ title: string;
4
+ body: string;
5
+ }
6
+
7
+ export interface ProblemContent {
8
+ cards: [ProblemCard, ProblemCard, ProblemCard];
9
+ }
@@ -0,0 +1,137 @@
1
+ /** @jsxImportSource preact */
2
+ import type { JSX } from "preact";
3
+ import { Section } from "@rizom/site-rizom";
4
+ import { ProductIllustration } from "./ProductIllustration";
5
+ import type { ProductCardContent, ProductVariant } from "./schema";
6
+
7
+ const VARIANT_INDEX: Record<ProductVariant, number> = {
8
+ rover: 0,
9
+ relay: 1,
10
+ ranger: 2,
11
+ };
12
+
13
+ interface VariantTone {
14
+ themeVars: string;
15
+ }
16
+
17
+ // `--card-text` is set per variant and switches in light mode where the
18
+ // dark-mode color would lack contrast on the paper-toned background.
19
+ const VARIANT_TONE: Record<ProductVariant, VariantTone> = {
20
+ rover: {
21
+ themeVars:
22
+ "[--card-bg:var(--color-card-rover-bg)] [--card-border:var(--color-card-rover-border)] [--card-border-hover:var(--color-card-rover-border-hover)] [--card-glow:var(--color-glow-rover)] [--card-text:var(--color-accent)] [--card-bar:var(--color-accent)]",
23
+ },
24
+ relay: {
25
+ themeVars:
26
+ "[--card-bg:var(--color-card-relay-bg)] [--card-border:var(--color-card-relay-border)] [--card-border-hover:var(--color-card-relay-border-hover)] [--card-glow:var(--color-glow-relay)] [--card-text:var(--color-secondary)] [--card-bar:var(--color-secondary)]",
27
+ },
28
+ ranger: {
29
+ themeVars:
30
+ "[--card-bg:var(--color-card-ranger-bg)] [--card-border:var(--color-card-ranger-border)] [--card-border-hover:var(--color-card-ranger-border-hover)] [--card-glow:var(--color-glow-ranger)] [--card-text:var(--color-accent-bright)] [--card-bar:var(--color-accent-bright)] light:[--card-text:var(--palette-amber-dark)] light:[--card-bar:var(--palette-amber-dark)]",
31
+ },
32
+ };
33
+
34
+ const ARTICLE_BASE =
35
+ "group relative grid grid-cols-1 gap-8 overflow-hidden rounded-[20px] border [border-color:var(--card-border)] [background-image:var(--card-bg)] p-7 transition-[border-color,box-shadow] duration-500 ease-[cubic-bezier(0.2,0.8,0.2,1)] hover:[border-color:var(--card-border-hover)] hover:[box-shadow:0_30px_80px_-30px_var(--card-glow)] md:grid-cols-[minmax(0,1fr)_minmax(0,1.05fr)] md:gap-16 md:p-12";
36
+
37
+ const ARTICLE_RULE =
38
+ "before:content-[''] before:absolute before:inset-x-0 before:top-0 before:h-[2px] before:opacity-90 before:[background:linear-gradient(90deg,transparent,var(--card-bar)_22%,var(--card-bar)_78%,transparent)]";
39
+
40
+ const ARTICLE_GRAIN =
41
+ "after:content-[''] after:pointer-events-none after:absolute after:inset-0 after:[background-image:var(--bg-noise)] after:[background-size:240px_240px] after:opacity-[0.06] after:mix-blend-overlay";
42
+
43
+ const HEADLINE_ACCENT =
44
+ "font-[520] italic tracking-normal [color:var(--card-text)]";
45
+
46
+ const renderEditorialHeadline = (headline: string): JSX.Element => {
47
+ const explicit = headline.match(/^(.*)\*([^*]+)\*(.*)$/);
48
+ if (explicit) {
49
+ return (
50
+ <>
51
+ {explicit[1]}
52
+ <em className={HEADLINE_ACCENT}>{explicit[2]}</em>
53
+ {explicit[3]}
54
+ </>
55
+ );
56
+ }
57
+
58
+ const lastWord = headline.match(/^(.*\s)(\S+)$/);
59
+ if (!lastWord) {
60
+ return <>{headline}</>;
61
+ }
62
+
63
+ return (
64
+ <>
65
+ {lastWord[1]}
66
+ <em className={HEADLINE_ACCENT}>{lastWord[2]}</em>
67
+ </>
68
+ );
69
+ };
70
+
71
+ export const ProductCard = ({
72
+ variant,
73
+ label,
74
+ badge,
75
+ headline,
76
+ description,
77
+ tags,
78
+ }: ProductCardContent): JSX.Element => {
79
+ const tone = VARIANT_TONE[variant];
80
+ const flip = VARIANT_INDEX[variant] === 1;
81
+
82
+ return (
83
+ <Section className="reveal py-9">
84
+ <article
85
+ className={`${ARTICLE_BASE} ${ARTICLE_RULE} ${ARTICLE_GRAIN} ${tone.themeVars}`}
86
+ >
87
+ <div
88
+ className={`relative z-[1] flex min-w-0 flex-col ${
89
+ flip ? "md:order-2" : ""
90
+ }`}
91
+ >
92
+ <header className="flex items-center justify-between gap-4">
93
+ <span className="font-mono text-[14px] font-medium uppercase tracking-[0.16em] [color:var(--card-text)] transition-[letter-spacing] duration-500 ease-[cubic-bezier(0.2,0.8,0.2,1)] group-hover:tracking-[0.22em] md:text-[16px]">
94
+ {label}
95
+ </span>
96
+ <span className="inline-flex items-center font-mono text-[11px] font-medium uppercase tracking-[0.16em] [color:var(--card-text)] whitespace-nowrap">
97
+ <span className="mr-2 opacity-65">[</span>
98
+ {badge}
99
+ <span className="ml-2 opacity-65">]</span>
100
+ </span>
101
+ </header>
102
+
103
+ <h3 className="mt-3.5 font-display text-[36px] font-[520] leading-[1.08] tracking-[-0.4px] text-theme md:text-[52px] md:tracking-[-1px]">
104
+ {renderEditorialHeadline(headline)}
105
+ </h3>
106
+
107
+ <p className="mt-[22px] max-w-[58ch] text-[16px] leading-[1.55] text-theme-muted">
108
+ {description}
109
+ </p>
110
+
111
+ <footer className="mt-auto pt-8">
112
+ <div className="flex flex-wrap items-center gap-x-3 gap-y-1.5 font-mono text-[9px] font-medium uppercase tracking-[0.18em] text-theme-muted">
113
+ {tags.map((tag, index) => (
114
+ <span key={tag} className="whitespace-nowrap">
115
+ {tag}
116
+ {index < tags.length - 1 ? (
117
+ <span className="ml-3 opacity-50">·</span>
118
+ ) : null}
119
+ </span>
120
+ ))}
121
+ </div>
122
+ </footer>
123
+ </div>
124
+
125
+ <div
126
+ className={`relative z-[1] order-first aspect-[511/320] self-center ${
127
+ flip
128
+ ? "mx-[-28px] w-[calc(100%+56px)] md:order-1 md:ml-[-48px] md:mr-0 md:w-[calc(100%+48px)]"
129
+ : "mx-[-28px] w-[calc(100%+56px)] md:order-none md:ml-0 md:mr-[-48px] md:w-[calc(100%+48px)]"
130
+ }`}
131
+ >
132
+ <ProductIllustration variant={variant} />
133
+ </div>
134
+ </article>
135
+ </Section>
136
+ );
137
+ };