source for getorbyt.com getorbyt.com/
client bsky orbytapp app orbyt bluesky getorbyt orbytvideo atproto video
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Revert "Add i18n locale support (de, es, fr, ja, ko, pt-BR) and Klipy app-ads.txt"

This reverts commit e8a826ee975b490e0d7bac215bc746dc91a0c318.

+1871 -2127
-7
astro.config.mjs
··· 3 3 4 4 export default defineConfig({ 5 5 site: 'https://getorbyt.com', 6 - i18n: { 7 - locales: ['en', 'de', 'es', 'es-419', 'fr', 'ja', 'ko', 'pt-BR'], 8 - defaultLocale: 'en', 9 - routing: { 10 - prefixDefaultLocale: false, 11 - }, 12 - }, 13 6 adapter: cloudflare({ 14 7 imageService: 'cloudflare', 15 8 platformProxy: {
-1
package.json
··· 4 4 "description": "", 5 5 "main": "index.js", 6 6 "scripts": { 7 - "generate:locales": "node scripts/generate-locale-pages.mjs", 8 7 "dev": "astro dev", 9 8 "start": "astro dev", 10 9 "build": "astro build",
-306
public/app-ads.txt
··· 1 - # KLIPY Start 2 - admixer.net, 191fda99-2fb4-452b-a444-db82b2817832, RESELLER 3 - inmobi.com, 61d733c3779d43e590c51c8bc078e10c, RESELLER, 83e75a7ae333ca9d 4 - pubmatic.com, 167254, RESELLER, 5d62403b186f2ace 5 - video.unrulymedia.com, 827123801, RESELLER 6 - richaudience.com, 9komJKwMhZ, RESELLER 7 - lijit.com, 380632, RESELLER, fafdf38b16bf6b2b 8 - adform.com, 1762, RESELLER 9 - smartadserver.com, 3627, RESELLER, 060d053dcf45cbf3 10 - adsinteractive.com,39993,RESELLER 11 - improvedigital.com, 2625, RESELLER 12 - appnexus.com, 17553, RESELLER, f5ab79cb980f11d1 13 - admanmedia.com, 2320, RESELLER 14 - rubiconproject.com, 25692, RESELLER, 0bfd66d529a55807 15 - adagio.io, 1280, RESELLER 16 - e-planning.net,ec771b05828a67fa,RESELLER,c1ba615865ed87b2 17 - theaudiencestreet.com, 92335, RESELLER 18 - mgid.com, 885240, RESELLER, d4c29acad76ce94f 19 - inmobi.com, 4d00d63dc86a44c881cbabaf85866264, RESELLER, 83e75a7ae333ca9d 20 - rubiconproject.com, 11726, RESELLER, 0bfd66d529a55807 21 - rubiconproject.com, 12266, RESELLER, 0bfd66d529a55807 22 - blis.com, 33, RESELLER, 61453ae19a4b73f4 23 - verve.com, 5897, RESELLER, 0c8f5958fc2d6270 24 - loopme.com, 9724, RESELLER, 6c8d5f95897a5a3b 25 - pubmatic.com, 157097, RESELLER, 5d62403b186f2ace 26 - pubnative.net, 1006951, RESELLER, d641df8625486a7b 27 - loopme.com, 15665, RESELLER, 6c8d5f95897a5a3b 28 - algorix.co, 54190, RESELLER, 5b394c12fea27a1d 29 - smaato.com, 1100053415, RESELLER, 07bcf65f187117b4 30 - zmaticoo.com, 5135082, RESELLER 31 - gamaigroup.com, 320201, RESELLER 32 - zmaticoo.com, 113149, RESELLER 33 - conversantmedia.com, 40881, RESELLER, 03113cd04947736d 34 - pubmatic.com, 156931, RESELLER, 5d62403b186f2ace 35 - bidmachine.io, 100, RESELLER 36 - pubmatic.com, 159035, RESELLER, 5d62403b186f2ace 37 - thebrave.io, 1234568, RESELLER, c25b2154543746ac 38 - outbrain.com, 00bba279fec6daa01a0cb6fdccb023f0d5, RESELLER 39 - se7en.es, 212430, RESELLER, 064bc410192443d8 40 - mintegral.com, 10003, RESELLER 41 - rubiconproject.com, 20744, RESELLER, 0bfd66d529a55807 42 - mars.media, 1010450, RESELLER, 8624339f102fb076 43 - adiiix.com, 3de5db9, RESELLER 44 - iqzone.com, IQ87, RESELLER 45 - opera.com, pub6871903319744, RESELLER, 55a0c5fd61378de3 46 - yeahmobi.com, 5135082, RESELLER 47 - rhebus.works, 7597018658, RESELLER 48 - pubmatic.com, 163758, RESELLER, 5d62403b186f2ace 49 - rubiconproject.com, 16928, RESELLER, 0bfd66d529a55807 50 - openx.com, 540298543, RESELLER, 6a698e2ec38604c6 51 - contextweb.com, 562499, RESELLER, 89ff185a4c4e857c 52 - appnexus.com, 8178, RESELLER, f5ab79cb980f11d1 53 - advertising.com, 28246, RESELLER 54 - onetag.com, 59aa7be4921bac8, RESELLER 55 - video.unrulymedia.com, 322585001, RESELLER 56 - adelement.com, 30020, RESELLER 57 - adelement.com, 36001, RESELLER 58 - criteo.com, B-057955, RESELLER, 9fac4a4a87c2a44f 59 - loopme.com, 11414, RESELLER, 6c8d5f95897a5a3b 60 - algorix.co, 60367, RESELLER, 5b394c12fea27a1d 61 - aralego.com, par-9A2627866DA4BBE4F4AA4A67A3DDE686, RESELLER 62 - sharethrough.com, DQQogebZ, RESELLER, d53b998a7bd4ecd2 63 - loopme.com, 11605, RESELLER, 6c8d5f95897a5a3b 64 - lunamedia.io, b82743ba2ddf4bcaab3d1d4f91b1330e, RESELLER, 524ecb396915caaf 65 - acexchange.co.kr, 1562884725, RESELLER 66 - rubiconproject.com, 24400, RESELLER, 0bfd66d529a55807 67 - pubmatic.com, 157559, RESELLER, 5d62403b186f2ace 68 - jio.com, 1547219295, RESELLER 69 - rubiconproject.com, 20050, RESELLER, 0bfd66d529a55807 70 - programmaticx.ai, 6999766, RESELLER 71 - added.tv, d4cfaf78, RESELLER 72 - lemmatechnologies.com, 89, RESELLER, 7829010c5bebd1fb 73 - growintech.co, 102275, RESELLER 74 - prequel.tv, 518, RESELLER 75 - eskimi.com, 2020000030, RESELLER 76 - 9dotsmedia.com, 122262, RESELLER, 45ff185b4c4e857d 77 - ssp.e-volution.ai, AJxF6R378a9M6CaTvK, RESELLER 78 - app-stock.com, 315417, RESELLER 79 - markappmedia.site, 587286, RESELLER 80 - keenkale.com, 841, RESELLER 81 - admanmedia.com, 2063, RESELLER 82 - peak226.com, 12700, RESELLER 83 - thunder-monetize.com, 3905806597, RESELLER 84 - rubiconproject.com, 26064, RESELLER, 0bfd66d529a55807 85 - bold-win.com, 148, RESELLER, 71746737d0bab951 86 - orangeclickmedia.com, C-1034, RESELLER 87 - lijit.com, 481366, RESELLER, fafdf38b16bf6b2b 88 - castify.ai, 1279123, RESELLER 89 - onlinemediasolutions.com, 29556, RESELLER 90 - bematterfull.com, 111399861, RESELLER 91 - videoheroes.tv, 212430, RESELLER, 064bc410192443d8 92 - video.unrulymedia.com, 188404962, RESELLER 93 - fromthetop.io, 65529, RESELLER 94 - themediagrid.com, JAZ4RI, RESELLER, 35d5010d7789b49d 95 - lijit.com, 502742, RESELLER, fafdf38b16bf6b2b 96 - rubiconproject.com, 26168, RESELLER, 0bfd66d529a55807 97 - rubiconproject.com, 26192, RESELLER, 0bfd66d529a55807 98 - adtonos.com, PUB2356156682, RESELLER 99 - voisetech.com, 1023, RESELLER 100 - pubmatic.com, 165116, RESELLER, 5d62403b186f2ace 101 - rubiconproject.com, 25906, RESELLER, 0bfd66d529a55807 102 - smartyads.com, 478, RESELLER, fd2bde0ff2e62c5d 103 - playwire.com, 1025119, RESELLER 104 - contextweb.com, 558638, RESELLER, 89ff185a4c4e857c 105 - video.unrulymedia.com, 764922532, RESELLER 106 - dauup.com, 34103, RESELLER 107 - smartadserver.com, 3232, RESELLER, 060d053dcf45cbf3 108 - tredio.io, 357a6fdf7642bf815a88822c447d9dc432901, RESELLER 109 - waardex.com, 99236, RESELLER 110 - sabio.us, 100071, RESELLER, 96ed93aaa9795702 111 - freewheel.tv, 1606620, RESELLER 112 - freewheel.tv, 1606633, RESELLER 113 - e-planning.net, 608359bb987625b2, RESELLER, c1ba615865ed87b2 114 - magicmedia.ae, 6f828808, RESELLER 115 - onetag.com, 8d4bbebb8fa701e, RESELLER 116 - yieldmo.com, 3591550211052216798, RESELLER, 6a92c77cfc3d2258 117 - lijit.com, 500520, RESELLER, fafdf38b16bf6b2b 118 - eskimi.com, 2020000766, RESELLER 119 - appnexus.com,15670,RESELLER 120 - rubiconproject.com, 26250, RESELLER, 0bfd66d529a55807 121 - pubmatic.com, 162270, RESELLER, 5d62403b186f2ace 122 - video.unrulymedia.com, 498216989, RESELLER 123 - lijit.com, 502284, RESELLER, fafdf38b16bf6b2b 124 - onetag.com, 8c90176af2e65c8, RESELLER 125 - smartadserver.com, 4998, RESELLER 126 - improvedigital.com, 2650, RESELLER 127 - loopme.com,12852, RESELLER,6c8d5f95897a5a3b 128 - lijit.com, 400766, RESELLER, fafdf38b16bf6b2b 129 - axonix.com, 59054, RESELLER, bc385f2b4a87b721 130 - conversantmedia.com, 100246, RESELLER, 03113cd04947736d 131 - pubmatic.com, 158154, RESELLER, 5d62403b186f2ace 132 - sharethrough.com, 6qlnf8SY, RESELLER, d53b998a7bd4ecd2 133 - xandr.com, 13799, RESELLER 134 - triplelift.com, 12158, RESELLER, 6c33edb13117fd86 135 - adform.com, 3083, RESELLER 136 - sonobi.com, b43e9530e7, RESELLER, d1a215d9eb5aee9e 137 - video.unrulymedia.com, 2148207460, RESELLER 138 - programmaticx.ai, 6845438, RESELLER 139 - showheroes.com, 6023, RESELLER 140 - opera.com, pub7275292332480, RESELLER, 55a0c5fd61378de3 141 - media.net, 8CUN37DCC, RESELLER 142 - adwmg.com, 100765, RESELLER, c9688a22012618e7 143 - thebrave.io, 1234585, RESELLER, c25b2154543746ac 144 - bold-win.com, 165, RESELLER, 71746737d0bab951 145 - videoheroes.tv, 212503, RESELLER, 064bc410192443d8 146 - yeahmobi.com, 5135284, RESELLER 147 - onlinemediasolutions.com, 20498, RESELLER 148 - streamkey.tv, 929221, RESELLER, f5ab793he40f11d1 149 - eliteappgrade.com, 494350, RESELLER 150 - pokkt.com, 7606, RESELLER, c45702d9311e25fd 151 - appads.in, 107606, RESELLER 152 - admanmedia.com, 594, RESELLER 153 - eskimi.com, eas-2020000005, RESELLER 154 - markappmedia.site, 313916, RESELLER 155 - cazhype.com, efe937780e95574250dabe07151bdc2332800, RESELLER 156 - improvedigital.com, 1532, RESELLER 157 - acexchange.co.kr, 1601362449, RESELLER 158 - app-stock.com, 480922, RESELLER 159 - iqzone.com, IQ137, RESELLER 160 - onairglobal.com, 4357627, RESELLER 161 - prequel.tv, 515, RESELLER 162 - growintech.co, 101696, RESELLER 163 - admixer.net, eab09ea6-bcf7-4eff-965d-b4f43962c73c, RESELLER 164 - lunamedia.io, 2fb901cd79f4453b90b68bee79da71b5, RESELLER, 524ecb396915caaf 165 - e-planning.net, 9522026ef023606d, RESELLER, c1ba615865ed87b2 166 - mars.media, 1010443, RESELLER, 8624339f102fb076 167 - sparcmedia.com, 70012, RESELLER 168 - boostappi.com, 10013, RESELLER 169 - rhebus.works, 3639733593, RESELLER 170 - thunder-monetize.com, 3639733593, RESELLER 171 - inmobi.com, ebc44eef6c104f7fbf7bc52ee29f7939, RESELLER, 83e75a7ae333ca9d 172 - madopi.media, 161153, RESELLER 173 - ssp.e-volution.ai, AJxF6R118a9M6CaTvK, RESELLER 174 - 9dotsmedia.com, 1358, RESELLER, 45ff185b4c4e857d 175 - pubmatic.com, 165646, RESELLER, 5d62403b186f2ace 176 - pubnative.net, 1016244, RESELLER, d641df8625486a7b 177 - rubiconproject.com, 17328, RESELLER, 0bfd66d529a55807 178 - smaato.com, 1100049757, RESELLER, 07bcf65f187117b4 179 - start.io, 178854833, RESELLER 180 - rubiconproject.com, 15278, RESELLER, 0bfd66d529a55807 181 - spinx.biz, 3198992692, RESELLER 182 - adelement.com, 42001, RESELLER 183 - mobimight.com, 75764, RESELLER 184 - app-stock.com, 317847, RESELLER 185 - lunamedia.io, a49272ae78e0ed4beb713268644928f0, RESELLER, 524ecb396915caaf 186 - media.net, 8CUNEOK3R, RESELLER 187 - playdigo.com, 1951, RESELLER 188 - gitberry.com, 577, RESELLER 189 - tubia.com, 40103, RESELLER 190 - bold-win.com, 147, RESELLER, 71746737d0bab951 191 - mediayo.ai, AJxF6R15a9M6CaTvK, RESELLER 192 - fourthdimentionconsulting.com, 19118818, RESELLER 193 - brainx.tech, 267, RESELLER 194 - opera.com, pub6920333082432, RESELLER, 55a0c5fd61378de3 195 - dauup.com, 34101, RESELLER 196 - onomagic.com, 203721, RESELLER 197 - pubmatic.com, 160145, RESELLER, 5d62403b186f2ace 198 - pubmatic.com, 161144, RESELLER, 5d62403b186f2ace 199 - pubmatic.com, 161368, RESELLER, 5d62403b186f2ace 200 - azberry.com, 210127, RESELLER 201 - thebrave.io, 1234589, RESELLER, c25b2154543746ac 202 - myfeature.tv, 3b1033e428f84e9aae82, RESELLER 203 - bridgeupp.com, 14725, RESELLER 204 - blueseasx.com, 7138094, RESELLER, 7998eac5087f6110 205 - zmaticoo.com, 5135128, RESELLER 206 - brightmountainmedia.com, 2686539, RESELLER 207 - sipo-inc.com, 867525, RESELLER 208 - beapup.com, 658874, RESELLER 209 - eliteappgrade.com, 312569, RESELLER 210 - dauup.com, 34102, RESELLER 211 - advlion.com, 3570, RESELLER 212 - risecodes.com, 6769332c8eb1b90001e9ed6f, RESELLER 213 - programmaticx.ai, 6888888, RESELLER, b42d42eb28400efa 214 - pmc.com, 1243696, RESELLER, 8dd52f825890bb44 215 - getmediamx.com, 1220372, RESELLER 216 - uis.mobfox.com, 1944, RESELLER, 5529a3d1f59865be 217 - grouprvn.com, n1o21Ga0G23, RESELLER 218 - pubmatic.com, 156517, RESELLER, 5d62403b186f2ace 219 - azurebid.com, 4603949149, RESELLER 220 - selectmedia.asia, 645b5a31e427dc0a87091fe1, RESELLER, e365c871a27c655d 221 - 9dotsmedia.com, 1182, RESELLER, 45ff185b4c4e857d 222 - media.net, 8CU82Q277, RESELLER 223 - pubeasy.io, 110049, RESELLER 224 - bematterfull.com, 85470298878, RESELLER 225 - webeyemob.com, 80056, RESELLER 226 - smartadserver.com, 4083, RESELLER 227 - ushareit.com, LC5a6e860b2be2187f, RESELLER 228 - boostappi.com, 10012, RESELLER 229 - prequel.tv, 513, RESELLER 230 - voisetech.com, 1004, RESELLER 231 - smartyads.com, 4244, RESELLER, fd2bde0ff2e62c5d 232 - nitrouppi.com, 889928, RESELLER 233 - videoheroes.tv, 212432, RESELLER, 064bc410192443d8 234 - radiancemedia.net, 21966, RESELLER 235 - showheroes.com, 6016, RESELLER 236 - carbonatix.com, 164931029, RESELLER 237 - adsinteractive.com, 99998, RESELLER 238 - cazhype.com, efe937780e95574250dabe07151bdc2332794, RESELLER 239 - conversantmedia.com, 100097, RESELLER, 03113cd04947736d 240 - growintech.co, 36854, RESELLER 241 - toponad.com, 1636caf4cee9fe, RESELLER, 1d49fe424a1a456d 242 - keenkale.com, 94044, RESELLER, 6c1c00b269ccdfa4 243 - adview.com, 53218885, RESELLER, 1b2cc038a11ea319 244 - eskimi.com, 2020000009, RESELLER 245 - mediaverse.ai, 302152862, RESELLER 246 - singularads.com, 554789, RESELLER 247 - motorik.io, 65, RESELLER 248 - mars.media, 1010380, RESELLER, 8624339f102fb076 249 - castify.ai, 1252012, RESELLER 250 - screenil.com, 521732, RESELLER 251 - bidence.com, c960681141c40bb4da24a183d273fd37, RESELLER 252 - loopme.com, 11322, RESELLER, 6c8d5f95897a5a3b 253 - bidtox.com, 38299, RESELLER 254 - admixer.net, 8e380da6-31ba-488c-939c-290c48d577e4, RESELLER 255 - targetbetter.com, 2082019, RESELLER 256 - omnifytv.com, d5a3a5930fdc446da59b50688d96a372, RESELLER 257 - ppc.buzz, b7b16ecf8ca53723593894116071700c35311, RESELLER 258 - tpmn.io, 256, RESELLER 259 - markappmedia.site, 0012998, RESELLER 260 - intenze.co, 3167, RESELLER 261 - velonium.com, 133428, RESELLER 262 - video.unrulymedia.com, 4173858586, RESELLER 263 - iqzone.com, d2e0f8e604576d3403d193e3c28b0dcf, RESELLER 264 - theaudiencestreet.com, 92311, RESELLER 265 - aceex.io, 1187, RESELLER, b1cf3c874d5c6682 266 - connekt.ai, 8271873578, RESELLER 267 - my-cast.tv, 986754, RESELLER, 71746737d0bab951 268 - tunnl.com, 61027, RESELLER 269 - mobupps.com, c74d97b01eae257e44aa9d5bade97baf5658, RESELLER 270 - mangomob.net, 8afe297r56, RESELLER 271 - openx.com, 537152826, RESELLER, 6a698e2ec38604c6 272 - prado.co, 15633, RESELLER 273 - balloonlabs.ai, 5782070138, RESELLER 274 - gamoshi.io, 267-b3609, RESELLER, 20e30b2ae1f670f2 275 - adsphere360.com, 1042795, RESELLER 276 - rediads.com, 1002, RESELLER 277 - betweendigital.com, 44249, RESELLER 278 - sharethrough.com, 77240395, RESELLER, d53b998a7bd4ecd2 279 - bid-matrix.com, 3b1aacf42de85b72, RESELLER 280 - indexexchange.com, 207402, RESELLER, 50b1c356f2c5c8fc 281 - 360playvid.com, 7825da23dav, RESELLER 282 - pokkt.com, 7246, RESELLER, c45702d9311e25fd 283 - copper6.com, 749492, RESELLER 284 - metaxsoft.com, 82541531, RESELLER 285 - danoler.com, 1347, RESELLER 286 - leapmobs.com, 36597, RESELLER 287 - smoot.ai, 100207, RESELLER 288 - adform.com, 3320, RESELLER, 9f5210a2f0999e32 289 - improvedigital.com, 2570, RESELLER 290 - smartadserver.com, 4721, RESELLER 291 - pubmatic.com, 163797, RESELLER, 5d62403b186f2ace 292 - pubmatic.com, 163799, RESELLER, 5d62403b186f2ace 293 - showheroes.com, 7405, RESELLER 294 - lijit.com, 497492, RESELLER, fafdf38b16bf6b2b 295 - inmobi.com, 55f6bbc746644a129c19be75052aaeda, RESELLER, 83e75a7ae333ca9d 296 - zmaticoo.com, 5135884, RESELLER 297 - amxrtb.com, 105199934, RESELLER 298 - yandex.com, 311761695, RESELLER 299 - pgamssp.com, 691b541c7d147656200bf075, RESELLER 300 - rubiconproject.com, 24852, RESELLER, 0bfd66d529a55807 301 - pubmatic.com, 162623, RESELLER, 5d62403b186f2ace 302 - video.unrulymedia.com, 5921144960123684292, RESELLER 303 - onetag.com, 87f80e5d9d55274, RESELLER 304 - smilewanted.com, 5314, RESELLER 305 - lijit.com, 402418, RESELLER, fafdf38b16bf6b2b 306 - # KLIPY End https://partner.klipy.com/faqs#3 http://docs.klipy.com/
-68
scripts/generate-locale-pages.mjs
··· 1 - #!/usr/bin/env node 2 - /** 3 - * Generates locale page files for non-default locales. 4 - * Run: node scripts/generate-locale-pages.mjs 5 - */ 6 - import { mkdir, writeFile } from 'fs/promises'; 7 - import { dirname, join } from 'path'; 8 - import { fileURLToPath } from 'url'; 9 - 10 - const __dirname = dirname(fileURLToPath(import.meta.url)); 11 - const pagesDir = join(__dirname, '..', 'src', 'pages'); 12 - 13 - const NON_DEFAULT_LOCALES = ['de', 'es', 'es-419', 'fr', 'ja', 'ko', 'pt-BR']; 14 - const SLUGS = ['about', 'app', 'beta', 'contact', 'discord', 'terms', 'privacy']; 15 - 16 - const indexTemplate = (locale) => `--- 17 - export const prerender = true; 18 - 19 - import Base from '../../layouts/Base.astro'; 20 - import IndexContent from '../../components/pages/IndexContent.astro'; 21 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 22 - 23 - const locale = '${locale}'; 24 - const t = useTranslations(locale); 25 - const translatePath = useTranslatedPath(locale); 26 - --- 27 - 28 - <Base lang={locale} title={t('index.title')} description={t('index.metaDescription')} canonicalUrl={"https://getorbyt.com/" + locale + "/"}> 29 - <div class="index-container"> 30 - <div class="main-content"> 31 - <IndexContent t={t} translatePath={translatePath} /> 32 - </div> 33 - </div> 34 - </Base> 35 - 36 - <style> 37 - .index-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 38 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 39 - </style> 40 - `; 41 - 42 - const slugTemplate = (locale, slug) => `--- 43 - export const prerender = true; 44 - 45 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 46 - 47 - const locale = '${locale}'; 48 - const slug = '${slug}'; 49 - --- 50 - 51 - <LocaleSlugPage locale={locale} slug={slug} /> 52 - `; 53 - 54 - async function main() { 55 - for (const locale of NON_DEFAULT_LOCALES) { 56 - const localeDir = join(pagesDir, locale); 57 - await mkdir(localeDir, { recursive: true }); 58 - 59 - await writeFile(join(localeDir, 'index.astro'), indexTemplate(locale)); 60 - 61 - for (const slug of SLUGS) { 62 - await writeFile(join(localeDir, `${slug}.astro`), slugTemplate(locale, slug)); 63 - } 64 - } 65 - console.log('Generated locale pages for:', NON_DEFAULT_LOCALES.join(', ')); 66 - } 67 - 68 - main().catch(console.error);
-72
src/components/Footer.astro
··· 1 - --- 2 - interface Props { 3 - lang: string; 4 - } 5 - 6 - const { lang } = Astro.props; 7 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 8 - import LanguagePicker from './LanguagePicker.astro'; 9 - 10 - const t = useTranslations(lang as import('../i18n/ui').defaultLang); 11 - const translatePath = useTranslatedPath(lang as import('../i18n/ui').defaultLang); 12 - --- 13 - 14 - <footer class="site-footer"> 15 - <div class="separator"> 16 - <svg height="7" fill="none" xmlns="http://www.w3.org/2000/svg"> 17 - <defs> 18 - <pattern id="squigglePattern" width="374" height="7" patternUnits="userSpaceOnUse"> 19 - <path d="M0 1C5.50021 1 5.50021 5.84456 11.0004 5.84456C16.5006 5.84456 16.5006 1 22.0008 1C27.501 1 27.501 5.84456 33.0013 5.84456C38.5015 5.84456 38.5015 1 44.0017 1C49.5019 1 49.5019 5.84456 55.0021 5.84456C60.5023 5.84456 60.5023 1 66.0025 1C71.5027 1 71.5027 5.84456 77.0029 5.84456C82.5031 5.84456 82.5031 1 87.9998 1C93.5 1 93.5 5.84456 98.9967 5.84456C104.497 5.84456 104.497 1 109.994 1C115.494 1 115.494 5.84456 120.99 5.84456C126.491 5.84456 126.491 1 131.991 1C137.491 1 137.491 5.84456 142.991 5.84456C148.491 5.84456 148.491 1 153.992 1C159.492 1 159.492 5.84456 164.988 5.84456C170.489 5.84456 170.489 1 175.985 1C181.486 1 181.486 5.84456 186.986 5.84456C192.486 5.84456 192.486 1 197.986 1C203.486 1 203.486 5.84456 208.987 5.84456C214.487 5.84456 214.487 1 219.987 1C225.487 1 225.487 5.84456 230.987 5.84456C236.488 5.84456 236.488 1 241.988 1C247.488 1 247.488 5.84456 252.988 5.84456C258.488 5.84456 258.488 1 263.989 1C269.489 1 269.489 5.84456 274.989 5.84456C280.489 5.84456 280.489 1 285.99 1C291.49 1 291.49 5.84456 296.99 5.84456C302.49 5.84456 302.49 1 307.99 1C313.491 1 313.491 5.84456 318.991 5.84456C324.491 5.84456 324.491 1 329.991 1C335.491 1 335.491 5.84456 340.992 5.84456C346.492 5.84456 346.492 1 351.992 1C357.492 1 357.492 5.84456 362.996 5.84456C368.5 5.84456 368.496 1 374 1" stroke="#551DEF" stroke-width="2" stroke-miterlimit="10"/> 20 - </pattern> 21 - </defs> 22 - <rect fill="url(#squigglePattern)" width="100%" height="100%"/> 23 - </svg> 24 - </div> 25 - <nav class="footer-nav" aria-label="Site navigation"> 26 - <a href={translatePath('/about')}>{t('nav.about')}</a> 27 - <a href="https://bsky.app/profile/getorbyt.com">{t('nav.bluesky')}</a> 28 - <a href="https://community.getorbyt.com">{t('nav.community')}</a> 29 - <a href={translatePath('/terms')}>{t('nav.terms')}</a> 30 - <a href={translatePath('/privacy')}>{t('nav.privacy')}</a> 31 - <a href={translatePath('/contact')}>{t('nav.contact')}</a> 32 - </nav> 33 - <div class="lang-picker-wrap"> 34 - <LanguagePicker /> 35 - </div> 36 - </footer> 37 - 38 - <style> 39 - .site-footer { 40 - margin-top: auto; 41 - margin-bottom: 40px; 42 - } 43 - .site-footer .separator { 44 - height: 7px; 45 - margin-bottom: 20px; 46 - overflow: hidden; 47 - } 48 - .site-footer .separator svg { 49 - width: 100%; 50 - height: 7px; 51 - } 52 - .site-footer .footer-nav { 53 - display: flex; 54 - flex-wrap: wrap; 55 - gap: 12px 32px; 56 - } 57 - .site-footer .footer-nav a { 58 - font-style: normal; 59 - font-weight: bold; 60 - font-size: 13px; 61 - letter-spacing: 0.2em; 62 - text-transform: uppercase; 63 - text-decoration: none; 64 - color: #ccc; 65 - } 66 - .site-footer .footer-nav a:hover { 67 - color: #f3f5fe; 68 - } 69 - .site-footer .lang-picker-wrap { 70 - margin-top: 1rem; 71 - } 72 - </style>
-41
src/components/LanguagePicker.astro
··· 1 - --- 2 - import { languages } from '../i18n/ui'; 3 - import { getLangFromUrl, useTranslatedPath, getRouteFromUrl } from '../i18n/utils'; 4 - 5 - const lang = getLangFromUrl(Astro.url); 6 - const translatePath = useTranslatedPath(lang); 7 - const route = getRouteFromUrl(Astro.url); 8 - --- 9 - 10 - <ul class="language-picker" aria-label="Select language"> 11 - {Object.entries(languages).map(([code, label]) => ( 12 - <li> 13 - <a href={translatePath(route, code)} hreflang={code} class:list={[code === lang && 'current']}> 14 - {label} 15 - </a> 16 - </li> 17 - ))} 18 - </ul> 19 - 20 - <style> 21 - .language-picker { 22 - list-style: none; 23 - margin: 0; 24 - padding: 0; 25 - display: flex; 26 - flex-wrap: wrap; 27 - gap: 0.5rem 1rem; 28 - } 29 - .language-picker a { 30 - font-size: 0.85rem; 31 - color: #888; 32 - text-decoration: none; 33 - } 34 - .language-picker a:hover { 35 - color: #f3f5fe; 36 - } 37 - .language-picker a.current { 38 - color: #01f5b3; 39 - font-weight: 600; 40 - } 41 - </style>
-192
src/components/LocaleSlugPage.astro
··· 1 - --- 2 - import Base from '../layouts/Base.astro'; 3 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 4 - 5 - interface Props { 6 - locale: string; 7 - slug: string; 8 - } 9 - const { locale, slug } = Astro.props; 10 - 11 - const t = useTranslations(locale as import('../i18n/ui').defaultLang); 12 - const translatePath = useTranslatedPath(locale as import('../i18n/ui').defaultLang); 13 - 14 - const appUrl = 'https://getorbyt.com/app'; 15 - const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&bgcolor=000000&color=f3f5fe&data=${encodeURIComponent(appUrl)}`; 16 - 17 - const titles: Record<string, string> = { 18 - about: t('about.title'), 19 - app: t('app.title'), 20 - beta: t('beta.title'), 21 - contact: t('contact.title'), 22 - discord: t('discord.title'), 23 - terms: t('terms.title'), 24 - privacy: t('privacy.title'), 25 - }; 26 - --- 27 - 28 - <Base 29 - lang={locale} 30 - noFooter={slug === 'beta' || slug === 'discord'} 31 - title={titles[slug]} 32 - description={slug === 'about' ? t('about.metaDescription') : undefined} 33 - canonicalUrl={`https://getorbyt.com/${locale}/${slug}/`} 34 - > 35 - <Fragment slot="head"> 36 - {slug === 'discord' && ( 37 - <> 38 - <meta http-equiv="refresh" content="0; url=https://community.getorbyt.com" /> 39 - <link rel="canonical" href="https://community.getorbyt.com" /> 40 - <script is:inline> 41 - window.location.href = "https://community.getorbyt.com"; 42 - </script> 43 - </> 44 - )} 45 - {slug === 'beta' && ( 46 - <> 47 - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" /> 48 - <script is:inline async src="https://tally.so/widgets/embed.js"></script> 49 - <style is:inline> 50 - html { margin: 0; height: 100%; overflow: hidden; background: #000; } 51 - body { margin: 0; background: #000; } 52 - iframe { position: absolute; top: 0; right: 0; bottom: 0; left: 0; border: 0; } 53 - </style> 54 - </> 55 - )} 56 - </Fragment> 57 - 58 - {slug === 'discord' && ( 59 - <div class="discord-container"> 60 - <div class="discord-content"> 61 - <div class="emojis">🚙💨</div> 62 - <div class="text">{t('discord.routing')}</div> 63 - </div> 64 - </div> 65 - )} 66 - 67 - {slug === 'beta' && ( 68 - <div class="beta-container"> 69 - <iframe data-tally-src="https://tally.so/r/nWx7zQ?transparentBackground=1&formEventsForwarding=1" width="100%" height="100%" style="border: 0; margin: 0;" title={t('beta.iframeTitle')}></iframe> 70 - </div> 71 - )} 72 - 73 - {['about', 'app', 'contact', 'terms', 'privacy'].includes(slug) && ( 74 - <div class="page-container"> 75 - <div class="header"> 76 - <a href={translatePath('/')} class="logo"> 77 - <img src="/images/orbyt-logo.svg" alt={t('common.orbytAlt')} /> 78 - </a> 79 - {slug === 'contact' && <h1>{t('contact.title').replace(' - Orbyt', '')}</h1>} 80 - {slug === 'terms' && <h1>{t('terms.title').replace(' - Orbyt', '')}</h1>} 81 - {slug === 'privacy' && <h1>{t('privacy.title').replace(' - Orbyt', '')}</h1>} 82 - </div> 83 - 84 - {slug === 'app' && ( 85 - <div class="app-main"> 86 - <a href={translatePath('/')} class="logo"> 87 - <svg class="logo-icon" xmlns="http://www.w3.org/2000/svg" viewBox="65 65 894 894"> 88 - <defs> 89 - <mask id="cutout-mask"> 90 - <rect x="65" y="65" width="894" height="894" fill="white"/> 91 - <path fill="black" fill-rule="evenodd" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3m249.3 243.2C752 381.3 773.8 399.3 781.1 523c5.8 98.9-9.4 168.3-41.5 189.8-59.5 39.8-394.7 39.8-454.2 0-32.1-21.5-47.3-90.9-41.5-189.8 7.7-130.5 28.8-144.8 226.1-152.4 19.3-.8 104.3.5 128.3 1.9Z"/> 92 - <rect fill="black" x="372" y="482" width="82" height="136" rx="41" ry="41"/> 93 - <rect fill="black" x="570" y="482" width="82" height="136" rx="41" ry="41"/> 94 - </mask> 95 - </defs> 96 - <path class="logo-fg" mask="url(#cutout-mask)" stroke-width="65" stroke-linejoin="round" stroke-linecap="round" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3Z"/> 97 - </svg> 98 - </a> 99 - <div class="subtitle subtitle-desktop">{t('app.subtitleDesktop')}</div> 100 - <div class="subtitle subtitle-mobile">{t('app.subtitleMobile')}</div> 101 - <div class="qr-section"> 102 - <div class="qr-container"> 103 - <img class="qr-code" src={qrCodeUrl} alt={t('app.qrAlt')} /> 104 - </div> 105 - <div class="qr-label">{t('app.qrLabel')}</div> 106 - </div> 107 - <div class="buttons"> 108 - <a href="https://apps.apple.com/us/app/orbyt-video/id6751679299" class="download-button">{t('index.ios')}</a> 109 - <a href={translatePath('/beta')} class="download-button">{t('index.androidWaitlist')}</a> 110 - </div> 111 - </div> 112 - )} 113 - 114 - {slug === 'about' && ( 115 - <div class="content"> 116 - <h3>{t('about.questions')}</h3> 117 - <h2>{t('about.whatMakesDifferent')}</h2> 118 - <p>{t('about.whatMakesDifferentBody')}</p> 119 - <h2>{t('about.howBuilt')}</h2> 120 - <p>{t('about.howBuiltBody')}</p> 121 - <h2>{t('about.whereIdea')}</h2> 122 - <p>{t('about.whereIdeaBody')}</p> 123 - <h2>{t('about.whatsNext')}</h2> 124 - <p set:html={t('about.whatsNextBody') 125 - .replace('{bluesky}', `<a href="https://bsky.app/profile/getorbyt.com">Bluesky</a>`) 126 - .replace('{community}', `<a href="https://community.getorbyt.com">community forum</a>`) 127 - .replace('{sayHello}', `<a href="${translatePath('/contact')}">${t('about.sayHello')}</a>`)} /> 128 - </div> 129 - )} 130 - 131 - {slug === 'contact' && ( 132 - <div class="content"> 133 - <h2>{t('contact.generalInquiries')}</h2> 134 - <p><a href="mailto:hello@getorbyt.com">hello@getorbyt.com</a></p> 135 - <h2>{t('contact.pressMedia')}</h2> 136 - <p><a href="mailto:press@getorbyt.com">press@getorbyt.com</a></p> 137 - <h2>{t('contact.bugReports')}</h2> 138 - <p><a href="mailto:support@getorbyt.com">support@getorbyt.com</a></p> 139 - <h2>{t('contact.copyrightIssues')}</h2> 140 - <p>{t('contact.copyrightBody')}</p> 141 - </div> 142 - )} 143 - 144 - {slug === 'terms' && ( 145 - <div class="content"> 146 - <p>Welcome to Orbyt, a video social networking application! We're excited to have you on board as a user. Orbyt runs on the Authenticated Transfer Protocol ("AT Protocol"), a decentralized social networking protocol that supports many different kinds of services. These Terms of Service apply to the Orbyt service.</p> 147 - <p>Before you dive into using Orbyt, please take a moment to carefully read these Terms. This document outlines our legal obligations to each other and they apply to your use of our services.</p> 148 - <p>Orbyt is available as a mobile application ("Orbyt App" or "the App"). Your use of Orbyt is subject to these Terms of Service ("Terms"), and Orbyt Privacy Policy, which are referenced in our Terms and available to read separately.</p> 149 - <a href={translatePath('/privacy')}>Privacy Policy</a> 150 - </div> 151 - )} 152 - 153 - {slug === 'privacy' && ( 154 - <div class="content"> 155 - <p>This Privacy Policy describes how Orbyt ("we", "us", "our") collects, uses, and shares information when you use our mobile application ("Orbyt" or "the App").</p> 156 - <p><strong>Important:</strong> Orbyt is a third-party client application that connects to the Bluesky network via the AT Protocol. When you use Orbyt, your data is shared with the decentralized AT Protocol network. You should also review <a href="https://bsky.social/about/support/privacy-policy" target="_blank" rel="noopener">Bluesky's Privacy Policy</a> to understand how your data is handled on their network.</p> 157 - <a href={translatePath('/terms')}>Terms of Use</a> 158 - </div> 159 - )} 160 - </div> 161 - )} 162 - </Base> 163 - 164 - <style> 165 - .page-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 166 - .logo { display: block; margin-bottom: 2rem; } 167 - .logo img { width: 80px; height: 80px; } 168 - .header h1 { font-family: 'Figtree', sans-serif; font-size: 3rem; font-weight: 900; color: #f3f5fe; margin-bottom: 1rem; } 169 - .content h2 { font-family: 'Figtree', sans-serif; font-size: 1.8rem; color: #f3f5fe; margin: 2rem 0 1rem 0; font-weight: 900; } 170 - .content p { font-family: 'Figtree', sans-serif; font-size: 1rem; color: #cfd6e8; margin-bottom: 1rem; } 171 - .content a { color: #01f5b3; text-decoration: none; } 172 - .discord-container { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; } 173 - .discord-content { display: flex; flex-direction: column; align-items: center; gap: 20px; } 174 - .emojis { font-size: 70px; } 175 - .discord-content .text { font-family: 'Figtree', sans-serif; font-weight: 300; font-size: 20px; color: #fff; } 176 - .beta-container { position: absolute; inset: 0; } 177 - .beta-container iframe { position: absolute; top: 0; right: 0; bottom: 0; left: 0; border: 0; } 178 - .app-main { flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; } 179 - .app-main .logo { display: block; transition: opacity 0.2s ease; } 180 - .app-main .logo:hover { opacity: 0.8; } 181 - .app-main .logo-icon { width: 120px; height: auto; } 182 - .app-main .logo-icon .logo-fg { fill: #f3f5fe; stroke: #f3f5fe; } 183 - .subtitle { font-family: 'Figtree', sans-serif; font-weight: 600; font-size: 28px; color: #f3f5fe; margin: 20px auto 0; } 184 - .subtitle-mobile { display: none; } 185 - .qr-section { margin: 40px 0; } 186 - .qr-container { padding: 20px; border-radius: 20px; border: 2px solid #333; display: inline-block; } 187 - .qr-code { width: 200px; height: 200px; display: block; } 188 - .qr-label { font-size: 14px; color: #888; margin-top: 16px; } 189 - .buttons { text-align: center; margin-top: 10px; width: 100%; max-width: 400px; } 190 - .download-button { background: #551DEF; border: 2px solid #000; border-radius: 30px; height: 60px; font-weight: bold; font-size: 20px; display: flex; align-items: center; justify-content: center; text-decoration: none; color: #E0EBFF; width: 100%; margin: 0 auto 12px auto; } 191 - @media (hover: none) and (pointer: coarse) { .qr-section { display: none; } .subtitle-desktop { display: none; } .subtitle-mobile { display: block; } } 192 - </style>
-43
src/components/ProfileLanguagePicker.astro
··· 1 - --- 2 - import { languages } from '../i18n/ui'; 3 - 4 - const currentLocale = Astro.props.currentLocale ?? 'en'; 5 - const currentUrl = Astro.url.pathname + Astro.url.search; 6 - --- 7 - 8 - <ul class="profile-lang-picker" aria-label="Select language"> 9 - {Object.entries(languages).map(([code, label]) => ( 10 - <li> 11 - <a 12 - href={code === currentLocale ? '#' : `/api/set-locale?locale=${code}&redirect=${encodeURIComponent(currentUrl)}`} 13 - class:list={[code === currentLocale && 'current']} 14 - {...(code !== currentLocale && { 'data-no-prefetch': 'true' })} 15 - > 16 - {label} 17 - </a> 18 - </li> 19 - ))} 20 - </ul> 21 - 22 - <style> 23 - .profile-lang-picker { 24 - list-style: none; 25 - margin: 0; 26 - padding: 0; 27 - display: flex; 28 - flex-wrap: wrap; 29 - gap: 0.5rem 1rem; 30 - } 31 - .profile-lang-picker a { 32 - font-size: 0.8rem; 33 - color: rgba(255, 255, 255, 0.6); 34 - text-decoration: none; 35 - } 36 - .profile-lang-picker a:hover { 37 - color: rgba(255, 255, 255, 0.9); 38 - } 39 - .profile-lang-picker a.current { 40 - color: rgba(255, 255, 255, 0.95); 41 - font-weight: 600; 42 - } 43 - </style>
-34
src/components/pages/IndexContent.astro
··· 1 - --- 2 - interface Props { 3 - t: (key: string) => string; 4 - translatePath: (path: string) => string; 5 - } 6 - 7 - const { t, translatePath } = Astro.props; 8 - --- 9 - 10 - <div class="row first"> 11 - <div class="logo twelve columns"> 12 - <svg class="logo-icon" xmlns="http://www.w3.org/2000/svg" viewBox="65 65 894 894"> 13 - <defs> 14 - <mask id="cutout-mask"> 15 - <rect x="65" y="65" width="894" height="894" fill="white"/> 16 - <path fill="black" fill-rule="evenodd" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3m249.3 243.2C752 381.3 773.8 399.3 781.1 523c5.8 98.9-9.4 168.3-41.5 189.8-59.5 39.8-394.7 39.8-454.2 0-32.1-21.5-47.3-90.9-41.5-189.8 7.7-130.5 28.8-144.8 226.1-152.4 19.3-.8 104.3.5 128.3 1.9Z"/> 17 - <rect fill="black" x="372" y="482" width="82" height="136" rx="41" ry="41"/> 18 - <rect fill="black" x="570" y="482" width="82" height="136" rx="41" ry="41"/> 19 - </mask> 20 - </defs> 21 - <path class="logo-fg" mask="url(#cutout-mask)" stroke-width="65" stroke-linejoin="round" stroke-linecap="round" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3Z"/> 22 - </svg> 23 - <div class="logo-text">{t('common.orbyt')}</div> 24 - </div> 25 - </div> 26 - <div class="row motto"> 27 - <div class="twelve columns"> 28 - <div class="motto-text">{t('index.motto')}</div> 29 - </div> 30 - </div> 31 - <div class="row buttons"> 32 - <a href="https://apps.apple.com/us/app/orbyt-video/id6751679299" class="download-button">{t('index.ios')}</a> 33 - <a href={translatePath('/beta')} class="download-button">{t('index.androidWaitlist')}</a> 34 - </div>
-100
src/i18n/ui.ts
··· 1 - export const languages: Record<string, string> = { 2 - en: 'English', 3 - de: 'Deutsch', 4 - es: 'Español', 5 - 'es-419': 'Español (Latinoamérica)', 6 - fr: 'Français', 7 - ja: '日本語', 8 - ko: '한국어', 9 - 'pt-BR': 'Português (Brasil)', 10 - }; 11 - 12 - export const defaultLang = 'en'; 13 - export const showDefaultLang = false; 14 - 15 - export const ui = { 16 - en: { 17 - // Nav / Footer 18 - 'nav.home': 'about', 19 - 'nav.about': 'about', 20 - 'nav.terms': 'terms', 21 - 'nav.privacy': 'privacy', 22 - 'nav.contact': 'contact', 23 - 'nav.bluesky': 'bluesky', 24 - 'nav.community': 'community', 25 - // Index 26 - 'index.title': 'orbyt - video communities', 27 - 'index.motto': 'video communities', 28 - 'index.ios': 'iOS', 29 - 'index.androidWaitlist': 'Android Waitlist', 30 - 'index.metaDescription': 'a new video app for bluesky', 31 - // App 32 - 'app.title': 'Get the App - orbyt', 33 - 'app.subtitleDesktop': 'scan to get the app', 34 - 'app.subtitleMobile': 'get the app', 35 - 'app.qrAlt': 'QR code to download orbyt app', 36 - 'app.qrLabel': 'or download directly below', 37 - // Beta 38 - 'beta.title': 'Join the waitlist', 39 - 'beta.iframeTitle': 'Join the waitlist', 40 - // About 41 - 'about.title': 'About - Orbyt', 42 - 'about.metaDescription': 'Learn about Orbyt, a video social networking application for Bluesky', 43 - 'about.questions': 'Questions', 44 - 'about.whatMakesDifferent': 'What makes Orbyt different?', 45 - 'about.whatMakesDifferentBody': 'Every video gets equal visibility. No ranking algorithms deciding what you see. When you subscribe to a community on Orbyt, you see everything that community shares. Discovery happens through people and communities you trust, not through what an algorithm thinks will keep you scrolling.', 46 - 'about.howBuilt': 'How is Orbyt built?', 47 - 'about.howBuiltBody': "Orbyt is a third-party client for Bluesky built on the AT Protocol. When you post videos through Orbyt, they're part of the broader Bluesky network. Your content isn't locked into one platform.", 48 - 'about.whereIdea': "Where did the idea come from?", 49 - 'about.whereIdeaBody': 'I loved Byte, a video app that shut down a few years ago. When Bluesky launched, I saw an opportunity to bring that interface to an open, decentralized network. I built Orbyt from scratch to run on the AT Protocol instead of a closed platform.', 50 - 'about.whatsNext': "What's next?", 51 - 'about.whatsNextBody': 'If you want to follow along or share feedback, you can find us on {bluesky} or visit our {community}. Have questions? {sayHello}.', 52 - 'about.sayHello': 'Say hello', 53 - // Contact 54 - 'contact.title': 'Contact Us - Orbyt', 55 - 'contact.generalInquiries': 'For general inquiries', 56 - 'contact.pressMedia': 'Press & media requests', 57 - 'contact.bugReports': 'Bug reports', 58 - 'contact.copyrightIssues': 'Copyright and content issues', 59 - 'contact.copyrightBody': 'Orbyt is a third-party client for Bluesky. Content is hosted on the AT Protocol network, not by Orbyt. For copyright takedowns or content-related legal issues, please contact Bluesky Support directly.', 60 - // Terms 61 - 'terms.title': 'Terms of Use - Orbyt', 62 - 'terms.lastUpdated': 'Last Updated: February 2, 2026', 63 - // Privacy 64 - 'privacy.title': 'Privacy Policy - Orbyt', 65 - 'privacy.lastUpdated': 'Last Updated: February 2, 2026', 66 - // Discord / Community redirect 67 - 'discord.title': 'Community - orbyt', 68 - 'discord.routing': 'routing you to community...', 69 - // 404 70 - '404.title': '404 - Page Not Found | Orbyt', 71 - '404.errorTitle': 'No Signal', 72 - '404.errorMessage': "This page doesn't exist or has been moved.", 73 - '404.goBack': 'Go Back', 74 - // Profile 75 - 'profile.getBeta': 'Get the beta', 76 - 'profile.betaTester': 'Beta Tester', 77 - 'profile.joinedOn': 'Joined on', 78 - 'profile.home': 'Home', 79 - 'profile.nothingHere': 'nothing here, yet...', 80 - 'profile.loadMore': 'Load more', 81 - 'profile.loading': 'Loading...', 82 - 'profile.viewPost': 'View post', 83 - 'profile.postBy': 'post by @{handle}', 84 - // Common 85 - 'common.orbyt': 'orbyt', 86 - 'common.orbytAlt': 'Orbyt', 87 - }, 88 - de: {}, 89 - es: {}, 90 - 'es-419': {}, 91 - fr: {}, 92 - ja: {}, 93 - ko: {}, 94 - 'pt-BR': {}, 95 - } as const; 96 - 97 - export type UITranslationKey = keyof (typeof ui)[typeof defaultLang]; 98 - 99 - export const locales = Object.keys(ui) as (keyof typeof ui)[]; 100 - export const nonDefaultLocales = locales.filter((l) => l !== defaultLang);
-39
src/i18n/utils.ts
··· 1 - import { ui, defaultLang, showDefaultLang, locales } from './ui'; 2 - 3 - export function getLangFromUrl(url: URL): keyof typeof ui { 4 - const pathname = url.pathname; 5 - const segments = pathname.split('/').filter(Boolean); 6 - const first = segments[0]; 7 - if (first && locales.includes(first as keyof typeof ui)) { 8 - return first as keyof typeof ui; 9 - } 10 - return defaultLang as keyof typeof ui; 11 - } 12 - 13 - export function useTranslations(lang: keyof typeof ui) { 14 - return function t(key: keyof (typeof ui)[typeof defaultLang]): string { 15 - return ui[lang]?.[key] || ui[defaultLang][key] || String(key); 16 - }; 17 - } 18 - 19 - export function useTranslatedPath(lang: keyof typeof ui) { 20 - return function translatePath(path: string, l: string = lang): string { 21 - const pathWithLeadingSlash = path.startsWith('/') ? path : `/${path}`; 22 - const normalized = pathWithLeadingSlash.endsWith('/') && pathWithLeadingSlash !== '/' ? pathWithLeadingSlash : pathWithLeadingSlash; 23 - if (!showDefaultLang && l === defaultLang) { 24 - return normalized; 25 - } 26 - return `/${l}${normalized}`; 27 - }; 28 - } 29 - 30 - export function getRouteFromUrl(url: URL): string { 31 - const pathname = url.pathname; 32 - const segments = pathname.split('/').filter(Boolean); 33 - const first = segments[0]; 34 - if (first && locales.includes(first)) { 35 - const rest = segments.slice(1); 36 - return rest.length > 0 ? `/${rest.join('/')}` : '/'; 37 - } 38 - return pathname || '/'; 39 - }
-85
src/layouts/Base.astro
··· 1 - --- 2 - import Footer from '../components/Footer.astro'; 3 - import { getLangFromUrl, useTranslatedPath } from '../i18n/utils'; 4 - import { nonDefaultLocales } from '../i18n/ui'; 5 - 6 - interface Props { 7 - lang: string; 8 - title: string; 9 - description?: string; 10 - canonicalUrl?: string; 11 - ogImage?: string; 12 - noFooter?: boolean; 13 - } 14 - 15 - const { lang, title, description, canonicalUrl, ogImage = 'https://getorbyt.com/images/orbyt-logotype.png', noFooter = false } = Astro.props; 16 - const siteUrl = Astro.site?.toString().replace(/\/$/, '') || 'https://getorbyt.com'; 17 - const currentLang = getLangFromUrl(Astro.url); 18 - const translatePath = useTranslatedPath(currentLang as import('../i18n/ui').defaultLang); 19 - 20 - function getCurrentRoute(pathname: string, locale: string): string { 21 - const localePrefix = `/${locale}`; 22 - if (pathname === localePrefix || pathname.startsWith(`${localePrefix}/`)) { 23 - return pathname.slice(localePrefix.length) || '/'; 24 - } 25 - return pathname || '/'; 26 - } 27 - const currentRoute = getCurrentRoute(Astro.url.pathname, currentLang); 28 - const url = canonicalUrl || `${siteUrl}${Astro.url.pathname}`; 29 - const defaultLocaleUrl = `${siteUrl}${translatePath(currentRoute, 'en')}`; 30 - --- 31 - 32 - <!doctype html> 33 - <html lang={lang}> 34 - <head> 35 - <meta name="google-site-verification" content="4xaimUg28uLCqKwsPNIz6QeG_tfnLlAdHgS5bC_90JU"/> 36 - <meta charset="utf-8"> 37 - <title>{title}</title> 38 - {description && <meta name="description" content={description} />} 39 - <meta name="viewport" content="width=device-width, initial-scale=1"> 40 - 41 - <style is:global> 42 - @font-face { 43 - font-family: 'Figtree'; 44 - src: url('/fonts/Figtree/Figtree-VariableFont_wght.woff2') format('woff2'); 45 - font-weight: 300 900; 46 - font-style: normal; 47 - font-display: fallback; 48 - } 49 - </style> 50 - <link rel="preload" href="/fonts/Figtree/Figtree-VariableFont_wght.woff2" as="font" type="font/woff2" crossorigin> 51 - 52 - <meta property="og:title" content={title} /> 53 - {description && <meta property="og:description" content={description} />} 54 - <meta property="og:type" content="website" /> 55 - <meta property="og:url" content={url} /> 56 - <meta property="og:image" content={ogImage} /> 57 - <meta property="og:image:type" content="image/png" /> 58 - 59 - <meta name="twitter:card" content="summary_large_image" /> 60 - <meta name="twitter:title" content={title} /> 61 - {description && <meta name="twitter:description" content={description} />} 62 - <meta name="twitter:image" content={ogImage} /> 63 - 64 - <!-- hreflang --> 65 - <link rel="alternate" hreflang="x-default" href={defaultLocaleUrl} /> 66 - <link rel="alternate" hreflang="en" href={defaultLocaleUrl} /> 67 - {nonDefaultLocales.map((l) => { 68 - const localizedUrl = `${siteUrl}${translatePath(currentRoute, l)}`; 69 - return <link rel="alternate" hreflang={l} href={localizedUrl} />; 70 - })} 71 - 72 - <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"> 73 - <link rel="icon" type="image/x-icon" href="/favicon/favicon.ico"> 74 - <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"> 75 - <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"> 76 - <link rel="manifest" href="/favicon/site.webmanifest"> 77 - 78 - <slot name="head" /> 79 - </head> 80 - <body> 81 - <slot /> 82 - 83 - {!noFooter && <Footer lang={lang} />} 84 - </body> 85 - </html>
-37
src/middleware.ts
··· 1 - import { defineMiddleware } from 'astro:middleware'; 2 - import { getRelativeLocaleUrl } from 'astro:i18n'; 3 - 4 - const LOCALES = ['en', 'de', 'es', 'es-419', 'fr', 'ja', 'ko', 'pt-BR']; 5 - const DEFAULT_LOCALE = 'en'; 6 - 7 - function getPreferredLocale(acceptLanguage: string | null): string | null { 8 - if (!acceptLanguage) return null; 9 - const parts = acceptLanguage.split(',').map((p) => { 10 - const [lang, q] = p.trim().split(';q='); 11 - return { lang: lang.split('-')[0].toLowerCase(), full: lang.trim(), q: q ? parseFloat(q) : 1 }; 12 - }); 13 - parts.sort((a, b) => b.q - a.q); 14 - for (const { lang, full } of parts) { 15 - const match = LOCALES.find((l) => l === full || l.startsWith(lang + '-')); 16 - if (match) return match; 17 - } 18 - return null; 19 - } 20 - 21 - export const onRequest = defineMiddleware(async (context, next) => { 22 - const pathname = context.url.pathname; 23 - 24 - if (pathname.startsWith('/@')) { 25 - return next(); 26 - } 27 - 28 - if (pathname === '/' || pathname === '') { 29 - const preferred = getPreferredLocale(context.request.headers.get('Accept-Language')); 30 - if (preferred && preferred !== DEFAULT_LOCALE) { 31 - const localeUrl = getRelativeLocaleUrl(preferred, ''); 32 - return context.redirect(localeUrl, 302); 33 - } 34 - } 35 - 36 - return next(); 37 - });
+203 -26
src/pages/404.astro
··· 1 1 --- 2 2 export const prerender = true; 3 + --- 3 4 4 - import Base from '../layouts/Base.astro'; 5 - import { getLangFromUrl, useTranslations, useTranslatedPath } from '../i18n/utils'; 5 + <!doctype html> 6 + <html lang="en"> 7 + <head> 8 + <meta charset="UTF-8"> 9 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 10 + <title>404 - Page Not Found | Orbyt</title> 11 + 12 + <!-- Critical: Load fonts immediately, before any external resources --> 13 + <link rel="stylesheet" href="/css/fonts.css"> 14 + <!-- Preload Figtree font to prevent FOUT --> 15 + <link rel="preload" href="/fonts/Figtree/Figtree-VariableFont_wght.woff2" as="font" type="font/woff2" crossorigin> 16 + 17 + <!-- Favicon setup --> 18 + <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"> 19 + <link rel="icon" type="image/x-icon" href="/favicon/favicon.ico"> 20 + <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"> 21 + <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"> 22 + <link rel="manifest" href="/favicon/site.webmanifest"> 23 + 24 + <!-- Theme colors for mobile browsers --> 25 + <meta name="theme-color" content="#000000"> 26 + <meta name="msapplication-TileColor" content="#000000"> 27 + <style> 28 + * { 29 + margin: 0; 30 + padding: 0; 31 + box-sizing: border-box; 32 + } 33 + 34 + body { 35 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 36 + background: #000000; 37 + min-height: 100vh; 38 + min-height: 100dvh; 39 + color: #f3f5fe; 40 + overflow-x: hidden; 41 + } 42 + 43 + .container { 44 + width: min(100% - clamp(2rem, 5vw, 4rem), 800px); 45 + margin-inline: auto; 46 + min-height: 100vh; 47 + min-height: 100dvh; 48 + display: flex; 49 + flex-direction: column; 50 + } 51 + 52 + .main-content { 53 + flex: 1; 54 + display: flex; 55 + flex-direction: column; 56 + align-items: center; 57 + justify-content: center; 58 + text-align: center; 59 + gap: clamp(0.75rem, 2vw, 1rem); 60 + padding-block: clamp(2rem, 8vh, 6rem); 61 + } 62 + 63 + /* Hero element - TV static is the focal point */ 64 + .tv-static { 65 + width: clamp(120px, 20vw, 160px); 66 + aspect-ratio: 1; 67 + background-image: url('/images/tv-static-sprite-sheet.webp'); 68 + background-size: 500% 500%; 69 + animation: tv-static-animation 0.8s steps(1) infinite; 70 + } 71 + 72 + @keyframes tv-static-animation { 73 + 0% { background-position: 0% 0%; } 74 + 4% { background-position: 25% 0%; } 75 + 8% { background-position: 50% 0%; } 76 + 12% { background-position: 75% 0%; } 77 + 16% { background-position: 100% 0%; } 78 + 20% { background-position: 0% 25%; } 79 + 24% { background-position: 25% 25%; } 80 + 28% { background-position: 50% 25%; } 81 + 32% { background-position: 75% 25%; } 82 + 36% { background-position: 100% 25%; } 83 + 40% { background-position: 0% 50%; } 84 + 44% { background-position: 25% 50%; } 85 + 48% { background-position: 50% 50%; } 86 + 52% { background-position: 75% 50%; } 87 + 56% { background-position: 100% 50%; } 88 + 60% { background-position: 0% 75%; } 89 + 64% { background-position: 25% 75%; } 90 + 68% { background-position: 50% 75%; } 91 + 72% { background-position: 75% 75%; } 92 + 76% { background-position: 100% 75%; } 93 + 80% { background-position: 0% 100%; } 94 + 84% { background-position: 25% 100%; } 95 + 88% { background-position: 50% 100%; } 96 + 92% { background-position: 75% 100%; } 97 + 96% { background-position: 100% 100%; } 98 + } 99 + 100 + /* Primary text - the error code */ 101 + .error-title { 102 + font-size: clamp(2rem, 5vw, 2.5rem); 103 + font-weight: 900; 104 + line-height: 1; 105 + letter-spacing: -0.02em; 106 + } 107 + 108 + /* Supporting text - explains the error */ 109 + .error-message { 110 + font-size: clamp(1rem, 2vw, 1.125rem); 111 + color: #9ca3af; 112 + max-width: 32ch; 113 + line-height: 1.5; 114 + } 115 + 116 + /* Action - clear call to action */ 117 + .nav-button { 118 + display: inline-flex; 119 + background: #f3f5fe; 120 + color: #000000; 121 + text-decoration: none; 122 + padding: 0.625rem 1.25rem; 123 + border-radius: 999px; 124 + font-size: 0.875rem; 125 + font-weight: 700; 126 + transition: transform 0.2s ease, box-shadow 0.2s ease; 127 + margin-top: 0.5rem; 128 + } 129 + 130 + .nav-button:hover { 131 + transform: translateY(-2px); 132 + box-shadow: 0 8px 24px rgba(255, 255, 255, 0.1); 133 + } 6 134 7 - const lang = getLangFromUrl(Astro.url); 8 - const t = useTranslations(lang); 9 - const translatePath = useTranslatedPath(lang); 10 - --- 135 + .nav-button:active { 136 + transform: translateY(0); 137 + } 11 138 12 - <Base lang={lang} title={t('404.title')} noFooter={false}> 13 - <div class="container"> 14 - <div class="main-content"> 15 - <div class="tv-static" aria-hidden="true"></div> 16 - <h1 class="error-title">{t('404.errorTitle')}</h1> 17 - <p class="error-message">{t('404.errorMessage')}</p> 18 - <a href="#" onclick="window.history.back(); return false;" class="nav-button">{t('404.goBack')}</a> 19 - </div> 20 - </div> 21 - </Base> 139 + .separator { 140 + height: 7px; 141 + margin-bottom: 20px; 142 + position: relative; 143 + overflow: hidden; 144 + display: flex; 145 + align-items: center; 146 + justify-content: flex-start; 147 + width: 100%; 148 + } 22 149 23 - <style> 24 - .container { width: min(100% - clamp(2rem, 5vw, 4rem), 800px); margin-inline: auto; min-height: 100vh; display: flex; flex-direction: column; } 25 - .main-content { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: clamp(0.75rem, 2vw, 1rem); padding-block: clamp(2rem, 8vh, 6rem); } 26 - .tv-static { width: clamp(120px, 20vw, 160px); aspect-ratio: 1; background-image: url('/images/tv-static-sprite-sheet.webp'); background-size: 500% 500%; animation: tv-static-animation 0.8s steps(1) infinite; } 27 - @keyframes tv-static-animation { 0% { background-position: 0% 0%; } 4% { background-position: 25% 0%; } 8% { background-position: 50% 0%; } 12% { background-position: 75% 0%; } 16% { background-position: 100% 0%; } 20% { background-position: 0% 25%; } 24% { background-position: 25% 25%; } 28% { background-position: 50% 25%; } 32% { background-position: 75% 25%; } 36% { background-position: 100% 25%; } 40% { background-position: 0% 50%; } 44% { background-position: 25% 50%; } 48% { background-position: 50% 50%; } 52% { background-position: 75% 50%; } 56% { background-position: 100% 50%; } 60% { background-position: 0% 75%; } 64% { background-position: 25% 75%; } 68% { background-position: 50% 75%; } 72% { background-position: 75% 75%; } 76% { background-position: 100% 75%; } 80% { background-position: 0% 100%; } 84% { background-position: 25% 100%; } 88% { background-position: 50% 100%; } 92% { background-position: 75% 100%; } 96% { background-position: 100% 100%; } } 28 - .error-title { font-size: clamp(2rem, 5vw, 2.5rem); font-weight: 900; line-height: 1; letter-spacing: -0.02em; } 29 - .error-message { font-size: clamp(1rem, 2vw, 1.125rem); color: #9ca3af; max-width: 32ch; line-height: 1.5; } 30 - .nav-button { display: inline-flex; background: #f3f5fe; color: #000; text-decoration: none; padding: 0.625rem 1.25rem; border-radius: 999px; font-size: 0.875rem; font-weight: 700; margin-top: 0.5rem; } 31 - .nav-button:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(255, 255, 255, 0.1); } 32 - </style> 150 + .separator svg { 151 + width: 100%; 152 + height: 7px; 153 + } 154 + 155 + .footer { 156 + display: flex; 157 + flex-wrap: wrap; 158 + justify-content: flex-start; 159 + gap: 12px 32px; 160 + margin-top: auto; 161 + margin-bottom: 40px; 162 + } 163 + 164 + .footer a { 165 + font-style: normal; 166 + font-weight: bold; 167 + font-size: 13px; 168 + text-align: left; 169 + letter-spacing: 0.2em; 170 + text-transform: uppercase; 171 + text-decoration: none; 172 + color: #ccc; 173 + } 174 + 175 + .footer a:hover { 176 + color: #f3f5fe; 177 + } 178 + </style> 179 + </head> 180 + <body> 181 + <div class="container"> 182 + <div class="main-content"> 183 + <div class="tv-static" aria-hidden="true"></div> 184 + <h1 class="error-title">No Signal</h1> 185 + <p class="error-message">This page doesn't exist or has been moved.</p> 186 + <a href="#" onclick="window.history.back(); return false;" class="nav-button">Go Back</a> 187 + </div> 188 + 189 + <footer class="footer"> 190 + <div class="separator"> 191 + <svg height="7" fill="none" xmlns="http://www.w3.org/2000/svg"> 192 + <defs> 193 + <pattern id="squigglePattern" width="374" height="7" patternUnits="userSpaceOnUse"> 194 + <path d="M0 1C5.50021 1 5.50021 5.84456 11.0004 5.84456C16.5006 5.84456 16.5006 1 22.0008 1C27.501 1 27.501 5.84456 33.0013 5.84456C38.5015 5.84456 38.5015 1 44.0017 1C49.5019 1 49.5019 5.84456 55.0021 5.84456C60.5023 5.84456 60.5023 1 66.0025 1C71.5027 1 71.5027 5.84456 77.0029 5.84456C82.5031 5.84456 82.5031 1 87.9998 1C93.5 1 93.5 5.84456 98.9967 5.84456C104.497 5.84456 104.497 1 109.994 1C115.494 1 115.494 5.84456 120.99 5.84456C126.491 5.84456 126.491 1 131.991 1C137.491 1 137.491 5.84456 142.991 5.84456C148.491 5.84456 148.491 1 153.992 1C159.492 1 159.492 5.84456 164.988 5.84456C170.489 5.84456 170.489 1 175.985 1C181.486 1 181.486 5.84456 186.986 5.84456C192.486 5.84456 192.486 1 197.986 1C203.486 1 203.486 5.84456 208.987 5.84456C214.487 5.84456 214.487 1 219.987 1C225.487 1 225.487 5.84456 230.987 5.84456C236.488 5.84456 236.488 1 241.988 1C247.488 1 247.488 5.84456 252.988 5.84456C258.488 5.84456 258.488 1 263.989 1C269.489 1 269.489 5.84456 274.989 5.84456C280.489 5.84456 280.489 1 285.99 1C291.49 1 291.49 5.84456 296.99 5.84456C302.49 5.84456 302.49 1 307.99 1C313.491 1 313.491 5.84456 318.991 5.84456C324.491 5.84456 324.491 1 329.991 1C335.491 1 335.491 5.84456 340.992 5.84456C346.492 5.84456 346.492 1 351.992 1C357.492 1 357.492 5.84456 362.996 5.84456C368.5 5.84456 368.496 1 374 1" stroke="#551DEF" stroke-width="2" stroke-miterlimit="10"/> 195 + </pattern> 196 + </defs> 197 + <rect fill="url(#squigglePattern)" width="100%" height="100%"/> 198 + </svg> 199 + </div> 200 + <a href="/about">about</a> 201 + <a href="https://bsky.app/profile/getorbyt.com">bluesky</a> 202 + <a href="https://community.getorbyt.com">community</a> 203 + <a href="/terms">terms</a> 204 + <a href="/privacy">privacy</a> 205 + <a href="/contact">contact</a> 206 + </footer> 207 + </div> 208 + </body> 209 + </html>
+13 -23
src/pages/@[handle].astro
··· 6 6 import { getColor } from '../utils/orbyt-api'; 7 7 import { parseRichText } from '../utils/richtext'; 8 8 import { ClientRouter } from 'astro:transitions'; 9 - import ProfileLanguagePicker from '../components/ProfileLanguagePicker.astro'; 10 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 11 - 12 - const LOCALES = ['en', 'de', 'es', 'es-419', 'fr', 'ja', 'ko', 'pt-BR']; 13 9 14 10 const { handle } = Astro.params; 15 - 16 - const cookieLocale = Astro.cookies.get('locale')?.value; 17 - const locale = (cookieLocale && LOCALES.includes(cookieLocale) ? cookieLocale : (Astro.currentLocale ?? 'en')) as 'en'; 18 - const t = useTranslations(locale); 19 - const translatePath = useTranslatedPath(locale); 20 11 21 12 if (!handle) { 22 13 return new Response(null, { status: 404 }); ··· 59 50 60 51 // Format join date for beta badge tooltip 61 52 const joinDate = colorData?.joinedAt 62 - ? new Date(colorData.joinedAt).toLocaleDateString(locale === 'en' ? 'en-US' : locale, { month: 'long', day: 'numeric', year: 'numeric' }) 53 + ? new Date(colorData.joinedAt).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }) 63 54 : null; 64 55 65 56 // Color styles ··· 71 62 --- 72 63 73 64 <!doctype html> 74 - <html lang={locale}> 65 + <html lang="en"> 75 66 76 67 <head> 77 68 <meta charset="utf-8"> ··· 108 99 <meta property="og:type" content="profile" /> 109 100 <meta property="profile:username" content={handle} /> 110 101 <meta property="og:site_name" content="orbyt" /> 111 - <meta property="og:locale" content={({ en: 'en_US', de: 'de_DE', es: 'es_ES', 'es-419': 'es_419', fr: 'fr_FR', ja: 'ja_JP', ko: 'ko_KR', 'pt-BR': 'pt_BR' } as Record<string, string>)[locale] || 'en_US'} /> 102 + <meta property="og:locale" content="en_US" /> 112 103 <meta name="twitter:card" content="summary_large_image" /> 113 104 <meta name="twitter:title" content={ogTitle} /> 114 105 <meta name="twitter:description" content={ogDescription} /> ··· 122 113 { 123 114 "@type": "ListItem", 124 115 "position": 1, 125 - "name": t('profile.home'), 116 + "name": "Home", 126 117 "item": "https://getorbyt.com" 127 118 }, 128 119 { ··· 222 213 <div id="profile" data-handle={handle} data-cursor={nextCursor || ''}> 223 214 <header style={`background-color: ${backgroundColor};`}> 224 215 <div class="links"> 225 - <a href={translatePath('/')} class="logo-wordmark"> 216 + <a href='/' class="logo-wordmark"> 226 217 <span class="logo-text" style={`color: ${textColor}; view-transition-name: orbyt-logo;`}>orbyt</span> 227 218 </a> 228 - <a class='install-button mobile' href={translatePath('/beta')} style={`background-color: ${textColor}; color: ${backgroundColor}; view-transition-name: orbyt-beta;`}> 229 - {t('profile.getBeta')} 219 + <a class='install-button mobile' href="/beta" style={`background-color: ${textColor}; color: ${backgroundColor}; view-transition-name: orbyt-beta;`}> 220 + Get the beta 230 221 <svg class="svg-button" viewBox="0 0 21 22" fill="none" xmlns="http://www.w3.org/2000/svg"> 231 222 <path d="M1.93892 19.9988L18.9736 2.96362" stroke="currentColor" stroke-width="3.993"/> 232 223 <path d="M19 16.7368L19 2.93875L5.20193 2.93875" stroke="currentColor" stroke-width="3.993"/> ··· 247 238 <img class='avatar' src={avatar} alt={`${displayName}'s avatar`} style={`border-color: ${textColor};`} /> 248 239 <h1 class="username" style={`color: ${textColor};`}>{displayName}{colorData?.isBeta && ( 249 240 <span class="beta-badge">&#8288;<svg class="beta-icon" viewBox="28 24 160 160" xmlns="http://www.w3.org/2000/svg" style={`color: ${textColor};`}> 250 - <title>{joinDate ? `${t('profile.betaTester')} | ${t('profile.joinedOn')} ${joinDate}` : t('profile.betaTester')}</title> 241 + <title>{joinDate ? `Beta Tester | Joined on ${joinDate}` : 'Beta Tester'}</title> 251 242 <polygon points="62.5,25 62.5,130 153.43,77.5" fill="currentColor" fill-opacity="0.5"/> 252 243 <polygon points="62.5,77.5 62.5,182.5 153.43,130" fill="currentColor" fill-opacity="0.5"/> 253 244 </svg></span> ··· 264 255 </div> 265 256 266 257 <div class="install-button-wrapper desktop"> 267 - <a class='install-button' href={translatePath('/beta')} style={`background-color: ${textColor}; color: ${backgroundColor};`}> 268 - {t('profile.getBeta')} 258 + <a class='install-button' href="/beta" style={`background-color: ${textColor}; color: ${backgroundColor};`}> 259 + Get the beta 269 260 <svg class="svg-button" viewBox="0 0 21 22" fill="none" xmlns="http://www.w3.org/2000/svg"> 270 261 <path d="M1.93892 19.9988L18.9736 2.96362" stroke="currentColor" stroke-width="3.993"/> 271 262 <path d="M19 16.7368L19 2.93875L5.20193 2.93875" stroke="currentColor" stroke-width="3.993"/> 272 263 </svg> 273 264 </a> 274 265 </div> 275 - <ProfileLanguagePicker currentLocale={locale} /> 276 266 </header> 277 267 278 268 <div class="posts"> ··· 285 275 <div class="caption" style={`view-transition-name: caption-${post.postId}`} set:html={parseRichText(post.caption)}></div> 286 276 </div> 287 277 </div> 288 - <a href={`/@${handle}/${post.postId}`} aria-label={t('profile.viewPost')}></a> 278 + <a href={`/@${handle}/${post.postId}`} aria-label="View post"></a> 289 279 </div> 290 280 )) : ( 291 281 <div class="empty-feed"> 292 282 <div class="tv-static" aria-hidden="true"></div> 293 - <p class="empty-feed-message">{t('profile.nothingHere')}</p> 283 + <p class="empty-feed-message">nothing here, yet...</p> 294 284 </div> 295 285 )} 296 286 {nextCursor && ( 297 287 <footer> 298 - <a id="load-more" style={`background-color: ${textColor}; color: ${backgroundColor};`}>{t('profile.loadMore')}</a> 288 + <a id="load-more" style={`background-color: ${textColor}; color: ${backgroundColor};`}>Load more</a> 299 289 </footer> 300 290 )} 301 291 </div>
+10 -20
src/pages/@[handle]/[postId].astro
··· 4 4 import { fetchPost, toAbsoluteUrl, getImageMimeType } from '../../utils/bluesky-api'; 5 5 import { parseRichText } from '../../utils/richtext'; 6 6 import { ClientRouter } from 'astro:transitions'; 7 - import ProfileLanguagePicker from '../../components/ProfileLanguagePicker.astro'; 8 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 9 - 10 - const LOCALES = ['en', 'de', 'es', 'es-419', 'fr', 'ja', 'ko', 'pt-BR']; 11 7 12 8 const { handle, postId } = Astro.params; 13 - const cookieLocale = Astro.cookies.get('locale')?.value; 14 - const locale = (cookieLocale && LOCALES.includes(cookieLocale) ? cookieLocale : (Astro.currentLocale ?? 'en')) as 'en'; 15 - const t = useTranslations(locale); 16 - const translatePath = useTranslatedPath(locale); 17 9 // handle is "wsj.com", postId is "3mc3tjpupzo2i" 18 10 19 11 // Fetch post data for Open Graph metadata ··· 92 84 : `Post by @${authorHandle}`; 93 85 94 86 // Title: "post by @[handle]" 95 - const finalOgTitle = t('profile.postBy').replace('{handle}', authorHandle); 87 + const finalOgTitle = `post by @${authorHandle}`; 96 88 --- 97 89 98 90 <!doctype html> 99 - <html lang={locale}> 91 + <html lang="en"> 100 92 101 93 <head> 102 94 <meta charset="utf-8"> ··· 285 277 <meta property="og:image:height" content={imageHeight.toString()} /> 286 278 <meta property="og:image:alt" content={ogImageAlt} /> 287 279 <meta property="og:site_name" content="orbyt" /> 288 - <meta property="og:locale" content={({ en: 'en_US', de: 'de_DE', es: 'es_ES', 'es-419': 'es_419', fr: 'fr_FR', ja: 'ja_JP', ko: 'ko_KR', 'pt-BR': 'pt_BR' } as Record<string, string>)[locale] || 'en_US'} /> 280 + <meta property="og:locale" content="en_US" /> 289 281 {videoUrl && ( 290 282 <> 291 283 <meta property="og:video" content={videoUrl} /> ··· 315 307 { 316 308 "@type": "ListItem", 317 309 "position": 1, 318 - "name": t('profile.home'), 310 + "name": "Home", 319 311 "item": "https://getorbyt.com" 320 312 }, 321 313 { ··· 379 371 <div class="logo-overlay mobile-overlay"> 380 372 381 373 <div class="logo"> 382 - <a href={translatePath('/')} class="logo-wordmark"> 374 + <a href='/' class="logo-wordmark"> 383 375 <span class="logo-text" style="view-transition-name: orbyt-logo;">orbyt</span> 384 376 </a> 385 377 </div> 386 378 387 - <a class='install-button' href={translatePath('/beta')} style="view-transition-name: orbyt-beta;"> 388 - {t('profile.getBeta')} 379 + <a class='install-button' href="/beta" style="view-transition-name: orbyt-beta;"> 380 + Get the beta 389 381 <svg class="svg-button" viewBox="0 0 21 22" fill="none" xmlns="http://www.w3.org/2000/svg"> 390 382 <path d="M1.93892 19.9988L18.9736 2.96362" stroke-width="3.993"/> 391 383 <path d="M19 16.7368L19 2.93875L5.20193 2.93875" stroke-width="3.993"/> 392 384 </svg> 393 385 </a> 394 386 395 - <ProfileLanguagePicker currentLocale={locale} /> 396 387 </div> 397 388 398 389 <div id="post-overlay" class="post-overlay"> ··· 443 434 <div class="desktop-title"> 444 435 445 436 <div class="logo"> 446 - <a href={translatePath('/')} class="logo-wordmark"> 437 + <a href='/' class="logo-wordmark"> 447 438 <span class="logo-text">orbyt</span> 448 439 </a> 449 440 </div> 450 441 451 - <a class='install-button' href={translatePath('/beta')}> 452 - {t('profile.getBeta')} 442 + <a class='install-button' href="/beta"> 443 + Get the beta 453 444 <svg class="svg-button" viewBox="0 0 21 22" fill="none" xmlns="http://www.w3.org/2000/svg"> 454 445 <path d="M1.93892 19.9988L18.9736 2.96362" stroke-width="3.993"/> 455 446 <path d="M19 16.7368L19 2.93875L5.20193 2.93875" stroke-width="3.993"/> 456 447 </svg> 457 448 </a> 458 449 459 - <ProfileLanguagePicker currentLocale={locale} /> 460 450 </div> 461 451 462 452 <div class="horizontal-rule">
+312 -45
src/pages/about.astro
··· 1 1 --- 2 2 export const prerender = true; 3 3 4 - import Base from '../layouts/Base.astro'; 5 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 4 + // Use default profile grey for avatar border 5 + const avatarBorderColor = '#ccd7e9'; 6 + --- 7 + <!DOCTYPE html> 8 + <html lang="en"> 9 + <head> 10 + <meta charset="UTF-8"> 11 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 12 + <title>About - Orbyt</title> 13 + 14 + <!-- Critical: Inline font-face for immediate availability (no network delay) --> 15 + <style> 16 + @font-face { 17 + font-family: 'Figtree'; 18 + src: url('/fonts/Figtree/Figtree-VariableFont_wght.woff2') format('woff2'); 19 + font-weight: 300 900; 20 + font-style: normal; 21 + font-display: fallback; /* Short block window, then swap - prevents FOUT */ 22 + } 23 + </style> 24 + <!-- Preload Figtree font to start download early --> 25 + <link rel="preload" href="/fonts/Figtree/Figtree-VariableFont_wght.woff2" as="font" type="font/woff2" crossorigin> 26 + 27 + <!-- Favicon setup --> 28 + <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"> 29 + <link rel="icon" type="image/x-icon" href="/favicon/favicon.ico"> 30 + <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"> 31 + <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"> 32 + <link rel="manifest" href="/favicon/site.webmanifest"> 33 + 34 + <!-- Theme colors for mobile browsers --> 35 + <meta name="theme-color" content="#f3f5fe"> 36 + <meta name="msapplication-TileColor" content="#f3f5fe"> 37 + 38 + <!-- Open Graph / Social Media --> 39 + <meta property="og:title" content="About - Orbyt"/> 40 + <meta property="og:description" content="Learn about Orbyt, a video social networking application for Bluesky"/> 41 + <meta property="og:type" content="website"/> 42 + <meta property="og:url" content="https://getorbyt.com/about/"/> 43 + <meta property="og:image" content="https://getorbyt.com/images/orbyt-logotype.png"/> 44 + 45 + <!-- Twitter --> 46 + <meta name="twitter:card" content="summary_large_image"/> 47 + <meta name="twitter:title" content="About - Orbyt"/> 48 + <meta name="twitter:description" content="Learn about Orbyt, a video social networking application for Bluesky"/> 49 + <meta name="twitter:image" content="https://getorbyt.com/images/orbyt-logotype.png"/> 50 + 51 + <style define:vars={{ avatarBorderColor }}> 52 + * { 53 + margin: 0; 54 + padding: 0; 55 + box-sizing: border-box; 56 + } 57 + 58 + body { 59 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 60 + background: #000000; 61 + min-height: 100vh; 62 + color: #f3f5fe; 63 + position: relative; 64 + overflow-x: hidden; 65 + overflow-y: auto; 66 + } 67 + 68 + .container { 69 + max-width: 800px; 70 + margin: 0 auto; 71 + padding: 2rem; 72 + min-height: 100vh; 73 + display: flex; 74 + flex-direction: column; 75 + } 76 + 77 + .header { 78 + text-align: left; 79 + margin-bottom: 1.5rem; 80 + padding-bottom: 2rem; 81 + } 82 + 83 + .logo { 84 + display: block; 85 + margin-bottom: 2rem; 86 + } 87 + 88 + .logo img { 89 + width: 80px; 90 + height: 80px; 91 + } 92 + 93 + h1 { 94 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 95 + font-size: 3rem; 96 + font-weight: 900; 97 + color: #f3f5fe; 98 + margin-bottom: 1rem; 99 + } 100 + 101 + .personal-intro { 102 + margin-bottom: 3rem; 103 + padding-bottom: 2rem; 104 + } 105 + 106 + .personal-intro-content { 107 + display: flex; 108 + gap: 2rem; 109 + align-items: center; 110 + } 111 + 112 + .profile-photo { 113 + flex-shrink: 0; 114 + } 115 + 116 + .profile-photo a { 117 + display: block; 118 + } 119 + 120 + .profile-photo img { 121 + width: 150px; 122 + height: 150px; 123 + border-radius: 50%; 124 + object-fit: cover; 125 + border: 2px solid var(--avatarBorderColor); 126 + display: block; 127 + } 128 + 129 + .personal-text { 130 + flex: 1; 131 + } 132 + 133 + .personal-text p { 134 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 135 + font-size: 1.1rem; 136 + color: #cfd6e8; 137 + line-height: 1.7; 138 + margin-bottom: 1rem; 139 + } 140 + 141 + .personal-links { 142 + margin-top: 1rem; 143 + display: flex; 144 + flex-wrap: wrap; 145 + gap: 1rem; 146 + } 147 + 148 + .personal-links a { 149 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 150 + font-size: 0.95rem; 151 + color: #01f5b3; 152 + text-decoration: none; 153 + transition: opacity 0.2s ease; 154 + } 155 + 156 + .personal-links a:hover { 157 + opacity: 0.8; 158 + } 159 + 160 + .content { 161 + line-height: 1.6; 162 + flex: 1; 163 + } 164 + 165 + .content h2 { 166 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 167 + font-size: 1.8rem; 168 + color: #f3f5fe; 169 + margin: 2rem 0 1rem 0; 170 + font-weight: 900; 171 + } 172 + 173 + .content h3 { 174 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 175 + font-size: 1.2rem; 176 + color: #cfd6e8; 177 + margin: 2rem 0 0.5rem 0; 178 + font-weight: 500; 179 + text-transform: uppercase; 180 + letter-spacing: 0.1em; 181 + } 182 + 183 + .content p { 184 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 185 + font-size: 1rem; 186 + color: #cfd6e8; 187 + margin-bottom: 1rem; 188 + } 189 + 190 + .content a { 191 + color: #01f5b3; 192 + text-decoration: none; 193 + transition: color 0.3s ease; 194 + } 195 + 196 + .content a:hover { 197 + color: #01f5b3; 198 + } 199 + 200 + .separator { 201 + height: 7px; 202 + margin-bottom: 20px; 203 + position: relative; 204 + overflow: hidden; 205 + display: flex; 206 + align-items: center; 207 + justify-content: flex-start; 208 + width: 100%; 209 + } 210 + 211 + .separator svg { 212 + width: 100%; 213 + height: 7px; 214 + } 215 + 216 + .footer { 217 + display: flex; 218 + flex-wrap: wrap; 219 + justify-content: flex-start; 220 + gap: 12px 32px; 221 + margin-top: auto; 222 + padding-top: 80px; 223 + margin-bottom: 40px; 224 + } 225 + 226 + .footer a { 227 + font-style: normal; 228 + font-weight: bold; 229 + font-size: 13px; 230 + letter-spacing: 0.2em; 231 + text-transform: uppercase; 232 + text-decoration: none; 233 + color: #ccc; 234 + } 6 235 7 - const lang = 'en'; 8 - const t = useTranslations(lang); 9 - const translatePath = useTranslatedPath(lang); 10 - --- 236 + .footer a:hover { 237 + color: #f3f5fe; 238 + } 11 239 12 - <Base lang={lang} title={t('about.title')} description={t('about.metaDescription')} canonicalUrl="https://getorbyt.com/about/"> 13 - <div class="container"> 14 - <div class="header"> 15 - <a href={translatePath('/')} class="logo"> 16 - <img src="/images/orbyt-logo.svg" alt={t('common.orbytAlt')} /> 17 - </a> 18 - </div> 19 - <div class="content"> 20 - <h3>{t('about.questions')}</h3> 21 - <h2>{t('about.whatMakesDifferent')}</h2> 22 - <p>{t('about.whatMakesDifferentBody')}</p> 23 - <h2>{t('about.howBuilt')}</h2> 24 - <p>{t('about.howBuiltBody')}</p> 25 - <h2>{t('about.whereIdea')}</h2> 26 - <p>{t('about.whereIdeaBody')}</p> 27 - <h2>{t('about.whatsNext')}</h2> 28 - <p set:html={t('about.whatsNextBody') 29 - .replace('{bluesky}', '<a href="https://bsky.app/profile/getorbyt.com">Bluesky</a>') 30 - .replace('{community}', '<a href="https://community.getorbyt.com">community forum</a>') 31 - .replace('{sayHello}', `<a href="${translatePath('/contact')}">${t('about.sayHello')}</a>`)} /> 240 + @media (max-width: 768px) { 241 + .container { 242 + padding: 1rem; 243 + } 244 + 245 + h1 { 246 + font-size: 2.5rem; 247 + } 248 + 249 + .content h2 { 250 + font-size: 1.5rem; 251 + } 252 + 253 + .personal-intro-content { 254 + flex-direction: column; 255 + gap: 1.5rem; 256 + } 257 + 258 + .profile-photo { 259 + align-self: center; 260 + } 261 + 262 + .profile-photo img { 263 + width: 120px; 264 + height: 120px; 265 + } 266 + 267 + .personal-text p { 268 + font-size: 1rem; 269 + } 270 + } 271 + </style> 272 + </head> 273 + <body> 274 + 275 + <div class="container"> 276 + <div class="header"> 277 + <a href="/" class="logo"> 278 + <img src="/images/orbyt-logo.svg" alt="Orbyt"> 279 + </a> 280 + </div> 281 + 282 + <div class="content"> 283 + <h3>Questions</h3> 284 + 285 + <h2>What makes Orbyt different?</h2> 286 + <p>Every video gets equal visibility. No ranking algorithms deciding what you see. When you subscribe to a community on Orbyt, you see everything that community shares. Discovery happens through people and communities you trust, not through what an algorithm thinks will keep you scrolling.</p> 287 + 288 + <h2>How is Orbyt built?</h2> 289 + <p>Orbyt is a third-party client for Bluesky built on the AT Protocol. When you post videos through Orbyt, they're part of the broader Bluesky network. Your content isn't locked into one platform.</p> 290 + 291 + <h2>Where did the idea come from?</h2> 292 + <p>I loved Byte, a video app that shut down a few years ago. When Bluesky launched, I saw an opportunity to bring that interface to an open, decentralized network. I built Orbyt from scratch to run on the AT Protocol instead of a closed platform.</p> 293 + 294 + <h2>What's next?</h2> 295 + <p>If you want to follow along or share feedback, you can find us on <a href="https://bsky.app/profile/getorbyt.com">Bluesky</a> or visit our <a href="https://community.getorbyt.com">community forum</a>. Have questions? <a href="/contact">Say hello</a>.</p> 296 + </div> 297 + 298 + <footer class="footer"> 299 + <div class="separator"> 300 + <svg height="7" fill="none" xmlns="http://www.w3.org/2000/svg"> 301 + <defs> 302 + <pattern id="squigglePattern" width="374" height="7" patternUnits="userSpaceOnUse"> 303 + <path d="M0 1C5.50021 1 5.50021 5.84456 11.0004 5.84456C16.5006 5.84456 16.5006 1 22.0008 1C27.501 1 27.501 5.84456 33.0013 5.84456C38.5015 5.84456 38.5015 1 44.0017 1C49.5019 1 49.5019 5.84456 55.0021 5.84456C60.5023 5.84456 60.5023 1 66.0025 1C71.5027 1 71.5027 5.84456 77.0029 5.84456C82.5031 5.84456 82.5031 1 87.9998 1C93.5 1 93.5 5.84456 98.9967 5.84456C104.497 5.84456 104.497 1 109.994 1C115.494 1 115.494 5.84456 120.99 5.84456C126.491 5.84456 126.491 1 131.991 1C137.491 1 137.491 5.84456 142.991 5.84456C148.491 5.84456 148.491 1 153.992 1C159.492 1 159.492 5.84456 164.988 5.84456C170.489 5.84456 170.489 1 175.985 1C181.486 1 181.486 5.84456 186.986 5.84456C192.486 5.84456 192.486 1 197.986 1C203.486 1 203.486 5.84456 208.987 5.84456C214.487 5.84456 214.487 1 219.987 1C225.487 1 225.487 5.84456 230.987 5.84456C236.488 5.84456 236.488 1 241.988 1C247.488 1 247.488 5.84456 252.988 5.84456C258.488 5.84456 258.488 1 263.989 1C269.489 1 269.489 5.84456 274.989 5.84456C280.489 5.84456 280.489 1 285.99 1C291.49 1 291.49 5.84456 296.99 5.84456C302.49 5.84456 302.49 1 307.99 1C313.491 1 313.491 5.84456 318.991 5.84456C324.491 5.84456 324.491 1 329.991 1C335.491 1 335.491 5.84456 340.992 5.84456C346.492 5.84456 346.492 1 351.992 1C357.492 1 357.492 5.84456 362.996 5.84456C368.5 5.84456 368.496 1 374 1" stroke="#551DEF" stroke-width="2" stroke-miterlimit="10"/> 304 + </pattern> 305 + </defs> 306 + <rect fill="url(#squigglePattern)" width="100%" height="100%"/> 307 + </svg> 308 + </div> 309 + <a href="/about">about</a> 310 + <a href="https://bsky.app/profile/getorbyt.com">bluesky</a> 311 + <a href="https://community.getorbyt.com">community</a> 312 + <a href="/terms">terms</a> 313 + <a href="/privacy">privacy</a> 314 + <a href="/contact">contact</a> 315 + </footer> 32 316 </div> 33 - </div> 34 - </Base> 35 317 36 - <style define:vars={{ avatarBorderColor: '#ccd7e9' }}> 37 - * { margin: 0; padding: 0; box-sizing: border-box; } 38 - body { font-family: 'Figtree', sans-serif; background: #000; min-height: 100vh; color: #f3f5fe; } 39 - .container { max-width: 800px; margin: 0 auto; padding: 2rem; min-height: 100vh; display: flex; flex-direction: column; } 40 - .header { text-align: left; margin-bottom: 1.5rem; padding-bottom: 2rem; } 41 - .logo { display: block; margin-bottom: 2rem; } 42 - .logo img { width: 80px; height: 80px; } 43 - .content h2 { font-size: 1.8rem; color: #f3f5fe; margin: 2rem 0 1rem 0; font-weight: 900; } 44 - .content h3 { font-size: 1.2rem; color: #cfd6e8; margin: 2rem 0 0.5rem 0; font-weight: 500; text-transform: uppercase; letter-spacing: 0.1em; } 45 - .content p { font-size: 1rem; color: #cfd6e8; margin-bottom: 1rem; line-height: 1.7; } 46 - .content a { color: #01f5b3; text-decoration: none; } 47 - @media (max-width: 768px) { 48 - .container { padding: 1rem; } 49 - .content h2 { font-size: 1.5rem; } 50 - .content h3 { font-size: 1rem; } 51 - } 52 - </style> 318 + </body> 319 + </html>
-21
src/pages/api/set-locale.ts
··· 1 - import type { APIRoute } from 'astro'; 2 - 3 - const LOCALES = ['en', 'de', 'es', 'es-419', 'fr', 'ja', 'ko', 'pt-BR']; 4 - 5 - export const prerender = false; 6 - 7 - export const GET: APIRoute = ({ redirect, url }) => { 8 - const locale = url.searchParams.get('locale'); 9 - const redirectTo = url.searchParams.get('redirect') || '/'; 10 - 11 - if (!locale || !LOCALES.includes(locale)) { 12 - return redirect(redirectTo, 302); 13 - } 14 - 15 - const response = redirect(redirectTo, 302); 16 - response.headers.set( 17 - 'Set-Cookie', 18 - `locale=${locale}; Path=/; Max-Age=31536000; SameSite=Lax` 19 - ); 20 - return response; 21 - };
+208 -60
src/pages/app.astro
··· 1 1 --- 2 - export const prerender = true; 3 - 2 + // All users see this landing page with QR code 4 3 const appUrl = 'https://getorbyt.com/app'; 5 4 const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&bgcolor=000000&color=f3f5fe&data=${encodeURIComponent(appUrl)}`; 6 - 7 - import Base from '../layouts/Base.astro'; 8 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 9 - 10 - const lang = 'en'; 11 - const t = useTranslations(lang); 12 - const translatePath = useTranslatedPath(lang); 13 5 --- 14 - 15 - <Base lang={lang} title={t('app.title')} canonicalUrl="https://getorbyt.com/app/"> 16 - <div class="container"> 17 - <div class="main-content"> 18 - <a href={translatePath('/')} class="logo"> 19 - <svg class="logo-icon" xmlns="http://www.w3.org/2000/svg" viewBox="65 65 894 894"> 20 - <defs> 21 - <mask id="cutout-mask"> 22 - <rect x="65" y="65" width="894" height="894" fill="white"/> 23 - <path fill="black" fill-rule="evenodd" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3m249.3 243.2C752 381.3 773.8 399.3 781.1 523c5.8 98.9-9.4 168.3-41.5 189.8-59.5 39.8-394.7 39.8-454.2 0-32.1-21.5-47.3-90.9-41.5-189.8 7.7-130.5 28.8-144.8 226.1-152.4 19.3-.8 104.3.5 128.3 1.9Z"/> 24 - <rect fill="black" x="372" y="482" width="82" height="136" rx="41" ry="41"/> 25 - <rect fill="black" x="570" y="482" width="82" height="136" rx="41" ry="41"/> 26 - </mask> 27 - </defs> 28 - <path class="logo-fg" mask="url(#cutout-mask)" stroke-width="65" stroke-linejoin="round" stroke-linecap="round" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3Z"/> 29 - </svg> 30 - </a> 31 - <div class="subtitle subtitle-desktop">{t('app.subtitleDesktop')}</div> 32 - <div class="subtitle subtitle-mobile">{t('app.subtitleMobile')}</div> 33 - <div class="qr-section"> 34 - <div class="qr-container"> 35 - <img class="qr-code" src={qrCodeUrl} alt={t('app.qrAlt')} /> 6 + <!doctype html> 7 + <html lang="en"> 8 + <head> 9 + <meta charset="utf-8"> 10 + <meta name="viewport" content="width=device-width, initial-scale=1"> 11 + <title>Get the App - orbyt</title> 12 + 13 + <style> 14 + @font-face { 15 + font-family: 'Figtree'; 16 + src: url('/fonts/Figtree/Figtree-VariableFont_wght.woff2') format('woff2'); 17 + font-weight: 300 900; 18 + font-style: normal; 19 + font-display: fallback; 20 + } 21 + </style> 22 + <link rel="preload" href="/fonts/Figtree/Figtree-VariableFont_wght.woff2" as="font" type="font/woff2" crossorigin> 23 + 24 + <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"> 25 + <link rel="icon" type="image/x-icon" href="/favicon/favicon.ico"> 26 + 27 + <style> 28 + * { 29 + box-sizing: border-box; 30 + } 31 + html, body { 32 + margin: 0; 33 + padding: 0; 34 + height: 100%; 35 + min-height: 100vh; 36 + } 37 + body { 38 + --bg-color: #000; 39 + --fg-color: #f3f5fe; 40 + background: var(--bg-color); 41 + font-family: 'Figtree', sans-serif; 42 + display: flex; 43 + align-items: center; 44 + justify-content: center; 45 + } 46 + .container { 47 + position: relative; 48 + width: 100%; 49 + max-width: 800px; 50 + padding: 20px; 51 + min-height: 100vh; 52 + display: flex; 53 + flex-direction: column; 54 + } 55 + .main-content { 56 + flex: 1; 57 + display: flex; 58 + flex-direction: column; 59 + justify-content: center; 60 + align-items: center; 61 + text-align: center; 62 + } 63 + .logo-icon { 64 + width: 120px; 65 + height: auto; 66 + display: block; 67 + margin: 0 auto; 68 + } 69 + .logo-icon .logo-fg { 70 + fill: var(--fg-color); 71 + stroke: var(--fg-color); 72 + } 73 + .logo { 74 + display: block; 75 + transition: opacity 0.2s ease; 76 + } 77 + .logo:hover { 78 + opacity: 0.8; 79 + } 80 + .subtitle { 81 + font-family: 'Figtree', sans-serif; 82 + font-weight: 600; 83 + font-size: 28px; 84 + color: var(--fg-color); 85 + text-align: center; 86 + display: block; 87 + margin: 20px auto 0; 88 + } 89 + .qr-section { 90 + margin: 40px 0; 91 + text-align: center; 92 + } 93 + .qr-container { 94 + background: var(--bg-color); 95 + padding: 20px; 96 + border-radius: 20px; 97 + border: 2px solid #333; 98 + display: inline-block; 99 + } 100 + .qr-code { 101 + width: 200px; 102 + height: 200px; 103 + display: block; 104 + } 105 + .qr-label { 106 + font-size: 14px; 107 + color: #888; 108 + margin-top: 16px; 109 + letter-spacing: 0.05em; 110 + } 111 + .buttons { 112 + text-align: center; 113 + margin-top: 10px; 114 + width: 100%; 115 + max-width: 400px; 116 + } 117 + .download-button { 118 + background: #551DEF; 119 + border: 2px solid #000000; 120 + border-radius: 30px; 121 + height: 60px; 122 + font-weight: bold; 123 + font-size: 20px; 124 + line-height: 28px; 125 + display: flex; 126 + align-items: center; 127 + justify-content: center; 128 + text-align: center; 129 + text-decoration: none; 130 + color: #E0EBFF; 131 + width: 100%; 132 + margin: 0 auto 12px auto; 133 + } 134 + .download-button:hover { 135 + opacity: 0.9; 136 + } 137 + 138 + /* Mobile-specific text */ 139 + .subtitle-mobile { 140 + display: none; 141 + } 142 + 143 + /* Touch devices (mobile/tablet) */ 144 + @media (hover: none) and (pointer: coarse) { 145 + .qr-section { 146 + display: none; 147 + } 148 + .subtitle-desktop { 149 + display: none; 150 + } 151 + .subtitle-mobile { 152 + display: block; 153 + } 154 + .buttons { 155 + margin-top: 40px; 156 + } 157 + } 158 + 159 + @media (max-width: 500px) { 160 + .logo-icon { 161 + width: 90px; 162 + } 163 + .subtitle { 164 + font-size: 22px; 165 + } 166 + .qr-section { 167 + margin: 30px 0; 168 + } 169 + .qr-code { 170 + width: 180px; 171 + height: 180px; 172 + } 173 + .download-button { 174 + height: 50px; 175 + font-size: 18px; 176 + } 177 + } 178 + </style> 179 + </head> 180 + <body> 181 + <div class="container"> 182 + <div class="main-content"> 183 + <a href="/" class="logo"> 184 + <svg class="logo-icon" xmlns="http://www.w3.org/2000/svg" viewBox="65 65 894 894"> 185 + <defs> 186 + <mask id="cutout-mask"> 187 + <rect x="65" y="65" width="894" height="894" fill="white"/> 188 + <path fill="black" fill-rule="evenodd" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3m249.3 243.2C752 381.3 773.8 399.3 781.1 523c5.8 98.9-9.4 168.3-41.5 189.8-59.5 39.8-394.7 39.8-454.2 0-32.1-21.5-47.3-90.9-41.5-189.8 7.7-130.5 28.8-144.8 226.1-152.4 19.3-.8 104.3.5 128.3 1.9Z"/> 189 + <rect fill="black" x="372" y="482" width="82" height="136" rx="41" ry="41"/> 190 + <rect fill="black" x="570" y="482" width="82" height="136" rx="41" ry="41"/> 191 + </mask> 192 + </defs> 193 + <path class="logo-fg" mask="url(#cutout-mask)" stroke-width="65" stroke-linejoin="round" stroke-linecap="round" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3Z"/> 194 + </svg> 195 + </a> 196 + 197 + <div class="subtitle subtitle-desktop">scan to get the app</div> 198 + <div class="subtitle subtitle-mobile">get the app</div> 199 + 200 + <div class="qr-section"> 201 + <div class="qr-container"> 202 + <img class="qr-code" src={qrCodeUrl} alt="QR code to download orbyt app" /> 203 + </div> 204 + <div class="qr-label">or download directly below</div> 205 + </div> 206 + 207 + <div class="buttons"> 208 + <a href="https://apps.apple.com/us/app/orbyt-video/id6751679299" class="download-button">iOS</a> 209 + <a href="/beta" class="download-button">Android Waitlist</a> 210 + </div> 36 211 </div> 37 - <div class="qr-label">{t('app.qrLabel')}</div> 38 - </div> 39 - <div class="buttons"> 40 - <a href="https://apps.apple.com/us/app/orbyt-video/id6751679299" class="download-button">{t('index.ios')}</a> 41 - <a href={translatePath('/beta')} class="download-button">{t('index.androidWaitlist')}</a> 42 - </div> 43 212 </div> 44 - </div> 45 - </Base> 46 - 47 - <style> 48 - html, body { margin: 0; padding: 0; height: 100%; min-height: 100vh; } 49 - body { --bg-color: #000; --fg-color: #f3f5fe; background: var(--bg-color); font-family: 'Figtree', sans-serif; display: flex; align-items: center; justify-content: center; } 50 - .container { width: 100%; max-width: 800px; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 51 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; } 52 - .logo { display: block; transition: opacity 0.2s ease; } 53 - .logo:hover { opacity: 0.8; } 54 - .logo-icon { width: 120px; height: auto; } 55 - .logo-icon .logo-fg { fill: var(--fg-color); stroke: var(--fg-color); } 56 - .subtitle { font-weight: 600; font-size: 28px; color: var(--fg-color); margin: 20px auto 0; } 57 - .subtitle-mobile { display: none; } 58 - .qr-section { margin: 40px 0; } 59 - .qr-container { padding: 20px; border-radius: 20px; border: 2px solid #333; display: inline-block; } 60 - .qr-code { width: 200px; height: 200px; display: block; } 61 - .qr-label { font-size: 14px; color: #888; margin-top: 16px; } 62 - .buttons { text-align: center; margin-top: 10px; width: 100%; max-width: 400px; } 63 - .download-button { background: #551DEF; border: 2px solid #000; border-radius: 30px; height: 60px; font-weight: bold; font-size: 20px; display: flex; align-items: center; justify-content: center; text-decoration: none; color: #E0EBFF; width: 100%; margin: 0 auto 12px auto; } 64 - @media (hover: none) and (pointer: coarse) { .qr-section { display: none; } .subtitle-desktop { display: none; } .subtitle-mobile { display: block; } .buttons { margin-top: 40px; } } 65 - @media (max-width: 500px) { .logo-icon { width: 90px; } .subtitle { font-size: 22px; } .qr-code { width: 180px; height: 180px; } .download-button { height: 50px; font-size: 18px; } } 66 - </style> 213 + </body> 214 + </html>
+13 -15
src/pages/beta.astro
··· 1 1 --- 2 2 export const prerender = true; 3 - 4 - import Base from '../layouts/Base.astro'; 5 - import { useTranslations } from '../i18n/utils'; 6 - 7 - const lang = 'en'; 8 - const t = useTranslations(lang); 9 3 --- 10 4 11 - <Base lang={lang} title={t('beta.title')} noFooter={true}> 12 - <Fragment slot="head"> 13 - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" /> 5 + <!doctype html> 6 + <html> 7 + <head> 8 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> 9 + <title>Join the waitlist</title> 14 10 <script is:inline async src="https://tally.so/widgets/embed.js"></script> 15 - <style is:inline> 16 - html { margin: 0; height: 100%; overflow: hidden; background: #000; } 17 - body { margin: 0; background: #000; } 11 + <style type="text/css"> 12 + html { margin: 0; height: 100%; overflow: hidden; background: #000000; } 13 + body { margin: 0; background: #000000; } 18 14 iframe { position: absolute; top: 0; right: 0; bottom: 0; left: 0; border: 0; } 19 15 </style> 20 - </Fragment> 21 - <iframe data-tally-src="https://tally.so/r/nWx7zQ?transparentBackground=1&formEventsForwarding=1" width="100%" height="100%" style="border: 0; margin: 0;" title={t('beta.iframeTitle')}></iframe> 22 - </Base> 16 + </head> 17 + <body> 18 + <iframe data-tally-src="https://tally.so/r/nWx7zQ?transparentBackground=1&formEventsForwarding=1" width="100%" height="100%" style="border: 0; margin: 0;" title="Join the waitlist"></iframe> 19 + </body> 20 + </html>
+208 -37
src/pages/contact.astro
··· 1 1 --- 2 2 export const prerender = true; 3 + --- 4 + <!DOCTYPE html> 5 + <html lang="en"> 6 + <head> 7 + <meta charset="UTF-8"> 8 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 9 + <title>Contact Us - Orbyt</title> 10 + 11 + <!-- Critical: Inline font-face for immediate availability (no network delay) --> 12 + <style> 13 + @font-face { 14 + font-family: 'Figtree'; 15 + src: url('/fonts/Figtree/Figtree-VariableFont_wght.woff2') format('woff2'); 16 + font-weight: 300 900; 17 + font-style: normal; 18 + font-display: fallback; /* Short block window, then swap - prevents FOUT */ 19 + } 20 + </style> 21 + <!-- Preload Figtree font to start download early --> 22 + <link rel="preload" href="/fonts/Figtree/Figtree-VariableFont_wght.woff2" as="font" type="font/woff2" crossorigin> 23 + 24 + <!-- Favicon setup --> 25 + <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"> 26 + <link rel="icon" type="image/x-icon" href="/favicon/favicon.ico"> 27 + <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"> 28 + <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"> 29 + <link rel="manifest" href="/favicon/site.webmanifest"> 30 + 31 + <!-- Theme colors for mobile browsers --> 32 + <meta name="theme-color" content="#f3f5fe"> 33 + <meta name="msapplication-TileColor" content="#f3f5fe"> 34 + <style> 35 + * { 36 + margin: 0; 37 + padding: 0; 38 + box-sizing: border-box; 39 + } 3 40 4 - import Base from '../layouts/Base.astro'; 5 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 41 + body { 42 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 43 + background: #000000; 44 + min-height: 100vh; 45 + color: #f3f5fe; 46 + position: relative; 47 + overflow-x: hidden; 48 + overflow-y: auto; 49 + } 50 + 51 + .container { 52 + max-width: 800px; 53 + margin: 0 auto; 54 + padding: 2rem; 55 + min-height: 100vh; 56 + display: flex; 57 + flex-direction: column; 58 + } 59 + 60 + .header { 61 + text-align: left; 62 + margin-bottom: 1.5rem; 63 + padding-bottom: 2rem; 64 + } 65 + 66 + .logo { 67 + display: block; 68 + margin-bottom: 2rem; 69 + } 70 + 71 + .logo img { 72 + width: 80px; 73 + height: 80px; 74 + } 75 + 76 + h1 { 77 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 78 + font-size: 3rem; 79 + font-weight: 900; 80 + color: #f3f5fe; 81 + margin-bottom: 1rem; 82 + } 83 + 84 + .content { 85 + line-height: 1.6; 86 + flex: 1; 87 + } 88 + 89 + .content h2 { 90 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 91 + font-size: 1.8rem; 92 + color: #f3f5fe; 93 + margin: 2rem 0 1rem 0; 94 + font-weight: 900; 95 + } 96 + 97 + .content p { 98 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 99 + font-size: 1rem; 100 + color: #cfd6e8; 101 + margin-bottom: 1rem; 102 + } 103 + 104 + .content a { 105 + color: #01f5b3; 106 + text-decoration: none; 107 + transition: color 0.3s ease; 108 + } 109 + 110 + .content a:hover { 111 + color: #01f5b3; 112 + } 113 + 114 + .separator { 115 + height: 7px; 116 + margin-bottom: 20px; 117 + position: relative; 118 + overflow: hidden; 119 + display: flex; 120 + align-items: center; 121 + justify-content: flex-start; 122 + width: 100%; 123 + } 124 + 125 + .separator svg { 126 + width: 100%; 127 + height: 7px; 128 + } 129 + 130 + .footer { 131 + display: flex; 132 + flex-wrap: wrap; 133 + justify-content: flex-start; 134 + gap: 12px 32px; 135 + margin-top: auto; 136 + padding-top: 80px; 137 + margin-bottom: 40px; 138 + } 139 + 140 + .footer a { 141 + font-style: normal; 142 + font-weight: bold; 143 + font-size: 13px; 144 + letter-spacing: 0.2em; 145 + text-transform: uppercase; 146 + text-decoration: none; 147 + color: #ccc; 148 + } 149 + 150 + .footer a:hover { 151 + color: #f3f5fe; 152 + } 153 + 154 + @media (max-width: 768px) { 155 + .container { 156 + padding: 1rem; 157 + } 6 158 7 - const lang = 'en'; 8 - const t = useTranslations(lang); 9 - const translatePath = useTranslatedPath(lang); 10 - --- 159 + h1 { 160 + font-size: 2.5rem; 161 + } 11 162 12 - <Base lang={lang} title={t('contact.title')} canonicalUrl="https://getorbyt.com/contact/"> 13 - <div class="container"> 14 - <div class="header"> 15 - <a href={translatePath('/')} class="logo"> 16 - <img src="/images/orbyt-logo.svg" alt={t('common.orbytAlt')} /> 17 - </a> 18 - <h1>{t('contact.title').replace(' - Orbyt', '')}</h1> 19 - </div> 20 - <div class="content"> 21 - <h2>{t('contact.generalInquiries')}</h2> 22 - <p><a href="mailto:hello@getorbyt.com">hello@getorbyt.com</a></p> 23 - <h2>{t('contact.pressMedia')}</h2> 24 - <p><a href="mailto:press@getorbyt.com">press@getorbyt.com</a></p> 25 - <h2>{t('contact.bugReports')}</h2> 26 - <p><a href="mailto:support@getorbyt.com">support@getorbyt.com</a></p> 27 - <h2>{t('contact.copyrightIssues')}</h2> 28 - <p>{t('contact.copyrightBody')}</p> 163 + .content h2 { 164 + font-size: 1.5rem; 165 + } 166 + } 167 + </style> 168 + </head> 169 + <body> 170 + 171 + <div class="container"> 172 + <div class="header"> 173 + <a href="/" class="logo"> 174 + <img src="/images/orbyt-logo.svg" alt="Orbyt"> 175 + </a> 176 + <h1>Contact Us</h1> 177 + </div> 178 + 179 + <div class="content"> 180 + 181 + <h2>For general inquiries</h2> 182 + <p><a href="mailto:hello@getorbyt.com">hello@getorbyt.com</a></p> 183 + 184 + <h2>Press &amp; media requests</h2> 185 + <p><a href="mailto:press@getorbyt.com">press@getorbyt.com</a></p> 186 + 187 + <h2>Bug reports</h2> 188 + <p><a href="mailto:support@getorbyt.com">support@getorbyt.com</a></p> 189 + 190 + <h2>Copyright and content issues</h2> 191 + <p>Orbyt is a third-party client for Bluesky. Content is hosted on the AT Protocol network, not by Orbyt. For copyright takedowns or content-related legal issues, please contact <a href="https://bsky.social/about/support" target="_blank" rel="noopener">Bluesky Support</a> directly.</p> 192 + </div> 193 + 194 + <footer class="footer"> 195 + <div class="separator"> 196 + <svg height="7" fill="none" xmlns="http://www.w3.org/2000/svg"> 197 + <defs> 198 + <pattern id="squigglePattern" width="374" height="7" patternUnits="userSpaceOnUse"> 199 + <path d="M0 1C5.50021 1 5.50021 5.84456 11.0004 5.84456C16.5006 5.84456 16.5006 1 22.0008 1C27.501 1 27.501 5.84456 33.0013 5.84456C38.5015 5.84456 38.5015 1 44.0017 1C49.5019 1 49.5019 5.84456 55.0021 5.84456C60.5023 5.84456 60.5023 1 66.0025 1C71.5027 1 71.5027 5.84456 77.0029 5.84456C82.5031 5.84456 82.5031 1 87.9998 1C93.5 1 93.5 5.84456 98.9967 5.84456C104.497 5.84456 104.497 1 109.994 1C115.494 1 115.494 5.84456 120.99 5.84456C126.491 5.84456 126.491 1 131.991 1C137.491 1 137.491 5.84456 142.991 5.84456C148.491 5.84456 148.491 1 153.992 1C159.492 1 159.492 5.84456 164.988 5.84456C170.489 5.84456 170.489 1 175.985 1C181.486 1 181.486 5.84456 186.986 5.84456C192.486 5.84456 192.486 1 197.986 1C203.486 1 203.486 5.84456 208.987 5.84456C214.487 5.84456 214.487 1 219.987 1C225.487 1 225.487 5.84456 230.987 5.84456C236.488 5.84456 236.488 1 241.988 1C247.488 1 247.488 5.84456 252.988 5.84456C258.488 5.84456 258.488 1 263.989 1C269.489 1 269.489 5.84456 274.989 5.84456C280.489 5.84456 280.489 1 285.99 1C291.49 1 291.49 5.84456 296.99 5.84456C302.49 5.84456 302.49 1 307.99 1C313.491 1 313.491 5.84456 318.991 5.84456C324.491 5.84456 324.491 1 329.991 1C335.491 1 335.491 5.84456 340.992 5.84456C346.492 5.84456 346.492 1 351.992 1C357.492 1 357.492 5.84456 362.996 5.84456C368.5 5.84456 368.496 1 374 1" stroke="#551DEF" stroke-width="2" stroke-miterlimit="10"/> 200 + </pattern> 201 + </defs> 202 + <rect fill="url(#squigglePattern)" width="100%" height="100%"/> 203 + </svg> 204 + </div> 205 + <a href="/about">about</a> 206 + <a href="https://bsky.app/profile/getorbyt.com">bluesky</a> 207 + <a href="https://community.getorbyt.com">community</a> 208 + <a href="/terms">terms</a> 209 + <a href="/privacy">privacy</a> 210 + <a href="/contact">contact</a> 211 + </footer> 29 212 </div> 30 - </div> 31 - </Base> 32 213 33 - <style> 34 - * { margin: 0; padding: 0; box-sizing: border-box; } 35 - body { font-family: 'Figtree', sans-serif; background: #000; min-height: 100vh; color: #f3f5fe; } 36 - .container { max-width: 800px; margin: 0 auto; padding: 2rem; min-height: 100vh; display: flex; flex-direction: column; } 37 - .header { text-align: left; margin-bottom: 1.5rem; padding-bottom: 2rem; } 38 - .logo { display: block; margin-bottom: 2rem; } 39 - .logo img { width: 80px; height: 80px; } 40 - h1 { font-size: 3rem; font-weight: 900; color: #f3f5fe; margin-bottom: 1rem; } 41 - .content h2 { font-size: 1.8rem; color: #f3f5fe; margin: 2rem 0 1rem 0; font-weight: 900; } 42 - .content p { font-size: 1rem; color: #cfd6e8; margin-bottom: 1rem; } 43 - .content a { color: #01f5b3; text-decoration: none; } 44 - </style> 214 + </body> 215 + </html>
-10
src/pages/de/about.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'de'; 7 - const slug = 'about'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/de/app.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'de'; 7 - const slug = 'app'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/de/beta.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'de'; 7 - const slug = 'beta'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/de/contact.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'de'; 7 - const slug = 'contact'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/de/discord.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'de'; 7 - const slug = 'discord'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-24
src/pages/de/index.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import Base from '../../layouts/Base.astro'; 5 - import IndexContent from '../../components/pages/IndexContent.astro'; 6 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 7 - 8 - const locale = 'de'; 9 - const t = useTranslations(locale); 10 - const translatePath = useTranslatedPath(locale); 11 - --- 12 - 13 - <Base lang={locale} title={t('index.title')} description={t('index.metaDescription')} canonicalUrl={"https://getorbyt.com/" + locale + "/"}> 14 - <div class="index-container"> 15 - <div class="main-content"> 16 - <IndexContent t={t} translatePath={translatePath} /> 17 - </div> 18 - </div> 19 - </Base> 20 - 21 - <style> 22 - .index-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 23 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 24 - </style>
-10
src/pages/de/privacy.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'de'; 7 - const slug = 'privacy'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/de/terms.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'de'; 7 - const slug = 'terms'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
+78 -27
src/pages/discord.astro
··· 1 1 --- 2 2 export const prerender = true; 3 - 4 - import Base from '../layouts/Base.astro'; 5 - import { useTranslations } from '../i18n/utils'; 6 - 7 - const lang = 'en'; 8 - const t = useTranslations(lang); 9 3 --- 10 - 11 - <Base lang={lang} title={t('discord.title')} noFooter={true}> 12 - <Fragment slot="head"> 13 - <meta http-equiv="refresh" content="0; url=https://community.getorbyt.com" /> 14 - <link rel="canonical" href="https://community.getorbyt.com" /> 15 - <script is:inline> 16 - window.location.href = "https://community.getorbyt.com"; 4 + <!doctype html> 5 + <html lang="en"> 6 + <head> 7 + <meta charset="utf-8"> 8 + <meta name="viewport" content="width=device-width, initial-scale=1"> 9 + <title>Community - orbyt</title> 10 + <meta http-equiv="refresh" content="0; url=https://community.getorbyt.com"> 11 + <link rel="canonical" href="https://community.getorbyt.com"> 12 + <script> 13 + window.location.href = "https://community.getorbyt.com"; 17 14 </script> 18 - </Fragment> 19 - <div class="container"> 20 - <div class="emojis">🚙💨</div> 21 - <div class="text">{t('discord.routing')}</div> 22 - </div> 23 - </Base> 24 - 25 - <style> 26 - html, body { margin: 0; padding: 0; background: #000; color: #fff; height: 100vh; } 27 - body { display: flex; flex-direction: column; align-items: center; justify-content: center; } 28 - .container { display: flex; flex-direction: column; align-items: center; gap: 20px; } 29 - .emojis { font-size: 70px; } 30 - .text { font-family: 'Figtree', sans-serif; font-weight: 300; font-size: 20px; color: #fff; } 31 - </style> 15 + <style> 16 + @font-face { 17 + font-family: 'Figtree'; 18 + src: url('/fonts/Figtree/Figtree-VariableFont_wght.woff2') format('woff2'); 19 + font-weight: 300 900; 20 + font-style: normal; 21 + font-display: fallback; 22 + } 23 + html, body { 24 + margin: 0; 25 + padding: 0; 26 + background: #000; 27 + color: #fff; 28 + height: 100vh; 29 + } 30 + body { 31 + display: flex; 32 + flex-direction: column; 33 + align-items: center; 34 + justify-content: center; 35 + position: relative; 36 + } 37 + .container { 38 + display: flex; 39 + flex-direction: column; 40 + align-items: center; 41 + gap: 20px; 42 + } 43 + .emojis { 44 + font-size: 70px; 45 + line-height: 1; 46 + } 47 + .text { 48 + font-family: 'Figtree', sans-serif; 49 + font-weight: 300; 50 + font-size: 20px; 51 + color: #fff; 52 + } 53 + .logo-icon { 54 + width: 80px; 55 + height: auto; 56 + position: absolute; 57 + bottom: 40px; 58 + } 59 + .logo-icon .logo-fg { 60 + fill: #f3f5fe; 61 + stroke: #f3f5fe; 62 + } 63 + </style> 64 + </head> 65 + <body> 66 + <div class="container"> 67 + <div class="emojis">🚙💨</div> 68 + <div class="text">routing you to community...</div> 69 + </div> 70 + <svg class="logo-icon" xmlns="http://www.w3.org/2000/svg" viewBox="65 65 894 894"> 71 + <defs> 72 + <mask id="cutout-mask"> 73 + <rect x="65" y="65" width="894" height="894" fill="white"/> 74 + <path fill="black" fill-rule="evenodd" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3m249.3 243.2C752 381.3 773.8 399.3 781.1 523c5.8 98.9-9.4 168.3-41.5 189.8-59.5 39.8-394.7 39.8-454.2 0-32.1-21.5-47.3-90.9-41.5-189.8 7.7-130.5 28.8-144.8 226.1-152.4 19.3-.8 104.3.5 128.3 1.9Z"/> 75 + <rect fill="black" x="372" y="482" width="82" height="136" rx="41" ry="41"/> 76 + <rect fill="black" x="570" y="482" width="82" height="136" rx="41" ry="41"/> 77 + </mask> 78 + </defs> 79 + <path class="logo-fg" mask="url(#cutout-mask)" stroke-width="65" stroke-linejoin="round" stroke-linecap="round" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3Z"/> 80 + </svg> 81 + </body> 82 + </html>
-10
src/pages/es-419/about.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es-419'; 7 - const slug = 'about'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es-419/app.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es-419'; 7 - const slug = 'app'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es-419/beta.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es-419'; 7 - const slug = 'beta'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es-419/contact.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es-419'; 7 - const slug = 'contact'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es-419/discord.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es-419'; 7 - const slug = 'discord'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-24
src/pages/es-419/index.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import Base from '../../layouts/Base.astro'; 5 - import IndexContent from '../../components/pages/IndexContent.astro'; 6 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 7 - 8 - const locale = 'es-419'; 9 - const t = useTranslations(locale); 10 - const translatePath = useTranslatedPath(locale); 11 - --- 12 - 13 - <Base lang={locale} title={t('index.title')} description={t('index.metaDescription')} canonicalUrl={"https://getorbyt.com/" + locale + "/"}> 14 - <div class="index-container"> 15 - <div class="main-content"> 16 - <IndexContent t={t} translatePath={translatePath} /> 17 - </div> 18 - </div> 19 - </Base> 20 - 21 - <style> 22 - .index-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 23 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 24 - </style>
-10
src/pages/es-419/privacy.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es-419'; 7 - const slug = 'privacy'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es-419/terms.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es-419'; 7 - const slug = 'terms'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es/about.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es'; 7 - const slug = 'about'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es/app.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es'; 7 - const slug = 'app'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es/beta.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es'; 7 - const slug = 'beta'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es/contact.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es'; 7 - const slug = 'contact'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es/discord.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es'; 7 - const slug = 'discord'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-24
src/pages/es/index.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import Base from '../../layouts/Base.astro'; 5 - import IndexContent from '../../components/pages/IndexContent.astro'; 6 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 7 - 8 - const locale = 'es'; 9 - const t = useTranslations(locale); 10 - const translatePath = useTranslatedPath(locale); 11 - --- 12 - 13 - <Base lang={locale} title={t('index.title')} description={t('index.metaDescription')} canonicalUrl={"https://getorbyt.com/" + locale + "/"}> 14 - <div class="index-container"> 15 - <div class="main-content"> 16 - <IndexContent t={t} translatePath={translatePath} /> 17 - </div> 18 - </div> 19 - </Base> 20 - 21 - <style> 22 - .index-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 23 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 24 - </style>
-10
src/pages/es/privacy.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es'; 7 - const slug = 'privacy'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/es/terms.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'es'; 7 - const slug = 'terms'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/fr/about.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'fr'; 7 - const slug = 'about'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/fr/app.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'fr'; 7 - const slug = 'app'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/fr/beta.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'fr'; 7 - const slug = 'beta'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/fr/contact.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'fr'; 7 - const slug = 'contact'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/fr/discord.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'fr'; 7 - const slug = 'discord'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-24
src/pages/fr/index.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import Base from '../../layouts/Base.astro'; 5 - import IndexContent from '../../components/pages/IndexContent.astro'; 6 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 7 - 8 - const locale = 'fr'; 9 - const t = useTranslations(locale); 10 - const translatePath = useTranslatedPath(locale); 11 - --- 12 - 13 - <Base lang={locale} title={t('index.title')} description={t('index.metaDescription')} canonicalUrl={"https://getorbyt.com/" + locale + "/"}> 14 - <div class="index-container"> 15 - <div class="main-content"> 16 - <IndexContent t={t} translatePath={translatePath} /> 17 - </div> 18 - </div> 19 - </Base> 20 - 21 - <style> 22 - .index-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 23 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 24 - </style>
-10
src/pages/fr/privacy.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'fr'; 7 - const slug = 'privacy'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/fr/terms.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'fr'; 7 - const slug = 'terms'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
+336 -55
src/pages/index.astro
··· 1 1 --- 2 2 export const prerender = true; 3 + --- 3 4 4 - import Base from '../layouts/Base.astro'; 5 - import IndexContent from '../components/pages/IndexContent.astro'; 6 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 5 + <!doctype html> 6 + <html lang="en"> 7 + <head> 8 + <meta name="google-site-verification" content="4xaimUg28uLCqKwsPNIz6QeG_tfnLlAdHgS5bC_90JU"/> 9 + <meta charset="utf-8"> 10 + <title>orbyt - video communities</title> 11 + <meta name="description" content="a new video app for bluesky"> 12 + <meta name="viewport" content="width=device-width, initial-scale=1"> 13 + 14 + <!-- Critical: Inline font-face for immediate availability (no network delay) --> 15 + <style> 16 + @font-face { 17 + font-family: 'Figtree'; 18 + src: url('/fonts/Figtree/Figtree-VariableFont_wght.woff2') format('woff2'); 19 + font-weight: 300 900; 20 + font-style: normal; 21 + font-display: fallback; /* Short block window, then swap - prevents FOUT for logo */ 22 + } 23 + </style> 24 + <!-- Preload Figtree font to start download early --> 25 + <link rel="preload" href="/fonts/Figtree/Figtree-VariableFont_wght.woff2" as="font" type="font/woff2" crossorigin> 7 26 8 - const lang = 'en'; 9 - const t = useTranslations(lang); 10 - const translatePath = useTranslatedPath(lang); 11 - --- 27 + <meta property="og:title" content="orbyt - video communities"/> 28 + <meta property="og:description" content="a new video app for bluesky"/> 29 + <meta property="og:type" content="website"/> 30 + <meta property="og:url" content="https://getorbyt.com/"/> 31 + <meta property="og:image" content="https://getorbyt.com/images/orbyt-logotype.png"/> 32 + <meta property="og:image:type" content="image/png"/> 12 33 13 - <Base 14 - lang={lang} 15 - title={t('index.title')} 16 - description={t('index.metaDescription')} 17 - canonicalUrl="https://getorbyt.com/" 18 - > 19 - <Fragment slot="head"> 20 - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" /> 21 - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" /> 22 - <script type="application/ld+json" is:inline set:html={JSON.stringify({ 34 + <!-- Twitter --> 35 + <meta name="twitter:card" content="summary_large_image"/> 36 + <meta name="twitter:title" content="orbyt - video communities"/> 37 + <meta name="twitter:description" content="a new video app for bluesky"/> 38 + <meta name="twitter:image" content="https://getorbyt.com/images/orbyt-logotype.png"/> 39 + 40 + <!-- Structured Data for SEO --> 41 + <script type="application/ld+json" is:inline> 42 + { 23 43 "@context": "https://schema.org", 24 44 "@type": "Organization", 25 45 "name": "orbyt", 26 46 "url": "https://getorbyt.com", 27 47 "logo": "https://getorbyt.com/images/orbyt-logotype.png", 28 - "description": t('index.metaDescription'), 29 - "sameAs": ["https://bsky.app/profile/getorbyt.com", "https://community.getorbyt.com"] 30 - })} /> 31 - <script type="application/ld+json" is:inline set:html={JSON.stringify({ 48 + "description": "a new video app for bluesky", 49 + "sameAs": [ 50 + "https://bsky.app/profile/getorbyt.com", 51 + "https://community.getorbyt.com" 52 + ] 53 + } 54 + </script> 55 + <script type="application/ld+json" is:inline> 56 + { 32 57 "@context": "https://schema.org", 33 58 "@type": "WebSite", 34 59 "name": "orbyt", 35 60 "url": "https://getorbyt.com", 36 - "description": t('index.metaDescription'), 61 + "description": "a new video app for bluesky", 37 62 "potentialAction": { 38 63 "@type": "SearchAction", 39 - "target": { "@type": "EntryPoint", "urlTemplate": "https://getorbyt.com/@{search_term_string}" }, 64 + "target": { 65 + "@type": "EntryPoint", 66 + "urlTemplate": "https://getorbyt.com/@{search_term_string}" 67 + }, 40 68 "query-input": "required name=search_term_string" 41 69 } 42 - })} /> 43 - </Fragment> 70 + } 71 + </script> 72 + 73 + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"> 74 + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css"> 75 + 76 + <!-- Favicon setup --> 77 + <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"> 78 + <link rel="icon" type="image/x-icon" href="/favicon/favicon.ico"> 79 + <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"> 80 + <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"> 81 + <link rel="manifest" href="/favicon/site.webmanifest"> 82 + 83 + <style> 84 + a:hover { 85 + color: #f3f5fe; 86 + } 87 + 88 + html, body { 89 + margin: 0; 90 + padding: 0; 91 + height: 100%; 92 + min-height: 100vh; 93 + } 94 + 95 + body { 96 + --bg-color: #000; 97 + --fg-color: #f3f5fe; 98 + background: var(--bg-color); 99 + font-family: 'Figtree', sans-serif; 100 + position: relative; 101 + display: flex; 102 + align-items: center; 103 + justify-content: center; 104 + } 105 + 106 + .container { 107 + position: relative; 108 + width: 100%; 109 + max-width: 800px; 110 + padding: 20px; 111 + min-height: 100vh; 112 + display: flex; 113 + flex-direction: column; 114 + } 115 + 116 + .main-content { 117 + flex: 1; 118 + display: flex; 119 + flex-direction: column; 120 + justify-content: center; 121 + } 122 + 123 + .download-button { 124 + background: #551DEF; 125 + border: 2px solid #000000; 126 + border-radius: 30px; 127 + height: 60px; 128 + font-weight: bold; 129 + font-size: 23px; 130 + line-height: 28px; 131 + display: flex; 132 + align-items: center; 133 + justify-content: center; 134 + text-align: center; 135 + text-decoration: none; 136 + color: #E0EBFF; 137 + max-width: 500px; 138 + margin: 0 auto 10px auto; 139 + } 140 + 141 + .row.first { 142 + margin-top: 0; 143 + margin-bottom: 0; 144 + } 145 + 146 + .row.buttons { 147 + text-align: center; 148 + margin-top: 40px; 149 + } 150 + 151 + .logo { 152 + text-align: center; 153 + } 154 + 155 + .logo-icon { 156 + width: 160px; 157 + height: auto; 158 + display: block; 159 + margin: 0 auto 0px auto; 160 + color: var(--fg-color); 161 + } 162 + 163 + .logo-icon .logo-bg { 164 + fill: var(--bg-color); 165 + } 166 + 167 + .logo-icon .logo-fg { 168 + fill: var(--fg-color); 169 + stroke: var(--fg-color); 170 + } 171 + 172 + .logo-icon .logo-outline { 173 + fill: none; 174 + } 175 + 176 + .logo-text { 177 + font-family: 'Figtree', sans-serif; 178 + font-weight: 900; 179 + font-size: 48px; 180 + color: var(--fg-color); 181 + text-align: center; 182 + text-box-trim: trim-start; 183 + text-box-edge: cap alphabetic; 184 + display: block; 185 + margin: 0 auto; 186 + } 187 + 188 + .motto { 189 + margin-top: 10px; 190 + margin-bottom: 0; 191 + } 192 + 193 + .motto div { 194 + text-align: center; 195 + } 196 + 197 + .motto-text { 198 + font-family: 'Figtree', sans-serif; 199 + font-weight: 500; 200 + font-size: 28px; 201 + color: #01f5b3; 202 + text-align: center; 203 + display: block; 204 + margin: 20px auto; 205 + letter-spacing: -0.02em; 206 + } 207 + 208 + .separator { 209 + height: 7px; 210 + margin-bottom: 20px; 211 + position: relative; 212 + overflow: hidden; 213 + display: flex; 214 + align-items: center; 215 + justify-content: flex-start; 216 + width: 100%; 217 + } 218 + 219 + .separator svg { 220 + width: 100%; 221 + height: 7px; 222 + } 223 + 224 + .footer { 225 + display: flex; 226 + flex-wrap: wrap; 227 + justify-content: flex-start; 228 + gap: 12px 32px; 229 + margin-top: auto; 230 + margin-bottom: 40px; 231 + } 232 + 233 + .footer a { 234 + font-style: normal; 235 + font-weight: bold; 236 + font-size: 13px; 237 + text-align: left; 238 + letter-spacing: 0.2em; 239 + text-transform: uppercase; 240 + text-decoration: none; 241 + color: #ccc; 242 + } 243 + 244 + .footer a:hover { 245 + color: #f3f5fe; 246 + } 247 + 248 + @media (max-width: 500px) { 249 + .row.first { 250 + margin-top: 0; 251 + } 252 + 253 + body { 254 + background: #000; 255 + } 256 + 257 + .motto { 258 + margin-top: 10px; 259 + margin-bottom: 0; 260 + } 261 + 262 + .motto-text { 263 + font-size: 22px; 264 + } 265 + 266 + .row.buttons { 267 + margin-top: 30px; 268 + } 269 + 270 + .logo-icon { 271 + width: 115px; 272 + height: auto; 273 + } 274 + 275 + .logo-text { 276 + font-size: 36px; 277 + } 278 + 279 + .download-button { 280 + height: 50px; 281 + font-size: 20px; 282 + margin-bottom: 15px; 283 + } 284 + 285 + } 286 + 287 + @media (min-width: 1500px) { 288 + body { 289 + background-position-y: -100px; 290 + } 291 + } 292 + 293 + @media (min-width: 1800px) { 294 + body { 295 + background-position-y: -200px; 296 + } 297 + } 298 + </style> 299 + </head> 300 + 301 + <body> 302 + <div class="container"> 303 + <div class="main-content"> 304 + <div class="row first"> 305 + <div class="logo twelve columns"> 306 + <svg class="logo-icon" xmlns="http://www.w3.org/2000/svg" viewBox="65 65 894 894"> 307 + <defs> 308 + <!-- Mask: white = visible, black = fully transparent --> 309 + <mask id="cutout-mask"> 310 + <!-- Start with full white rectangle (everything visible) --> 311 + <rect x="65" y="65" width="894" height="894" fill="white"/> 312 + <!-- Cut out the body shape with evenodd so screen stays visible --> 313 + <path fill="black" fill-rule="evenodd" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3m249.3 243.2C752 381.3 773.8 399.3 781.1 523c5.8 98.9-9.4 168.3-41.5 189.8-59.5 39.8-394.7 39.8-454.2 0-32.1-21.5-47.3-90.9-41.5-189.8 7.7-130.5 28.8-144.8 226.1-152.4 19.3-.8 104.3.5 128.3 1.9Z"/> 314 + <!-- Cut out the eyes --> 315 + <rect fill="black" x="372" y="482" width="82" height="136" rx="41" ry="41"/> 316 + <rect fill="black" x="570" y="482" width="82" height="136" rx="41" ry="41"/> 317 + </mask> 318 + </defs> 319 + <!-- White outline with cutout mask applied --> 320 + <path class="logo-fg" mask="url(#cutout-mask)" stroke-width="65" stroke-linejoin="round" stroke-linecap="round" d="M349 129.3c-23.9 5.4-36.8 31.5-26.1 53.2 8 16.2 25.2 24.4 42.1 20.1 7.4-1.9 5.3-3.4 18.5 13.9 10.3 13.6 33.6 44.2 36.7 48.4 3.5 4.6 3.5 4.6-1.1 8.9-5.9 5.6-14.1 16.9-19 26-5 9.4-3.4 8.6-22.1 10.2-136.2 11.1-177 34.7-192.6 111.5-16.5 81.2-16.5 193.6.1 276.7 10.5 52.9 39.8 83.7 89.3 93.9 6 1.3 6 1.3 2.5 8.8-18.4 39.4-5.4 73.1 30 78.1 24.5 3.5 32.3-1.8 73.7-50 20.8-24.3 16.8-22.1 37-20.5 50.9 4.1 138.2 4.1 189 0 20.2-1.6 16.2-3.8 37 20.5 41.4 48.2 49.2 53.5 73.7 50 35.4-5 48.4-38.7 30-78.1-3.5-7.5-3.5-7.5 2.5-8.8 49.5-10.2 78.8-41 89.3-93.9 16.6-83.1 16.6-195.5.1-276.7C824 344.7 783.2 321.1 647 310c-18.7-1.6-17.1-.8-22.1-10.2-4.9-9.1-13.1-20.4-19-26-4.6-4.3-4.6-4.3-1.1-8.9 3.1-4.2 26.4-34.8 36.7-48.4 13.2-17.3 11.1-15.8 18.5-13.9 41.5 10.6 64.2-49.2 26.3-69.3-32-17.1-68 17.2-51.7 49.3 3.1 6.1 3.1 6.1-11.8 25.5-8.2 10.7-20 26-26.1 34.1-11.1 14.8-11.1 14.8-14.7 12.6-44.5-26.5-94.5-26.5-139 0-3.6 2.2-3.6 2.2-14.7-12.6-6.1-8.1-17.9-23.4-26.1-34.1-14.9-19.4-14.9-19.4-11.8-25.5 13.8-27.1-11.7-59.8-41.4-53.3Z"/> 321 + </svg> 322 + <div class="logo-text">orbyt</div> 323 + </div> 324 + </div> 325 + <div class="row motto"> 326 + <div class="twelve columns"> 327 + <div class="motto-text">video communities</div> 328 + </div> 329 + </div> 330 + <div class="row buttons"> 331 + <a href="https://apps.apple.com/us/app/orbyt-video/id6751679299" class="download-button">iOS</a> 332 + <a href="/beta" class="download-button">Android Waitlist</a> 333 + </div> 334 + </div> 44 335 45 - <div class="container"> 46 - <div class="main-content"> 47 - <IndexContent t={t} translatePath={translatePath} /> 336 + <footer class="footer"> 337 + <div class="separator"> 338 + <svg height="7" fill="none" xmlns="http://www.w3.org/2000/svg"> 339 + <defs> 340 + <pattern id="squigglePattern" width="374" height="7" patternUnits="userSpaceOnUse"> 341 + <path d="M0 1C5.50021 1 5.50021 5.84456 11.0004 5.84456C16.5006 5.84456 16.5006 1 22.0008 1C27.501 1 27.501 5.84456 33.0013 5.84456C38.5015 5.84456 38.5015 1 44.0017 1C49.5019 1 49.5019 5.84456 55.0021 5.84456C60.5023 5.84456 60.5023 1 66.0025 1C71.5027 1 71.5027 5.84456 77.0029 5.84456C82.5031 5.84456 82.5031 1 87.9998 1C93.5 1 93.5 5.84456 98.9967 5.84456C104.497 5.84456 104.497 1 109.994 1C115.494 1 115.494 5.84456 120.99 5.84456C126.491 5.84456 126.491 1 131.991 1C137.491 1 137.491 5.84456 142.991 5.84456C148.491 5.84456 148.491 1 153.992 1C159.492 1 159.492 5.84456 164.988 5.84456C170.489 5.84456 170.489 1 175.985 1C181.486 1 181.486 5.84456 186.986 5.84456C192.486 5.84456 192.486 1 197.986 1C203.486 1 203.486 5.84456 208.987 5.84456C214.487 5.84456 214.487 1 219.987 1C225.487 1 225.487 5.84456 230.987 5.84456C236.488 5.84456 236.488 1 241.988 1C247.488 1 247.488 5.84456 252.988 5.84456C258.488 5.84456 258.488 1 263.989 1C269.489 1 269.489 5.84456 274.989 5.84456C280.489 5.84456 280.489 1 285.99 1C291.49 1 291.49 5.84456 296.99 5.84456C302.49 5.84456 302.49 1 307.99 1C313.491 1 313.491 5.84456 318.991 5.84456C324.491 5.84456 324.491 1 329.991 1C335.491 1 335.491 5.84456 340.992 5.84456C346.492 5.84456 346.492 1 351.992 1C357.492 1 357.492 5.84456 362.996 5.84456C368.5 5.84456 368.496 1 374 1" stroke="#551DEF" stroke-width="2" stroke-miterlimit="10"/> 342 + </pattern> 343 + </defs> 344 + <rect fill="url(#squigglePattern)" width="100%" height="100%"/> 345 + </svg> 346 + </div> 347 + <a href="/about">about</a> 348 + <a href="https://bsky.app/profile/getorbyt.com">bluesky</a> 349 + <a href="https://community.getorbyt.com">community</a> 350 + <a href="/terms">terms</a> 351 + <a href="/privacy">privacy</a> 352 + <a href="/contact">contact</a> 353 + </footer> 48 354 </div> 49 - </div> 50 - </Base> 51 - 52 - <style> 53 - a:hover { color: #f3f5fe; } 54 - html, body { margin: 0; padding: 0; height: 100%; min-height: 100vh; } 55 - body { --bg-color: #000; --fg-color: #f3f5fe; background: var(--bg-color); font-family: 'Figtree', sans-serif; display: flex; align-items: center; justify-content: center; } 56 - .container { position: relative; width: 100%; max-width: 800px; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 57 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 58 - .row.first { margin-top: 0; margin-bottom: 0; } 59 - .logo { text-align: center; } 60 - .logo-icon { width: 160px; height: auto; display: block; margin: 0 auto; color: var(--fg-color); } 61 - .logo-icon .logo-bg { fill: var(--bg-color); } 62 - .logo-icon .logo-fg { fill: var(--fg-color); stroke: var(--fg-color); } 63 - .logo-text { font-family: 'Figtree', sans-serif; font-weight: 900; font-size: 48px; color: var(--fg-color); text-align: center; display: block; margin: 0 auto; } 64 - .motto { margin-top: 10px; margin-bottom: 0; } 65 - .motto div { text-align: center; } 66 - .motto-text { font-family: 'Figtree', sans-serif; font-weight: 500; font-size: 28px; color: #01f5b3; text-align: center; display: block; margin: 20px auto; } 67 - .row.buttons { text-align: center; margin-top: 40px; } 68 - .download-button { background: #551DEF; border: 2px solid #000; border-radius: 30px; height: 60px; font-weight: bold; font-size: 23px; display: flex; align-items: center; justify-content: center; text-decoration: none; color: #E0EBFF; max-width: 500px; margin: 0 auto 10px auto; } 69 - @media (max-width: 500px) { 70 - .motto-text { font-size: 22px; } 71 - .logo-icon { width: 115px; } 72 - .logo-text { font-size: 36px; } 73 - .download-button { height: 50px; font-size: 20px; margin-bottom: 15px; } 74 - } 75 - </style> 355 + </body> 356 + </html>
-10
src/pages/ja/about.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ja'; 7 - const slug = 'about'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ja/app.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ja'; 7 - const slug = 'app'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ja/beta.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ja'; 7 - const slug = 'beta'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ja/contact.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ja'; 7 - const slug = 'contact'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ja/discord.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ja'; 7 - const slug = 'discord'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-24
src/pages/ja/index.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import Base from '../../layouts/Base.astro'; 5 - import IndexContent from '../../components/pages/IndexContent.astro'; 6 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 7 - 8 - const locale = 'ja'; 9 - const t = useTranslations(locale); 10 - const translatePath = useTranslatedPath(locale); 11 - --- 12 - 13 - <Base lang={locale} title={t('index.title')} description={t('index.metaDescription')} canonicalUrl={"https://getorbyt.com/" + locale + "/"}> 14 - <div class="index-container"> 15 - <div class="main-content"> 16 - <IndexContent t={t} translatePath={translatePath} /> 17 - </div> 18 - </div> 19 - </Base> 20 - 21 - <style> 22 - .index-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 23 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 24 - </style>
-10
src/pages/ja/privacy.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ja'; 7 - const slug = 'privacy'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ja/terms.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ja'; 7 - const slug = 'terms'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ko/about.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ko'; 7 - const slug = 'about'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ko/app.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ko'; 7 - const slug = 'app'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ko/beta.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ko'; 7 - const slug = 'beta'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ko/contact.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ko'; 7 - const slug = 'contact'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ko/discord.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ko'; 7 - const slug = 'discord'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-24
src/pages/ko/index.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import Base from '../../layouts/Base.astro'; 5 - import IndexContent from '../../components/pages/IndexContent.astro'; 6 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 7 - 8 - const locale = 'ko'; 9 - const t = useTranslations(locale); 10 - const translatePath = useTranslatedPath(locale); 11 - --- 12 - 13 - <Base lang={locale} title={t('index.title')} description={t('index.metaDescription')} canonicalUrl={"https://getorbyt.com/" + locale + "/"}> 14 - <div class="index-container"> 15 - <div class="main-content"> 16 - <IndexContent t={t} translatePath={translatePath} /> 17 - </div> 18 - </div> 19 - </Base> 20 - 21 - <style> 22 - .index-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 23 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 24 - </style>
-10
src/pages/ko/privacy.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ko'; 7 - const slug = 'privacy'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/ko/terms.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'ko'; 7 - const slug = 'terms'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
+236 -36
src/pages/privacy.astro
··· 1 1 --- 2 2 export const prerender = true; 3 + --- 4 + <!DOCTYPE html> 5 + <html lang="en"> 6 + <head> 7 + <meta charset="UTF-8"> 8 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 9 + <title>Privacy Policy - Orbyt</title> 10 + 11 + <!-- Critical: Inline font-face for immediate availability (no network delay) --> 12 + <style> 13 + @font-face { 14 + font-family: 'Figtree'; 15 + src: url('/fonts/Figtree/Figtree-VariableFont_wght.woff2') format('woff2'); 16 + font-weight: 300 900; 17 + font-style: normal; 18 + font-display: fallback; /* Short block window, then swap - prevents FOUT */ 19 + } 20 + </style> 21 + <!-- Preload Figtree font to start download early --> 22 + <link rel="preload" href="/fonts/Figtree/Figtree-VariableFont_wght.woff2" as="font" type="font/woff2" crossorigin> 23 + 24 + <!-- Favicon setup --> 25 + <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"> 26 + <link rel="icon" type="image/x-icon" href="/favicon/favicon.ico"> 27 + <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"> 28 + <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"> 29 + <link rel="manifest" href="/favicon/site.webmanifest"> 30 + 31 + <!-- Theme colors for mobile browsers --> 32 + <meta name="theme-color" content="#f3f5fe"> 33 + <meta name="msapplication-TileColor" content="#f3f5fe"> 34 + <style> 35 + * { 36 + margin: 0; 37 + padding: 0; 38 + box-sizing: border-box; 39 + } 3 40 4 - import Base from '../layouts/Base.astro'; 5 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 41 + body { 42 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 43 + background: #000000; 44 + min-height: 100vh; 45 + color: #f3f5fe; 46 + position: relative; 47 + overflow-x: hidden; 48 + overflow-y: auto; 49 + } 6 50 7 - const lang = 'en'; 8 - const t = useTranslations(lang); 9 - const translatePath = useTranslatedPath(lang); 10 - --- 11 - <Base lang={lang} title={t('privacy.title')} canonicalUrl="https://getorbyt.com/privacy/"> 12 - <div class="container"> 13 - <div class="header"> 14 - <a href={translatePath('/')} class="logo"> 15 - <img src="/images/orbyt-logo.svg" alt={t('common.orbytAlt')} /> 16 - </a> 17 - <h1>{t('privacy.title').replace(' - Orbyt', '')}</h1> 18 - <p class="subtitle">{t('privacy.lastUpdated')}</p> 19 - </div> 20 - <div class="content"> 51 + .container { 52 + max-width: 800px; 53 + margin: 0 auto; 54 + padding: 2rem; 55 + min-height: 100vh; 56 + } 57 + 58 + .header { 59 + text-align: left; 60 + margin-bottom: 1.5rem; 61 + padding-bottom: 2rem; 62 + } 63 + 64 + .logo { 65 + display: block; 66 + margin-bottom: 2rem; 67 + } 68 + 69 + .logo img { 70 + width: 80px; 71 + height: 80px; 72 + } 73 + 74 + h1 { 75 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 76 + font-size: 3rem; 77 + font-weight: 900; 78 + color: #f3f5fe; 79 + margin-bottom: 1rem; 80 + } 81 + 82 + .subtitle { 83 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 84 + font-size: 0.9rem; 85 + color: #cfd6e8; 86 + opacity: 0.8; 87 + } 88 + 89 + .content { 90 + line-height: 1.6; 91 + } 92 + 93 + .content h2 { 94 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 95 + font-size: 1.8rem; 96 + color: #f3f5fe; 97 + margin: 2rem 0 1rem 0; 98 + font-weight: 900; 99 + } 100 + 101 + .content h3 { 102 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 103 + font-size: 1.4rem; 104 + color: #f3f5fe; 105 + margin: 1.5rem 0 0.5rem 0; 106 + font-weight: 900; 107 + } 108 + 109 + .content p { 110 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 111 + font-size: 1rem; 112 + color: #cfd6e8; 113 + margin-bottom: 1rem; 114 + } 115 + 116 + .content ul { 117 + margin: 1rem 0; 118 + padding-left: 2rem; 119 + } 120 + 121 + .content li { 122 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 123 + font-size: 1rem; 124 + color: #cfd6e8; 125 + margin-bottom: 0.5rem; 126 + } 127 + 128 + .content strong { 129 + color: #f3f5fe; 130 + font-weight: 600; 131 + } 132 + 133 + .content a { 134 + color: #01f5b3; 135 + text-decoration: none; 136 + transition: color 0.3s ease; 137 + } 138 + 139 + .content a:hover { 140 + color: #01f5b3; 141 + } 142 + 143 + .separator { 144 + height: 7px; 145 + margin-bottom: 20px; 146 + position: relative; 147 + overflow: hidden; 148 + display: flex; 149 + align-items: center; 150 + justify-content: flex-start; 151 + width: 100%; 152 + } 153 + 154 + .separator svg { 155 + width: 100%; 156 + height: 7px; 157 + } 158 + 159 + .footer { 160 + display: flex; 161 + flex-wrap: wrap; 162 + justify-content: flex-start; 163 + gap: 12px 32px; 164 + margin-top: 80px; 165 + margin-bottom: 40px; 166 + } 167 + 168 + .footer a { 169 + font-style: normal; 170 + font-weight: bold; 171 + font-size: 13px; 172 + text-align: left; 173 + letter-spacing: 0.2em; 174 + text-transform: uppercase; 175 + text-decoration: none; 176 + color: #ccc; 177 + } 178 + 179 + .footer a:hover { 180 + color: #f3f5fe; 181 + } 182 + 183 + @media (max-width: 768px) { 184 + .container { 185 + padding: 1rem; 186 + } 187 + 188 + h1 { 189 + font-size: 2.5rem; 190 + } 191 + 192 + .content h2 { 193 + font-size: 1.5rem; 194 + } 195 + 196 + .content h3 { 197 + font-size: 1.2rem; 198 + } 199 + 200 + } 201 + </style> 202 + </head> 203 + <body> 204 + 205 + 206 + <div class="container"> 207 + <div class="header"> 208 + <a href="/" class="logo"> 209 + <img src="/images/orbyt-logo.svg" alt="Orbyt"> 210 + </a> 211 + <h1>Privacy Policy</h1> 212 + <p class="subtitle">Last Updated: February 2, 2026</p> 213 + </div> 214 + 215 + <div class="content"> 21 216 <p>This Privacy Policy describes how Orbyt ("we", "us", "our") collects, uses, and shares information when you use our mobile application ("Orbyt" or "the App").</p> 22 217 23 218 <p><strong>Important:</strong> Orbyt is a third-party client application that connects to the Bluesky network via the AT Protocol. When you use Orbyt, your data is shared with the decentralized AT Protocol network. You should also review <a href="https://bsky.social/about/support/privacy-policy" target="_blank" rel="noopener">Bluesky's Privacy Policy</a> to understand how your data is handled on their network.</p> ··· 112 307 <ul> 113 308 <li><strong>Local Data</strong>: Use "Clear Cache" in app settings, or delete and reinstall the app</li> 114 309 <li><strong>Account Removal</strong>: Use "Remove Account" in app settings to clear all local account data</li> 115 - <li><strong>Server Data</strong>: <a href={translatePath('/contact')}>Contact us</a> to request deletion of your server-side data</li> 310 + <li><strong>Server Data</strong>: <a href="/contact">Contact us</a> to request deletion of your server-side data</li> 116 311 </ul> 117 312 118 313 <h3>Limitations on Deletion (AT Protocol)</h3> ··· 144 339 <li>Deletion of your information</li> 145 340 <li>Data portability</li> 146 341 </ul> 147 - <p>To exercise these rights, <a href={translatePath('/contact')}>contact us</a>.</p> 342 + <p>To exercise these rights, <a href="/contact">contact us</a>.</p> 148 343 149 344 <h2>Changes to This Policy</h2> 150 345 <p>We may update this Privacy Policy from time to time. We will notify you of significant changes through the app or by updating the "Last Updated" date above.</p> 151 346 152 347 <h2>Contact Us</h2> 153 - <p>If you have questions about this Privacy Policy, visit our <a href={translatePath('/contact')}>contact page</a>.</p> 348 + <p>If you have questions about this Privacy Policy, visit our <a href="/contact">contact page</a>.</p> 349 + </div> 350 + 351 + <footer class="footer"> 352 + <div class="separator"> 353 + <svg height="7" fill="none" xmlns="http://www.w3.org/2000/svg"> 354 + <defs> 355 + <pattern id="squigglePattern" width="374" height="7" patternUnits="userSpaceOnUse"> 356 + <path d="M0 1C5.50021 1 5.50021 5.84456 11.0004 5.84456C16.5006 5.84456 16.5006 1 22.0008 1C27.501 1 27.501 5.84456 33.0013 5.84456C38.5015 5.84456 38.5015 1 44.0017 1C49.5019 1 49.5019 5.84456 55.0021 5.84456C60.5023 5.84456 60.5023 1 66.0025 1C71.5027 1 71.5027 5.84456 77.0029 5.84456C82.5031 5.84456 82.5031 1 87.9998 1C93.5 1 93.5 5.84456 98.9967 5.84456C104.497 5.84456 104.497 1 109.994 1C115.494 1 115.494 5.84456 120.99 5.84456C126.491 5.84456 126.491 1 131.991 1C137.491 1 137.491 5.84456 142.991 5.84456C148.491 5.84456 148.491 1 153.992 1C159.492 1 159.492 5.84456 164.988 5.84456C170.489 5.84456 170.489 1 175.985 1C181.486 1 181.486 5.84456 186.986 5.84456C192.486 5.84456 192.486 1 197.986 1C203.486 1 203.486 5.84456 208.987 5.84456C214.487 5.84456 214.487 1 219.987 1C225.487 1 225.487 5.84456 230.987 5.84456C236.488 5.84456 236.488 1 241.988 1C247.488 1 247.488 5.84456 252.988 5.84456C258.488 5.84456 258.488 1 263.989 1C269.489 1 269.489 5.84456 274.989 5.84456C280.489 5.84456 280.489 1 285.99 1C291.49 1 291.49 5.84456 296.99 5.84456C302.49 5.84456 302.49 1 307.99 1C313.491 1 313.491 5.84456 318.991 5.84456C324.491 5.84456 324.491 1 329.991 1C335.491 1 335.491 5.84456 340.992 5.84456C346.492 5.84456 346.492 1 351.992 1C357.492 1 357.492 5.84456 362.996 5.84456C368.5 5.84456 368.496 1 374 1" stroke="#551DEF" stroke-width="2" stroke-miterlimit="10"/> 357 + </pattern> 358 + </defs> 359 + <rect fill="url(#squigglePattern)" width="100%" height="100%"/> 360 + </svg> 361 + </div> 362 + <a href="/about">about</a> 363 + <a href="https://bsky.app/profile/getorbyt.com">bluesky</a> 364 + <a href="https://community.getorbyt.com">community</a> 365 + <a href="/terms">terms</a> 366 + <a href="/privacy">privacy</a> 367 + <a href="/contact">contact</a> 368 + </footer> 154 369 </div> 155 - </div> 156 - </Base> 157 370 158 - <style> 159 - .container { max-width: 800px; margin: 0 auto; padding: 2rem; min-height: 100vh; } 160 - .header { text-align: left; margin-bottom: 1.5rem; padding-bottom: 2rem; } 161 - .logo { display: block; margin-bottom: 2rem; } 162 - .logo img { width: 80px; height: 80px; } 163 - h1 { font-size: 3rem; font-weight: 900; color: #f3f5fe; margin-bottom: 1rem; } 164 - .subtitle { font-size: 0.9rem; color: #cfd6e8; opacity: 0.8; } 165 - .content h2 { font-size: 1.8rem; color: #f3f5fe; margin: 2rem 0 1rem 0; font-weight: 900; } 166 - .content h3 { font-size: 1.4rem; color: #f3f5fe; margin: 1.5rem 0 0.5rem 0; font-weight: 900; } 167 - .content p { font-size: 1rem; color: #cfd6e8; margin-bottom: 1rem; } 168 - .content ul { margin: 1rem 0; padding-left: 2rem; } 169 - .content li { font-size: 1rem; color: #cfd6e8; margin-bottom: 0.5rem; } 170 - .content strong { color: #f3f5fe; font-weight: 600; } 171 - .content a { color: #01f5b3; text-decoration: none; } 172 - </style> 371 + </body> 372 + </html>
-10
src/pages/pt-BR/about.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'pt-BR'; 7 - const slug = 'about'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/pt-BR/app.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'pt-BR'; 7 - const slug = 'app'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/pt-BR/beta.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'pt-BR'; 7 - const slug = 'beta'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/pt-BR/contact.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'pt-BR'; 7 - const slug = 'contact'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/pt-BR/discord.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'pt-BR'; 7 - const slug = 'discord'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-24
src/pages/pt-BR/index.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import Base from '../../layouts/Base.astro'; 5 - import IndexContent from '../../components/pages/IndexContent.astro'; 6 - import { useTranslations, useTranslatedPath } from '../../i18n/utils'; 7 - 8 - const locale = 'pt-BR'; 9 - const t = useTranslations(locale); 10 - const translatePath = useTranslatedPath(locale); 11 - --- 12 - 13 - <Base lang={locale} title={t('index.title')} description={t('index.metaDescription')} canonicalUrl={"https://getorbyt.com/" + locale + "/"}> 14 - <div class="index-container"> 15 - <div class="main-content"> 16 - <IndexContent t={t} translatePath={translatePath} /> 17 - </div> 18 - </div> 19 - </Base> 20 - 21 - <style> 22 - .index-container { max-width: 800px; margin: 0 auto; padding: 20px; min-height: 100vh; display: flex; flex-direction: column; } 23 - .main-content { flex: 1; display: flex; flex-direction: column; justify-content: center; } 24 - </style>
-10
src/pages/pt-BR/privacy.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'pt-BR'; 7 - const slug = 'privacy'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
-10
src/pages/pt-BR/terms.astro
··· 1 - --- 2 - export const prerender = true; 3 - 4 - import LocaleSlugPage from '../../components/LocaleSlugPage.astro'; 5 - 6 - const locale = 'pt-BR'; 7 - const slug = 'terms'; 8 - --- 9 - 10 - <LocaleSlugPage locale={locale} slug={slug} />
+18 -43
src/pages/sitemap.xml.ts
··· 1 1 import type { APIRoute } from 'astro'; 2 - import { useTranslatedPath } from '../i18n/utils'; 3 - import { locales, defaultLang } from '../i18n/ui'; 4 2 5 3 export const prerender = true; 6 4 7 5 export const GET: APIRoute = ({ site }) => { 8 6 const siteUrl = (typeof site === 'string' ? site : site?.href || 'https://getorbyt.com').replace(/\/$/, ''); 9 7 const today = new Date().toISOString().split('T')[0]; 10 - 11 - const routes = [ 12 - { path: '/', changefreq: 'weekly' as const, priority: '1.0' }, 13 - { path: '/app', changefreq: 'monthly' as const, priority: '0.8' }, 14 - { path: '/beta', changefreq: 'monthly' as const, priority: '0.8' }, 15 - { path: '/about', changefreq: 'monthly' as const, priority: '0.7' }, 16 - { path: '/contact', changefreq: 'monthly' as const, priority: '0.6' }, 17 - { path: '/discord', changefreq: 'monthly' as const, priority: '0.6' }, 18 - { path: '/privacy', changefreq: 'yearly' as const, priority: '0.4' }, 19 - { path: '/terms', changefreq: 'yearly' as const, priority: '0.4' }, 8 + 9 + const pages = [ 10 + { path: '/', changefreq: 'weekly', priority: '1.0' }, 11 + { path: '/app/', changefreq: 'monthly', priority: '0.8' }, 12 + { path: '/beta/', changefreq: 'monthly', priority: '0.8' }, 13 + { path: '/about/', changefreq: 'monthly', priority: '0.7' }, 14 + { path: '/contact/', changefreq: 'monthly', priority: '0.6' }, 15 + { path: '/discord/', changefreq: 'monthly', priority: '0.6' }, 16 + { path: '/privacy/', changefreq: 'yearly', priority: '0.4' }, 17 + { path: '/terms/', changefreq: 'yearly', priority: '0.4' }, 20 18 ]; 21 - 22 - const urlEntries: string[] = []; 23 - const translatePath = useTranslatedPath(defaultLang as 'en'); 24 - 25 - for (const route of routes) { 26 - const path = route.path === '/' ? '/' : route.path; 27 - const alternateUrls = locales.map((locale) => ({ 28 - locale, 29 - url: `${siteUrl}${translatePath(path, locale)}`, 30 - })); 31 - 32 - const defaultUrl = alternateUrls.find((a) => a.locale === defaultLang)!.url; 33 - const xhtmlLinks = [ 34 - ` <xhtml:link rel="alternate" hreflang="x-default" href="${defaultUrl}"/>`, 35 - ...alternateUrls.map((a) => ` <xhtml:link rel="alternate" hreflang="${a.locale}" href="${a.url}"/>`), 36 - ]; 37 - 38 - for (const { url } of alternateUrls) { 39 - urlEntries.push(` <url> 40 - <loc>${url}</loc> 19 + 20 + const sitemap = `<?xml version="1.0" encoding="UTF-8"?> 21 + <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 22 + ${pages.map(page => ` <url> 23 + <loc>${siteUrl}${page.path}</loc> 41 24 <lastmod>${today}</lastmod> 42 - <changefreq>${route.changefreq}</changefreq> 43 - <priority>${route.priority}</priority> 44 - ${xhtmlLinks.join('\n')} 45 - </url>`); 46 - } 47 - } 48 - 49 - const sitemap = `<?xml version="1.0" encoding="UTF-8"?> 50 - <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" 51 - xmlns:xhtml="http://www.w3.org/1999/xhtml"> 52 - ${urlEntries.join('\n')} 25 + <changefreq>${page.changefreq}</changefreq> 26 + <priority>${page.priority}</priority> 27 + </url>`).join('\n')} 53 28 </urlset>`; 54 29 55 30 return new Response(sitemap, {
+236 -36
src/pages/terms.astro
··· 1 1 --- 2 2 export const prerender = true; 3 + --- 4 + <!DOCTYPE html> 5 + <html lang="en"> 6 + <head> 7 + <meta charset="UTF-8"> 8 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 9 + <title>Terms of Use - Orbyt</title> 10 + 11 + <!-- Critical: Inline font-face for immediate availability (no network delay) --> 12 + <style> 13 + @font-face { 14 + font-family: 'Figtree'; 15 + src: url('/fonts/Figtree/Figtree-VariableFont_wght.woff2') format('woff2'); 16 + font-weight: 300 900; 17 + font-style: normal; 18 + font-display: fallback; /* Short block window, then swap - prevents FOUT */ 19 + } 20 + </style> 21 + <!-- Preload Figtree font to start download early --> 22 + <link rel="preload" href="/fonts/Figtree/Figtree-VariableFont_wght.woff2" as="font" type="font/woff2" crossorigin> 23 + 24 + <!-- Favicon setup --> 25 + <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"> 26 + <link rel="icon" type="image/x-icon" href="/favicon/favicon.ico"> 27 + <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"> 28 + <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"> 29 + <link rel="manifest" href="/favicon/site.webmanifest"> 30 + 31 + <!-- Theme colors for mobile browsers --> 32 + <meta name="theme-color" content="#f3f5fe"> 33 + <meta name="msapplication-TileColor" content="#f3f5fe"> 34 + <style> 35 + * { 36 + margin: 0; 37 + padding: 0; 38 + box-sizing: border-box; 39 + } 3 40 4 - import Base from '../layouts/Base.astro'; 5 - import { useTranslations, useTranslatedPath } from '../i18n/utils'; 41 + body { 42 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 43 + background: #000000; 44 + min-height: 100vh; 45 + color: #f3f5fe; 46 + position: relative; 47 + overflow-x: hidden; 48 + overflow-y: auto; 49 + } 6 50 7 - const lang = 'en'; 8 - const t = useTranslations(lang); 9 - const translatePath = useTranslatedPath(lang); 10 - --- 11 - <Base lang={lang} title={t('terms.title')} canonicalUrl="https://getorbyt.com/terms/"> 12 - <div class="container"> 13 - <div class="header"> 14 - <a href={translatePath('/')} class="logo"> 15 - <img src="/images/orbyt-logo.svg" alt={t('common.orbytAlt')} /> 16 - </a> 17 - <h1>{t('terms.title').replace(' - Orbyt', '')}</h1> 18 - <p class="subtitle">{t('terms.lastUpdated')}</p> 19 - </div> 20 - <div class="content"> 51 + .container { 52 + max-width: 800px; 53 + margin: 0 auto; 54 + padding: 2rem; 55 + min-height: 100vh; 56 + } 57 + 58 + .header { 59 + text-align: left; 60 + margin-bottom: 1.5rem; 61 + padding-bottom: 2rem; 62 + } 63 + 64 + .logo { 65 + display: block; 66 + margin-bottom: 2rem; 67 + } 68 + 69 + .logo img { 70 + width: 80px; 71 + height: 80px; 72 + } 73 + 74 + h1 { 75 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 76 + font-size: 3rem; 77 + font-weight: 900; 78 + color: #f3f5fe; 79 + margin-bottom: 1rem; 80 + } 81 + 82 + .subtitle { 83 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 84 + font-size: 0.9rem; 85 + color: #cfd6e8; 86 + opacity: 0.8; 87 + } 88 + 89 + .content { 90 + line-height: 1.6; 91 + } 92 + 93 + .content h2 { 94 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 95 + font-size: 1.8rem; 96 + color: #f3f5fe; 97 + margin: 2rem 0 1rem 0; 98 + font-weight: 900; 99 + } 100 + 101 + .content h3 { 102 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 103 + font-size: 1.4rem; 104 + color: #f3f5fe; 105 + margin: 1.5rem 0 0.5rem 0; 106 + font-weight: 900; 107 + } 108 + 109 + .content p { 110 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 111 + font-size: 1rem; 112 + color: #cfd6e8; 113 + margin-bottom: 1rem; 114 + } 115 + 116 + .content ul { 117 + margin: 1rem 0; 118 + padding-left: 2rem; 119 + } 120 + 121 + .content li { 122 + font-family: 'Figtree', -apple-system, BlinkMacSystemFont, sans-serif; 123 + font-size: 1rem; 124 + color: #cfd6e8; 125 + margin-bottom: 0.5rem; 126 + } 127 + 128 + .content strong { 129 + color: #f3f5fe; 130 + font-weight: 600; 131 + } 132 + 133 + .content a { 134 + color: #01f5b3; 135 + text-decoration: none; 136 + transition: color 0.3s ease; 137 + } 138 + 139 + .content a:hover { 140 + color: #01f5b3; 141 + } 142 + 143 + .separator { 144 + height: 7px; 145 + margin-bottom: 20px; 146 + position: relative; 147 + overflow: hidden; 148 + display: flex; 149 + align-items: center; 150 + justify-content: flex-start; 151 + width: 100%; 152 + } 153 + 154 + .separator svg { 155 + width: 100%; 156 + height: 7px; 157 + } 158 + 159 + .footer { 160 + display: flex; 161 + flex-wrap: wrap; 162 + justify-content: flex-start; 163 + gap: 12px 32px; 164 + margin-top: 80px; 165 + margin-bottom: 40px; 166 + } 167 + 168 + .footer a { 169 + font-style: normal; 170 + font-weight: bold; 171 + font-size: 13px; 172 + text-align: left; 173 + letter-spacing: 0.2em; 174 + text-transform: uppercase; 175 + text-decoration: none; 176 + color: #ccc; 177 + } 178 + 179 + .footer a:hover { 180 + color: #f3f5fe; 181 + } 182 + 183 + @media (max-width: 768px) { 184 + .container { 185 + padding: 1rem; 186 + } 187 + 188 + h1 { 189 + font-size: 2.5rem; 190 + } 191 + 192 + .content h2 { 193 + font-size: 1.5rem; 194 + } 195 + 196 + .content h3 { 197 + font-size: 1.2rem; 198 + } 199 + 200 + } 201 + </style> 202 + </head> 203 + <body> 204 + 205 + 206 + <div class="container"> 207 + <div class="header"> 208 + <a href="/" class="logo"> 209 + <img src="/images/orbyt-logo.svg" alt="Orbyt"> 210 + </a> 211 + <h1>Terms of Use</h1> 212 + <p class="subtitle">Last Updated: February 2, 2026</p> 213 + </div> 214 + 215 + <div class="content"> 21 216 <p>Welcome to Orbyt, a video social networking application! We're excited to have you on board as a user. Orbyt runs on the Authenticated Transfer Protocol ("AT Protocol"), a decentralized social networking protocol that supports many different kinds of services. These Terms of Service apply to the Orbyt service.</p> 22 217 23 218 <p>Before you dive into using Orbyt, please take a moment to carefully read these Terms. This document outlines our legal obligations to each other and they apply to your use of our services.</p> ··· 52 247 <p>To use Orbyt, you must be at least 13 years old and legally permitted to use Orbyt based on the laws in your country. If you are not yet an adult according to the laws of your country, your parent or legal guardian must read these Terms on your behalf. If you're a parent or legal guardian, and you allow your child (who meets the minimum age for your country) to use the services, then these terms also apply to you, and you're responsible for your child's activity on the services.</p> 53 248 54 249 <h3>Accounts</h3> 55 - <p>To use Orbyt, you must create an account ("Account"). Don't share your Account with others, because you're responsible for all activities under your Account, whether or not you know about them. If you're using Orbyt on behalf of a company or organization, you confirm that you have the authority to represent your company or organization. If you believe your Account created through Orbyt has been compromised, <a href={translatePath('/contact')}>report it to us</a>.</p> 250 + <p>To use Orbyt, you must create an account ("Account"). Don't share your Account with others, because you're responsible for all activities under your Account, whether or not you know about them. If you're using Orbyt on behalf of a company or organization, you confirm that you have the authority to represent your company or organization. If you believe your Account created through Orbyt has been compromised, <a href="/contact">report it to us</a>.</p> 56 251 57 252 <h2>User Content</h2> 58 253 ··· 105 300 </ul> 106 301 107 302 <h2>Appeals</h2> 108 - <p>You can appeal any enforcement decision from these Terms, including suspension or termination, by <a href={translatePath('/contact')}>contacting us</a>.</p> 303 + <p>You can appeal any enforcement decision from these Terms, including suspension or termination, by <a href="/contact">contacting us</a>.</p> 109 304 110 305 <h2>Updates</h2> 111 306 <p>We update our terms from time to time, and will either post the updated Terms or send other communications to let you know about any changes. If you keep using Orbyt, you agree to the updated Terms. If you don't agree, you must stop using Orbyt.</p> ··· 122 317 <p>These Terms and any action related thereto will be governed by the laws of the State of New Jersey, without regard to conflict of laws provisions.</p> 123 318 124 319 <h2>Contact Information</h2> 125 - <p>If you have any questions about these Terms or Orbyt, visit our <a href={translatePath('/contact')}>contact page</a>.</p> 320 + <p>If you have any questions about these Terms or Orbyt, visit our <a href="/contact">contact page</a>.</p> 321 + </div> 322 + 323 + <footer class="footer"> 324 + <div class="separator"> 325 + <svg height="7" fill="none" xmlns="http://www.w3.org/2000/svg"> 326 + <defs> 327 + <pattern id="squigglePattern" width="374" height="7" patternUnits="userSpaceOnUse"> 328 + <path d="M0 1C5.50021 1 5.50021 5.84456 11.0004 5.84456C16.5006 5.84456 16.5006 1 22.0008 1C27.501 1 27.501 5.84456 33.0013 5.84456C38.5015 5.84456 38.5015 1 44.0017 1C49.5019 1 49.5019 5.84456 55.0021 5.84456C60.5023 5.84456 60.5023 1 66.0025 1C71.5027 1 71.5027 5.84456 77.0029 5.84456C82.5031 5.84456 82.5031 1 87.9998 1C93.5 1 93.5 5.84456 98.9967 5.84456C104.497 5.84456 104.497 1 109.994 1C115.494 1 115.494 5.84456 120.99 5.84456C126.491 5.84456 126.491 1 131.991 1C137.491 1 137.491 5.84456 142.991 5.84456C148.491 5.84456 148.491 1 153.992 1C159.492 1 159.492 5.84456 164.988 5.84456C170.489 5.84456 170.489 1 175.985 1C181.486 1 181.486 5.84456 186.986 5.84456C192.486 5.84456 192.486 1 197.986 1C203.486 1 203.486 5.84456 208.987 5.84456C214.487 5.84456 214.487 1 219.987 1C225.487 1 225.487 5.84456 230.987 5.84456C236.488 5.84456 236.488 1 241.988 1C247.488 1 247.488 5.84456 252.988 5.84456C258.488 5.84456 258.488 1 263.989 1C269.489 1 269.489 5.84456 274.989 5.84456C280.489 5.84456 280.489 1 285.99 1C291.49 1 291.49 5.84456 296.99 5.84456C302.49 5.84456 302.49 1 307.99 1C313.491 1 313.491 5.84456 318.991 5.84456C324.491 5.84456 324.491 1 329.991 1C335.491 1 335.491 5.84456 340.992 5.84456C346.492 5.84456 346.492 1 351.992 1C357.492 1 357.492 5.84456 362.996 5.84456C368.5 5.84456 368.496 1 374 1" stroke="#551DEF" stroke-width="2" stroke-miterlimit="10"/> 329 + </pattern> 330 + </defs> 331 + <rect fill="url(#squigglePattern)" width="100%" height="100%"/> 332 + </svg> 333 + </div> 334 + <a href="/about">about</a> 335 + <a href="https://bsky.app/profile/getorbyt.com">bluesky</a> 336 + <a href="https://community.getorbyt.com">community</a> 337 + <a href="/terms">terms</a> 338 + <a href="/privacy">privacy</a> 339 + <a href="/contact">contact</a> 340 + </footer> 126 341 </div> 127 - </div> 128 - </Base> 129 342 130 - <style> 131 - .container { max-width: 800px; margin: 0 auto; padding: 2rem; min-height: 100vh; } 132 - .header { text-align: left; margin-bottom: 1.5rem; padding-bottom: 2rem; } 133 - .logo { display: block; margin-bottom: 2rem; } 134 - .logo img { width: 80px; height: 80px; } 135 - h1 { font-size: 3rem; font-weight: 900; color: #f3f5fe; margin-bottom: 1rem; } 136 - .subtitle { font-size: 0.9rem; color: #cfd6e8; opacity: 0.8; } 137 - .content h2 { font-size: 1.8rem; color: #f3f5fe; margin: 2rem 0 1rem 0; font-weight: 900; } 138 - .content h3 { font-size: 1.4rem; color: #f3f5fe; margin: 1.5rem 0 0.5rem 0; font-weight: 900; } 139 - .content p { font-size: 1rem; color: #cfd6e8; margin-bottom: 1rem; } 140 - .content ul { margin: 1rem 0; padding-left: 2rem; } 141 - .content li { font-size: 1rem; color: #cfd6e8; margin-bottom: 0.5rem; } 142 - .content strong { color: #f3f5fe; font-weight: 600; } 143 - .content a { color: #01f5b3; text-decoration: none; } 144 - </style> 343 + </body> 344 + </html>