diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..ce12710
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,28 @@
+# Deviant Wallet — environment config (on-ramp providers, ADR-0036)
+#
+# HOW TO USE:
+# 1. Copy this file to `.env` in the project root: cp .env.example .env
+# 2. Fill in the values you have (all are OPTIONAL — the wallet runs without
+# any of them; missing keys just leave that provider's feature dormant).
+# 3. Start the web UI: npm run deviant:web (it auto-loads `.env`)
+#
+# `.env` is gitignored — it is never committed. This `.env.example` is the
+# safe, value-free template; keep your real secrets only in `.env`.
+
+# --- Reputation (external, read-only) ---
+# kycnot.me API key. Admin-approved: request access at https://kycnot.me/contact,
+# then generate the key at https://kycnot.me/account/api-keys (format: kycnot_...).
+# Without it, providers show "kycnot.me · n/a" and ranking uses facts + your ratings.
+KYCNOT_API_KEY=
+
+# --- Provider API keys (Tier-2 live quotes) ---
+# Trocador affiliate/API key — required for Trocador live quotes (handoff works
+# without it). Register: https://trocador.app/en/register/affiliate
+TROCADOR_API_KEY=
+
+# --- Referral / affiliate IDs (optional revenue; never affect ranking) ---
+# These only ride in the Tier-1 handoff URL to attribute commission to you.
+GUARDARIAN_REF=
+SIDESHIFT_AFFILIATE=
+TROCADOR_REF=
+CHANGELLY_REF=
diff --git a/public/app.js b/public/app.js
index 5f913cf..3156502 100644
--- a/public/app.js
+++ b/public/app.js
@@ -497,6 +497,12 @@ const KYC_LABEL = {
};
const JOURNEY_LABEL = { 'fiat-to-bch': 'Fiat→BCH', 'crypto-to-bch': 'Crypto→BCH' };
+// Tier-3 (ADR-0036): is the in-wallet SideShift shift flow available on this
+// server? Set from /api/onramp/config. Single active status poll at a time.
+let buyShiftEnabled = false;
+let shiftPollTimer = null;
+const SHIFT_TERMINAL = ['settled', 'refunded', 'expired'];
+
// Warnings shown as a prominent banner vs. quietly in a fine-print footnote.
const LOUD_WARNINGS = new Set(['onramp-regulator-flag', 'onramp-region-unavailable']);
@@ -536,13 +542,21 @@ function buyCardHtml(p) {
const featured = p.featuredEligible ? `Top match` : '';
const unavailable = p.availableInRegion === false ? ' unavailable' : '';
const canQuote = (p.id === 'sideshift' || p.id === 'trocador') && p.journeys.includes('crypto-to-bch');
- return `