anthemus.dex visitors previously landed on the Deviant wallet's Anthemus tab through an iframe. This makes anthemus.dex render the DEX itself: - AnthemusKiosk — full-screen ExchangePanel, no tabs / no wallet chrome. Session-optional; view-only when disconnected. Corner "Connect wallet" chip opens the full wallet in a new tab to sign in and cross back with ?panel=anthemus&view=exchange. Inline unlock is a later slice. - src/kiosk.ts::isKioskAnthemus() — trigger surface: `?kiosk=anthemus`, `window.__ANTHEMUS_KIOSK__=true`, `/bns/anthemus.dex/` path prefix, or hostname == anthemus.dex (native BCDN one day). - App.tsx short-circuits to <AnthemusKiosk /> before any wallet-boot state. Hooks-order is stable per session (trigger surface is constant). - main.tsx stamps `body.anthemus-kiosk`; anthemus.css neutralises the shell's mobile-first .phone / .screen-scroll frames in kiosk mode. - index.html gates the wallet's welcome splash EARLY (before it paints) and swaps the tab title to "Anthemus DEX" so kiosk visitors never see Deviant-wallet branding. - site/deviant/anthemus/index.html now redirects to ../wallet/?kiosk=anthemus so silentmode.st/deviant/anthemus/ also lands on the DEX (previously an iframe wrapper). Same JS bundle serves deviant.x/wallet/ (full shell) and anthemus.dex/ (kiosk) — the trigger is client-side. Deployed to bns/deviant.x/wallet/ and bns/deviant.x/anthemus/ (same dist, kiosk fires via path detection). Verified navigate.st/bns/anthemus.dex/ renders full-screen v4 layout, no wallet tabs, no topbar, no splash. deviant.x/wallet/ still boots the wallet normally. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
233 lines
12 KiB
HTML
233 lines
12 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||
<title>Deviant — Chipnet Beta</title>
|
||
<meta name="description" content="Non-custodial BCH two-tier token wallet — chipnet beta." />
|
||
<!-- Kiosk mode (anthemus.dex) — decide EARLY, before the splash renders, so
|
||
the DEX-only shell doesn't flash the wallet's welcome overlay. Same
|
||
trigger surface as src/kiosk.ts. -->
|
||
<script>
|
||
(function () {
|
||
try {
|
||
var p = new URLSearchParams(location.search);
|
||
var kioskAnthemus =
|
||
window.__ANTHEMUS_KIOSK__ === true ||
|
||
p.get('kiosk') === 'anthemus' ||
|
||
(location.pathname || '').indexOf('/bns/anthemus.dex/') !== -1 ||
|
||
location.hostname === 'anthemus.dex';
|
||
if (kioskAnthemus) {
|
||
window.__ANTHEMUS_KIOSK__ = true;
|
||
document.documentElement.classList.add('kiosk-anthemus');
|
||
document.title = 'Anthemus DEX';
|
||
}
|
||
} catch (e) { /* ignore */ }
|
||
})();
|
||
</script>
|
||
<style>
|
||
/* Kiosk mode — the wallet-branded splash is not shown at anthemus.dex.
|
||
The React kiosk renders directly under <div id="root">. */
|
||
html.kiosk-anthemus #splash { display: none !important; }
|
||
</style>
|
||
<!-- PWA: installable on Android/desktop Chrome; chipnet-only beta. -->
|
||
<link rel="manifest" href="/manifest.webmanifest" />
|
||
<meta name="theme-color" content="#02040a" />
|
||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||
<!-- iOS falls back to a generated icon for SVG apple-touch-icon (see BETA_TESTING.md). -->
|
||
<link rel="apple-touch-icon" href="/icon.svg" />
|
||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||
<meta name="apple-mobile-web-app-title" content="Deviant" />
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,500;0,9..144,600;1,9..144,300;1,9..144,400;1,9..144,500;1,9..144,600&family=JetBrains+Mono:wght@400;500&display=swap"
|
||
rel="stylesheet"
|
||
/>
|
||
<!--
|
||
CosmoGenesis loading splash (ADR-0038 surface copy). Inlined in <head> +
|
||
sibling of #root so it paints on the FIRST frame — before the JS bundle loads
|
||
(matters for Capacitor cold start). It is sized to the phone frame (matches
|
||
.phone in shell.css) and OVERLAYS the React app, which mounts into #root
|
||
underneath. By design it stays until the user taps/clicks (or presses Enter) —
|
||
it does NOT auto-dismiss on a timer.
|
||
-->
|
||
<style>
|
||
/* Backdrop: a solid deep-sky fill so there is no white flash before the
|
||
React stylesheet loads. The splash content itself lives in a phone-shaped
|
||
box centered within it. cursor:pointer hints that the screen is tappable. */
|
||
#splash {
|
||
position: fixed; inset: 0; z-index: 9999;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: #02040a;
|
||
opacity: 1; transition: opacity .55s ease;
|
||
cursor: pointer; -webkit-tap-highlight-color: transparent;
|
||
}
|
||
#splash.hide { opacity: 0; pointer-events: none; }
|
||
@media (min-width: 480px) { #splash { padding: 24px 0; } }
|
||
|
||
/* The phone-shaped screen — geometry mirrors .phone in shell.css. */
|
||
.splash-phone {
|
||
position: relative; box-sizing: border-box;
|
||
display: flex; flex-direction: column; justify-content: space-between;
|
||
width: 100%; max-width: 420px; height: 100vh; height: 100dvh;
|
||
padding: 2.4rem 1.6rem 3.4rem; overflow-y: auto;
|
||
background:
|
||
linear-gradient(180deg, rgba(19,26,42,.55), rgba(10,15,28,.55)),
|
||
radial-gradient(ellipse at top, #131a2a 0%, transparent 60%),
|
||
radial-gradient(ellipse at bottom right, #0a0f1c 0%, transparent 50%),
|
||
#050810;
|
||
color: #d6dde9;
|
||
border-left: 1px solid rgba(120,140,180,.16);
|
||
border-right: 1px solid rgba(120,140,180,.16);
|
||
scrollbar-width: none;
|
||
}
|
||
.splash-phone::-webkit-scrollbar { width: 0; }
|
||
@media (min-width: 480px) {
|
||
.splash-phone {
|
||
height: min(860px, calc(100vh - 48px));
|
||
border: 1px solid rgba(120,140,180,.16); border-radius: 30px;
|
||
}
|
||
}
|
||
/* Three stacked regions: title (top), main paragraph (middle), complementary
|
||
text (bottom). space-between on .splash-phone spaces them top/middle/bottom. */
|
||
.splash-top, .splash-mid, .splash-bottom { width: 100%; }
|
||
.splash-mid { text-align: center; }
|
||
.splash-wordmark {
|
||
font-family: 'Fraunces', Georgia, serif; font-weight: 500; font-size: 22px;
|
||
letter-spacing: -.01em; color: #f1f4fa; margin-bottom: 30px;
|
||
}
|
||
.splash-wordmark::after {
|
||
content: ""; display: inline-block; width: 5px; height: 5px; background: #d6ff3d;
|
||
border-radius: 50%; margin-left: 7px; vertical-align: middle; transform: translateY(-2px);
|
||
box-shadow: 0 0 6px rgba(214,255,61,.55), 0 0 14px rgba(214,255,61,.22);
|
||
}
|
||
.splash-label {
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 9.5px;
|
||
letter-spacing: .26em; text-transform: uppercase; color: #6a7488; margin-bottom: 22px;
|
||
}
|
||
.splash-opening {
|
||
font-family: 'Fraunces', Georgia, serif; font-weight: 300;
|
||
font-size: 18px; line-height: 1.55; letter-spacing: -.012em; color: #d6dde9;
|
||
margin: 0; text-align: center;
|
||
}
|
||
/* "Deviation" — highlighted inline within the running paragraph (upright;
|
||
emphasis via acid colour + weight, no italic). */
|
||
.splash-deviation {
|
||
color: #d6ff3d; font-weight: 500;
|
||
text-shadow: 0 0 4px rgba(214,255,61,.55), 0 0 14px rgba(214,255,61,.22), 0 0 32px rgba(214,255,61,.08);
|
||
}
|
||
/* Rotating word, rendered as a typewriter. The inline-block reserves a fixed
|
||
slot (min-width) so typing/deleting never reflows the surrounding text. The
|
||
::after is a blinking caret pinned to the end of the typed characters. */
|
||
.splash-rot {
|
||
display: inline-block; min-width: 12ch; text-align: center;
|
||
color: #f1f4fa;
|
||
}
|
||
.splash-rot::after {
|
||
content: ''; display: inline-block;
|
||
width: 1px; height: .95em; margin-left: 2px; vertical-align: -1px;
|
||
background: #d6ff3d; box-shadow: 0 0 6px rgba(214,255,61,.6);
|
||
animation: splashCaret 1s steps(1) infinite;
|
||
}
|
||
@keyframes splashCaret { 50% { opacity: 0; } }
|
||
.splash-response {
|
||
margin-top: 26px; margin-bottom: 0; position: relative; padding-left: 18px;
|
||
font-family: 'Fraunces', Georgia, serif; font-size: 15px; line-height: 1.55; color: #d6dde9;
|
||
}
|
||
.splash-response::before {
|
||
content: ""; position: absolute; left: 0; top: 5px; bottom: 5px; width: 2px;
|
||
background: #d6ff3d; box-shadow: 0 0 6px rgba(214,255,61,.55), 0 0 14px rgba(214,255,61,.22);
|
||
}
|
||
.splash-response strong { color: #f1f4fa; font-weight: 500; }
|
||
.splash-response em { font-style: normal; color: #f1f4fa; }
|
||
.splash-foot {
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 9.5px;
|
||
letter-spacing: .2em; text-transform: uppercase; color: #6a7488; margin-top: 28px;
|
||
}
|
||
/* Tap-to-continue hint, pinned to the bottom of the phone screen. */
|
||
.splash-hint {
|
||
position: absolute; left: 0; right: 0; bottom: 18px; text-align: center;
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 10px;
|
||
letter-spacing: .2em; text-transform: uppercase; color: #d6ff3d;
|
||
animation: splashPulse 1.8s ease-in-out infinite; pointer-events: none;
|
||
}
|
||
@keyframes splashPulse { 0%, 100% { opacity: .35; } 50% { opacity: .95; } }
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.splash-rot::after { animation: none; }
|
||
.splash-hint { animation: none; opacity: .7; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- CosmoGenesis loading splash — phone-framed; stays until the user taps. -->
|
||
<div id="splash" role="button" tabindex="0" aria-label="Tap to enter Deviant Wallet">
|
||
<div class="splash-phone">
|
||
<div class="splash-top">
|
||
<div class="splash-wordmark">Deviant</div>
|
||
<div class="splash-label">A response to a default trajectory</div>
|
||
</div>
|
||
<div class="splash-mid">
|
||
<p class="splash-opening">When the institutional, technical, economic, and rhetorical forces all point in the same direction, the outcome they point toward becomes the default trajectory. <span class="splash-deviation">Deviation</span> from the trajectory requires active resistance. In a <span class="splash-rot" id="splash-rot">captured</span> information environment with limited resistance capacity, the default trajectory is what occurs.</p>
|
||
</div>
|
||
<div class="splash-bottom">
|
||
<p class="splash-response"><strong>Deviant</strong> exists to enable that resistance — by giving users on-chain experience that <em>intermediation cannot mediate</em>.</p>
|
||
<div class="splash-foot">Chipnet Beta · CosmoGenesis · on Bitcoin Cash</div>
|
||
</div>
|
||
<div class="splash-hint">Tap anywhere to enter ›</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="root"></div>
|
||
<script>
|
||
// CosmoGenesis splash controller: typewriter the rotating word (captured ->
|
||
// compromised -> constrained) — type it out, hold, delete, type the next — and
|
||
// dismiss ONLY on a user gesture (tap/click or Enter/Space). The splash stays
|
||
// until the user acts; no timer auto-dismisses it. The React shell mounts
|
||
// underneath; tapping reveals it. window.__deviantSplash.dismiss() is exposed
|
||
// for completeness but is not auto-called.
|
||
(function () {
|
||
var splash = document.getElementById('splash');
|
||
if (!splash) { window.__deviantSplash = { dismiss: function () {} }; return; }
|
||
var rotTimer = null, rot = document.getElementById('splash-rot');
|
||
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||
if (rot && !reduce) {
|
||
var words = ['captured', 'compromised', 'constrained'];
|
||
var wi = 0, ci = 0, deleting = false;
|
||
var TYPE = 95, ERASE = 45, HOLD = 5000, GAP = 400;
|
||
rot.textContent = '';
|
||
function tick() {
|
||
var w = words[wi];
|
||
if (!deleting) {
|
||
ci++;
|
||
rot.textContent = w.slice(0, ci);
|
||
if (ci >= w.length) { deleting = true; rotTimer = setTimeout(tick, HOLD); return; }
|
||
rotTimer = setTimeout(tick, TYPE);
|
||
} else {
|
||
ci--;
|
||
rot.textContent = w.slice(0, ci);
|
||
if (ci <= 0) { deleting = false; wi = (wi + 1) % words.length; rotTimer = setTimeout(tick, GAP); return; }
|
||
rotTimer = setTimeout(tick, ERASE);
|
||
}
|
||
}
|
||
rotTimer = setTimeout(tick, 650);
|
||
}
|
||
var FADE = 600, done = false;
|
||
function dismiss() {
|
||
if (done) return; done = true;
|
||
if (rotTimer) clearTimeout(rotTimer);
|
||
splash.classList.add('hide');
|
||
setTimeout(function () { if (splash.parentNode) splash.parentNode.removeChild(splash); }, FADE);
|
||
}
|
||
// Stay until the user interacts.
|
||
splash.addEventListener('click', dismiss);
|
||
splash.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); dismiss(); }
|
||
});
|
||
window.__deviantSplash = { dismiss: dismiss };
|
||
})();
|
||
</script>
|
||
<script type="module" src="/src/main.tsx"></script>
|
||
</body>
|
||
</html>
|