What changed, and why

Every page's footer showed Privacy ยท Terms ยท Disclaimer ยท Contact, and all four were href="#" placeholders โ€” on a product that takes real money through Stripe. No policy document existed anywhere, and there was no support address for refund or account help. Issue #234 flagged it after an adversarial review.

This PR publishes the three documents as static pages (/terms, /privacy, /disclaimer), points the footer at them, and shows the support address. The blast radius is deliberately small: three new prose-only pages, the footer component, one new constant, and a handful of corrected comments elsewhere. No game logic, no store, no server route changes.

The documents are the risky part, because a legal page that misdescribes the product is a defect you can't lint for. Every product claim in them was grounded in the code first, then adversarially fact-checked against it by an independent review pass; three claims failed that check and were corrected before this landed (see the last section).

One address, one constant

The support address appears in the footer and in all three documents. It lives once, in utils/site-meta.ts โ€” the module that already owns the brand strings โ€” so the footer and the three documents can't drift apart.

The one new export. Everything else in the file is untouched.

utils/site-meta.ts ยท 87 lines
utils/site-meta.ts87 lines ยท TypeScript
โ‹ฏ 26 lines hidden (lines 1โ€“26)
1/**
2 * Brand identity + the default social card โ€” the single source of truth shared by the
3 * app shell (the site-wide default, `app.vue`) and the landing page (`pages/index.vue`),
4 * so the brand strings and the card's shape live in exactly one place. A second copy is
5 * one edit away from drifting (issue #118).
6 *
7 * The per-run share page (`pages/r/[token].vue`, issue #88) already sets its own complete
8 * og/twitter block pointed at the live per-run PNG; this module is the DEFAULT every other
9 * public page falls back to โ€” the brand card (`public/og-default.png`).
10 *
11 * Absolute-URL rule: `og:image` / `og:url` MUST be absolute to unfurl โ€” crawlers silently
12 * ignore relative URLs. So the helpers take an `origin` and return absolute URLs. Callers
13 * get the origin from `useRequestURL()` โ€” the real SSR request origin the crawler hit, the
14 * window origin on the client โ€” exactly as the share page already does.
15 */
16 
17/** The brand name. Used for `og:site_name` and as the schema.org game name. */
18export const SITE_NAME = "Everwhen";
19 
20/** The default browser title + og/twitter title for a page without its own. */
21export const SITE_TITLE = "Everwhen โ€” Rewrite History";
22 
23/** The default meta + og/twitter description (the landing's hook copy). */
24export const SITE_DESCRIPTION =
25 "Send 160-character messages to anyone in history. A D20 and an AI decide how the timeline bends. Your first run is free.";
26 
27/**
28 * The support + legal contact address, shown in the site footer and on the policy
29 * pages (issue #234). One constant so the footer and the documents can't drift.
30 */
31export const SUPPORT_EMAIL = "hello@playeverwhen.com";
โ‹ฏ 56 lines hidden (lines 32โ€“87)
32 
33/** og:locale โ€” the brand ships in US English today. Pairs with `<html lang="en">`. */
34export const OG_LOCALE = "en_US";
35 
36/** The committed brand card (`public/og-default.png`), 1200x630. Relative โ€” absolutized per request. */
37export const OG_DEFAULT_IMAGE_PATH = "/og-default.png";
38export const OG_IMAGE_WIDTH = 1200;
39export const OG_IMAGE_HEIGHT = 630;
40export const OG_IMAGE_TYPE = "image/png";
41 
42/**
43 * Accurate alt text for `og-default.png` โ€” surfaced by some platforms and read by screen
44 * readers, so it describes what the card actually shows (a wax-seal astrolabe + wordmark).
45 */
46export const OG_IMAGE_ALT =
47 "The Everwhen brand card: a terracotta wax-seal astrolabe above the wordmark โ€œEverwhenโ€ and the line โ€œEvery message rewrites history.โ€";
48 
49/**
50 * Absolute URL for a public path against the request origin. Uses the URL parser so an
51 * extra/missing slash can't produce a malformed link. `toAbsolute(origin, '/og-default.png')`
52 * โ†’ `https://everwhen.example/og-default.png`; `toAbsolute(origin, '/')` โ†’ `https://everwhen.example/`.
53 */
54export function toAbsolute(origin: string, pathname: string): string {
55 return new URL(pathname, origin).href;
57 
58/**
59 * The site-wide JSON-LD (schema.org `VideoGame`) for Google rich results. Pure, constant
60 * brand data (no user input), so it's safe to inline as `application/ld+json`. Kept minimal
61 * and accurate โ€” no `offers`, since the game is freemium, not free software. The return type
62 * is the inferred object literal (every key statically known), so a dropped/renamed field is
63 * a compile error, not a silent runtime gap.
64 */
65export function buildJsonLd(origin: string, image: string) {
66 return {
67 "@context": "https://schema.org",
68 "@type": "VideoGame",
69 name: SITE_NAME,
70 url: toAbsolute(origin, "/"),
71 image,
72 description: SITE_DESCRIPTION,
73 genre: "Strategy",
74 gamePlatform: "Web browser",
75 };
77 
78/**
79 * Serialize data as JSON safe to inline inside a `<script>` tag. `JSON.stringify` does NOT
80 * escape `<`, so a `</script>` in any value would break out of the script context; escaping
81 * every `<` to its Unicode-escape form closes that off regardless of input. Today the only
82 * dynamic value in the JSON-LD is the request origin, which the URL parser already forbids
83 * `<` in โ€” this keeps the inline script safe even if that ever changes.
84 */
85export function inlineJson(data: unknown): string {
86 return JSON.stringify(data).replace(/</g, "\\u003c");

hello@playeverwhen.com matches the production domain. The drafts this work started from predated the domain move and said everwhen.gg, which resolves to nothing; that address appears nowhere in the shipped pages.

Terms of Service: the document states what the code does

The page is static prose in the house idiom โ€” hairline sections, serif headings, existing utility classes only. The text is the finalized agreement: Two Ls LLC (Minnesota), 18+, Hennepin County courts, a $50 / trailing-12-months liability cap. Three spots deserve a reviewer's attention.

Accounts (ยง3) describes sign-in as it actually works: a third-party provider (Google or Discord), never a password. AI content (ยง5) carries the no-reliance core: outputs are fiction, not fact, not advice. Payments (ยง9) speaks the game's own vocabulary โ€” runs and one-time packs, never "plays" โ€” and settles the refund policy: final except where law requires, discretionary otherwise, with the EU/UK immediate-delivery withdrawal acknowledgment.

ยง3 Accounts and ยง9 Runs, purchases, and payments. Expand the fold to read the full document โ€” the whole agreement is in this file.

pages/terms.vue ยท 432 lines
pages/terms.vue432 lines ยท Vue
โ‹ฏ 63 lines hidden (lines 1โ€“63)
1<template>
2 <!-- Terms of Service (#234): the binding document behind the footer's "Terms" link.
3 Static prose in the warm-ledger idiom โ€” hairline sections, serif headings, no
4 new tokens. The content is the finalized legal text; edits to it are legal
5 edits, not copy polish. -->
6 <div class="min-h-screen ew-bg flex flex-col">
7 <SiteHeader />
8 
9 <main class="flex-1 px-5 py-10 sm:py-14">
10 <article class="max-w-2xl mx-auto">
11 <p class="ew-label">Everwhen</p>
12 <h2
13 class="ew-serif ew-fg text-3xl sm:text-4xl font-bold mt-2 leading-tight"
14 >
15 Terms of Service
16 </h2>
17 <p class="ew-faint text-xs mt-3">Last updated: July 5, 2026</p>
18 
19 <p class="ew-muted text-sm mt-6 leading-relaxed">
20 These Terms of Service (the "Terms") are a
21 <strong class="ew-fg"
22 >binding agreement between you and Two Ls LLC</strong
23 >
24 ("Two Ls," "we," "us"), a Minnesota limited liability company,
25 governing your use of <strong class="ew-fg">Everwhen</strong> (the
26 "Game"), available at
27 <a href="https://playeverwhen.com" class="underline ew-hover-fg"
28 >https://playeverwhen.com</a
29 >.
30 </p>
31 <p class="ew-muted text-sm mt-3 leading-relaxed">
32 <strong class="ew-fg"
33 >By creating an account, clicking "I agree," or using the Game, you
34 accept these Terms and our
35 <NuxtLink to="/privacy" class="underline ew-hover-fg"
36 >Privacy Policy</NuxtLink
37 >.</strong
38 >
39 If you do not agree, do not use the Game.
40 </p>
41 
42 <section id="eligibility" class="border-t ew-line pt-6 mt-8">
43 <h3 class="ew-serif ew-fg text-lg font-bold">1. Eligibility</h3>
44 <p class="ew-muted text-sm mt-2 leading-relaxed">
45 You must be at least <strong class="ew-fg">18</strong> years old to
46 use the Game. By using it, you represent that you meet this
47 requirement and that you have the legal capacity to enter into these
48 Terms.
49 </p>
50 </section>
51 
52 <section id="license" class="border-t ew-line pt-6 mt-8">
53 <h3 class="ew-serif ew-fg text-lg font-bold">
54 2. License to use the Game
55 </h3>
56 <p class="ew-muted text-sm mt-2 leading-relaxed">
57 Subject to these Terms, we grant you a personal, limited,
58 non-exclusive, non-transferable, revocable license to access and use
59 the Game for your own non-commercial entertainment. We reserve all
60 rights not expressly granted.
61 </p>
62 </section>
63 
64 <section id="accounts" class="border-t ew-line pt-6 mt-8">
65 <h3 class="ew-serif ew-fg text-lg font-bold">3. Accounts</h3>
66 <p class="ew-muted text-sm mt-2 leading-relaxed">
67 You can try the Game without an account. Creating an account works
68 through a third-party sign-in provider (currently Google or
69 Discord); we never ask for or store a password for you. You are
70 responsible for your account and for all activity under it. Keep
71 your sign-in method secure and notify us at
72 <a :href="mailtoHref" class="underline ew-hover-fg">{{
73 SUPPORT_EMAIL
74 }}</a>
75 of any unauthorized use. We may suspend or terminate accounts as
76 described in Section 13.
77 </p>
78 </section>
โ‹ฏ 172 lines hidden (lines 79โ€“250)
79 
80 <section id="fiction" class="border-t ew-line pt-6 mt-8">
81 <h3 class="ew-serif ew-fg text-lg font-bold">
82 4. The Game is a work of interactive fiction
83 </h3>
84 <p class="ew-muted text-sm mt-2 leading-relaxed">
85 Everwhen is an entertainment product. Its characters, "historical
86 figures," timelines, events, and outcomes are
87 <strong class="ew-fg">fictional and dramatized</strong>, generated
88 in part by artificial intelligence and influenced by in-game
89 randomness (e.g., dice). Nothing in the Game is a factual account of
90 real people or events.
91 </p>
92 </section>
93 
94 <section id="ai-content" class="border-t ew-line pt-6 mt-8">
95 <h3 class="ew-serif ew-fg text-lg font-bold">
96 5. AI-generated content and no reliance
97 </h3>
98 <ul
99 class="list-disc pl-5 ew-muted text-sm mt-2 leading-relaxed space-y-2"
100 >
101 <li>
102 <strong class="ew-fg">Responses are generated by AI.</strong>
103 The Game uses Anthropic's <strong class="ew-fg">Claude</strong>
104 models to generate replies, narrative, and outcomes, and may also
105 use OpenAI models for generation. This content is produced
106 automatically and
107 <strong class="ew-fg"
108 >may be inaccurate, misleading, incomplete, offensive, or
109 entirely fabricated.</strong
110 >
111 </li>
112 <li>
113 <strong class="ew-fg">Not fact, not advice.</strong> AI output is
114 <strong class="ew-fg">not</strong> historical fact, news, or a
115 real statement by any real or historical person, and it is
116 <strong class="ew-fg">not</strong> professional advice of any kind
117 (legal, medical, financial, or otherwise). Do not rely on it for
118 any decision.
119 </li>
120 <li>
121 <strong class="ew-fg"
122 >Depictions of real or historical figures are fictional.</strong
123 >
124 Any portrayal of a real or historical person is an AI-generated
125 dramatization for entertainment and does not represent that
126 person's actual views, words, or conduct.
127 </li>
128 <li>
129 <strong class="ew-fg">Provided "as is."</strong> AI features are
130 provided without warranty of accuracy, availability, or fitness
131 for any purpose. You use them at your own risk.
132 </li>
133 </ul>
134 </section>
135 
136 <section id="your-content" class="border-t ew-line pt-6 mt-8">
137 <h3 class="ew-serif ew-fg text-lg font-bold">
138 6. Your content and conduct
139 </h3>
140 <ul
141 class="list-disc pl-5 ew-muted text-sm mt-2 leading-relaxed space-y-2"
142 >
143 <li>
144 <strong class="ew-fg">Your content.</strong> "User Content" means
145 anything you submit, including the messages and prompts you type.
146 You are solely responsible for your User Content and represent
147 that you have the rights to submit it and that it does not violate
148 these Terms or any law.
149 </li>
150 <li>
151 <strong class="ew-fg">License to us.</strong> You grant Two Ls a
152 worldwide, non-exclusive, royalty-free license to host, store,
153 reproduce, process, transmit (including
154 <strong class="ew-fg"
155 >to our AI and infrastructure providers</strong
156 >), modify (e.g., for formatting or moderation), and display your
157 User Content
158 <strong class="ew-fg"
159 >as needed to operate, moderate, secure, and improve the
160 Game</strong
161 >. This license ends when your content is deleted from our active
162 systems, except for copies retained for legal, backup, or safety
163 purposes.
164 </li>
165 <li>
166 <strong class="ew-fg">Acceptable use.</strong> You agree
167 <strong class="ew-fg">not</strong> to use the Game, or submit User
168 Content, to:
169 <ul class="list-disc pl-5 mt-2 space-y-1.5">
170 <li>break any law or infringe anyone's rights;</li>
171 <li>
172 generate, solicit, or distribute content that is unlawful,
173 harassing, hateful, defamatory, or that sexualizes or
174 endangers minors;
175 </li>
176 <li>
177 attempt to produce real personal data about real individuals,
178 or to harass, impersonate, or harm a real person;
179 </li>
180 <li>
181 generate instructions that facilitate serious harm (e.g.,
182 weapons, violence, illegal activity);
183 </li>
184 <li>
185 probe, attack, reverse-engineer, scrape, or circumvent the
186 Game or its safeguards, or misuse the underlying AI services
187 in violation of Anthropic's Usage Policy (including attempting
188 to use outputs to train or improve another AI model); or
189 </li>
190 <li>
191 interfere with the Game's operation or other users' enjoyment
192 of it.
193 </li>
194 </ul>
195 </li>
196 <li>
197 <strong class="ew-fg">Moderation.</strong> We may (but are not
198 obligated to) review, filter, refuse, remove, or restrict any
199 content, and we may suspend or terminate accounts, at our
200 discretion. We use automated moderation โ€” including OpenAI's
201 moderation service and an additional Anthropic model โ€” to screen
202 the content you submit and key responses the Game generates. We
203 may report unlawful content (including any child-sexual-abuse
204 material) to the authorities and relevant organizations.
205 </li>
206 </ul>
207 </section>
208 
209 <section id="providers" class="border-t ew-line pt-6 mt-8">
210 <h3 class="ew-serif ew-fg text-lg font-bold">
211 7. Compliance with our providers' terms
212 </h3>
213 <p class="ew-muted text-sm mt-2 leading-relaxed">
214 The Game's AI features are powered by
215 <strong class="ew-fg">Anthropic</strong>, and content moderation is
216 provided in part by <strong class="ew-fg">OpenAI</strong>; we may
217 also use OpenAI models for generation. Your use of the Game is also
218 subject to those providers' acceptable-use and usage policies โ€” in
219 particular
220 <a
221 href="https://www.anthropic.com/legal/aup"
222 class="underline ew-hover-fg"
223 >Anthropic's Usage Policy</a
224 >
225 โ€” and you agree not to use the Game in any way that would violate
226 them. Among other things, Anthropic's policy requires that users be
227 told they are interacting with an AI system (which we disclose in
228 the Game) and prohibits using the service to train competing AI
229 models, to generate content that sexualizes or endangers minors, or
230 for other prohibited purposes.
231 </p>
232 </section>
233 
234 <section id="ip" class="border-t ew-line pt-6 mt-8">
235 <h3 class="ew-serif ew-fg text-lg font-bold">
236 8. Intellectual property
237 </h3>
238 <p class="ew-muted text-sm mt-2 leading-relaxed">
239 The Game, including its software, design, art, text, and trademarks
240 (including "Two Ls" and "Everwhen"), is owned by Two Ls or its
241 licensors and is protected by law. Except for the license in Section
242 2, you receive no ownership rights. AI-generated outputs are
243 delivered to you as part of the Game for your personal,
244 non-commercial play; note that, under applicable copyright law,
245 purely AI-generated material may not be protectable by anyone. If
246 you send us feedback or suggestions, we may use them freely without
247 obligation to you.
248 </p>
249 </section>
250 
251 <section id="payments" class="border-t ew-line pt-6 mt-8">
252 <h3 class="ew-serif ew-fg text-lg font-bold">
253 9. Runs, purchases, and payments
254 </h3>
255 <p class="ew-muted text-sm mt-2 leading-relaxed">
256 Everwhen is free to try and uses one-time purchases. Your first run
257 is free and needs no account. More runs come as one-time run packs
258 you can buy in the Game, and you can sometimes earn extra runs (for
259 example, through sharing rewards). Runs never expire.
260 </p>
261 <ul
262 class="list-disc pl-5 ew-muted text-sm mt-3 leading-relaxed space-y-2"
263 >
264 <li>
265 <strong class="ew-fg">Pricing.</strong> Prices for run packs, and
266 any free-run allowances, are shown in the Game. We may change
267 prices and allowances at any time; changes apply on a
268 going-forward basis.
269 </li>
270 <li>
271 <strong class="ew-fg">Billing.</strong> Payments are processed by
272 our payment processor, <strong class="ew-fg">Stripe</strong>,
273 under its own terms and privacy policy. We do not store full
274 payment card numbers.
275 </li>
276 <li>
277 <strong class="ew-fg">Taxes.</strong> Listed prices may not
278 include taxes; you are responsible for any taxes that apply to
279 your purchase.
280 </li>
281 <li>
282 <strong class="ew-fg">Refunds.</strong> Purchases are final and
283 non-refundable, except where a refund is required by law. We may
284 choose to grant a refund in other cases at our discretion. If you
285 are in the EU or UK, you agree that digital content is delivered
286 immediately on purchase and acknowledge that you lose the
287 statutory right of withdrawal once delivery has begun.
288 </li>
289 <li>
290 <strong class="ew-fg">No real-world value.</strong> Runs, credits,
291 and virtual items are licensed to you for use in the Game only.
292 They have no monetary value, are not your property, and cannot be
293 redeemed for cash or transferred.
294 </li>
295 </ul>
296 </section>
โ‹ฏ 136 lines hidden (lines 297โ€“432)
297 
298 <section id="disclaimers" class="border-t ew-line pt-6 mt-8">
299 <h3 class="ew-serif ew-fg text-lg font-bold">10. Disclaimers</h3>
300 <p class="ew-fg text-sm mt-2 leading-relaxed">
301 THE GAME AND ALL CONTENT (INCLUDING AI OUTPUT) ARE PROVIDED "AS IS"
302 AND "AS AVAILABLE," WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
303 IMPLIED, INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
304 FOR A PARTICULAR PURPOSE, ACCURACY, AND NON-INFRINGEMENT. WE DO NOT
305 WARRANT THAT THE GAME WILL BE UNINTERRUPTED, SECURE, OR ERROR-FREE,
306 OR THAT AI OUTPUT WILL BE ACCURATE OR APPROPRIATE.
307 </p>
308 <p class="ew-muted text-sm mt-3 leading-relaxed">
309 Some jurisdictions do not allow certain warranty disclaimers, so
310 parts of the above may not apply to you; this section applies to the
311 maximum extent permitted by law.
312 </p>
313 </section>
314 
315 <section id="liability" class="border-t ew-line pt-6 mt-8">
316 <h3 class="ew-serif ew-fg text-lg font-bold">
317 11. Limitation of liability
318 </h3>
319 <p class="ew-fg text-sm mt-2 leading-relaxed">
320 TO THE MAXIMUM EXTENT PERMITTED BY LAW, TWO LS WILL NOT BE LIABLE
321 FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE
322 DAMAGES, OR ANY LOSS OF DATA, PROFITS, OR GOODWILL, ARISING FROM OR
323 RELATING TO THE GAME OR THESE TERMS. OUR TOTAL LIABILITY FOR ALL
324 CLAIMS WILL NOT EXCEED THE GREATER OF (A) THE AMOUNT YOU PAID US IN
325 THE 12 MONTHS BEFORE THE CLAIM, OR (B) US$50.
326 </p>
327 </section>
328 
329 <section id="indemnification" class="border-t ew-line pt-6 mt-8">
330 <h3 class="ew-serif ew-fg text-lg font-bold">12. Indemnification</h3>
331 <p class="ew-muted text-sm mt-2 leading-relaxed">
332 You agree to indemnify and hold harmless Two Ls and its members and
333 agents from any claims, damages, losses, and expenses (including
334 reasonable legal fees) arising from your User Content, your use of
335 the Game, or your violation of these Terms or the rights of others.
336 </p>
337 </section>
338 
339 <section id="termination" class="border-t ew-line pt-6 mt-8">
340 <h3 class="ew-serif ew-fg text-lg font-bold">
341 13. Suspension and termination
342 </h3>
343 <p class="ew-muted text-sm mt-2 leading-relaxed">
344 We may suspend or terminate your access at any time, with or without
345 notice, including for violations of these Terms. You may stop using
346 the Game at any time. Sections that by their nature should survive
347 termination (including 5, 6, 8, 10, 11, 12, and 14) will survive.
348 </p>
349 </section>
350 
351 <section id="governing-law" class="border-t ew-line pt-6 mt-8">
352 <h3 class="ew-serif ew-fg text-lg font-bold">
353 14. Governing law and disputes
354 </h3>
355 <p class="ew-muted text-sm mt-2 leading-relaxed">
356 These Terms are governed by the laws of the State of Minnesota,
357 without regard to conflict-of-laws rules. You agree that any dispute
358 arising out of or relating to the Game or these Terms will be
359 brought and resolved exclusively in the state or federal courts
360 located in
361 <strong class="ew-fg">Hennepin County, Minnesota</strong>, and you
362 and Two Ls consent to the personal jurisdiction of those courts.
363 </p>
364 </section>
365 
366 <section id="changes" class="border-t ew-line pt-6 mt-8">
367 <h3 class="ew-serif ew-fg text-lg font-bold">
368 15. Changes to these Terms
369 </h3>
370 <p class="ew-muted text-sm mt-2 leading-relaxed">
371 We may update these Terms from time to time. We will post the
372 updated version with a new date and, for material changes, provide
373 additional notice. Continued use after changes take effect means you
374 accept them.
375 </p>
376 </section>
377 
378 <section id="miscellaneous" class="border-t ew-line pt-6 mt-8">
379 <h3 class="ew-serif ew-fg text-lg font-bold">16. Miscellaneous</h3>
380 <p class="ew-muted text-sm mt-2 leading-relaxed">
381 These Terms (with the
382 <NuxtLink to="/privacy" class="underline ew-hover-fg"
383 >Privacy Policy</NuxtLink
384 >
385 and any posted rules) are the entire agreement between you and Two
386 Ls regarding the Game. If any provision is unenforceable, the rest
387 remains in effect. Our failure to enforce a provision is not a
388 waiver. You may not assign these Terms; we may assign them in
389 connection with a business transfer.
390 </p>
391 </section>
392 
393 <section id="contact" class="border-t ew-line pt-6 mt-8">
394 <h3 class="ew-serif ew-fg text-lg font-bold">17. Contact</h3>
395 <p class="ew-muted text-sm mt-2 leading-relaxed">
396 Questions:
397 <a :href="mailtoHref" class="underline ew-hover-fg">{{
398 SUPPORT_EMAIL
399 }}</a
400 >, or Two Ls LLC, 910 Emerson Ave N, Minneapolis, MN 55411.
401 </p>
402 </section>
403 </article>
404 </main>
405 
406 <SiteFooter lead="back" />
407 </div>
408</template>
409 
410<script setup lang="ts">
411/**
412 * Terms of Service (issue #234) โ€” the finalized legal document behind the footer's
413 * "Terms" link. The text is legal content: change it deliberately, keep the
414 * "Last updated" date honest, and keep the section numbering stable (Sections 3
415 * and 13 cross-reference it, and Section 13's survival list names 5, 6, 8, 10,
416 * 11, 12, and 14).
417 */
418import { SUPPORT_EMAIL } from "~/utils/site-meta";
419 
420const mailtoHref = `mailto:${SUPPORT_EMAIL}`;
421 
422useHead({
423 title: "Terms of Service โ€” Everwhen",
424 meta: [
425 {
426 name: "description",
427 content:
428 "The terms that govern playing Everwhen โ€” eligibility, AI-generated content, purchases, and disputes.",
429 },
430 ],
431});
432</script>

Privacy Policy: precise about moderation, providers, and cookies

Same idiom, thirteen sections. The novel parts are the ones a generic template can't write: how player text flows through AI providers, and what the moderation stack actually screens.

Section 4 names the flow exactly. Inputs go to Anthropic (or OpenAI, if generation is routed there); both the player's messages and the core per-turn responses pass through OpenAI's moderation endpoint plus an Anthropic model. And it names the cut honestly: derived narration like the run's chronicle is composed from already-screened turns and is not separately screened โ€” which is the repo's documented moderation boundary, not an oversight.

ยง4 AI processing (with the chronicle cut named) and the ยง5 provider list. The full policy is in the fold.

pages/privacy.vue ยท 431 lines
pages/privacy.vue431 lines ยท Vue
โ‹ฏ 158 lines hidden (lines 1โ€“158)
1<template>
2 <!-- Privacy Policy (#234): the document behind the footer's "Privacy" link. Static
3 prose in the warm-ledger idiom. The content is the finalized legal text; edits
4 to it are legal edits, not copy polish. -->
5 <div class="min-h-screen ew-bg flex flex-col">
6 <SiteHeader />
7 
8 <main class="flex-1 px-5 py-10 sm:py-14">
9 <article class="max-w-2xl mx-auto">
10 <p class="ew-label">Everwhen</p>
11 <h2
12 class="ew-serif ew-fg text-3xl sm:text-4xl font-bold mt-2 leading-tight"
13 >
14 Privacy Policy
15 </h2>
16 <p class="ew-faint text-xs mt-3">Last updated: July 5, 2026</p>
17 
18 <p class="ew-muted text-sm mt-6 leading-relaxed">
19 <strong class="ew-fg">Operator:</strong> Two Ls LLC ("Two Ls," "we,"
20 "us," "our"), a Minnesota limited liability company, 910 Emerson Ave
21 N, Minneapolis, MN 55411.
22 <strong class="ew-fg">Contact:</strong>
23 <a :href="mailtoHref" class="underline ew-hover-fg">{{
24 SUPPORT_EMAIL
25 }}</a
26 >.
27 </p>
28 <p class="ew-muted text-sm mt-3 leading-relaxed">
29 This Privacy Policy explains how Two Ls LLC collects, uses, shares,
30 and protects information when you use
31 <strong class="ew-fg">Everwhen</strong> (the "Game"), available at
32 <a href="https://playeverwhen.com" class="underline ew-hover-fg"
33 >https://playeverwhen.com</a
34 >. By using the Game, you agree to the practices described here. If
35 you do not agree, please do not use the Game.
36 </p>
37 
38 <section id="how-everwhen-works" class="border-t ew-line pt-6 mt-8">
39 <h3 class="ew-serif ew-fg text-lg font-bold">
40 1. A quick note about how Everwhen works
41 </h3>
42 <p class="ew-muted text-sm mt-2 leading-relaxed">
43 Everwhen lets you send text messages to AI-generated, fictional
44 representations of people and moments in history.
45 <strong class="ew-fg"
46 >The things you type are sent to Anthropic โ€” the maker of the
47 Claude AI models โ€” to generate responses (in some cases, to
48 OpenAI), and to content-moderation services so we can keep the
49 Game safe.</strong
50 >
51 Please keep this in mind throughout:
52 <strong class="ew-fg"
53 >do not send personal, sensitive, or confidential
54 information</strong
55 >
56 in your in-game messages. See Section 4.
57 </p>
58 </section>
59 
60 <section id="information-we-collect" class="border-t ew-line pt-6 mt-8">
61 <h3 class="ew-serif ew-fg text-lg font-bold">
62 2. Information we collect
63 </h3>
64 <p class="ew-muted text-sm mt-2 leading-relaxed">
65 <strong class="ew-fg">Information you provide:</strong>
66 </p>
67 <ul
68 class="list-disc pl-5 ew-muted text-sm mt-2 leading-relaxed space-y-2"
69 >
70 <li>
71 <strong class="ew-fg">Account information</strong> โ€” when you sign
72 in, you use a third-party provider (currently Google or Discord),
73 and we receive your email address, a sign-in identifier, and basic
74 profile details (such as a name or avatar) from that provider. We
75 never receive or store a password. If you play without signing in,
76 your progress is tied to an anonymous account and a device
77 identifier.
78 </li>
79 <li>
80 <strong class="ew-fg">Gameplay content</strong> โ€” the messages,
81 prompts, and other text you submit while playing, and the in-game
82 choices you make.
83 </li>
84 <li>
85 <strong class="ew-fg">Communications</strong> โ€” information you
86 send us (support requests, feedback, survey responses).
87 </li>
88 <li>
89 <strong class="ew-fg">Payment information</strong> โ€” when you buy
90 run packs, our payment processor, Stripe, collects and processes
91 your payment details.
92 <strong class="ew-fg"
93 >We do not store full payment card numbers.</strong
94 >
95 </li>
96 </ul>
97 <p class="ew-muted text-sm mt-3 leading-relaxed">
98 <strong class="ew-fg">Information collected automatically:</strong>
99 </p>
100 <ul
101 class="list-disc pl-5 ew-muted text-sm mt-2 leading-relaxed space-y-2"
102 >
103 <li>
104 <strong class="ew-fg">Device and usage data</strong> โ€” IP address,
105 browser/device type, operating system, the pages you request,
106 referring URLs, and similar diagnostic data from standard server
107 logs.
108 </li>
109 <li>
110 <strong class="ew-fg">Cookies and similar technologies</strong> โ€”
111 see Section 6.
112 </li>
113 </ul>
114 <p class="ew-muted text-sm mt-3 leading-relaxed">
115 We do not intentionally collect special-category or highly sensitive
116 personal information, and we ask that you not submit it (see Section
117 4).
118 </p>
119 </section>
120 
121 <section id="how-we-use-information" class="border-t ew-line pt-6 mt-8">
122 <h3 class="ew-serif ew-fg text-lg font-bold">
123 3. How we use information
124 </h3>
125 <p class="ew-muted text-sm mt-2 leading-relaxed">
126 We use information to:
127 </p>
128 <ul
129 class="list-disc pl-5 ew-muted text-sm mt-2 leading-relaxed space-y-1.5"
130 >
131 <li>
132 operate, provide, and maintain the Game, including generating AI
133 responses and gameplay;
134 </li>
135 <li>
136 moderate content and enforce our
137 <NuxtLink to="/terms" class="underline ew-hover-fg"
138 >Terms of Service</NuxtLink
139 >, and keep the Game and our users safe;
140 </li>
141 <li>process transactions and provide customer support;</li>
142 <li>understand how the Game is used and improve it;</li>
143 <li>
144 communicate with you about updates, security notices, and (with
145 your consent where required) marketing;
146 </li>
147 <li>
148 detect, prevent, and address fraud, abuse, security, or technical
149 issues; and
150 </li>
151 <li>comply with legal obligations and enforce our rights.</li>
152 </ul>
153 <p class="ew-muted text-sm mt-3 leading-relaxed">
154 We do not use the content of your in-game messages to train AI
155 models.
156 </p>
157 </section>
158 
159 <section id="ai-processing" class="border-t ew-line pt-6 mt-8">
160 <h3 class="ew-serif ew-fg text-lg font-bold">
161 4. AI processing and your inputs
162 </h3>
163 <p class="ew-muted text-sm mt-2 leading-relaxed">
164 Everwhen uses <strong class="ew-fg">Anthropic</strong> (the maker of
165 the <strong class="ew-fg">Claude</strong> AI models) to generate
166 responses (and may use OpenAI models for the same purpose), plus a
167 two-layer <strong class="ew-fg">content-moderation</strong> stack to
168 screen content.
169 </p>
170 <ul
171 class="list-disc pl-5 ew-muted text-sm mt-3 leading-relaxed space-y-2"
172 >
173 <li>
174 <strong class="ew-fg"
175 >Your inputs are transmitted to Anthropic.</strong
176 >
177 When you send an in-game message or prompt, that text (and
178 necessary context) is sent to Anthropic's systems to generate a
179 response; if we route generation to OpenAI, it is sent to OpenAI's
180 systems instead. Each provider's handling of that data is governed
181 by its own terms and policies โ€” see
182 <a
183 href="https://www.anthropic.com/legal/aup"
184 class="underline ew-hover-fg"
185 >Anthropic's Usage Policy</a
186 >. Because we access Claude through Anthropic's commercial API,
187 <strong class="ew-fg"
188 >Anthropic does not use your inputs or the generated outputs to
189 train its models by default.</strong
190 >
191 </li>
192 <li>
193 <strong class="ew-fg"
194 >Your content is screened for safety.</strong
195 >
196 To enforce our rules and protect users, the messages you send and
197 the core responses the Game generates each turn are checked by
198 automated moderation โ€”
199 <strong class="ew-fg">OpenAI's moderation endpoint</strong> (a
200 fast first-pass classifier) and an
201 <strong class="ew-fg">additional Anthropic model</strong> that
202 performs a more holistic review. This means content is also
203 transmitted to OpenAI for that check; OpenAI processes it under
204 its own policies. Derived narration composed from already-screened
205 turns (such as the run's chronicle) is not separately screened.
206 </li>
207 <li>
โ‹ฏ 38 lines hidden (lines 208โ€“245)
208 <strong class="ew-fg"
209 >Outputs are AI-generated and fictional.</strong
210 >
211 Responses are produced by a language model. They may be
212 inaccurate, offensive, or entirely invented, and they are
213 <strong class="ew-fg">not</strong> statements of historical fact
214 or real communications from any real person. See the
215 <NuxtLink to="/terms" class="underline ew-hover-fg"
216 >Terms of Service</NuxtLink
217 >
218 for the full disclaimer.
219 </li>
220 <li>
221 <strong class="ew-fg"
222 >Do not submit sensitive information.</strong
223 >
224 Please do not include personal data about yourself or others (real
225 names, contact details, financial or health information,
226 credentials, etc.) in your in-game messages.
227 </li>
228 <li>
229 <strong class="ew-fg">Storage and review.</strong> We and/or our
230 providers may store conversation content and may review it
231 (including via automated tools and, where necessary, human review)
232 to operate the Game, enforce our rules, and improve safety and
233 quality.
234 </li>
235 </ul>
236 </section>
237 
238 <section id="how-we-share" class="border-t ew-line pt-6 mt-8">
239 <h3 class="ew-serif ew-fg text-lg font-bold">
240 5. How we share information
241 </h3>
242 <p class="ew-muted text-sm mt-2 leading-relaxed">
243 We do not sell your personal information, and we do not use
244 third-party analytics or advertising services. We share information
245 only as follows:
246 </p>
247 <ul
248 class="list-disc pl-5 ew-muted text-sm mt-2 leading-relaxed space-y-2"
249 >
250 <li>
251 <strong class="ew-fg">Service providers</strong> โ€” companies that
252 help us run the Game:
253 <strong class="ew-fg">Anthropic</strong> (generates the AI
254 responses and provides one layer of content moderation),
255 <strong class="ew-fg">OpenAI</strong> (the moderation-endpoint
256 screening layer; may also generate responses),
257 <strong class="ew-fg">Supabase</strong> (database and
258 authentication), <strong class="ew-fg">DigitalOcean</strong>
259 (cloud hosting),
260 <strong class="ew-fg">Stripe</strong> (payment processing), and
261 email/communications providers when we correspond with you โ€” each
262 acting on our behalf and bound by appropriate obligations.
263 </li>
โ‹ฏ 168 lines hidden (lines 264โ€“431)
264 <li>
265 <strong class="ew-fg">Sharing you choose</strong> โ€” if you share a
266 run, anyone with the link can view that run's content.
267 </li>
268 <li>
269 <strong class="ew-fg">Legal and safety</strong> โ€” when we believe
270 disclosure is reasonably necessary to comply with law, legal
271 process, or government requests; to enforce our Terms; or to
272 protect the rights, safety, or property of Two Ls, our users, or
273 others.
274 </li>
275 <li>
276 <strong class="ew-fg">Business transfers</strong> โ€” in connection
277 with a merger, acquisition, financing, or sale of assets,
278 information may be transferred as part of that transaction.
279 </li>
280 <li>
281 <strong class="ew-fg">With your consent</strong> โ€” for any other
282 purpose disclosed to you with your permission.
283 </li>
284 </ul>
285 </section>
286 
287 <section id="cookies" class="border-t ew-line pt-6 mt-8">
288 <h3 class="ew-serif ew-fg text-lg font-bold">
289 6. Cookies and similar technologies
290 </h3>
291 <p class="ew-muted text-sm mt-2 leading-relaxed">
292 We use only essential cookies and similar technologies: they keep
293 you signed in, remember your device so anonymous progress and
294 free-run limits work, credit referrals, and remember preferences
295 (like theme and sound settings, kept in your browser's local
296 storage). We do not use third-party analytics or advertising
297 cookies. You can control cookies through your browser settings, but
298 blocking essential cookies may break sign-in or gameplay.
299 </p>
300 </section>
301 
302 <section id="data-retention" class="border-t ew-line pt-6 mt-8">
303 <h3 class="ew-serif ew-fg text-lg font-bold">7. Data retention</h3>
304 <p class="ew-muted text-sm mt-2 leading-relaxed">
305 We keep personal information only as long as needed for the purposes
306 described here โ€” for example, for the life of your account, and
307 afterward as needed to comply with legal obligations, resolve
308 disputes, and enforce agreements. We may retain de-identified or
309 aggregated data indefinitely.
310 </p>
311 </section>
312 
313 <section id="security" class="border-t ew-line pt-6 mt-8">
314 <h3 class="ew-serif ew-fg text-lg font-bold">8. Security</h3>
315 <p class="ew-muted text-sm mt-2 leading-relaxed">
316 We use reasonable technical and organizational measures designed to
317 protect information. No method of transmission or storage is
318 completely secure, however, and we cannot guarantee absolute
319 security.
320 </p>
321 </section>
322 
323 <section id="your-rights" class="border-t ew-line pt-6 mt-8">
324 <h3 class="ew-serif ew-fg text-lg font-bold">
325 9. Your rights and choices
326 </h3>
327 <p class="ew-muted text-sm mt-2 leading-relaxed">
328 Depending on where you live, you may have rights to access, correct,
329 delete, or port your personal information, to object to or restrict
330 certain processing, and to withdraw consent. To exercise any right,
331 contact us at
332 <a :href="mailtoHref" class="underline ew-hover-fg">{{
333 SUPPORT_EMAIL
334 }}</a
335 >. We will respond as required by applicable law. Any marketing
336 email we send will include an unsubscribe link.
337 </p>
338 <p class="ew-muted text-sm mt-3 leading-relaxed">
339 We do not sell personal information, and we do not share it for
340 cross-context behavioral advertising. If you are in the EU or UK: we
341 process personal information to perform our contract with you
342 (running the Game), for our legitimate interests (such as safety,
343 security, and improving the Game), and with your consent where
344 required; you also have the right to complain to your local
345 data-protection authority.
346 </p>
347 </section>
348 
349 <section id="children" class="border-t ew-line pt-6 mt-8">
350 <h3 class="ew-serif ew-fg text-lg font-bold">
351 10. Children's privacy
352 </h3>
353 <p class="ew-muted text-sm mt-2 leading-relaxed">
354 The Game is intended for users
355 <strong class="ew-fg">18 and older</strong>. We do not knowingly
356 collect personal information from anyone under 18. If you believe
357 someone under 18 has provided us information, contact us at
358 <a :href="mailtoHref" class="underline ew-hover-fg">{{
359 SUPPORT_EMAIL
360 }}</a>
361 and we will delete it.
362 </p>
363 </section>
364 
365 <section id="international" class="border-t ew-line pt-6 mt-8">
366 <h3 class="ew-serif ew-fg text-lg font-bold">
367 11. International users
368 </h3>
369 <p class="ew-muted text-sm mt-2 leading-relaxed">
370 We operate from the United States, and your information will be
371 processed in the United States and other countries that may have
372 different data-protection laws than where you live. Where local law
373 requires protections for international transfers, we rely on our
374 providers' safeguards and contractual commitments.
375 </p>
376 </section>
377 
378 <section id="policy-changes" class="border-t ew-line pt-6 mt-8">
379 <h3 class="ew-serif ew-fg text-lg font-bold">
380 12. Changes to this policy
381 </h3>
382 <p class="ew-muted text-sm mt-2 leading-relaxed">
383 We may update this Privacy Policy from time to time. We will post
384 the updated version with a new "Last updated" date and, for material
385 changes, provide additional notice where required.
386 </p>
387 </section>
388 
389 <section id="contact" class="border-t ew-line pt-6 mt-8">
390 <h3 class="ew-serif ew-fg text-lg font-bold">13. Contact us</h3>
391 <p class="ew-muted text-sm mt-2 leading-relaxed">
392 Questions or requests:
393 <a :href="mailtoHref" class="underline ew-hover-fg">{{
394 SUPPORT_EMAIL
395 }}</a
396 >, or Two Ls LLC, 910 Emerson Ave N, Minneapolis, MN 55411.
397 </p>
398 </section>
399 </article>
400 </main>
401 
402 <SiteFooter lead="back" />
403 </div>
404</template>
405 
406<script setup lang="ts">
407/**
408 * Privacy Policy (issue #234) โ€” the finalized legal document behind the footer's
409 * "Privacy" link. The text is legal content: it states what the Game actually does
410 * (OAuth-only sign-in, Anthropic generation with OpenAI available via the lane
411 * lever, two-layer moderation of the per-turn outputs with the chronicle as the
412 * documented cut, Supabase + DigitalOcean + Stripe, essential cookies only, no
413 * third-party analytics) โ€” if the product changes any of those facts, this
414 * document must change in the same PR. Keep the "Last updated" date honest, and
415 * keep the section numbering stable (Sections 1 and 2 cross-reference 4 and 6).
416 */
417import { SUPPORT_EMAIL } from "~/utils/site-meta";
418 
419const mailtoHref = `mailto:${SUPPORT_EMAIL}`;
420 
421useHead({
422 title: "Privacy Policy โ€” Everwhen",
423 meta: [
424 {
425 name: "description",
426 content:
427 "How Everwhen collects, uses, and protects information โ€” including how your messages are processed by AI and moderation services.",
428 },
429 ],
430});
431</script>

The Disclaimer page

A one-screen, plain-language restatement of the Terms' fiction and no-reliance clauses (ยง4, ยง5, ยง10): portrayals of real people are invented, AI output can be wrong, nothing is advice, don't type personal information. It exists for the visitor who lands on a shared run and will never read a 17-section agreement.

Short enough to read whole โ€” expand for the full page.

pages/disclaimer.vue ยท 109 lines
pages/disclaimer.vue109 lines ยท Vue
1<template>
2 <!-- Disclaimer (#234): the plain-language "this is fiction" page behind the footer's
3 "Disclaimer" link โ€” the one-screen summary of what the Terms spell out in full.
4 Useful on its own for visitors landing on a shared run without reading the
5 Terms. Static prose in the warm-ledger idiom. -->
6 <div class="min-h-screen ew-bg flex flex-col">
7 <SiteHeader />
8 
9 <main class="flex-1 px-5 py-10 sm:py-14">
10 <article class="max-w-2xl mx-auto">
11 <p class="ew-label">Everwhen</p>
12 <h2
13 class="ew-serif ew-fg text-3xl sm:text-4xl font-bold mt-2 leading-tight"
14 >
15 Disclaimer
16 </h2>
17 <p class="ew-faint text-xs mt-3">Last updated: July 5, 2026</p>
18 
19 <p class="ew-muted text-sm mt-6 leading-relaxed">
20 <strong class="ew-fg"
21 >Everwhen is a work of interactive fiction, for players 18 and
22 older.</strong
23 >
24 Its characters, timelines, events, and outcomes are invented and
25 dramatized โ€” generated in part by artificial intelligence and shaped
26 by in-game dice. Nothing in the Game is a factual account of real
27 people or events.
28 </p>
29 
30 <section class="border-t ew-line pt-6 mt-8">
31 <h3 class="ew-serif ew-fg text-lg font-bold">
32 Historical figures are fictional here
33 </h3>
34 <p class="ew-muted text-sm mt-2 leading-relaxed">
35 Any portrayal of a real or historical person in the Game is an
36 AI-generated dramatization for entertainment. It does not represent
37 that person's actual views, words, or conduct โ€” the replies are not
38 real history, real quotes, or real communications from anyone.
39 </p>
40 </section>
41 
42 <section class="border-t ew-line pt-6 mt-8">
43 <h3 class="ew-serif ew-fg text-lg font-bold">
44 AI output can be wrong
45 </h3>
46 <p class="ew-muted text-sm mt-2 leading-relaxed">
47 Responses are produced by AI models. They can be inaccurate,
48 misleading, offensive, or entirely fabricated, and they are not
49 professional advice of any kind โ€” legal, medical, financial, or
50 otherwise. Do not rely on anything in the Game for any decision.
51 </p>
52 </section>
53 
54 <section class="border-t ew-line pt-6 mt-8">
55 <h3 class="ew-serif ew-fg text-lg font-bold">Mind what you type</h3>
56 <p class="ew-muted text-sm mt-2 leading-relaxed">
57 The messages you send are processed by AI and content-moderation
58 services. Do not include personal, sensitive, or confidential
59 information โ€” the
60 <NuxtLink to="/privacy" class="underline ew-hover-fg"
61 >Privacy Policy</NuxtLink
62 >
63 explains how your inputs are handled.
64 </p>
65 </section>
66 
67 <section class="border-t ew-line pt-6 mt-8">
68 <h3 class="ew-serif ew-fg text-lg font-bold">The full terms</h3>
69 <p class="ew-muted text-sm mt-2 leading-relaxed">
70 The complete disclaimers, warranty terms, and liability limits live
71 in the
72 <NuxtLink to="/terms" class="underline ew-hover-fg"
73 >Terms of Service</NuxtLink
74 >. Questions:
75 <a :href="mailtoHref" class="underline ew-hover-fg">{{
76 SUPPORT_EMAIL
77 }}</a
78 >.
79 </p>
80 </section>
81 </article>
82 </main>
83 
84 <SiteFooter lead="back" />
85 </div>
86</template>
87 
88<script setup lang="ts">
89/**
90 * Disclaimer (issue #234) โ€” the plain-language summary of the Terms' fiction / AI /
91 * no-reliance clauses, behind the footer's "Disclaimer" link. It restates the
92 * Terms of Service (Sections 4, 5, and 10); if those clauses change, this page
93 * changes in the same PR.
94 */
95import { SUPPORT_EMAIL } from "~/utils/site-meta";
96 
97const mailtoHref = `mailto:${SUPPORT_EMAIL}`;
98 
99useHead({
100 title: "Disclaimer โ€” Everwhen",
101 meta: [
102 {
103 name: "description",
104 content:
105 "Everwhen is interactive fiction: AI-generated, dramatized, and not a factual account of real people or events.",
106 },
107 ],
108});
109</script>

Comment drift fixed, and how this was verified

Publishing a policy makes stale comments visible: the repo treats a comment that contradicts shipped fact as a bug to fix in the same PR. Four lines qualified โ€” three "magic link" references from before sign-in went OAuth-only, and a color-mode comment claiming cookie persistence when the module persists to localStorage (which the privacy page now states).

Both corrected comments; the config values are untouched.

nuxt.config.ts ยท 109 lines
nuxt.config.ts109 lines ยท TypeScript
โ‹ฏ 23 lines hidden (lines 1โ€“23)
1/**
2 * Nuxt 3 Configuration
3 * https://nuxt.com/docs/api/configuration/nuxt-config
4 */
5export default defineNuxtConfig({
6 // Ensure compatibility with modern Nuxt features
7 compatibilityDate: "2025-05-15",
8 
9 // Enable devtools for development
10 devtools: { enabled: true },
11 
12 // Declare the document language app-wide (issue #118). Set here rather than in app.vue so
13 // it also reaches routes that bypass the app component โ€” Nuxt's built-in error/404 page โ€”
14 // keeping <html lang="en"> consistent everywhere (accessibility; pairs with og:locale).
15 app: {
16 head: {
17 htmlAttrs: { lang: "en" },
18 },
19 },
20 
21 // Register required modules
22 modules: ["@nuxt/ui", "@pinia/nuxt", "@nuxtjs/supabase", "@nuxt/eslint"],
23 
24 // Supabase Auth (accounts). Anonymous-first: every visitor gets an anonymous
25 // user so the balance keys on a real user id from the first second; signing in
26 // (OAuth) upgrades that same user in place (balance carries over).
27 // redirect:false โ€” we DON'T gate play behind sign-in (anonymous play is the
28 // free-trial hook); buying is what requires an account. It also means the module
29 // registers no /confirm callback route, so the sign-in return is finalized in
30 // pages/play.vue (see utils/auth-return.ts). For that return to arrive, the
โ‹ฏ 18 lines hidden (lines 31โ€“48)
31 // Supabase project's Auth โ†’ URL Configuration โ†’ Redirect URLs must allowlist the
32 // deployed origin's /play (e.g. https://<origin>/play); otherwise Supabase falls
33 // back to the Site URL and the link never reaches the handler. The client key is
34 // the PUBLISHABLE key (safe to ship); the service key stays server-only in the
35 // balance store. Placeholders keep `nuxt build` working in CI without secrets;
36 // the real values come from NUXT_PUBLIC_SUPABASE_URL / _KEY at runtime.
37 supabase: {
38 url: process.env.SUPABASE_URL || "https://placeholder.supabase.co",
39 key: process.env.SUPABASE_PUBLISHABLE_KEY || "sb_publishable_placeholder",
40 redirect: false,
41 // We don't use generated DB types (the balance store talks to Supabase with the
42 // service client directly); disable the lookup so the module doesn't warn.
43 types: false,
44 },
45 
46 // Global CSS imports
47 css: ["~/assets/css/main.css"],
48 
49 // Color mode (via @nuxtjs/color-mode, bundled with @nuxt/ui). classSuffix '' so the
50 // class on <html> is exactly `dark` / `light` โ€” what the .dark token block and
51 // Tailwind's dark: variant key off. Defaulting to `system` respects the OS pref, and
52 // the module's inline no-flash script + localStorage persistence replace the old
53 // manual classList toggle (which didn't persist and could FOUC).
โ‹ฏ 56 lines hidden (lines 54โ€“109)
54 colorMode: {
55 classSuffix: "",
56 preference: "system",
57 fallback: "light",
58 },
59 
60 // Runtime configuration
61 runtimeConfig: {
62 // Private keys (only available on server-side)
63 openaiApiKey: process.env.OPENAI_API_KEY,
64 anthropicApiKey: process.env.ANTHROPIC_API_KEY,
65 // Supabase โ€” the run store (server-side only). Absent โ†’ in-memory dev store.
66 supabaseUrl: process.env.SUPABASE_URL,
67 supabaseSecretKey: process.env.SUPABASE_SECRET_KEY,
68 // Stripe โ€” run packs (server-side only). Absent โ†’ checkout returns 503.
69 stripeSecretKey: process.env.STRIPE_SECRET_KEY,
70 stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
71 // Canonical site origin for Stripe redirect URLs (never the client Host header).
72 siteUrl: process.env.EVERWHEN_BASE_URL,
73 // Referral credits (issue #96) โ€” server-side only; sane defaults, env-overridable.
74 // reward: runs credited to a sharer per qualified email signup. cap: soft per-sharer
75 // lifetime limit on credited referrals (<=0 disables). digest: the batched-notice
76 // throttle (a kill-switch + threshold + window). See server/utils/referral.ts.
77 referralReward: process.env.EVERWHEN_REFERRAL_REWARD,
78 referralCap: process.env.EVERWHEN_REFERRAL_CAP,
79 referralDigestEnabled: process.env.EVERWHEN_REFERRAL_DIGEST,
80 referralDigestThreshold: process.env.EVERWHEN_REFERRAL_DIGEST_THRESHOLD,
81 referralDigestWindowHours:
82 process.env.EVERWHEN_REFERRAL_DIGEST_WINDOW_HOURS,
83 // Operator allowlist for the Steward's Ledger ops dashboard (/steward) and its
84 // /api/steward/* endpoints โ€” comma-separated emails. Server-side only; empty โ†’
85 // no operators (deny-all). See server/utils/operator-auth.ts.
86 operatorEmails: process.env.EVERWHEN_OPERATOR_EMAILS,
87 // Public keys (exposed to client-side)
88 public: {
89 // Developer mode (issue #105). A dev build always has it (`import.meta.dev`);
90 // this flag additionally opts a DEPLOYED staging preview in. Never set in
91 // production. The server gates the fixture lane on the same env var, so the
92 // client panel and the server seam agree on one source of truth.
93 devMode: process.env.EVERWHEN_DEV_MODE === "true",
94 // The brand's X/Twitter @handle for twitter:site / twitter:creator attribution
95 // (issue #118). Unset โ†’ those two tags are simply omitted (a guessed handle would
96 // mis-attribute the card to a stranger). Set EVERWHEN_TWITTER_HANDLE (e.g.
97 // "@everwhen") in production to activate them.
98 twitterHandle: process.env.EVERWHEN_TWITTER_HANDLE || "",
99 },
100 },
101 
102 // The standalone agents/ tooling package has its own deps + tsconfig; keep it
103 // out of the app's type program so `nuxt typecheck` never compiles the loops.
104 typescript: {
105 tsConfig: {
106 exclude: ["../agents"],
107 },
108 },
109});

Verification, in order: eslint clean on every changed file, format:check clean, nuxt typecheck clean, and the unit suite green at 1350/1350 across 134 files. Then the app was booted and all five affected routes screenshotted in light and dark, at 375 and 1280 widths, and at tall and short heights for the /play guard.

The independent adversarial pass reviewed the documents against the code, refute-first. It confirmed eleven claims and killed three: the "automatic fallback" wording (no such mechanism), a blanket "responses are checked" claim (the chronicle is a documented moderation cut, now named), and "game state in localStorage" (run drafts persist server-side in Supabase). It also tightened the OAuth-profile disclosure, reassigned hosting from Supabase to DigitalOcean, and flagged the desk-footer squeeze that the max-height guard now handles.