···11+# The base URL of the PDS (Personal Data Server)
22+PUBLIC_PDS_URL=https://selfhosted.social
33+44+# Theme to be used (must match a folder in the themes/ directory)
55+VITE_THEME=dark
66+77+# The base URL of the frontend service for linking to replies/quotes/accounts etc.
88+PUBLIC_FRONTEND_URL=https://bsky.app
99+1010+# Maximum number of posts to fetch from the PDS per request
1111+# Should be around 20 for about 10 users on the PDS
1212+# The more users you have, the lower the number should be
1313+PUBLIC_MAX_POSTS=10
1414+1515+# Footer text for the dashboard. Supports HTML.
1616+PUBLIC_FOOTER_TEXT=
1717+1818+# Whether to show posts with timestamps that are in the future
1919+PUBLIC_SHOW_FUTURE_POSTS=false
···8899This fork is much the same but a few differences:
10101111-- [New theme](/themes/dark/theme.css)
1111+- [New theme](/src/lib/assets/theme.css)
1212- Uses the CDN for loading images and videos instead of
1313 `com.atproto.sync.getBlob`
1414- Caches a couple of things like did -> handle and PDS user profile lexicon
-44
config.ts.example
···11-/**
22- * Configuration module for the PDS Dashboard
33- */
44-export class Config {
55- /**
66- * The base URL of the PDS (Personal Data Server).
77- * @default none
88- */
99- static readonly PDS_URL: string = "";
1010-1111- /**
1212- * Theme to be used
1313- * @default "default"
1414- */
1515- static readonly THEME: string = "default";
1616-1717- /**
1818- * The base URL of the frontend service for linking to replies/quotes/accounts etc.
1919- * @default "https://deer.social" // or https://bsky.app if you're boring
2020- */
2121- static readonly FRONTEND_URL: string = "https://deer.social";
2222-2323- /**
2424- * Maximum number of posts to fetch from the PDS per request
2525- * Should be around 20 for about 10 users on the pds
2626- * The more users you have, the lower the number should be
2727- * since sorting is slow and is done on the frontend
2828- * @default 20
2929- */
3030- static readonly MAX_POSTS: number = 20;
3131-3232- /**
3333- * Footer text for the dashboard, you probably want to change this. Supports HTML.
3434- * @default "<a href='https://git.witchcraft.systems/scientific-witchery/pds-dash' target='_blank'>Source</a> (<a href='https://github.com/witchcraft-systems/pds-dash/' target='_blank'>github mirror</a>)"
3535- */
3636- static readonly FOOTER_TEXT: string =
3737- "<a href='https://git.witchcraft.systems/scientific-witchery/pds-dash' target='_blank'>Source</a> (<a href='https://github.com/witchcraft-systems/pds-dash/' target='_blank'>github mirror</a>)";
3838-3939- /**
4040- * Whether to show the posts with timestamps that are in the future.
4141- * @default false
4242- */
4343- static readonly SHOW_FUTURE_POSTS: boolean = false;
4444-}
···11+/**
22+ * Configuration module for the PDS Dashboard
33+ * Uses SvelteKit environment variables
44+ */
55+import {
66+ PUBLIC_PDS_URL,
77+ PUBLIC_FRONTEND_URL,
88+ PUBLIC_MAX_POSTS,
99+ PUBLIC_SHOW_FUTURE_POSTS
1010+} from '$env/static/public';
1111+1212+export class Config {
1313+ /**
1414+ * The base URL of the PDS (Personal Data Server).
1515+ */
1616+ static readonly PDS_URL: string = PUBLIC_PDS_URL;
1717+1818+ /**
1919+ * The base URL of the frontend service for linking to replies/quotes/accounts etc.
2020+ */
2121+ static readonly FRONTEND_URL: string = PUBLIC_FRONTEND_URL;
2222+2323+ /**
2424+ * Maximum number of posts to fetch from the PDS per request
2525+ */
2626+ static readonly MAX_POSTS: number = parseInt(PUBLIC_MAX_POSTS, 10);
2727+2828+2929+ /**
3030+ * Whether to show the posts with timestamps that are in the future.
3131+ */
3232+ static readonly SHOW_FUTURE_POSTS: boolean = PUBLIC_SHOW_FUTURE_POSTS === 'true';
3333+}
···11+import type { RequestHandler } from './$types';
22+import type { At } from '@atcute/client/lexicons';
33+import { json } from '@sveltejs/kit';
44+import { blueskyHandleFromDid } from '$lib/server/identity';
55+66+export const GET: RequestHandler = async ({ params }) => {
77+ const { did } = params;
88+99+ if (!did || (!did.startsWith('did:plc:') && !did.startsWith('did:web:'))) {
1010+ return json({ error: 'Invalid DID format' }, { status: 400 });
1111+ }
1212+1313+ try {
1414+ const handle = await blueskyHandleFromDid(did as At.Did);
1515+ return json({ handle });
1616+ } catch (error) {
1717+ console.error(`Error fetching handle for ${did}:`, error);
1818+ return json(
1919+ { error: 'Failed to fetch handle' },
2020+ { status: 500 }
2121+ );
2222+ }
2323+};
+215
src/routes/info/+page.svelte
···11+<script lang="ts">
22+ import PDSHeader from '$lib/components/PDSHeader.svelte';
33+44+ const faqs = [
55+ {
66+ id: 'what-is-selfhosted-social',
77+ question: 'What is selfhosted.social?',
88+ answer: `selfhosted.social is an <a href="https://atproto.com/guides/glossary#pds-personal-data-server"> ATProto PDS</a>. We welcome anyone who agrees with our <a href="/legal">Terms of Service and Privacy Policy</a>.
99+ `
1010+ },
1111+ {
1212+ id: 'invite-code',
1313+ question: 'Does your PDS require an invite code?',
1414+ answer: `
1515+ Nope! We have an open PDS as long as you agree with our <a href="/legal">Terms of Service and Privacy Policy</a>.
1616+ You can use this <a href="https://pdsmoover.com/moover/selfhosted.social">PDS MOOver link</a> to migrate your ATProto account to our PDS from another PDS.
1717+ Or if you are creating a new account, you can do this on <a href="https://bsky.app/">Bluesky's website</a>,
1818+ click create a new account, and then click the pencil icon next to the text "You are creating an account on Bluesky Social".
1919+ On the custom screen you can enter "https://selfhosted.social". Then you complete the registration process as normal and you're good to go!'.
2020+ `
2121+ },
2222+ {
2323+ id: 'server-location-backups',
2424+ question: 'What about the server? Where is it located? Is there backups?',
2525+ answer: `
2626+ The PDS is hosted on <a href="https://www.digitalocean.com/">DigitalOcean</a>. On a VPS that is located in Atlanta, GA, USA. We do nightly backups of the whole server, and each individual account is also backed up with <a href="https://pdsmoover.com">PDS MOOver</a>.
2727+ You can manage them by signing in to the <a href="https://pdsmoover.com/backups">backups page</a> with your ATProto account.
2828+ `
2929+ },
3030+ {
3131+ id: 'is-it-free',
3232+ question: 'Is it free?',
3333+ answer: `
3434+ Yes, but sponsorships and tips are welcomed to help cover server costs! Can use <a href="https://github.com/sponsors/fatfingers23">GitHub sponsors</a> or <a href="https://ko-fi.com/baileytownsend">Ko-Fi</a> to support us.
3535+ `
3636+ },
3737+ {
3838+ id: 'support-contact',
3939+ question: 'How do I get support? Reach an admin?',
4040+ answer: `
4141+ You can reach out directly to me on Bluesky <a href="https://bsky.app/profile/baileytownsend.dev">@baileytownsend.dev</a> or email us at <a href="mailto:modmail@selfhosted.social?subject=&cc=&bcc=&body=" style="" class="" id="">modmail@selfhosted.social</a>.
4242+ `
4343+ },
4444+ {
4545+ id: 'other-pdses',
4646+ question: 'I don\'t think this is the PDS for me.',
4747+ answer: `
4848+ Perfectly okay! Independent PDSs are becoming more common, and here is a list of some of our favorites that welcome new users and hope if you can't find a home with us that you can find one that works for you.
4949+ <ul>
5050+ <li><a href="https://docs.blacksky.community/migrating-to-blacksky-pds-complete-guide">Blacksky</a></li>
5151+ <li><a href="https://leaflet.pub/2191cd16-e793-4dd8-95ff-c6b02e89bb6a">Northsky</a></li>
5252+ <li><a href="https://bsky.app/profile/witchcraft.systems">witchcraft.systems</a></li>
5353+ <li><a href="https://tophhie.social/">tophhie.social</a></li>
5454+ <li><a href="https://bsky.app/profile/altq.net">altq.net</a></li>
5555+5656+ </ul>
5757+ `
5858+ }
5959+ ];
6060+</script>
6161+6262+<div class="page-container">
6363+6464+ <PDSHeader/>
6565+6666+ <section class="faq-section">
6767+ <h2>Frequently Asked Questions</h2>
6868+ <div class="faq-list">
6969+ {#each faqs as faq (faq.id)}
7070+ <div id={faq.id} class="faq-item">
7171+ <h3>
7272+ <a href="#{faq.id}" class="faq-question">{faq.question}</a>
7373+ </h3>
7474+ <div class="faq-answer">
7575+ <div class="answer-content">
7676+ {@html faq.answer}
7777+ </div>
7878+ </div>
7979+ </div>
8080+ {/each}
8181+ </div>
8282+ </section>
8383+</div>
8484+8585+<style>
8686+ .page-container {
8787+ max-width: 1200px;
8888+ margin: 2rem auto;
8989+ padding: 2rem;
9090+ }
9191+9292+ .faq-section {
9393+ margin-top: 3rem;
9494+ }
9595+9696+ h2 {
9797+ font-size: 2rem;
9898+ font-weight: 700;
9999+ color: var(--text-color, #111827);
100100+ margin-bottom: 2rem;
101101+ text-align: center;
102102+ }
103103+104104+ .faq-list {
105105+ display: flex;
106106+ flex-direction: column;
107107+ gap: 2.5rem;
108108+ }
109109+110110+ .faq-item {
111111+ scroll-margin-top: 6rem;
112112+ }
113113+114114+ h3 {
115115+ color: var(--text-color, #111827);
116116+ font-size: 1.5rem;
117117+ font-weight: 600;
118118+ margin: 0 0 1rem 0;
119119+ }
120120+121121+ .faq-question {
122122+ color: inherit;
123123+ text-decoration: none;
124124+ transition: color 0.2s ease;
125125+ }
126126+127127+ .faq-question:hover {
128128+ color: var(--link-color, #6366f1);
129129+ }
130130+131131+ .faq-question:focus {
132132+ outline: 2px solid var(--link-color, #6366f1);
133133+ outline-offset: 4px;
134134+ border-radius: 4px;
135135+ }
136136+137137+ .faq-answer {
138138+ padding: 0;
139139+ }
140140+141141+ .answer-content {
142142+ color: var(--text-color, #111827);
143143+ opacity: 0.8;
144144+ line-height: 1.7;
145145+ }
146146+147147+ .answer-content :global(a) {
148148+ color: var(--link-color, #6366f1);
149149+ text-decoration: none;
150150+ font-weight: 500;
151151+ transition: opacity 0.2s ease;
152152+ border-bottom: 1px solid rgba(99, 102, 241, 0.3);
153153+ }
154154+155155+ .answer-content :global(a:hover) {
156156+ opacity: 0.8;
157157+ border-bottom-color: var(--link-color, #6366f1);
158158+ }
159159+160160+ .answer-content :global(a:focus) {
161161+ outline: 2px solid var(--link-color, #6366f1);
162162+ outline-offset: 2px;
163163+ border-radius: 2px;
164164+ }
165165+166166+ @media (max-width: 768px) {
167167+ .page-container {
168168+ padding: 1rem;
169169+ }
170170+171171+ h2 {
172172+ font-size: 1.5rem;
173173+ margin-bottom: 1.5rem;
174174+ }
175175+176176+ h3 {
177177+ font-size: 1.25rem;
178178+ }
179179+180180+ .faq-list {
181181+ gap: 2rem;
182182+ }
183183+184184+ .faq-item {
185185+ scroll-margin-top: 5rem;
186186+ }
187187+ }
188188+189189+ @media (max-width: 480px) {
190190+ .page-container {
191191+ padding: 0.75rem;
192192+ margin: 1rem auto;
193193+ }
194194+195195+ h2 {
196196+ font-size: 1.25rem;
197197+ }
198198+199199+ h3 {
200200+ font-size: 1.1rem;
201201+ }
202202+203203+ .faq-list {
204204+ gap: 1.5rem;
205205+ }
206206+207207+ .faq-item {
208208+ scroll-margin-top: 4rem;
209209+ }
210210+211211+ .answer-content {
212212+ font-size: 0.95rem;
213213+ }
214214+ }
215215+</style>
+353
src/routes/legal/+page.svelte
···11+<script lang="ts">
22+</script>
33+44+<div class="page-container">
55+66+ <section id="privacy-policy" class="legal-section">
77+ <h1>
88+ <a href="#privacy-policy" class="section-link">Privacy Policy</a>
99+ </h1>
1010+1111+ <p><em>Last Updated: December 2, 2025</em></p>
1212+1313+ <h3 id="your-data">
1414+ <a href="#your-data" class="section-link">Your Data</a>
1515+ </h3>
1616+ <p>
1717+ Your <a href="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">ATProto data</a> is public and accessible to anyone. This is a core concept of the ATProto protocol. This data is accessible on the server via <a href="https://docs.bsky.app/docs/category/http-reference">API endpoints</a>.
1818+ It is also stored in our nightly backups as well as you are signed up for backups automatically with <a href="https://pdsmoover.com/backups">PDS MOOver</a>.
1919+ </p>
2020+ <p>
2121+ Your private data, such as your email address and password, are kept private on the server and included in nightly server backups. We do not share this data with any third parties or sell it to advertisers. Your private messages (DMs) are stored on Bluesky's servers; your ATProto account is what gives you access to those messages on their servers.
2222+ </p>
2323+2424+ <h3 id="service-provider-location">
2525+ <a href="#service-provider-location" class="section-link">Service Provider and Location</a>
2626+ </h3>
2727+ <p>
2828+ The PDS is hosted on <a href="https://www.digitalocean.com/">DigitalOcean</a> on a VPS located in Atlanta, GA, USA. We perform nightly backups of the entire server, and each individual account is also backed up with <a href="https://pdsmoover.com">PDS MOOver</a>.
2929+ You can manage your backups by signing in to the <a href="https://pdsmoover.com/backups">backups page</a> with your ATProto account.
3030+ </p>
3131+3232+ <h3 id="data-collection">
3333+ <a href="#data-collection" class="section-link">Data Collection</a>
3434+ </h3>
3535+ <p>
3636+ We collect only the information necessary to operate your ATProto account:
3737+ </p>
3838+ <ul>
3939+ <li><strong>Account Information:</strong> Email address, and other public ATProto data</li>
4040+ <li><strong>Usage Data:</strong> Server logs that may include IP addresses, access times, and API requests for operational purposes</li>
4141+ <li><strong>Public Content:</strong> Posts, media, and other content you publish through the ATProto network</li>
4242+ </ul>
4343+4444+4545+ <h3 id="data-retention">
4646+ <a href="#data-retention" class="section-link">Data Retention</a>
4747+ </h3>
4848+ <p>
4949+ Your account data is retained as long as your account is active. If your account becomes deactivated, or you migrate your account to another PDS, your data could be deleted after 30 days if resources are needed. If you would like to delete your data on our servers please delete your account when you migrate to another PDS or decide to no longer use the service.
5050+ </p>
5151+5252+5353+ <h3 id="childrens-privacy">
5454+ <a href="#childrens-privacy" class="section-link">Children's Privacy</a>
5555+ </h3>
5656+ <p>
5757+ Our service is not intended for users under the age of 13. We do not knowingly collect personal information from children under 13. If you believe a child has created an account, please contact us immediately.
5858+ </p>
5959+6060+ <h3 id="changes-policy">
6161+ <a href="#changes-policy" class="section-link">Changes to This Policy</a>
6262+ </h3>
6363+ <p>
6464+ We may update this Privacy Policy from time to time. We will notify users of any material changes by updating the "Last Updated" date at the top of this policy. Continued use of the service after changes constitutes acceptance of the updated policy.
6565+ </p>
6666+6767+ <h3 id="privacy-contact">
6868+ <a href="#privacy-contact" class="section-link">Contact</a>
6969+ </h3>
7070+ <p>
7171+ If you have questions about this Privacy Policy, please contact us at <a href="mailto:modmail@selfhosted.social">modmail@selfhosted.social</a> or reach out on Bluesky at <a href="https://bsky.app/profile/baileytownsend.dev">@baileytownsend.dev</a>.
7272+ </p>
7373+7474+ </section>
7575+7676+ <section id="terms-of-service" class="legal-section">
7777+ <h2>
7878+ <a href="#terms-of-service" class="section-link">Terms of Service</a>
7979+ </h2>
8080+8181+ <p><em>Last Updated: December 2, 2025</em></p>
8282+8383+ <h3 id="acceptance-terms">
8484+ <a href="#acceptance-terms" class="section-link">Acceptance of Terms</a>
8585+ </h3>
8686+ <p>
8787+ By creating an account on selfhosted.social or migrating an existing ATProto account, you agree to comply with these Terms of Service and our Privacy Policy. If you do not agree to these terms, please do not use our service.
8888+ </p>
8989+9090+ <h3 id="account-registration">
9191+ <a href="#account-registration" class="section-link">Account Registration</a>
9292+ </h3>
9393+ <p>
9494+ You must be at least 13 years old to create an account. You are responsible for maintaining the security of your account credentials. You must provide accurate information during registration and keep your contact information up to date.
9595+ </p>
9696+9797+ <h3 id="acceptable-use">
9898+ <a href="#acceptable-use" class="section-link">Acceptable Use</a>
9999+ </h3>
100100+ <p>
101101+ You agree to use selfhosted.social in compliance with all applicable laws and regulations. When using our service, you must not:
102102+ </p>
103103+ <ul>
104104+ <li>Create accounts solely for the purpose of spamming or harassment</li>
105105+ <li>Engage in spam, bulk messaging, or automated posting that disrupts the network</li>
106106+ <li>Post illegal content or content that violates the rights of others</li>
107107+ <li>Impersonate others or misrepresent your identity</li>
108108+ <li>Attempt to gain unauthorized access to the server or other users' accounts</li>
109109+ <li>Use the service to distribute malware or conduct phishing attacks</li>
110110+ <li>Engage in any activity that could harm the server or other users</li>
111111+ </ul>
112112+113113+ <h3 id="automated-accounts-bots">
114114+ <a href="#automated-accounts-bots" class="section-link">Automated Accounts and Bots</a>
115115+ </h3>
116116+ <p>
117117+ Automated accounts and bots are permitted on selfhosted.social, provided they comply with these terms. Bot accounts must:
118118+ </p>
119119+ <ul>
120120+ <li>Clearly identify themselves as automated accounts</li>
121121+ <li>Not engage in spam or disruptive behavior</li>
122122+ <li>Respect rate limits and not place excessive load on the server</li>
123123+ <li>Comply with all other terms of service and applicable laws</li>
124124+ </ul>
125125+126126+ <h3 id="content-conduct">
127127+ <a href="#content-conduct" class="section-link">Content and Conduct</a>
128128+ </h3>
129129+ <p>
130130+ Your public content on the ATProto network is subject to <a href="https://bsky.social/about/support/community-guidelines">Bluesky's Community Guidelines</a> when using Bluesky. While we operate independently, we expect users to maintain respectful conduct across the ATProto network.
131131+ </p>
132132+ <p>
133133+ We reserve the right to remove content or suspend accounts that violate these terms, applicable laws, or pose a risk to the service or other users.
134134+ </p>
135135+136136+ <h3 id="service-availability">
137137+ <a href="#service-availability" class="section-link">Service Availability</a>
138138+ </h3>
139139+ <p>
140140+ We strive to maintain reliable service but cannot guarantee 100% uptime. The service is provided "as is" without warranties of any kind. We may modify, suspend, or discontinue the service at any time with or without notice.
141141+ </p>
142142+143143+ <h3 id="account-portability">
144144+ <a href="#account-portability" class="section-link">Account Portability</a>
145145+ </h3>
146146+ <p>
147147+ One of the benefits of ATProto is account portability. You may migrate your account to another PDS at any time using <a href="https://pdsmoover.com/moover">PDS MOOver</a> or other ATProto migration tools.
148148+ </p>
149149+150150+ <h3 id="termination">
151151+ <a href="#termination" class="section-link">Termination</a>
152152+ </h3>
153153+ <p>
154154+ We reserve the right to suspend or terminate accounts that violate these terms without prior notice. You may also delete your account at any time. Upon termination, your private data will be deleted in accordance with our Privacy Policy, but your public ATProto data may persist on other servers in the network.
155155+ </p>
156156+157157+ <h3 id="limitation-liability">
158158+ <a href="#limitation-liability" class="section-link">Limitation of Liability</a>
159159+ </h3>
160160+ <p>
161161+ selfhosted.social is provided as a free community service. To the fullest extent permitted by law, we are not liable for any damages arising from your use of the service, including but not limited to data loss, service interruptions, or security breaches.
162162+ </p>
163163+164164+ <h3 id="changes-terms">
165165+ <a href="#changes-terms" class="section-link">Changes to Terms</a>
166166+ </h3>
167167+ <p>
168168+ We may update these Terms of Service from time to time. Material changes will be communicated by updating the "Last Updated" date at the top of this document. Continued use of the service after changes constitutes acceptance of the updated terms.
169169+ </p>
170170+171171+ <h3 id="terms-contact">
172172+ <a href="#terms-contact" class="section-link">Contact</a>
173173+ </h3>
174174+ <p>
175175+ If you have questions about these Terms of Service, please contact us at <a href="mailto:modmail@selfhosted.social">modmail@selfhosted.social</a> or reach out on Bluesky at <a href="https://bsky.app/profile/baileytownsend.dev">@baileytownsend.dev</a>.
176176+ </p>
177177+178178+ </section>
179179+</div>
180180+181181+<style>
182182+ .page-container {
183183+ max-width: 1200px;
184184+ margin: 2rem auto;
185185+ padding: 2rem;
186186+ }
187187+188188+ h1 {
189189+ color: var(--text-color, #111827);
190190+ margin-bottom: 2rem;
191191+ font-size: 2.5rem;
192192+ font-weight: 700;
193193+ }
194194+195195+ .legal-section {
196196+ margin-bottom: 4rem;
197197+ scroll-margin-top: 6rem;
198198+ }
199199+200200+ .legal-section:last-child {
201201+ margin-bottom: 2rem;
202202+ }
203203+204204+ h2 {
205205+ color: var(--text-color, #111827);
206206+ font-size: 2rem;
207207+ font-weight: 700;
208208+ margin-bottom: 1.5rem;
209209+ padding-bottom: 0.5rem;
210210+ border-bottom: 2px solid var(--border-color, #e2e8f0);
211211+ }
212212+213213+ h3 {
214214+ color: var(--text-color, #111827);
215215+ font-size: 1.5rem;
216216+ font-weight: 600;
217217+ margin-top: 2rem;
218218+ margin-bottom: 1rem;
219219+ scroll-margin-top: 6rem;
220220+ }
221221+222222+ .section-link {
223223+ color: inherit;
224224+ text-decoration: none;
225225+ transition: color 0.2s ease;
226226+ }
227227+228228+ .section-link:hover {
229229+ color: var(--link-color, #6366f1);
230230+ }
231231+232232+ .section-link:focus {
233233+ outline: 2px solid var(--link-color, #6366f1);
234234+ outline-offset: 4px;
235235+ border-radius: 4px;
236236+ }
237237+238238+ p {
239239+ color: var(--text-color, #111827);
240240+ line-height: 1.8;
241241+ margin-bottom: 1rem;
242242+ opacity: 0.9;
243243+ }
244244+245245+ p:last-child {
246246+ margin-bottom: 0;
247247+ }
248248+249249+ em {
250250+ color: var(--text-color, #111827);
251251+ opacity: 0.7;
252252+ font-size: 0.9rem;
253253+ }
254254+255255+ ul {
256256+ color: var(--text-color, #111827);
257257+ line-height: 1.8;
258258+ margin: 1rem 0 1.5rem 1.5rem;
259259+ opacity: 0.9;
260260+ }
261261+262262+ li {
263263+ margin-bottom: 0.5rem;
264264+ }
265265+266266+ li:last-child {
267267+ margin-bottom: 0;
268268+ }
269269+270270+ strong {
271271+ font-weight: 600;
272272+ color: var(--text-color, #111827);
273273+ }
274274+275275+ a {
276276+ color: var(--link-color, #6366f1);
277277+ text-decoration: none;
278278+ border-bottom: 1px solid rgba(99, 102, 241, 0.3);
279279+ transition: opacity 0.2s ease, border-bottom-color 0.2s ease;
280280+ }
281281+282282+ a:hover {
283283+ opacity: 0.8;
284284+ border-bottom-color: var(--link-color, #6366f1);
285285+ }
286286+287287+ a:focus {
288288+ outline: 2px solid var(--link-color, #6366f1);
289289+ outline-offset: 2px;
290290+ border-radius: 2px;
291291+ }
292292+293293+ @media (max-width: 768px) {
294294+ .page-container {
295295+ padding: 1rem;
296296+ }
297297+298298+ h1 {
299299+ font-size: 2rem;
300300+ margin-bottom: 1.5rem;
301301+ }
302302+303303+ h2 {
304304+ font-size: 1.5rem;
305305+ }
306306+307307+ h3 {
308308+ font-size: 1.25rem;
309309+ margin-top: 1.5rem;
310310+ scroll-margin-top: 5rem;
311311+ }
312312+313313+ .legal-section {
314314+ margin-bottom: 3rem;
315315+ scroll-margin-top: 5rem;
316316+ }
317317+318318+ ul {
319319+ margin-left: 1.25rem;
320320+ }
321321+ }
322322+323323+ @media (max-width: 480px) {
324324+ .page-container {
325325+ padding: 0.75rem;
326326+ margin: 1rem auto;
327327+ }
328328+329329+ h1 {
330330+ font-size: 1.75rem;
331331+ }
332332+333333+ h2 {
334334+ font-size: 1.25rem;
335335+ }
336336+337337+ h3 {
338338+ font-size: 1.1rem;
339339+ margin-top: 1.25rem;
340340+ scroll-margin-top: 4rem;
341341+ }
342342+343343+ .legal-section {
344344+ margin-bottom: 2rem;
345345+ scroll-margin-top: 4rem;
346346+ }
347347+348348+ ul {
349349+ margin-left: 1rem;
350350+ font-size: 0.95rem;
351351+ }
352352+ }
353353+</style>
+10-2
svelte.config.js
···11-import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
11+import adapter from '@sveltejs/adapter-node';
22+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
2333-export default {
44+/** @type {import('@sveltejs/kit').Config} */
55+const config = {
46 // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
57 // for more information about preprocessors
68 preprocess: vitePreprocess(),
99+1010+ kit: {
1111+ adapter: adapter()
1212+ }
713};
1414+1515+export default config;