@wenathlan/cobaia 0.0.0-stage → 2.0.155

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.
Files changed (58) hide show
  1. package/App.tsx +52 -0
  2. package/Dockerfile +45 -0
  3. package/Sol/Sol.tsx +91 -0
  4. package/Sol/apps/apps.tsx +66 -0
  5. package/Sol/blog/blog.tsx +86 -0
  6. package/Sol/home/home.tsx +140 -0
  7. package/Sol/intro/intro.tsx +187 -0
  8. package/Sol/legal/legal.tsx +76 -0
  9. package/Sol/notfound/notfound.tsx +32 -0
  10. package/Sol/pricing/pricing.tsx +98 -0
  11. package/Sol/shell/Shell.tsx +161 -0
  12. package/Sol/sol.css +1944 -0
  13. package/Sol/support/support.tsx +102 -0
  14. package/Sol/wingets/IntroFooter.tsx +69 -0
  15. package/Sol/wingets/IntroPlans.tsx +93 -0
  16. package/Sol/wingets/IntroSplash.tsx +105 -0
  17. package/Sol/wingets/ShellAdsslot.tsx +47 -0
  18. package/Sol/wingets/ShellBrand.tsx +86 -0
  19. package/Sol/wingets/index.ts +18 -0
  20. package/_headers +4 -0
  21. package/_redirects +1 -0
  22. package/adsconfig.ts +59 -0
  23. package/biome.jsonc +134 -0
  24. package/bun.lock +2331 -0
  25. package/capacitor.config.ts +51 -0
  26. package/checkoutlinks.ts +80 -0
  27. package/content.ts +425 -0
  28. package/db.ts +219 -0
  29. package/devthink.toml +5 -0
  30. package/docs/.gitkeep +0 -0
  31. package/familyurl.ts +22 -0
  32. package/favicon.svg +1 -0
  33. package/icon.svg +1 -0
  34. package/index.html +26 -0
  35. package/labels.ts +291 -0
  36. package/manifest.webmanifest +56 -0
  37. package/mime.types +12 -0
  38. package/netlify.toml +2 -0
  39. package/package.json +200 -4
  40. package/pnpm-workspace.yaml +82 -0
  41. package/posts.ts +54 -0
  42. package/robots.txt +90 -0
  43. package/roster.ts +29 -0
  44. package/sandboxmodality.ts +138 -0
  45. package/schema.prisma +31 -0
  46. package/sitemap.xml +42 -0
  47. package/tests/adsconfig.test.ts +51 -0
  48. package/tests/checkoutlinks.test.ts +73 -0
  49. package/tests/familyurl.test.ts +40 -0
  50. package/tests/posts.test.ts +76 -0
  51. package/tests/sandboxmodality.test.ts +77 -0
  52. package/tsconfig.json +22 -0
  53. package/tsconfig.node.json +22 -0
  54. package/vercel.json +3 -0
  55. package/vite.config.ts +44 -0
  56. package/vitest.config.ts +42 -0
  57. package/wrangler.toml +7 -0
  58. package/README.md +0 -3
