deviant/shells/app/index.html
Local Dev ca27b48c62 feat(anthemus.dex): serve DEX-only kiosk at root — no wallet chrome
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>
2026-09-16 01:02:20 +02:00

233 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>