@@ -0,0 +1,76 @@
1
+ /**
2
+ * legal page anchor — layer 3 of the anchor architecture.
3
+ * The file carrying the folder's own name is the path manager of the page:
4
+ * it imports the loose components beside it, mounts the page and re-exports
5
+ * the public component surface. Only the theme anchor (Sol/Sol.tsx) consumes
6
+ * this file. The legal page of the cobaia content site: the privacy policy
7
+ * and the terms, written seriously and directly. The page discloses the
8
+ * advertising plan (labeled, reserved slots, third-party cookies only once
9
+ * the network is approved and live) and the analytics rule (none by default,
10
+ * opt-in and disclosed here first if that ever changes). No ad slot lives on
11
+ * this page: it is the compliance and health-check surface of the site.
12
+ * every paragraph rides the label accessor — the store keeps the sentence,
13
+ * the anchor keeps the rendering.
14
+ */
15
+ import type { CSSProperties } from "react";
16
+ import { label } from "../../labels";
17
+
18
+ /** one legal paragraph: the strong lead and its body, both from the store. */
19
+ function Clause({ head, text }: { head: string; text: string }) {
20
+ return (
21
+ <p>
22
+ <strong>{head}</strong> {text}
23
+ </p>
24
+ );
25
+ }
26
+
27
+ export function Legal() {
28
+ return (
29
+ <div className="cb-page">
30
+ <section className="cb-hero" aria-label={label("legal.aria.hero")}>
31
+ <p className="cb-eyebrow cb-in" style={{ "--cb-d": 0 } as CSSProperties}>
32
+ {label("legal.eyebrow")}
33
+ </p>
34
+ <h1 className="cb-title cb-in" style={{ "--cb-d": 1 } as CSSProperties}>
35
+ {label("legal.title")}
36
+ </h1>
37
+ <p className="cb-lede cb-in" style={{ "--cb-d": 2 } as CSSProperties}>
38
+ {label("legal.lede")}
39
+ </p>
40
+ </section>
41
+
42
+ <article
43
+ className="cb-article cb-in"
44
+ style={{ "--cb-d": 1 } as CSSProperties}
45
+ aria-label={label("legal.privacy.aria")}
46
+ >
47
+ <h2 className="cb-secttitle">{label("legal.privacy.title")}</h2>
48
+ <div className="cb-articlebody">
49
+ <Clause head={label("legal.privacy.collect.head")} text={label("legal.privacy.collect.text")} />
50
+ <Clause head={label("legal.privacy.logs.head")} text={label("legal.privacy.logs.text")} />
51
+ <Clause head={label("legal.privacy.ads.head")} text={label("legal.privacy.ads.text")} />
52
+ <Clause head={label("legal.privacy.analytics.head")} text={label("legal.privacy.analytics.text")} />
53
+ <Clause head={label("legal.privacy.payments.head")} text={label("legal.privacy.payments.text")} />
54
+ <Clause head={label("legal.privacy.contact.head")} text={label("legal.privacy.contact.text")} />
55
+ </div>
56
+ </article>
57
+
58
+ <article
59
+ className="cb-article cb-in"
60
+ style={{ "--cb-d": 2 } as CSSProperties}
61
+ aria-label={label("legal.terms.aria")}
62
+ >
63
+ <h2 className="cb-secttitle">{label("legal.terms.title")}</h2>
64
+ <div className="cb-articlebody">
65
+ <Clause head={label("legal.terms.service.head")} text={label("legal.terms.service.text")} />
66
+ <Clause head={label("legal.terms.use.head")} text={label("legal.terms.use.text")} />
67
+ <Clause head={label("legal.terms.data.head")} text={label("legal.terms.data.text")} />
68
+ <Clause head={label("legal.terms.ip.head")} text={label("legal.terms.ip.text")} />
69
+ <Clause head={label("legal.terms.changes.head")} text={label("legal.terms.changes.text")} />
70
+ </div>
71
+ </article>
72
+ </div>
73
+ );
74
+ }
75
+
76
+ export default Legal;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * notfound page anchor — layer 3 of the anchor architecture.
3
+ * The file carrying the folder's own name is the path manager of the page:
4
+ * it imports the loose components beside it, mounts the page and re-exports
5
+ * the public component surface. Only the theme anchor (Sol/Sol.tsx) consumes
6
+ * this file. The cobaia copy answers for the family site: the address is not
7
+ * part of the family — the way out is the home and the directory.
8
+ */
9
+
10
+ import { Link } from "wouter";
11
+
12
+ import { label } from "../../labels";
13
+
14
+ export function NotFound() {
15
+ return (
16
+ <div className="cb-page cb-missing">
17
+ <p className="cb-eyebrow">{label("notfound.eyebrow")}</p>
18
+ <h1 className="cb-title">{label("notfound.title")}</h1>
19
+ <p className="cb-lede">{label("notfound.lede")}</p>
20
+ <div className="cb-btnrow">
21
+ <Link className="cb-btn" href="/">
22
+ {label("notfound.action.home")}
23
+ </Link>
24
+ <Link className="cb-btn cb-btn--ghost" href="/apps">
25
+ {label("notfound.action.apps")}
26
+ </Link>
27
+ </div>
28
+ </div>
29
+ );
30
+ }
31
+
32
+ export default NotFound;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * pricing page anchor — layer 3 of the anchor architecture.
3
+ * The file carrying the folder's own name is the path manager of the page:
4
+ * it imports the loose components beside it, mounts the page and re-exports
5
+ * the public component surface. Only the theme anchor (Sol/Sol.tsx) consumes
6
+ * this file.
7
+ *
8
+ * The pricing table of the cobaia content site: three honest plans (free,
9
+ * pro, studio — us$0 / 5 / 19 a month, placeholders until billing lands).
10
+ * The billing values answer from the pure config at the app root
11
+ * (checkoutlinks.ts — ids, prices, checkout hrefs) and every plan sentence
12
+ * (name, blurb, features) rides the label store: a configured link renders
13
+ * a real anchor, an empty one renders the disabled button with the honest
14
+ * note "checkout not configured yet". No checkout page carries an ad — the
15
+ * ad slots live only on the content pages. Zero network calls: the links
16
+ * are plain hrefs the visitor chooses to follow.
17
+ */
18
+ import type { CSSProperties } from "react";
19
+ import { checkoutlinks, type checkoutplan, isconfigured } from "../../checkoutlinks";
20
+ import { useContent } from "../../content.ts";
21
+ import { label, labelfill } from "../../labels";
22
+
23
+ /** renders one plan's checkout control: a live anchor when the owner
24
+ * configured the link, the honest disabled state until then. */
25
+ function PlanAction({ plan }: { plan: checkoutplan }) {
26
+ if (isconfigured(plan.href)) {
27
+ return (
28
+ <a className="cb-btn" href={plan.href} rel="noreferrer">
29
+ {labelfill("pricing.action.choose", { name: label(plan.nameKey) })}
30
+ </a>
31
+ );
32
+ }
33
+ return (
34
+ <>
35
+ <button className="cb-btn" type="button" disabled>
36
+ {labelfill("pricing.action.choose", { name: label(plan.nameKey) })}
37
+ </button>
38
+ <p className="cb-note">{label("pricing.action.notConfigured")}</p>
39
+ </>
40
+ );
41
+ }
42
+
43
+ export function Pricing() {
44
+ useContent();
45
+ return (
46
+ <div className="cb-page">
47
+ <section className="cb-hero" aria-label={label("pricing.aria.hero")}>
48
+ <p className="cb-eyebrow cb-in" style={{ "--cb-d": 0 } as CSSProperties}>
49
+ {label("pricing.eyebrow")}
50
+ </p>
51
+ <h1 className="cb-title cb-in" style={{ "--cb-d": 1 } as CSSProperties}>
52
+ {label("pricing.title")}
53
+ </h1>
54
+ <p className="cb-lede cb-in" style={{ "--cb-d": 2 } as CSSProperties}>
55
+ {label("pricing.lede")}
56
+ </p>
57
+ </section>
58
+
59
+ <section className="cb-section" aria-label={label("pricing.section.plansAria")}>
60
+ <div className="cb-plans">
61
+ {checkoutlinks().map((plan, index) => (
62
+ <article
63
+ key={plan.id}
64
+ className={plan.id === "studio" ? "cb-plan cb-plan--lead cb-in" : "cb-plan cb-in"}
65
+ style={{ "--cb-d": index + 1 } as CSSProperties}
66
+ aria-label={labelfill("pricing.card.planAria", { name: label(plan.nameKey) })}
67
+ >
68
+ <p className="cb-planname">{label(plan.nameKey)}</p>
69
+ <p className="cb-planprice">
70
+ ${plan.priceusd}
71
+ <span className="cb-planper"> / {label(plan.perKey)}</span>
72
+ </p>
73
+ <p className="cb-planblurb">{label(plan.blurbKey)}</p>
74
+ <ul className="cb-planfeats">
75
+ {plan.featureKeys.map((featureKey) => (
76
+ <li key={featureKey} className="cb-planfeat">
77
+ {label(featureKey)}
78
+ </li>
79
+ ))}
80
+ </ul>
81
+ <PlanAction plan={plan} />
82
+ </article>
83
+ ))}
84
+ </div>
85
+ </section>
86
+
87
+ <section className="cb-section" aria-label={label("pricing.section.moneyAria")}>
88
+ <div className="cb-secthead">
89
+ <h2 className="cb-secttitle">{label("pricing.section.moneyTitle")}</h2>
90
+ <p className="cb-sectlede">{label("pricing.section.moneyLede")}</p>
91
+ </div>
92
+ <p className="cb-note is-live">{label("pricing.section.moneyNote")}</p>
93
+ </section>
94
+ </div>
95
+ );
96
+ }
97
+
98
+ export default Pricing;
@@ -0,0 +1,161 @@
1
+ /**
2
+ * shell.tsx — the ONE chrome of the cobaia content site under the family
3
+ * fullscreen law: the direct url owns the whole viewport (no floating
4
+ * window, no title bar, no captions — the window chrome exists only inside
5
+ * the DevThink OS power clip, and the OS draws that window itself), so the
6
+ * site renders full-bleed with its own voice — the sticky section bar (the
7
+ * acrylic nav pins over the drawn home mark), the page surface and the
8
+ * worked footer carrying the family row: every deploy unit of the family
9
+ * linked through familyurl(), because the family redirects both ways. The
10
+ * bar rides the trylle canvas with the Windows hands: the section links
11
+ * are the pins — the acrylic bar, the hover wash, the press bounce (scale
12
+ * 0.7, 100ms), the dtPopIntro mount pop and the 3px ladder indicator that
13
+ * grows under the active section; the focus underline and the active pin
14
+ * answer the Windows accent — the identity amber lives only in the brand
15
+ * mark. The active section link reads the location so the visitor always
16
+ * knows where they stand. Inside the OS power clip the embedded contract
17
+ * renders the SAME site surface under the shell--embedded seat: the
18
+ * sticky section bar, the page surface and the worked footer answer in
19
+ * both seats so the design is IDENTICAL at the direct url and inside the
20
+ * os window (the window-era law) — the host window owns the chrome
21
+ * there. Styled entirely by Sol/sol.css (the fullscreen law
22
+ * block carries the two contracts).
23
+ */
24
+ import type { CSSProperties, ReactNode } from "react";
25
+ import { Link, useLocation } from "wouter";
26
+ import { useContent } from "../../content.ts";
27
+ import { familyurl } from "../../familyurl";
28
+ import { type LabelKey, label } from "../../labels";
29
+ import { roster } from "../../roster.ts";
30
+ import { BrandMark } from "../wingets";
31
+
32
+ /** the section links of the top bar, in reading order (home is the mark).
33
+ * the href rides the route table, the text rides the label accessor. */
34
+ const NAVLINKS: readonly { href: string; key: LabelKey }[] = [
35
+ { href: "/apps", key: "shell.nav.apps" },
36
+ { href: "/pricing", key: "shell.nav.pricing" },
37
+ { href: "/blog", key: "shell.nav.blog" },
38
+ { href: "/support", key: "shell.nav.support" },
39
+ ];
40
+
41
+ /** the shared chrome: the full-bleed site surface — the sticky section bar
42
+ * (with the ONE logo slot as the home mark), the page surface and the
43
+ * footer row; the embedded pass renders the SAME site surface inside the
44
+ * os power clip (the window-era law: one design in both seats). */
45
+ export function Shell({ children }: { children: ReactNode }) {
46
+ useContent();
47
+ const [location] = useLocation();
48
+
49
+ // the embedded contract of the family fullscreen law: inside the os
50
+ // power clip the app rides the host window chrome — the seat class
51
+ // answers the mount, the tree never changes between the two doors
52
+ // (the window-era law: one design in both seats)
53
+ const framed = (() => {
54
+ try {
55
+ return window.self !== window.top;
56
+ } catch {
57
+ return true;
58
+ }
59
+ })();
60
+
61
+ const isActive = (href: string): boolean =>
62
+ href === "/" ? location === "/" : location === href || location.startsWith(`${href}/`);
63
+
64
+ // the embedded seat: inside the os power clip the app rides the host
65
+ // window chrome, but the SURFACE stays the same — the seat class answers
66
+ // the mount so differential styles (if any) can find it, the tree never
67
+ // changes between the two doors (the window-era law: one design)
68
+ const seat = framed ? "shell--embedded" : "shell--standalone";
69
+
70
+ // the surface pass: the direct url answers full-bleed — the window
71
+ // chrome exists only inside the os power clip, so the site owns the whole
72
+ // viewport; the sticky section bar stays as the site's own navigation,
73
+ // the page surface fills the middle and the worked footer closes it
74
+ return (
75
+ <div className={`shell ${seat}`}>
76
+ {/* the skip link answers the viewport: the surface owns the whole
77
+ * document, the skip jumps straight to the content anchor */}
78
+ <a className="cb-skip" href="#content">
79
+ {label("shell.skip")}
80
+ </a>
81
+ <div className="cb-root">
82
+ <header className="cb-top">
83
+ <nav className="cb-nav" aria-label={label("shell.nav.aria")}>
84
+ <Link
85
+ className="cb-brand"
86
+ href="/"
87
+ aria-label={label("shell.brand.aria")}
88
+ aria-current={isActive("/") ? "page" : undefined}
89
+ >
90
+ <BrandMark size={18} />
91
+ <strong>{label("shell.name")}</strong>
92
+ <span className="cb-brandtag">{label("shell.role")}</span>
93
+ </Link>
94
+ {NAVLINKS.map((link) => (
95
+ <Link
96
+ key={link.href}
97
+ className="cb-navlink"
98
+ href={link.href}
99
+ aria-current={isActive(link.href) ? "page" : undefined}
100
+ >
101
+ {label(link.key)}
102
+ </Link>
103
+ ))}
104
+ </nav>
105
+ </header>
106
+
107
+ <main id="content" className="cb-main">
108
+ {children}
109
+ </main>
110
+
111
+ <footer className="cb-foot">
112
+ <div className="cb-footgrid">
113
+ <div className="cb-footcol cb-footabout">
114
+ <p className="cb-foothead">{label("shell.foot.aboutHead")}</p>
115
+ <p className="cb-foottext">{label("shell.foot.aboutText")}</p>
116
+ </div>
117
+ <div className="cb-footcol">
118
+ <p className="cb-foothead">{label("shell.foot.siteHead")}</p>
119
+ <Link className="cb-footlink" href="/apps">
120
+ {label("shell.foot.apps")}
121
+ </Link>
122
+ <Link className="cb-footlink" href="/pricing">
123
+ {label("shell.foot.pricing")}
124
+ </Link>
125
+ <Link className="cb-footlink" href="/blog">
126
+ {label("shell.foot.blog")}
127
+ </Link>
128
+ <Link className="cb-footlink" href="/support">
129
+ {label("shell.foot.support")}
130
+ </Link>
131
+ <Link className="cb-footlink" href="/legal">
132
+ {label("shell.foot.legal")}
133
+ </Link>
134
+ </div>
135
+ <div className="cb-footcol cb-footfamily">
136
+ <p className="cb-foothead">{label("shell.foot.familyHead")}</p>
137
+ <div className="cb-famgrid">
138
+ {roster().map((member) => (
139
+ <a
140
+ key={member.slug}
141
+ className="cb-famlink"
142
+ href={familyurl(member.slug)}
143
+ target="_blank"
144
+ rel="noreferrer"
145
+ style={{ "--cb-dot": member.accent } as CSSProperties}
146
+ >
147
+ <span className="cb-famdot" aria-hidden="true" />
148
+ {member.slug}
149
+ </a>
150
+ ))}
151
+ </div>
152
+ </div>
153
+ </div>
154
+ <p className="cb-colophon">{label("shell.colophon")}</p>
155
+ </footer>
156
+ </div>
157
+ </div>
158
+ );
159
+ }
160
+
161
+ export default Shell;