@zoreal/oauth2-js 0.1.18 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/jwt.ts","../src/wire.ts","../src/pairing.ts","../src/intent.ts","../src/i18n.ts","../src/styles.ts","../src/modal.ts","../src/pkce.ts","../src/login.ts"],"sourcesContent":["/**\n * Reads claims OUT of an ID token without verifying it.\n *\n * That is not a shortcut, it is the design: this code runs in a browser the\n * threat model assumes is attacker-controlled, so a signature check here\n * proves nothing to anyone. The token is verified where verification means\n * something: server-side against the JWKS. What this parser feeds is\n * convenience fields (acr on the response object) that the types document as\n * convenience, with the token staying the authority.\n */\n\nexport function unsafeClaims(idToken: string): Record<string, unknown> {\n try {\n const payload = idToken.split('.')[1] ?? '';\n const b64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);\n return JSON.parse(\n new TextDecoder().decode(Uint8Array.from(atob(padded), (c) => c.charCodeAt(0)))\n );\n } catch {\n return {};\n }\n}\n","/**\n * The wire protocol between this package and the ZOREAL OpenID Provider.\n *\n * VERSIONED: a shipped version keeps working until the provider explicitly\n * refuses it, and when it does, the reason is surfaced verbatim. Both the wire\n * version and the package version travel on every pairing request so a refusal\n * can be precise.\n *\n * Endpoints, all relative to the issuer and all CORS-gated on the client's\n * authorized JavaScript origins (the dashboard):\n *\n * POST /pair start a pairing request. Body carries the\n * authorize parameters plus PKCE challenge,\n * and `display`: which pairing surface this\n * package is about to show, 'qr' or 'link',\n * decided before the request is made. Returns\n * { request_id, pair_url, expires_in, display,\n * qr_refresh_seconds } or, for prompt=none\n * with a live consented session, { code }\n * immediately. `display` is echoed as the\n * provider bound it ('legacy' for a request\n * that sent none, which gets the static code\n * older versions of this package showed).\n * `qr_refresh_seconds` comes with 'qr' and is\n * how often to re-fetch the image; 3 today.\n * A 'link' pairing's pair_url carries\n * ?t=<start_token>: it can only be claimed by\n * the app that opened that exact link, and\n * the provider never renders a QR for it, so\n * nobody can turn a same-device link into a\n * static code to relay.\n * GET /pair/start the same-device sign-in as a NAVIGATION:\n * the /pair parameters as a query, plus\n * request_id (the page's own token) and\n * origin; answered with a redirect to the\n * pairing's universal link, inside the tap\n * GET /pair/:id/status poll: pending | claimed |\n * approved (with code) | denied | expired |\n * enrolling. Over-polling cancels the request\n * rather than throttling it, so the cadence\n * below is not a suggestion.\n * GET /pair/:id/qr.svg the QR image, rendered by the provider so\n * this package draws nothing and keeps zero\n * dependencies. For a 'qr' pairing it encodes\n * the CURRENT FRAME, the pairing URL with\n * ?f=<time>.<hmac>: `time` is whole seconds\n * since the pairing was created on the\n * provider's clock, `hmac` is keyed with a\n * secret only the provider holds. Served\n * Cache-Control: no-store, only while the\n * pairing is pending. The app sends the frame\n * it scanned with its claim, and the provider\n * refuses a frame older than 30 seconds, so a\n * screenshot of the code is dead on arrival.\n * This package re-fetches the image every\n * `qr_refresh_seconds` with a cache-busting\n * ?t=<Date.now()>. A 'link' pairing has no\n * image (404).\n * POST /token the code exchange. Browser-direct mode uses\n * it directly with PKCE and no client secret;\n * auth-code mode leaves it to the RP backend.\n */\n\nexport const WIRE_VERSION = 1;\nexport const SDK_VERSION = '0.1.18';\nexport const SDK_NAME = '@zoreal/oauth2-js';\nexport const DEFAULT_ISSUER = 'https://id.zoreal.com';\n\n/** Pending TTL is short. Poll gently; over-polling cancels the request. */\nexport const POLL_INTERVAL_MS = 2000;\n/** Enrolling extends the window well beyond a normal login; poll slower. */\nexport const POLL_INTERVAL_ENROLLING_MS = 5000;\n/**\n * How often the QR image is re-fetched while a pairing is pending, when the\n * provider does not say. The provider's own `qr_refresh_seconds` wins when\n * present. Refreshing is what makes the code on screen move, and a frame the\n * provider refuses after 30 seconds is what makes a screenshot of it useless.\n */\nexport const DEFAULT_QR_REFRESH_SECONDS = 3;\n\n/** The pairing surface this package will show, sent on POST /pair. */\nexport type PairDisplay = 'qr' | 'link';\n\nexport interface PairCreated {\n request_id: string;\n /**\n * https://zoreal.com/login/<request_id>. For a 'link' pairing the URL also\n * carries ?t=<start_token>, and only the app that opens that exact link can\n * claim it. Navigate to it verbatim.\n */\n pair_url: string;\n expires_in: number;\n /**\n * The surface the provider bound, echoed back. 'legacy' means the request\n * sent no `display` (an older version of this package) and got the static\n * code. Absent from a provider that predates the field, which also serves\n * the static code.\n */\n display?: PairDisplay | 'legacy';\n /** 'qr' pairings only: how often to re-fetch qr.svg. Defaults to 3 when absent. */\n qr_refresh_seconds?: number;\n}\n\nexport interface PairImmediate {\n /** prompt=none resolved silently: consented sector, live session. */\n code: string;\n}\n\nexport type PairStartResponse = PairCreated | PairImmediate;\n\nexport interface PairStatusResponse {\n status: 'pending' | 'claimed' | 'approved' | 'denied' | 'expired' | 'cancelled' | 'enrolling';\n code?: string;\n expires_in?: number;\n enrolment_deadline?: number;\n /**\n * The provider's reason on denial or refusal. Surfaced verbatim, never\n * rewritten: it is also how a site's own policy reaches the person, such\n * as a sign-in refused because the phone that approved it was in a\n * different country than the browser.\n */\n error?: string;\n error_description?: string;\n}\n\nexport interface TokenResponse {\n id_token: string;\n access_token?: string;\n token_type?: string;\n expires_in?: number;\n scope?: string;\n error?: string;\n error_description?: string;\n}\n","/**\n * The pairing channel, client side. wire.ts pins the endpoints.\n *\n * The browser polls; the phone never talks to the browser. Everything here is\n * therefore plain fetch against the issuer, CORS-gated on the client's\n * authorized origins, with the poll cadence fixed: the provider cancels an\n * over-polling request rather than throttling it, so a \"retry\n * faster on error\" strategy here would kill the login it is trying to save.\n */\n\nimport {\n POLL_INTERVAL_ENROLLING_MS,\n POLL_INTERVAL_MS,\n SDK_NAME,\n SDK_VERSION,\n WIRE_VERSION,\n type PairDisplay,\n type PairStartResponse,\n type PairStatusResponse,\n type TokenResponse,\n} from './wire';\nimport type { ErrorCode, NonOAuthError, PairingState } from './types';\n\nexport class OAuthFlowError extends Error {\n constructor(\n public error: ErrorCode,\n public description?: string\n ) {\n super(description ?? error);\n }\n}\n\nexport class FlowAbandonedError extends Error {\n constructor(public reason: NonOAuthError) {\n super(reason.description ?? reason.type);\n }\n}\n\nexport interface StartPairingParams {\n client_id: string;\n scope: string;\n state: string;\n nonce: string;\n code_challenge: string;\n redirect_uri?: string;\n acr_values?: string;\n max_age?: number;\n prompt?: string;\n locale?: string;\n /**\n * Which surface the caller will show: 'qr' binds the pairing to moving QR\n * frames, 'link' to a start token only the opened link carries. Decided\n * before the request, because the provider binds it at creation and will\n * not serve the other surface afterwards. Omitting it gets the static code.\n */\n display?: PairDisplay;\n}\n\nasync function parseJson(response: Response): Promise<Record<string, unknown>> {\n try {\n return (await response.json()) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport async function startPairing(\n issuer: string,\n params: StartPairingParams,\n signal?: AbortSignal\n): Promise<PairStartResponse> {\n const response = await fetch(`${issuer}/pair`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...params,\n code_challenge_method: 'S256',\n wire_version: WIRE_VERSION,\n sdk: `${SDK_NAME}/${SDK_VERSION}`,\n }),\n signal,\n });\n\n const body = await parseJson(response);\n if (!response.ok) {\n // The provider's words, verbatim. A refused package version arrives here,\n // and rewriting its reason would hide the only signal telling an integrator\n // to upgrade.\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n (body.error_description as string) ?? `The provider refused the request (${response.status})`\n );\n }\n return body as unknown as PairStartResponse;\n}\n\n/**\n * The same-device sign-in, as a URL to NAVIGATE to, not to fetch.\n *\n * A phone's browser hands a universal link to an app only inside a\n * navigation the person began, and a page that sets its location after a\n * network round trip has left that navigation behind: the link then loads\n * as a web page. So on a phone this package fetches nothing on the tap. The\n * tap itself navigates to the provider's start endpoint with what /pair\n * would have been sent, the provider creates the link pairing and answers\n * with a redirect to its universal link, still inside the person's\n * navigation, and the app opens. The page is not unloaded when it does, and\n * polls the pairing by the `request_id` it chose here. With no app installed\n * the same redirect lands on the page that installs it.\n */\nexport function sameDeviceStartUrl(\n issuer: string,\n params: StartPairingParams & { request_id: string; origin: string }\n): string {\n const query = new URLSearchParams();\n const all: Record<string, unknown> = {\n ...params,\n code_challenge_method: 'S256',\n wire_version: WIRE_VERSION,\n sdk: `${SDK_NAME}/${SDK_VERSION}`,\n };\n for (const [key, value] of Object.entries(all)) {\n if (value === undefined || value === null || value === '') continue;\n query.set(key, String(value));\n }\n return `${issuer}/pair/start?${query.toString()}`;\n}\n\nconst sleep = (ms: number, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new DOMException('aborted', 'AbortError'));\n return;\n }\n // The listener comes off when the sleep ends. One poll is one sleep, and a\n // pairing is dozens of polls on the SAME signal, so a listener left behind\n // per sleep accumulates for as long as the login is open.\n const onAbort = () => {\n clearTimeout(t);\n reject(new DOMException('aborted', 'AbortError'));\n };\n const t = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/**\n * Polls until the request resolves. Returns the authorization code.\n * Throws FlowAbandonedError for the human outcomes (denied, expired,\n * enrolment abandoned) and OAuthFlowError for protocol ones.\n */\nexport async function pollUntilApproved(\n issuer: string,\n requestId: string,\n onState?: (state: PairingState) => void,\n signal?: AbortSignal,\n options: {\n /**\n * Same-device navigation only. The page starts polling while the\n * provider is still answering the navigation that creates the pairing,\n * so a \"no such pairing\" answer before this instant (epoch ms) is the\n * pairing not existing YET, and is read as pending.\n */\n tolerateUnknownUntil?: number;\n } = {}\n): Promise<string> {\n for (;;) {\n const response = await fetch(`${issuer}/pair/${encodeURIComponent(requestId)}/status`, {\n signal,\n });\n const body = (await parseJson(response)) as unknown as PairStatusResponse;\n\n if (response.status === 404 && (options.tolerateUnknownUntil ?? 0) > Date.now()) {\n // Same-device navigation: the pairing is being created by the\n // navigation this page is polling ahead of; not there YET is pending.\n onState?.({ status: 'pending' });\n await sleep(POLL_INTERVAL_MS, signal);\n continue;\n }\n\n if (!response.ok) {\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n body.error_description ?? `Pairing status failed (${response.status})`\n );\n }\n\n onState?.({\n status: body.status,\n expiresIn: body.expires_in,\n enrolmentDeadline: body.enrolment_deadline,\n });\n\n switch (body.status) {\n case 'approved':\n if (!body.code) {\n throw new OAuthFlowError('server_error', 'approved with no authorization code');\n }\n return body.code;\n case 'denied':\n throw new FlowAbandonedError({ type: 'request_denied', description: body.error_description });\n case 'expired':\n throw new FlowAbandonedError({ type: 'request_expired', description: body.error_description });\n case 'cancelled':\n // The provider cancels an over-polled or abandoned request outright\n // (its pairing rows have a real cancelled state); a poll that treats\n // it as unknown spins on a dead request forever.\n throw new FlowAbandonedError({\n type: 'request_expired',\n description: body.error_description ?? 'the provider cancelled the pairing request',\n });\n case 'enrolling':\n await sleep(POLL_INTERVAL_ENROLLING_MS, signal);\n break;\n default:\n await sleep(POLL_INTERVAL_MS, signal);\n }\n }\n}\n\n/**\n * The code exchange, browser-direct mode only: a public client, PKCE and no\n * secret. What comes back can only ever be the pseudonymous tier, by\n * construction rather than by rule: personal data lives at /userinfo behind an\n * access token this mode is never issued, because personal-data scopes are\n * refused for public clients at the pairing step.\n */\nexport async function exchangeCode(\n issuer: string,\n input: { code: string; code_verifier: string; client_id: string }\n): Promise<TokenResponse> {\n const response = await fetch(`${issuer}/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: input.code,\n code_verifier: input.code_verifier,\n client_id: input.client_id,\n }),\n });\n\n const body = (await parseJson(response)) as unknown as TokenResponse;\n if (!response.ok || body.error) {\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n body.error_description ?? `Token exchange failed (${response.status})`\n );\n }\n return body;\n}\n\n/** A mobile user agent gets the app link, not a QR of its own screen. */\nexport function isMobileUserAgent(): boolean {\n if (typeof navigator === 'undefined') return false;\n return /android|iphone|ipad|ipod/i.test(navigator.userAgent);\n}\n","import type { PairingStrings } from './i18n';\nimport type { LoginIntent } from './types';\n\n/**\n * The scopes a relying party asks for in order to know who is signing in:\n * the identifier, and how to reach and address the person. Anything beyond\n * these is an attribute read from the identity document, and the dialog\n * should say that it is about to be shared rather than call it a sign-in.\n */\nconst SIGN_IN_SCOPES = new Set(['openid', 'email', 'profile.name']);\n\n/**\n * What the pairing dialog says it is for. An explicit intent wins. Otherwise\n * a request for document attributes is an identification; a request for the\n * identifier alone with a liveness capture is a presence check, since nothing\n * is being logged into; everything else is a sign-in.\n */\nexport function resolveIntent(\n intent: LoginIntent | undefined,\n scope: string | undefined,\n acrValues: string | readonly string[] | undefined\n): LoginIntent {\n if (intent) return intent;\n const scopes = (scope ?? 'openid').split(/\\s+/).filter(Boolean);\n if (scopes.some((s) => !SIGN_IN_SCOPES.has(s))) return 'identify';\n const acr = typeof acrValues === 'string' ? acrValues.split(/\\s+/) : (acrValues ?? []);\n if (scopes.every((s) => s === 'openid') && acr.includes('zoreal.live')) return 'presence';\n return 'sign-in';\n}\n\n/** The unscanned-code title for an intent. */\nexport function titleFor(t: PairingStrings, intent: LoginIntent): string {\n if (intent === 'identify') return t.titleIdentify;\n if (intent === 'presence') return t.titlePresence;\n return t.title;\n}\n","/**\n * Pairing-modal copy, carried by the SDK.\n *\n * The modal is rendered by this package, so its strings have to ship with it:\n * an integrator cannot translate a component they never write, and asking every\n * one of them to re-supply the same fifteen strings is how a sign-in screen\n * ends up half-English in production.\n *\n * No i18n runtime. A frozen record and one `{time}` substitution is the whole\n * requirement, and a dependency here would be inherited by every host app.\n *\n * Locales match the set ZOREAL's own pairing page serves, so the phone and the\n * browser say the same thing in the same language. `strings()` resolves BCP 47\n * down to that set; anything unknown falls back to English rather than\n * rendering a key.\n */\n\nexport interface PairingStrings {\n /** Dialog title while the code is still unscanned. */\n title: string;\n /** Dialog title when the request is for verified identity attributes. */\n titleIdentify: string;\n /** Dialog title when the request is a presence check and not a login. */\n titlePresence: string;\n /** Dialog title once the request is waiting in the app. */\n titleApprove: string;\n bodyScan: string;\n bodyApprove: string;\n bodyEnrolling: string;\n waiting: string;\n waitingApproval: string;\n /** Carries `{time}`, substituted with mm:ss. */\n expiresIn: string;\n secured: string;\n noIdTitle: string;\n noIdBody: string;\n cancel: string;\n close: string;\n qrAlt: string;\n /** The default label of the sign-in button. */\n buttonContinue: string;\n}\n\nconst en: PairingStrings = {\n title: 'Scan to sign in',\n titleIdentify: 'Scan to verify your identity',\n titlePresence: 'Scan to prove you are a real human',\n titleApprove: 'Approve on your phone',\n bodyScan: 'Scan with your phone camera or the ZOREAL ID app.',\n bodyApprove: 'Approve the login in your ZOREAL ID app.',\n bodyEnrolling: 'Finish setting up ZOREAL ID on your phone, then approve the login.',\n waiting: 'Waiting for scan',\n waitingApproval: 'Waiting for approval',\n expiresIn: 'Expires in {time}',\n secured: 'Proof-of-Human verification by ZOREAL',\n noIdTitle: 'No ZOREAL ID yet?',\n noIdBody: 'Scan the same code to download the app and create one for free. It only takes a minute.',\n cancel: 'Cancel',\n close: 'Close',\n qrAlt: 'QR code to sign in with ZOREAL',\n buttonContinue: 'Continue with ZOREAL',\n};\n\nconst TRANSLATIONS: Record<string, PairingStrings> = {\n en,\n sv: {\n title: 'Skanna för att logga in',\n titleIdentify: 'Skanna för att verifiera din identitet',\n titlePresence: 'Skanna för att bevisa att du är en riktig människa',\n titleApprove: 'Godkänn på telefonen',\n bodyScan: 'Skanna med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkänn inloggningen i ZOREAL ID-appen.',\n bodyEnrolling: 'Slutför konfigurationen av ZOREAL ID på telefonen och godkänn sedan inloggningen.',\n waiting: 'Väntar på skanning',\n waitingApproval: 'Väntar på godkännande',\n expiresIn: 'Upphör om {time}',\n secured: 'Proof-of-Human-verifiering av ZOREAL',\n noIdTitle: 'Har du inget ZOREAL ID?',\n noIdBody: 'Skanna samma kod för att ladda ner appen och skapa ett gratis. Det tar bara en minut.',\n cancel: 'Avbryt',\n close: 'Stäng',\n qrAlt: 'QR-kod för att logga in med ZOREAL',\n buttonContinue: 'Fortsätt med ZOREAL',\n },\n es: {\n title: 'Escanea para iniciar sesión',\n titleIdentify: 'Escanea para verificar tu identidad',\n titlePresence: 'Escanea para demostrar que eres una persona real',\n titleApprove: 'Apruébalo en tu teléfono',\n bodyScan: 'Escanea con la cámara de tu teléfono o con la app ZOREAL ID.',\n bodyApprove: 'Aprueba el inicio de sesión en tu app ZOREAL ID.',\n bodyEnrolling: 'Termina de configurar ZOREAL ID en tu teléfono y luego aprueba el inicio de sesión.',\n waiting: 'Esperando el escaneo',\n waitingApproval: 'Esperando aprobación',\n expiresIn: 'Caduca en {time}',\n secured: 'Verificación Proof-of-Human de ZOREAL',\n noIdTitle: '¿Aún no tienes ZOREAL ID?',\n noIdBody: 'Escanea el mismo código para descargar la app y crear una gratis. Solo toma un minuto.',\n cancel: 'Cancelar',\n close: 'Cerrar',\n qrAlt: 'Código QR para iniciar sesión con ZOREAL',\n buttonContinue: 'Continuar con ZOREAL',\n },\n pt: {\n title: 'Digitalize para entrar',\n titleIdentify: 'Digitalize para verificar a sua identidade',\n titlePresence: 'Digitalize para provar que é uma pessoa real',\n titleApprove: 'Aprove no seu telefone',\n bodyScan: 'Digitalize com a câmara do seu telefone ou com a app ZOREAL ID.',\n bodyApprove: 'Aprove o login no app ZOREAL ID.',\n bodyEnrolling: 'Termine de configurar o ZOREAL ID no seu telefone e depois aprove o login.',\n waiting: 'Aguardando digitalização',\n waitingApproval: 'Aguardando aprovação',\n expiresIn: 'Expira em {time}',\n secured: 'Verificação Proof-of-Human da ZOREAL',\n noIdTitle: 'Ainda não tem ZOREAL ID?',\n noIdBody: 'Digitalize o mesmo código para baixar o app e criar uma conta grátis. Leva só um minuto.',\n cancel: 'Cancelar',\n close: 'Fechar',\n qrAlt: 'Código QR para entrar com ZOREAL',\n buttonContinue: 'Continuar com ZOREAL',\n },\n fr: {\n title: 'Scannez pour vous connecter',\n titleIdentify: 'Scannez pour vérifier votre identité',\n titlePresence: 'Scannez pour prouver que vous êtes bien un humain',\n titleApprove: 'Approuvez sur votre téléphone',\n bodyScan: \"Scannez avec l'appareil photo de votre téléphone ou l'app ZOREAL ID.\",\n bodyApprove: 'Approuvez la connexion dans votre app ZOREAL ID.',\n bodyEnrolling: 'Terminez la configuration de ZOREAL ID sur votre téléphone, puis approuvez la connexion.',\n waiting: 'En attente du scan',\n waitingApproval: \"En attente d'approbation\",\n expiresIn: 'Expire dans {time}',\n secured: 'Vérification Proof-of-Human par ZOREAL',\n noIdTitle: \"Pas encore de ZOREAL ID ?\",\n noIdBody: \"Scannez le même code pour télécharger l'app et en créer un gratuitement. Cela prend une minute.\",\n cancel: 'Annuler',\n close: 'Fermer',\n qrAlt: 'Code QR pour se connecter avec ZOREAL',\n buttonContinue: 'Continuer avec ZOREAL',\n },\n de: {\n title: 'Zum Anmelden scannen',\n titleIdentify: 'Scannen, um Ihre Identität zu verifizieren',\n titlePresence: 'Scannen, um zu beweisen, dass Sie ein echter Mensch sind',\n titleApprove: 'Auf dem Handy bestätigen',\n bodyScan: 'Mit der Handykamera oder der ZOREAL ID App scannen.',\n bodyApprove: 'Anmeldung in der ZOREAL ID App bestätigen.',\n bodyEnrolling: 'ZOREAL ID auf dem Handy fertig einrichten und dann die Anmeldung bestätigen.',\n waiting: 'Warten auf Scan',\n waitingApproval: 'Warten auf Bestätigung',\n expiresIn: 'Läuft ab in {time}',\n secured: 'Proof-of-Human-Verifizierung von ZOREAL',\n noIdTitle: 'Noch keine ZOREAL ID?',\n noIdBody: 'Denselben Code scannen, um die App zu laden und kostenlos eine zu erstellen. Dauert nur eine Minute.',\n cancel: 'Abbrechen',\n close: 'Schließen',\n qrAlt: 'QR-Code für die Anmeldung mit ZOREAL',\n buttonContinue: 'Weiter mit ZOREAL',\n },\n ru: {\n title: 'Отсканируйте, чтобы войти',\n titleIdentify: 'Отсканируйте, чтобы подтвердить личность',\n titlePresence: 'Отсканируйте, чтобы доказать, что вы реальный человек',\n titleApprove: 'Подтвердите на телефоне',\n bodyScan: 'Отсканируйте камерой телефона или через приложение ZOREAL ID.',\n bodyApprove: 'Подтвердите вход в приложении ZOREAL ID.',\n bodyEnrolling: 'Завершите настройку ZOREAL ID на телефоне, затем подтвердите вход.',\n waiting: 'Ожидание сканирования',\n waitingApproval: 'Ожидание подтверждения',\n expiresIn: 'Истекает через {time}',\n secured: 'Проверка Proof-of-Human от ZOREAL',\n noIdTitle: 'Ещё нет ZOREAL ID?',\n noIdBody: 'Отсканируйте тот же код, чтобы скачать приложение и создать его бесплатно. Это займёт минуту.',\n cancel: 'Отмена',\n close: 'Закрыть',\n qrAlt: 'QR-код для входа через ZOREAL',\n buttonContinue: 'Продолжить с ZOREAL',\n },\n ja: {\n title: 'スキャンしてログイン',\n titleIdentify: 'スキャンして本人確認',\n titlePresence: 'スキャンして実在の人物であることを証明',\n titleApprove: 'スマートフォンで承認',\n bodyScan: 'スマートフォンのカメラまたはZOREAL IDアプリでスキャンしてください。',\n bodyApprove: 'ZOREAL IDアプリでログインを承認してください。',\n bodyEnrolling: 'スマートフォンでZOREAL IDの設定を完了し、ログインを承認してください。',\n waiting: 'スキャン待ち',\n waitingApproval: '承認待ち',\n expiresIn: '有効期限まで {time}',\n secured: 'ZOREALによるProof-of-Human認証',\n noIdTitle: 'ZOREAL IDをお持ちでないですか?',\n noIdBody: '同じコードをスキャンしてアプリをダウンロードし、無料で作成できます。1分ほどで完了します。',\n cancel: 'キャンセル',\n close: '閉じる',\n qrAlt: 'ZOREALでログインするためのQRコード',\n buttonContinue: 'ZOREALで続行',\n },\n hi: {\n title: 'साइन इन करने के लिए स्कैन करें',\n titleIdentify: 'अपनी पहचान सत्यापित करने के लिए स्कैन करें',\n titlePresence: 'यह साबित करने के लिए स्कैन करें कि आप एक वास्तविक इंसान हैं',\n titleApprove: 'अपने फोन पर स्वीकृत करें',\n bodyScan: 'अपने फोन के कैमरे या ZOREAL ID ऐप से स्कैन करें।',\n bodyApprove: 'अपने ZOREAL ID ऐप में लॉगिन स्वीकृत करें।',\n bodyEnrolling: 'अपने फोन पर ZOREAL ID सेटअप पूरा करें, फिर लॉगिन स्वीकृत करें।',\n waiting: 'स्कैन की प्रतीक्षा है',\n waitingApproval: 'स्वीकृति की प्रतीक्षा है',\n expiresIn: '{time} में समाप्त',\n secured: 'ZOREAL द्वारा Proof-of-Human सत्यापन',\n noIdTitle: 'अभी तक ZOREAL ID नहीं है?',\n noIdBody: 'ऐप डाउनलोड करने और मुफ्त में एक बनाने के लिए वही कोड स्कैन करें। इसमें बस एक मिनट लगता है।',\n cancel: 'रद्द करें',\n close: 'बंद करें',\n qrAlt: 'ZOREAL से साइन इन करने के लिए QR कोड',\n buttonContinue: 'ZOREAL के साथ जारी रखें',\n },\n zhs: {\n title: '扫码登录',\n titleIdentify: '扫码验证身份',\n titlePresence: '扫码证明您是真人',\n titleApprove: '在手机上批准',\n bodyScan: '使用手机相机或 ZOREAL ID 应用扫描。',\n bodyApprove: '请在 ZOREAL ID 应用中批准登录。',\n bodyEnrolling: '请在手机上完成 ZOREAL ID 设置,然后批准登录。',\n waiting: '等待扫描',\n waitingApproval: '等待批准',\n expiresIn: '{time} 后失效',\n secured: '由 ZOREAL 提供的 Proof-of-Human 验证',\n noIdTitle: '还没有 ZOREAL ID?',\n noIdBody: '扫描同一个二维码即可下载应用并免费创建,只需一分钟。',\n cancel: '取消',\n close: '关闭',\n qrAlt: '使用 ZOREAL 登录的二维码',\n buttonContinue: '使用 ZOREAL 继续',\n },\n zht: {\n title: '掃碼登入',\n titleIdentify: '掃碼驗證身分',\n titlePresence: '掃碼證明您是真人',\n titleApprove: '在手機上核准',\n bodyScan: '使用手機相機或 ZOREAL ID 應用程式掃描。',\n bodyApprove: '請在 ZOREAL ID 應用程式中核准登入。',\n bodyEnrolling: '請在手機上完成 ZOREAL ID 設定,然後核准登入。',\n waiting: '等待掃描',\n waitingApproval: '等待核准',\n expiresIn: '{time} 後失效',\n secured: '由 ZOREAL 提供的 Proof-of-Human 驗證',\n noIdTitle: '還沒有 ZOREAL ID?',\n noIdBody: '掃描同一個 QR code 即可下載應用程式並免費建立,只需一分鐘。',\n cancel: '取消',\n close: '關閉',\n qrAlt: '使用 ZOREAL 登入的 QR code',\n buttonContinue: '使用 ZOREAL 繼續',\n },\n ar: {\n title: 'امسح لتسجيل الدخول',\n titleIdentify: 'امسح للتحقق من هويتك',\n titlePresence: 'امسح لإثبات أنك إنسان حقيقي',\n titleApprove: 'وافق على هاتفك',\n bodyScan: 'امسح باستخدام كاميرا هاتفك أو تطبيق ZOREAL ID.',\n bodyApprove: 'وافق على تسجيل الدخول في تطبيق ZOREAL ID.',\n bodyEnrolling: 'أكمل إعداد ZOREAL ID على هاتفك، ثم وافق على تسجيل الدخول.',\n waiting: 'في انتظار المسح',\n waitingApproval: 'في انتظار الموافقة',\n expiresIn: 'تنتهي الصلاحية خلال {time}',\n secured: 'التحقق من Proof-of-Human بواسطة ZOREAL',\n noIdTitle: 'ليس لديك ZOREAL ID بعد؟',\n noIdBody: 'امسح الرمز نفسه لتنزيل التطبيق وإنشاء حساب مجاني. يستغرق الأمر دقيقة واحدة فقط.',\n cancel: 'إلغاء',\n close: 'إغلاق',\n qrAlt: 'رمز QR لتسجيل الدخول باستخدام ZOREAL',\n buttonContinue: 'المتابعة باستخدام ZOREAL',\n },\n ko: {\n title: '스캔하여 로그인',\n titleIdentify: '스캔하여 신원 확인',\n titlePresence: '스캔하여 실제 사람임을 증명',\n titleApprove: '휴대폰에서 승인',\n bodyScan: '휴대폰 카메라 또는 ZOREAL ID 앱으로 스캔하세요.',\n bodyApprove: 'ZOREAL ID 앱에서 로그인을 승인하세요.',\n bodyEnrolling: '휴대폰에서 ZOREAL ID 설정을 완료한 후 로그인을 승인하세요.',\n waiting: '스캔 대기 중',\n waitingApproval: '승인 대기 중',\n expiresIn: '{time} 후 만료',\n secured: 'ZOREAL의 Proof-of-Human 인증',\n noIdTitle: '아직 ZOREAL ID가 없으신가요?',\n noIdBody: '같은 코드를 스캔해 앱을 내려받고 무료로 만드세요. 1분이면 됩니다.',\n cancel: '취소',\n close: '닫기',\n qrAlt: 'ZOREAL로 로그인하기 위한 QR 코드',\n buttonContinue: 'ZOREAL로 계속',\n },\n // Български\n bg: {\n title: 'Сканирайте за вход',\n titleIdentify: 'Сканирайте, за да потвърдите самоличността си',\n titlePresence: 'Сканирайте, за да докажете, че сте истински човек',\n titleApprove: 'Потвърдете на телефона си',\n bodyScan: 'Сканирайте с камерата на телефона или с приложението ZOREAL ID.',\n bodyApprove: 'Потвърдете входа в приложението ZOREAL ID.',\n bodyEnrolling: 'Довършете настройката на ZOREAL ID на телефона си, след което потвърдете входа.',\n waiting: 'Изчакване на сканиране',\n waitingApproval: 'Изчакване на потвърждение',\n expiresIn: 'Изтича след {time}',\n secured: 'Проверка Proof-of-Human от ZOREAL',\n noIdTitle: 'Все още нямате ZOREAL ID?',\n noIdBody: 'Сканирайте същия код, за да изтеглите приложението и да си създадете безплатен акаунт. Отнема само минута.',\n cancel: 'Отказ',\n close: 'Затвори',\n qrAlt: 'QR код за вход със ZOREAL',\n buttonContinue: 'Продължи със ZOREAL',\n },\n // বাংলা\n bn: {\n title: 'সাইন ইন করতে স্ক্যান করুন',\n titleIdentify: 'আপনার পরিচয় যাচাই করতে স্ক্যান করুন',\n titlePresence: 'আপনি একজন প্রকৃত মানুষ তা প্রমাণ করতে স্ক্যান করুন',\n titleApprove: 'আপনার ফোনে অনুমোদন করুন',\n bodyScan: 'আপনার ফোনের ক্যামেরা বা ZOREAL ID অ্যাপ দিয়ে স্ক্যান করুন।',\n bodyApprove: 'আপনার ZOREAL ID অ্যাপে লগইন অনুমোদন করুন।',\n bodyEnrolling: 'আপনার ফোনে ZOREAL ID সেটআপ সম্পূর্ণ করুন, তারপর লগইন অনুমোদন করুন।',\n waiting: 'স্ক্যানের অপেক্ষায়',\n waitingApproval: 'অনুমোদনের অপেক্ষায়',\n expiresIn: '{time} পরে মেয়াদ শেষ হবে',\n secured: 'ZOREAL দ্বারা Proof-of-Human যাচাইকরণ',\n noIdTitle: 'এখনো ZOREAL ID নেই?',\n noIdBody: 'অ্যাপ ডাউনলোড করে বিনামূল্যে একটি তৈরি করতে একই কোড স্ক্যান করুন। এতে মাত্র এক মিনিট সময় লাগে।',\n cancel: 'বাতিল',\n close: 'বন্ধ',\n qrAlt: 'ZOREAL দিয়ে সাইন ইন করার জন্য QR কোড',\n buttonContinue: 'ZOREAL দিয়ে চালিয়ে যান',\n },\n // Bosanski\n bs: {\n title: 'Skenirajte za prijavu',\n titleIdentify: 'Skenirajte da potvrdite svoj identitet',\n titlePresence: 'Skenirajte da dokažete da ste stvarna osoba',\n titleApprove: 'Odobrite na svom telefonu',\n bodyScan: 'Skenirajte kamerom svog telefona ili aplikacijom ZOREAL ID.',\n bodyApprove: 'Odobrite prijavu u aplikaciji ZOREAL ID.',\n bodyEnrolling: 'Završite podešavanje ZOREAL ID-a na svom telefonu, a zatim odobrite prijavu.',\n waiting: 'Čeka se skeniranje',\n waitingApproval: 'Čeka se odobrenje',\n expiresIn: 'Ističe za {time}',\n secured: 'ZOREAL Proof-of-Human verifikacija',\n noIdTitle: 'Nemate ZOREAL ID?',\n noIdBody: 'Skenirajte isti kod da preuzmete aplikaciju i besplatno ga napravite. Traje samo minutu.',\n cancel: 'Otkaži',\n close: 'Zatvori',\n qrAlt: 'QR kod za prijavu putem ZOREAL-a',\n buttonContinue: 'Nastavi sa ZOREAL-om',\n },\n // Čeština\n cs: {\n title: 'Přihlaste se naskenováním',\n titleIdentify: 'Naskenujte pro ověření totožnosti',\n titlePresence: 'Naskenujte a prokažte, že jste skutečný člověk',\n titleApprove: 'Potvrďte v telefonu',\n bodyScan: 'Naskenujte fotoaparátem telefonu nebo aplikací ZOREAL ID.',\n bodyApprove: 'Potvrďte přihlášení v aplikaci ZOREAL ID.',\n bodyEnrolling: 'Dokončete nastavení ZOREAL ID v telefonu a poté potvrďte přihlášení.',\n waiting: 'Čekání na naskenování',\n waitingApproval: 'Čekání na potvrzení',\n expiresIn: 'Vyprší za {time}',\n secured: 'Ověření Proof-of-Human od ZOREAL',\n noIdTitle: 'Ještě nemáte ZOREAL ID?',\n noIdBody: 'Naskenováním stejného kódu si stáhnete aplikaci a zdarma vytvoříte ZOREAL ID. Zabere to jen minutu.',\n cancel: 'Zrušit',\n close: 'Zavřít',\n qrAlt: 'QR kód pro přihlášení pomocí ZOREAL',\n buttonContinue: 'Pokračovat se ZOREAL',\n },\n // Dansk\n da: {\n title: 'Scan for at logge ind',\n titleIdentify: 'Scan for at bekræfte din identitet',\n titlePresence: 'Scan for at bevise, at du er et rigtigt menneske',\n titleApprove: 'Godkend på din telefon',\n bodyScan: 'Scan med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkend login i din ZOREAL ID-app.',\n bodyEnrolling: 'Færdiggør opsætningen af ZOREAL ID på din telefon, og godkend derefter login.',\n waiting: 'Venter på scanning',\n waitingApproval: 'Venter på godkendelse',\n expiresIn: 'Udløber om {time}',\n secured: 'Proof-of-Human-verificering af ZOREAL',\n noIdTitle: 'Har du ikke et ZOREAL ID endnu?',\n noIdBody: 'Scan den samme kode for at hente appen og oprette et gratis. Det tager kun et minut.',\n cancel: 'Annuller',\n close: 'Luk',\n qrAlt: 'QR-kode til at logge ind med ZOREAL',\n buttonContinue: 'Fortsæt med ZOREAL',\n },\n // Ελληνικά\n el: {\n title: 'Σάρωση για σύνδεση',\n titleIdentify: 'Σάρωση για επαλήθευση ταυτότητας',\n titlePresence: 'Σάρωση για να αποδείξετε ότι είστε πραγματικός άνθρωπος',\n titleApprove: 'Έγκριση από το κινητό σας',\n bodyScan: 'Σαρώστε με την κάμερα του κινητού σας ή την εφαρμογή ZOREAL ID.',\n bodyApprove: 'Εγκρίνετε τη σύνδεση στην εφαρμογή ZOREAL ID.',\n bodyEnrolling: 'Ολοκληρώστε τη ρύθμιση του ZOREAL ID στο κινητό σας και έπειτα εγκρίνετε τη σύνδεση.',\n waiting: 'Αναμονή σάρωσης',\n waitingApproval: 'Αναμονή έγκρισης',\n expiresIn: 'Λήγει σε {time}',\n secured: 'Επαλήθευση Proof-of-Human από τη ZOREAL',\n noIdTitle: 'Δεν έχετε ακόμα ZOREAL ID;',\n noIdBody: 'Σαρώστε τον ίδιο κωδικό για να κατεβάσετε την εφαρμογή και να δημιουργήσετε ένα δωρεάν. Χρειάζεται μόνο ένα λεπτό.',\n cancel: 'Άκυρο',\n close: 'Κλείσιμο',\n qrAlt: 'Κωδικός QR για σύνδεση με ZOREAL',\n buttonContinue: 'Συνέχεια με ZOREAL',\n },\n // Español (LA)\n 'es-419': {\n title: 'Escanea para iniciar sesión',\n titleIdentify: 'Escanea para verificar tu identidad',\n titlePresence: 'Escanea para demostrar que eres una persona real',\n titleApprove: 'Aprueba desde tu celular',\n bodyScan: 'Escanea con la cámara de tu celular o con la app ZOREAL ID.',\n bodyApprove: 'Aprueba el inicio de sesión en tu app ZOREAL ID.',\n bodyEnrolling: 'Termina de configurar ZOREAL ID en tu celular y luego aprueba el inicio de sesión.',\n waiting: 'Esperando escaneo',\n waitingApproval: 'Esperando aprobación',\n expiresIn: 'Expira en {time}',\n secured: 'Verificación Proof-of-Human de ZOREAL',\n noIdTitle: '¿Todavía no tienes ZOREAL ID?',\n noIdBody: 'Escanea el mismo código para descargar la app y crear uno gratis. Solo toma un minuto.',\n cancel: 'Cancelar',\n close: 'Cerrar',\n qrAlt: 'Código QR para iniciar sesión con ZOREAL',\n buttonContinue: 'Continuar con ZOREAL',\n },\n // Suomi\n fi: {\n title: 'Kirjaudu sisään skannaamalla',\n titleIdentify: 'Vahvista henkilöllisyytesi skannaamalla',\n titlePresence: 'Todista skannaamalla, että olet oikea ihminen',\n titleApprove: 'Hyväksy puhelimessasi',\n bodyScan: 'Skannaa puhelimesi kameralla tai ZOREAL ID -sovelluksella.',\n bodyApprove: 'Hyväksy kirjautuminen ZOREAL ID -sovelluksessasi.',\n bodyEnrolling: 'Viimeistele ZOREAL ID -sovelluksen käyttöönotto puhelimellasi ja hyväksy sitten kirjautuminen.',\n waiting: 'Odotetaan skannausta',\n waitingApproval: 'Odotetaan hyväksyntää',\n expiresIn: 'Vanhenee {time} kuluttua',\n secured: 'ZOREALin Proof-of-Human-vahvistus',\n noIdTitle: 'Eikö sinulla ole vielä ZOREAL ID:tä?',\n noIdBody: 'Skannaa sama koodi ladataksesi sovelluksen ja luodaksesi tunnuksen ilmaiseksi. Se vie vain minuutin.',\n cancel: 'Peruuta',\n close: 'Sulje',\n qrAlt: 'QR-koodi ZOREAL-kirjautumista varten',\n buttonContinue: 'Jatka ZOREALilla',\n },\n // עברית\n he: {\n title: 'סרוק כדי להתחבר',\n titleIdentify: 'סרוק כדי לאמת את זהותך',\n titlePresence: 'סרוק כדי להוכיח שאתה אדם אמיתי',\n titleApprove: 'אשר בטלפון שלך',\n bodyScan: 'סרוק באמצעות מצלמת הטלפון שלך או אפליקציית ZOREAL ID.',\n bodyApprove: 'אשר את ההתחברות באפליקציית ZOREAL ID שלך.',\n bodyEnrolling: 'סיים להגדיר את ZOREAL ID בטלפון שלך, ואז אשר את ההתחברות.',\n waiting: 'ממתין לסריקה',\n waitingApproval: 'ממתין לאישור',\n expiresIn: 'יפוג בעוד {time}',\n secured: 'אימות Proof-of-Human מבית ZOREAL',\n noIdTitle: 'עדיין אין לך ZOREAL ID?',\n noIdBody: 'סרוק את אותו הקוד כדי להוריד את האפליקציה וליצור אחד בחינם. זה לוקח רק דקה.',\n cancel: 'ביטול',\n close: 'סגור',\n qrAlt: 'קוד QR להתחברות עם ZOREAL',\n buttonContinue: 'המשך עם ZOREAL',\n },\n // Hrvatski\n hr: {\n title: 'Skenirajte za prijavu',\n titleIdentify: 'Skenirajte za potvrdu identiteta',\n titlePresence: 'Skenirajte kako biste dokazali da ste stvarna osoba',\n titleApprove: 'Odobrite na svom mobitelu',\n bodyScan: 'Skenirajte kamerom svog mobitela ili aplikacijom ZOREAL ID.',\n bodyApprove: 'Odobrite prijavu u aplikaciji ZOREAL ID.',\n bodyEnrolling: 'Dovršite postavljanje ZOREAL ID-a na svom mobitelu, a zatim odobrite prijavu.',\n waiting: 'Čeka se skeniranje',\n waitingApproval: 'Čeka se odobrenje',\n expiresIn: 'Ističe za {time}',\n secured: 'ZOREAL Proof-of-Human provjera',\n noIdTitle: 'Nemate ZOREAL ID?',\n noIdBody: 'Skenirajte isti kod da preuzmete aplikaciju i besplatno ga izradite. Traje samo minutu.',\n cancel: 'Odustani',\n close: 'Zatvori',\n qrAlt: 'QR kod za prijavu putem ZOREAL-a',\n buttonContinue: 'Nastavi sa ZOREAL-om',\n },\n // Magyar\n hu: {\n title: 'Bejelentkezés beolvasással',\n titleIdentify: 'Olvassa be a személyazonossága igazolásához',\n titlePresence: 'Olvassa be annak igazolásához, hogy valódi ember',\n titleApprove: 'Jóváhagyás a telefonján',\n bodyScan: 'Olvassa be a telefonja kamerájával, vagy a ZOREAL ID alkalmazással.',\n bodyApprove: 'Hagyja jóvá a bejelentkezést a ZOREAL ID alkalmazásban.',\n bodyEnrolling: 'Fejezze be a ZOREAL ID beállítását a telefonján, majd hagyja jóvá a bejelentkezést.',\n waiting: 'Várakozás beolvasásra',\n waitingApproval: 'Várakozás jóváhagyásra',\n expiresIn: 'Lejár {time} múlva',\n secured: 'Proof-of-Human hitelesítés a ZOREAL-tól',\n noIdTitle: 'Még nincs ZOREAL ID-je?',\n noIdBody: 'Olvassa be ugyanazt a kódot az alkalmazás letöltéséhez, és hozzon létre egyet ingyenesen. Mindössze egy percet vesz igénybe.',\n cancel: 'Mégse',\n close: 'Bezárás',\n qrAlt: 'QR-kód a ZOREAL-lal való bejelentkezéshez',\n buttonContinue: 'Folytatás a ZOREAL-lal',\n },\n // Bahasa Indonesia\n id: {\n title: 'Pindai untuk masuk',\n titleIdentify: 'Pindai untuk memverifikasi identitas Anda',\n titlePresence: 'Pindai untuk membuktikan bahwa Anda manusia sungguhan',\n titleApprove: 'Setujui di ponsel Anda',\n bodyScan: 'Pindai dengan kamera ponsel atau aplikasi ZOREAL ID.',\n bodyApprove: 'Setujui proses masuk di aplikasi ZOREAL ID Anda.',\n bodyEnrolling: 'Selesaikan pengaturan ZOREAL ID di ponsel Anda, lalu setujui proses masuk.',\n waiting: 'Menunggu pemindaian',\n waitingApproval: 'Menunggu persetujuan',\n expiresIn: 'Berakhir dalam {time}',\n secured: 'Verifikasi Proof-of-Human oleh ZOREAL',\n noIdTitle: 'Belum punya ZOREAL ID?',\n noIdBody: 'Pindai kode yang sama untuk mengunduh aplikasi dan membuat akun secara gratis. Hanya butuh waktu satu menit.',\n cancel: 'Batal',\n close: 'Tutup',\n qrAlt: 'Kode QR untuk masuk dengan ZOREAL',\n buttonContinue: 'Lanjutkan dengan ZOREAL',\n },\n // Italiano\n it: {\n title: 'Scansiona per accedere',\n titleIdentify: 'Scansiona per verificare la tua identità',\n titlePresence: 'Scansiona per dimostrare di essere una persona reale',\n titleApprove: 'Approva sul tuo telefono',\n bodyScan: 'Scansiona con la fotocamera del telefono o con l\\'app ZOREAL ID.',\n bodyApprove: 'Approva l\\'accesso nell\\'app ZOREAL ID.',\n bodyEnrolling: 'Completa la configurazione di ZOREAL ID sul telefono, poi approva l\\'accesso.',\n waiting: 'In attesa della scansione',\n waitingApproval: 'In attesa di approvazione',\n expiresIn: 'Scade tra {time}',\n secured: 'Verifica Proof-of-Human di ZOREAL',\n noIdTitle: 'Non hai ancora uno ZOREAL ID?',\n noIdBody: 'Scansiona lo stesso codice per scaricare l\\'app e crearne uno gratis. Basta un minuto.',\n cancel: 'Annulla',\n close: 'Chiudi',\n qrAlt: 'Codice QR per accedere con ZOREAL',\n buttonContinue: 'Continua con ZOREAL',\n },\n // Bahasa Melayu\n ms: {\n title: 'Imbas untuk log masuk',\n titleIdentify: 'Imbas untuk mengesahkan identiti anda',\n titlePresence: 'Imbas untuk membuktikan anda manusia sebenar',\n titleApprove: 'Luluskan di telefon anda',\n bodyScan: 'Imbas dengan kamera telefon atau aplikasi ZOREAL ID.',\n bodyApprove: 'Luluskan log masuk dalam aplikasi ZOREAL ID anda.',\n bodyEnrolling: 'Selesaikan persediaan ZOREAL ID di telefon anda, kemudian luluskan log masuk.',\n waiting: 'Menunggu imbasan',\n waitingApproval: 'Menunggu kelulusan',\n expiresIn: 'Tamat tempoh dalam {time}',\n secured: 'Pengesahan Proof-of-Human oleh ZOREAL',\n noIdTitle: 'Belum ada ZOREAL ID?',\n noIdBody: 'Imbas kod yang sama untuk memuat turun aplikasi dan cipta satu secara percuma. Hanya mengambil masa seminit.',\n cancel: 'Batal',\n close: 'Tutup',\n qrAlt: 'Kod QR untuk log masuk dengan ZOREAL',\n buttonContinue: 'Teruskan dengan ZOREAL',\n },\n // Nederlands\n nl: {\n title: 'Scan om in te loggen',\n titleIdentify: 'Scan om je identiteit te verifiëren',\n titlePresence: 'Scan om te bewijzen dat je een echt mens bent',\n titleApprove: 'Keur goed op je telefoon',\n bodyScan: 'Scan met de camera van je telefoon of de ZOREAL ID-app.',\n bodyApprove: 'Keur de aanmelding goed in je ZOREAL ID-app.',\n bodyEnrolling: 'Rond het instellen van ZOREAL ID op je telefoon af en keur daarna de aanmelding goed.',\n waiting: 'Wachten op scan',\n waitingApproval: 'Wachten op goedkeuring',\n expiresIn: 'Verloopt over {time}',\n secured: 'Proof-of-Human-verificatie door ZOREAL',\n noIdTitle: 'Nog geen ZOREAL ID?',\n noIdBody: 'Scan dezelfde code om de app te downloaden en gratis een account aan te maken. Dit duurt maar een minuut.',\n cancel: 'Annuleren',\n close: 'Sluiten',\n qrAlt: 'QR-code om in te loggen met ZOREAL',\n buttonContinue: 'Doorgaan met ZOREAL',\n },\n // Norsk\n no: {\n title: 'Skann for å logge inn',\n titleIdentify: 'Skann for å bekrefte identiteten din',\n titlePresence: 'Skann for å bevise at du er et ekte menneske',\n titleApprove: 'Godkjenn på telefonen din',\n bodyScan: 'Skann med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkjenn innloggingen i ZOREAL ID-appen din.',\n bodyEnrolling: 'Fullfør oppsettet av ZOREAL ID på telefonen din, og godkjenn deretter innloggingen.',\n waiting: 'Venter på skanning',\n waitingApproval: 'Venter på godkjenning',\n expiresIn: 'Utløper om {time}',\n secured: 'Proof-of-Human-verifisering av ZOREAL',\n noIdTitle: 'Har du ikke ZOREAL ID ennå?',\n noIdBody: 'Skann den samme koden for å laste ned appen og opprette en gratis. Det tar bare et minutt.',\n cancel: 'Avbryt',\n close: 'Lukk',\n qrAlt: 'QR-kode for å logge inn med ZOREAL',\n buttonContinue: 'Fortsett med ZOREAL',\n },\n // Polski\n pl: {\n title: 'Zeskanuj, aby się zalogować',\n titleIdentify: 'Zeskanuj, aby zweryfikować swoją tożsamość',\n titlePresence: 'Zeskanuj, aby udowodnić, że jesteś prawdziwym człowiekiem',\n titleApprove: 'Zatwierdź w telefonie',\n bodyScan: 'Zeskanuj aparatem telefonu lub aplikacją ZOREAL ID.',\n bodyApprove: 'Zatwierdź logowanie w aplikacji ZOREAL ID.',\n bodyEnrolling: 'Dokończ konfigurację ZOREAL ID w telefonie, a następnie zatwierdź logowanie.',\n waiting: 'Czekanie na skan',\n waitingApproval: 'Czekanie na zatwierdzenie',\n expiresIn: 'Wygasa za {time}',\n secured: 'Weryfikacja Proof-of-Human od ZOREAL',\n noIdTitle: 'Nie masz jeszcze ZOREAL ID?',\n noIdBody: 'Zeskanuj ten sam kod, aby pobrać aplikację i bezpłatnie utworzyć ZOREAL ID. Zajmie to tylko minutę.',\n cancel: 'Anuluj',\n close: 'Zamknij',\n qrAlt: 'Kod QR do logowania za pomocą ZOREAL',\n buttonContinue: 'Kontynuuj z ZOREAL',\n },\n // Português (BR)\n 'pt-br': {\n title: 'Escaneie para entrar',\n titleIdentify: 'Escaneie para verificar sua identidade',\n titlePresence: 'Escaneie para provar que você é uma pessoa real',\n titleApprove: 'Aprove no seu celular',\n bodyScan: 'Escaneie com a câmera do seu celular ou com o app ZOREAL ID.',\n bodyApprove: 'Aprove o login no app ZOREAL ID.',\n bodyEnrolling: 'Termine de configurar o ZOREAL ID no seu celular e depois aprove o login.',\n waiting: 'Aguardando escaneamento',\n waitingApproval: 'Aguardando aprovação',\n expiresIn: 'Expira em {time}',\n secured: 'Verificação Proof-of-Human da ZOREAL',\n noIdTitle: 'Ainda não tem um ZOREAL ID?',\n noIdBody: 'Escaneie o mesmo código para baixar o app e criar um de graça. Leva só um minuto.',\n cancel: 'Cancelar',\n close: 'Fechar',\n qrAlt: 'Código QR para entrar com ZOREAL',\n buttonContinue: 'Continuar com ZOREAL',\n },\n // Română\n ro: {\n title: 'Scanați pentru conectare',\n titleIdentify: 'Scanați pentru a vă verifica identitatea',\n titlePresence: 'Scanați pentru a dovedi că sunteți o persoană reală',\n titleApprove: 'Aprobați de pe telefon',\n bodyScan: 'Scanați cu camera telefonului sau cu aplicația ZOREAL ID.',\n bodyApprove: 'Aprobați conectarea în aplicația ZOREAL ID.',\n bodyEnrolling: 'Finalizați configurarea ZOREAL ID pe telefon, apoi aprobați conectarea.',\n waiting: 'Se așteaptă scanarea',\n waitingApproval: 'Se așteaptă aprobarea',\n expiresIn: 'Expiră în {time}',\n secured: 'Verificare Proof-of-Human de la ZOREAL',\n noIdTitle: 'Nu aveți încă un ZOREAL ID?',\n noIdBody: 'Scanați același cod pentru a descărca aplicația și a crea unul gratuit. Durează doar un minut.',\n cancel: 'Anulează',\n close: 'Închide',\n qrAlt: 'Cod QR pentru conectare cu ZOREAL',\n buttonContinue: 'Continuați cu ZOREAL',\n },\n // Српски\n sr: {\n title: 'Скенирајте за пријаву',\n titleIdentify: 'Скенирајте да потврдите свој идентитет',\n titlePresence: 'Скенирајте да докажете да сте права особа',\n titleApprove: 'Одобрите на свом телефону',\n bodyScan: 'Скенирајте камером свог телефона или апликацијом ZOREAL ID.',\n bodyApprove: 'Одобрите пријаву у апликацији ZOREAL ID.',\n bodyEnrolling: 'Довршите подешавање ZOREAL ID-а на свом телефону, па одобрите пријаву.',\n waiting: 'Чека се скенирање',\n waitingApproval: 'Чека се одобрење',\n expiresIn: 'Истиче за {time}',\n secured: 'ZOREAL Proof-of-Human верификација',\n noIdTitle: 'Немате ZOREAL ID?',\n noIdBody: 'Скенирајте исти код да преузмете апликацију и бесплатно га направите. Траје само минут.',\n cancel: 'Откажи',\n close: 'Затвори',\n qrAlt: 'QR код за пријаву преко ZOREAL-а',\n buttonContinue: 'Настави са ZOREAL-ом',\n },\n // ไทย\n th: {\n title: 'สแกนเพื่อเข้าสู่ระบบ',\n titleIdentify: 'สแกนเพื่อยืนยันตัวตนของคุณ',\n titlePresence: 'สแกนเพื่อพิสูจน์ว่าคุณเป็นมนุษย์จริง',\n titleApprove: 'อนุมัติบนโทรศัพท์ของคุณ',\n bodyScan: 'สแกนด้วยกล้องโทรศัพท์หรือแอป ZOREAL ID',\n bodyApprove: 'อนุมัติการเข้าสู่ระบบในแอป ZOREAL ID ของคุณ',\n bodyEnrolling: 'ตั้งค่า ZOREAL ID บนโทรศัพท์ของคุณให้เสร็จสิ้น แล้วอนุมัติการเข้าสู่ระบบ',\n waiting: 'รอการสแกน',\n waitingApproval: 'รอการอนุมัติ',\n expiresIn: 'หมดอายุใน {time}',\n secured: 'การยืนยันตัวตน Proof-of-Human โดย ZOREAL',\n noIdTitle: 'ยังไม่มี ZOREAL ID ใช่ไหม',\n noIdBody: 'สแกนโค้ดเดียวกันเพื่อดาวน์โหลดแอปและสร้างบัญชีฟรี ใช้เวลาเพียงนาทีเดียว',\n cancel: 'ยกเลิก',\n close: 'ปิด',\n qrAlt: 'คิวอาร์โค้ดสำหรับเข้าสู่ระบบด้วย ZOREAL',\n buttonContinue: 'ดำเนินการต่อด้วย ZOREAL',\n },\n // Tagalog\n tl: {\n title: 'I-scan para mag-sign in',\n titleIdentify: 'I-scan para i-verify ang iyong pagkakakilanlan',\n titlePresence: 'I-scan para patunayang tunay kang tao',\n titleApprove: 'I-approve sa iyong telepono',\n bodyScan: 'I-scan gamit ang camera ng iyong telepono o ang ZOREAL ID app.',\n bodyApprove: 'I-approve ang login sa iyong ZOREAL ID app.',\n bodyEnrolling: 'Tapusin muna ang pag-set up ng ZOREAL ID sa iyong telepono, pagkatapos ay i-approve ang login.',\n waiting: 'Naghihintay ng scan',\n waitingApproval: 'Naghihintay ng approval',\n expiresIn: 'Mag-e-expire sa {time}',\n secured: 'Proof-of-Human verification mula sa ZOREAL',\n noIdTitle: 'Wala ka pang ZOREAL ID?',\n noIdBody: 'I-scan ang parehong code para i-download ang app at gumawa ng iyong ZOREAL ID nang libre. Isang minuto lang ito.',\n cancel: 'Kanselahin',\n close: 'Isara',\n qrAlt: 'QR code para mag-sign in gamit ang ZOREAL',\n buttonContinue: 'Magpatuloy gamit ang ZOREAL',\n },\n // Türkçe\n tr: {\n title: 'Giriş için tarayın',\n titleIdentify: 'Kimliğinizi doğrulamak için tarayın',\n titlePresence: 'Gerçek bir insan olduğunuzu kanıtlamak için tarayın',\n titleApprove: 'Telefonunuzdan onaylayın',\n bodyScan: 'Telefonunuzun kamerasıyla veya ZOREAL ID uygulamasıyla tarayın.',\n bodyApprove: 'Girişi ZOREAL ID uygulamanızdan onaylayın.',\n bodyEnrolling: 'Telefonunuzda ZOREAL ID kurulumunu tamamlayın, ardından girişi onaylayın.',\n waiting: 'Tarama bekleniyor',\n waitingApproval: 'Onay bekleniyor',\n expiresIn: '{time} içinde sona erer',\n secured: 'ZOREAL tarafından Proof-of-Human doğrulaması',\n noIdTitle: 'Henüz ZOREAL ID\\'niz yok mu?',\n noIdBody: 'Uygulamayı indirmek ve ücretsiz bir tane oluşturmak için aynı kodu tarayın. Sadece bir dakikanızı alır.',\n cancel: 'İptal',\n close: 'Kapat',\n qrAlt: 'ZOREAL ile giriş yapmak için QR kodu',\n buttonContinue: 'ZOREAL ile devam et',\n },\n // Українська\n uk: {\n title: 'Скануйте для входу',\n titleIdentify: 'Скануйте, щоб підтвердити особу',\n titlePresence: 'Скануйте, щоб довести, що ви справжня людина',\n titleApprove: 'Підтвердьте на телефоні',\n bodyScan: 'Скануйте камерою телефону або додатком ZOREAL ID.',\n bodyApprove: 'Підтвердьте вхід у додатку ZOREAL ID.',\n bodyEnrolling: 'Завершіть налаштування ZOREAL ID на телефоні, а потім підтвердьте вхід.',\n waiting: 'Очікування сканування',\n waitingApproval: 'Очікування підтвердження',\n expiresIn: 'Спливає через {time}',\n secured: 'Перевірка Proof-of-Human від ZOREAL',\n noIdTitle: 'Ще немає ZOREAL ID?',\n noIdBody: 'Скануйте той самий код, щоб завантажити додаток і безкоштовно створити його. Це займе лише хвилину.',\n cancel: 'Скасувати',\n close: 'Закрити',\n qrAlt: 'QR-код для входу через ZOREAL',\n buttonContinue: 'Продовжити з ZOREAL',\n },\n // اردو\n ur: {\n title: 'لاگ اِن کرنے کے لیے اسکین کریں',\n titleIdentify: 'اپنی شناخت کی تصدیق کے لیے اسکین کریں',\n titlePresence: 'یہ ثابت کرنے کے لیے اسکین کریں کہ آپ ایک حقیقی انسان ہیں',\n titleApprove: 'اپنے فون پر منظوری دیں',\n bodyScan: 'اپنے فون کے کیمرے یا ZOREAL ID ایپ سے اسکین کریں۔',\n bodyApprove: 'اپنی ZOREAL ID ایپ میں لاگ اِن کی منظوری دیں۔',\n bodyEnrolling: 'اپنے فون پر ZOREAL ID کی سیٹ اپ مکمل کریں، پھر لاگ اِن کی منظوری دیں۔',\n waiting: 'اسکین کا انتظار',\n waitingApproval: 'منظوری کا انتظار',\n expiresIn: '{time} میں ختم ہوگا',\n secured: 'ZOREAL کی جانب سے Proof-of-Human تصدیق',\n noIdTitle: 'ابھی تک ZOREAL ID نہیں ہے؟',\n noIdBody: 'ایپ ڈاؤن لوڈ کرنے اور مفت میں ایک بنانے کے لیے وہی کوڈ اسکین کریں۔ اس میں صرف ایک منٹ لگتا ہے۔',\n cancel: 'منسوخ کریں',\n close: 'بند کریں',\n qrAlt: 'ZOREAL کے ساتھ لاگ اِن کرنے کے لیے QR کوڈ',\n buttonContinue: 'ZOREAL کے ساتھ جاری رکھیں',\n },\n // Tiếng Việt\n vi: {\n title: 'Quét để đăng nhập',\n titleIdentify: 'Quét để xác minh danh tính của bạn',\n titlePresence: 'Quét để chứng minh bạn là người thật',\n titleApprove: 'Phê duyệt trên điện thoại của bạn',\n bodyScan: 'Quét bằng camera điện thoại hoặc ứng dụng ZOREAL ID.',\n bodyApprove: 'Phê duyệt đăng nhập trong ứng dụng ZOREAL ID của bạn.',\n bodyEnrolling: 'Hoàn tất thiết lập ZOREAL ID trên điện thoại, sau đó phê duyệt đăng nhập.',\n waiting: 'Đang chờ quét mã',\n waitingApproval: 'Đang chờ phê duyệt',\n expiresIn: 'Hết hạn sau {time}',\n secured: 'Xác minh Proof-of-Human bởi ZOREAL',\n noIdTitle: 'Chưa có ZOREAL ID?',\n noIdBody: 'Quét cùng mã này để tải ứng dụng và tạo tài khoản miễn phí. Chỉ mất một phút.',\n cancel: 'Hủy',\n close: 'Đóng',\n qrAlt: 'Mã QR để đăng nhập bằng ZOREAL',\n buttonContinue: 'Tiếp tục với ZOREAL',\n },\n};\n\n/** Locales whose script runs right to left, so the dialog flips with `dir`. */\n// Only languages we actually carry. Listing an RTL language we do not\n// translate would flip the dialog for someone who is then shown the English\n// fallback — LTR text in an RTL container, which is worse than either alone.\nconst RTL = new Set(['ar', 'he', 'iw', 'ur']);\n\n/**\n * One BCP 47 tag to a translation, or undefined if we do not carry it.\n *\n * Chinese is the only case needing more than the primary subtag: `zh-Hans` /\n * `zh-CN` / `zh-SG` are Simplified, everything else `zh` is treated as\n * Traditional, matching how the pairing page splits them.\n */\n/**\n * Primary subtags that reach the same table under another name: superseded ISO\n * codes some platforms still emit, and the written standards we carry one entry\n * for. Without these a Norwegian browser sending `nb` gets English while `no`\n * sits right there in the table.\n */\nconst ALIASES: Record<string, string> = {\n nb: 'no', // Bokmål — what we actually wrote\n nn: 'no', // Nynorsk reader, served Bokmål: closer than English\n fil: 'tl', // Filipino / Tagalog\n iw: 'he', // superseded code for Hebrew, still emitted by some platforms\n in: 'id', // superseded code for Indonesian\n};\n\n/**\n * Spanish and Portuguese ship two variants each, and the split that matters is\n * not the language but the side of the Atlantic. A `es-MX` browser resolving to\n * peninsular Spanish is the kind of near-miss that reads as nobody having\n * thought about it, so the Latin American regions are named explicitly.\n */\nconst LATAM = new Set([\n 'ar', 'bo', 'cl', 'co', 'cr', 'cu', 'do', 'ec', 'gt', 'hn',\n 'mx', 'ni', 'pa', 'pe', 'pr', 'py', 'sv', 'uy', 've', '419',\n]);\n\nfunction lookup(locale: string): PairingStrings | undefined {\n const tag = locale.toLowerCase().replace(/_/g, '-');\n const parts = tag.split('-');\n const primary = ALIASES[parts[0]] ?? parts[0];\n const region = parts[1];\n\n // Script, not region, is what separates these two.\n if (primary === 'zh') {\n const simplified = /(^|-)(hans|cn|sg|my)(-|$)/.test(tag);\n return TRANSLATIONS[simplified ? 'zhs' : 'zht'];\n }\n if (primary === 'es' && region && LATAM.has(region)) return TRANSLATIONS['es-419'];\n if (primary === 'pt' && region === 'br') return TRANSLATIONS['pt-br'];\n\n return TRANSLATIONS[tag] ?? TRANSLATIONS[primary];\n}\n\n/**\n * What the browser says the person reads, best first. `languages` is the whole\n * ordered preference list, which matters: someone whose first choice we do not\n * carry may well have a second we do, and falling straight to English would\n * skip it.\n */\nfunction browserLocales(): string[] {\n if (typeof navigator === 'undefined') return [];\n const nav = navigator as Navigator & { languages?: readonly string[] };\n if (nav.languages && nav.languages.length) return [...nav.languages];\n return nav.language ? [nav.language] : [];\n}\n\n/**\n * The strings to render.\n *\n * An explicit `locale` (from the provider) wins outright: the host app knows\n * which language it is currently showing, and the modal must not disagree with\n * the page it opened on. With none given we follow the browser's own preference\n * list, so an integrator who never sets `locale` still gets a translated modal\n * instead of English-by-default. Anything we do not carry falls back to English\n * rather than rendering a key.\n */\nexport function strings(locale?: string): PairingStrings {\n if (locale) return lookup(locale) ?? en;\n for (const candidate of browserLocales()) {\n const hit = lookup(candidate);\n if (hit) return hit;\n }\n return en;\n}\n\nexport function isRtl(locale?: string): boolean {\n const tag = locale ?? browserLocales()[0];\n if (!tag) return false;\n return RTL.has(tag.toLowerCase().replace(/_/g, '-').split('-')[0]);\n}\n\n/** The one substitution the copy needs. */\nexport function interpolate(template: string, time: string): string {\n return template.replace('{time}', time);\n}\n","/**\n * The pairing modal's stylesheet, injected once on first mount.\n *\n * Why a stylesheet and not inline styles: the modal needs hover, focus-visible,\n * keyframes, `prefers-color-scheme` and `prefers-reduced-motion`. None of those\n * exist as inline style properties, and a component that silently drops its\n * focus ring and its reduced-motion fallback is not shippable in a sign-in\n * flow.\n *\n * Why injected and not a `.css` file the integrator imports: a required import\n * step is a required support ticket. Plenty of hosts (Next.js app dir, CRA,\n * plain Vite, an app with no CSS pipeline at all) treat package CSS\n * differently, and the modal has to look the same in all of them.\n *\n * Every selector is prefixed `zrl-` and every declaration is scoped under one\n * of those classes, so nothing here can reach the host's markup. Values are\n * literal rather than inherited for the same reason: a host page with an\n * aggressive reset must not be able to break the layout of a dialog the person\n * is being asked to authenticate in. Font family is the one exception — it\n * inherits the host's UI font so the modal belongs to the page it opens on.\n */\n\nconst PREFIX = 'zrl';\nexport const cx = (name: string) => `${PREFIX}-${name}`;\n\nexport const STYLE_ELEMENT_ID = 'zoreal-pairing-styles';\n\n/**\n * Palette. `light`/`dark` force a theme, `auto` follows the OS. The tokens are\n * defined three times rather than once with overrides so a forced theme never\n * depends on media-query specificity to win.\n */\nconst LIGHT = `\n --zrl-scrim: rgba(16, 18, 27, 0.45);\n --zrl-surface: #ffffff;\n --zrl-surface-sunken: #f6f7f9;\n --zrl-ink: #16181c;\n --zrl-ink-soft: #4a4f57;\n --zrl-ink-mute: #6b7078;\n --zrl-line: #e4e6ea;\n --zrl-line-soft: #eef0f3;\n --zrl-accent: #00b4d9;\n --zrl-accent-soft: #dcf3fa;\n --zrl-accent-ink: #04698a;\n --zrl-urgent: #b4761a;\n --zrl-qr-bg: #ffffff;\n --zrl-qr-filter: none;\n --zrl-qr-spent-filter: blur(3px);\n --zrl-qr-blend: normal;\n --zrl-shadow: 0 1px 2px rgba(16, 18, 27, 0.06), 0 20px 50px -12px rgba(16, 18, 27, 0.3);\n --zrl-ring: rgba(16, 18, 27, 0.07);\n /* The light on the QR well's edge. Brand blue on both grounds, a lighter\n tint at the head; only its strength is themed, see the dark block. */\n --zrl-beam: #00b4d9;\n --zrl-beam-head: #7fe0f4;\n --zrl-beam-line: 2px;\n --zrl-glow-core: 4px;\n --zrl-glow-reach: 24px;\n --zrl-glow-blur: 8px;\n --zrl-glow-opacity: 0.6;\n`;\n\nconst DARK = `\n --zrl-scrim: rgba(0, 0, 0, 0.62);\n --zrl-surface: #17191d;\n --zrl-surface-sunken: #1f2226;\n --zrl-ink: #f4f5f7;\n --zrl-ink-soft: #b3b8c0;\n --zrl-ink-mute: #8b9199;\n --zrl-line: #2c3036;\n --zrl-line-soft: #24272c;\n --zrl-accent: #34c9e8;\n --zrl-accent-soft: #0d3b47;\n --zrl-accent-ink: #7fdcf0;\n --zrl-urgent: #e0a952;\n /* The code is drawn light on the dark surface: the panel is transparent\n and the image is inverted and screened, so only the modules and the\n mark show. */\n --zrl-qr-bg: transparent;\n --zrl-qr-filter: invert(1);\n --zrl-qr-spent-filter: invert(1) blur(3px);\n --zrl-qr-blend: screen;\n --zrl-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 50px -12px rgba(0, 0, 0, 0.65);\n --zrl-ring: rgba(255, 255, 255, 0.1);\n /* A glow that reads on a white card disappears on a dark one: the light\n here is brighter and wider, and its halo reaches further out. */\n --zrl-beam: #22c8ec;\n --zrl-beam-head: #c2f3fc;\n --zrl-beam-line: 3px;\n --zrl-glow-core: 6px;\n --zrl-glow-reach: 32px;\n --zrl-glow-blur: 10px;\n --zrl-glow-opacity: 0.85;\n`;\n\nexport const CSS = `\n.${PREFIX}-root { ${LIGHT} }\n.${PREFIX}-root[data-theme=\"dark\"] { ${DARK} }\n@media (prefers-color-scheme: dark) {\n .${PREFIX}-root[data-theme=\"auto\"] { ${DARK} }\n}\n\n.${PREFIX}-scrim {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n display: grid;\n place-items: center;\n overflow-y: auto;\n padding: 16px;\n background: var(--zrl-scrim);\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n font-family: inherit;\n animation: ${PREFIX}-fade 200ms ease-out both;\n}\n\n.${PREFIX}-card {\n position: relative;\n box-sizing: border-box;\n width: 100%;\n max-width: 380px;\n border-radius: 16px;\n background: var(--zrl-surface);\n color: var(--zrl-ink);\n box-shadow: var(--zrl-shadow);\n outline: 1px solid var(--zrl-ring);\n outline-offset: -1px;\n text-align: center;\n animation: ${PREFIX}-rise 300ms cubic-bezier(0.23, 1, 0.32, 1) both;\n}\n\n.${PREFIX}-body { padding: 28px 24px 20px; }\n\n.${PREFIX}-lockup { display: block; margin: 0 auto; color: var(--zrl-ink); }\n\n.${PREFIX}-title {\n margin: 18px 0 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n line-height: 1.3;\n color: var(--zrl-ink);\n}\n\n.${PREFIX}-body-text {\n margin: 6px auto 0;\n max-width: 30ch;\n font-size: 14px;\n line-height: 1.55;\n color: var(--zrl-ink-soft);\n}\n\n.${PREFIX}-qr-well {\n position: relative;\n display: grid;\n place-items: center;\n box-sizing: border-box;\n width: 204px;\n height: 204px;\n margin: 20px auto 0;\n padding: 12px;\n border: 1px solid var(--zrl-line);\n border-radius: var(--zrl-radius);\n background: var(--zrl-qr-bg);\n /* The light on the edge takes its shape from here and its colour and\n strength from the theme tokens above. One lap in 4s on every tier. */\n --zrl-radius: 16px;\n --zrl-beam-time: 4s;\n}\n\n/* The light on the well's edge: a short comet running along the border, with\n a soft glow outside it. Three overlays inside the well, each masked so the\n comet can only ever paint where its mask allows, and the white interior lies\n outside every mask: nothing here can reach the quiet zone a camera needs,\n whatever the comet is doing. The mask is the padding box cut out of the\n border box, a transparent layer clipped to the padding box intersected\n with a solid one clipped to the border box. The prefixed form is for Chrome\n before 120 and Safari before 15.4; the unprefixed one, declared after it,\n wins everywhere else.\n\n qr-beam keeps a thin ring on the border line: the comet itself.\n qr-beam-glow is the glow: a wide band outside the well that blurs whatever\n is inside it, and inside it qr-beam-glow-band keeps a 3px ring with a\n second copy of the comet. The blur has to sit on the parent because a\n filter is applied before a mask: blurred on the band itself, the glow\n would be cut back to the band's own edge. On the parent it runs after the\n band has clipped the comet thin and before the parent's mask cuts away the\n inward half, which is what makes it fade outward and never over the QR.\n All three share one containing block, the well's padding box, so the two\n comets ride the same path; the spent badge is a later sibling and paints\n above them. */\n.${PREFIX}-qr-beam,\n.${PREFIX}-qr-beam-glow,\n.${PREFIX}-qr-beam-glow-band {\n position: absolute;\n inset: calc(0px - var(--zrl-beam-line));\n border: var(--zrl-beam-line) solid transparent;\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-beam-line));\n pointer-events: none;\n -webkit-mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);\n -webkit-mask-clip: padding-box, border-box;\n -webkit-mask-composite: source-in;\n mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);\n mask-clip: padding-box, border-box;\n mask-composite: intersect;\n}\n.${PREFIX}-qr-beam-glow {\n inset: calc(0px - var(--zrl-glow-reach));\n border-width: var(--zrl-glow-reach);\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-reach));\n filter: blur(var(--zrl-glow-blur));\n opacity: var(--zrl-glow-opacity);\n will-change: filter;\n}\n.${PREFIX}-qr-beam-glow-band {\n inset: calc(0px - var(--zrl-glow-core));\n border-width: var(--zrl-glow-core);\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-core));\n}\n\n/* At rest the edge holds a dim, even blue: a 1px line on the border and, from\n the glow band, a soft halo outside it. Hidden while the comet runs, so the\n border reads as the well's own line with a light passing over it; shown\n once the light has stopped. Every path below ends here, which is what\n makes them look the same at rest. */\n.${PREFIX}-qr-beam::before,\n.${PREFIX}-qr-beam-glow-band::before {\n content: '';\n position: absolute;\n inset: -50%;\n background: var(--zrl-beam);\n opacity: 0;\n transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* The moving light: an oversized square carrying a conic sweep, rotated\n whole. A transform animation runs on the compositor, so the light keeps\n moving while the page is busy; animating the gradient angle instead\n repaints every frame on the main thread and stutters. */\n.${PREFIX}-qr-beam::after,\n.${PREFIX}-qr-beam-glow-band::after {\n content: '';\n position: absolute;\n inset: -50%;\n background: conic-gradient(\n from 0deg,\n transparent 0deg 220deg,\n var(--zrl-beam) 330deg,\n var(--zrl-beam-head) 348deg,\n transparent 356deg 360deg\n );\n animation: ${PREFIX}-orbit var(--zrl-beam-time) linear infinite;\n will-change: transform;\n transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* Spent: the light stops where it is and fades, and the edge settles to the\n dim glow. Paused rather than removed, so it does not jump back to its start\n on the way out. */\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam::after,\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam-glow-band::after {\n animation-play-state: paused;\n opacity: 0;\n}\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam::before { opacity: 0.55; }\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7; }\n\n.${PREFIX}-qr {\n display: block;\n width: 100%;\n height: 100%;\n border-radius: 8px;\n filter: var(--zrl-qr-filter);\n mix-blend-mode: var(--zrl-qr-blend);\n transition: filter 300ms cubic-bezier(0.23, 1, 0.32, 1),\n opacity 300ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 300ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* Once the code is claimed the QR is spent. Blurring it out rather than\n swapping it keeps one object on screen through the state change, so the eye\n reads a transformation instead of two things trading places. */\n.${PREFIX}-qr[data-spent=\"true\"] { opacity: 0.2; filter: var(--zrl-qr-spent-filter); transform: scale(0.96); }\n\n.${PREFIX}-qr-overlay {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n animation: ${PREFIX}-fade 200ms ease-out both;\n}\n\n.${PREFIX}-qr-badge {\n display: grid;\n place-items: center;\n width: 56px;\n height: 56px;\n border-radius: 999px;\n background: var(--zrl-accent-soft);\n color: var(--zrl-accent-ink);\n}\n\n.${PREFIX}-status {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin-top: 20px;\n font-size: 14px;\n font-weight: 500;\n color: var(--zrl-ink);\n}\n\n.${PREFIX}-dot { position: relative; display: grid; place-items: center; width: 8px; height: 8px; }\n.${PREFIX}-dot i {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 999px;\n background: var(--zrl-accent);\n font-style: normal;\n}\n.${PREFIX}-dot i:first-child { animation: ${PREFIX}-ping 1.8s cubic-bezier(0.23, 1, 0.32, 1) infinite; }\n\n.${PREFIX}-timer {\n margin: 4px 0 0;\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n color: var(--zrl-ink-mute);\n transition: color 200ms ease-out;\n}\n.${PREFIX}-timer[data-urgent=\"true\"] { color: var(--zrl-urgent); }\n\n.${PREFIX}-help {\n padding: 14px 24px;\n border-top: 1px solid var(--zrl-line-soft);\n background: var(--zrl-surface-sunken);\n border-radius: 0;\n}\n.${PREFIX}-help-title { margin: 0; font-size: 12px; font-weight: 600; color: var(--zrl-ink); }\n.${PREFIX}-help-body {\n margin: 4px auto 0;\n max-width: 34ch;\n font-size: 12px;\n line-height: 1.55;\n color: var(--zrl-ink-soft);\n}\n\n.${PREFIX}-footer { padding: 12px; border-top: 1px solid var(--zrl-line-soft); }\n\n.${PREFIX}-cancel {\n display: block;\n width: 100%;\n padding: 10px;\n border: 0;\n border-radius: 12px;\n background: transparent;\n font: inherit;\n font-size: 14px;\n font-weight: 500;\n color: var(--zrl-ink-soft);\n cursor: pointer;\n transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;\n}\n.${PREFIX}-cancel:hover { background: var(--zrl-surface-sunken); color: var(--zrl-ink); }\n.${PREFIX}-cancel:active { transform: scale(0.99); }\n\n.${PREFIX}-secured {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n margin: 6px 0 0;\n font-size: 12px;\n color: var(--zrl-ink-mute);\n text-decoration: none;\n border-radius: 6px;\n transition: color 150ms ease-out;\n}\n.${PREFIX}-secured:hover { color: var(--zrl-ink); }\n\n.${PREFIX}-close {\n position: absolute;\n top: 12px;\n inset-inline-end: 12px;\n display: grid;\n place-items: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--zrl-ink-mute);\n cursor: pointer;\n transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;\n}\n.${PREFIX}-close:hover { background: var(--zrl-surface-sunken); color: var(--zrl-ink); }\n.${PREFIX}-close:active { transform: scale(0.95); }\n\n.${PREFIX}-card :focus-visible {\n outline: 2px solid var(--zrl-accent);\n outline-offset: 2px;\n}\n\n@keyframes ${PREFIX}-fade { from { opacity: 0 } to { opacity: 1 } }\n@keyframes ${PREFIX}-rise {\n from { opacity: 0; transform: translateY(10px) scale(0.98) }\n to { opacity: 1; transform: none }\n}\n@keyframes ${PREFIX}-ping {\n 0% { transform: scale(1); opacity: 0.5 }\n 70%, 100% { transform: scale(2.6); opacity: 0 }\n}\n\n@keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }\n/* THE BUSY RING. The light of the QR well, around any control that is\n waiting on the provider: the button on a phone between the tap and the\n hand-over to the app. The well's sweep is a cone from the centre, which is\n even on a square and useless on a wide button: it crawls along the long\n sides and lights two edges at once near the ends. So here the light is a\n dash on an SVG outline, which moves at one speed the whole way round\n whatever the shape, drawn with the well's tokens: its colour and head\n tint, its line width, its halo, its four second lap. The outline's length\n is measured by the component and set as --zrl-ring-len, and every dash\n and offset is a fraction of it, because pathLength does not scale dash\n values given from CSS. A stroke cannot fade along its length, so the tail\n is a stack of dashes sharing one head, each shorter and more opaque than\n the one under it, with opacities chosen so the stack composes to a\n straight fade from the head to nothing three tenths of the way back; the\n component sets each layer's length, offset and opacity. Shown only while\n busy. */\n.${PREFIX}-ring {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n --zrl-beam-time: 4s;\n}\n.${PREFIX}-ring-svg {\n position: absolute;\n inset: -4px;\n width: calc(100% + 8px);\n height: calc(100% + 8px);\n overflow: visible;\n pointer-events: none;\n opacity: 0;\n transition: opacity 200ms ease-out;\n}\n.${PREFIX}-ring[data-busy=\"true\"] > .${PREFIX}-ring-svg { opacity: 1; }\n.${PREFIX}-ring-svg rect {\n --zrl-l: var(--zrl-ring-len, 600px);\n x: 2px;\n y: 2px;\n width: calc(100% - 4px);\n height: calc(100% - 4px);\n fill: none;\n stroke: var(--zrl-beam);\n stroke-width: var(--zrl-beam-line);\n stroke-linecap: round;\n stroke-dashoffset: var(--zrl-s, 0px);\n animation: ${PREFIX}-dash var(--zrl-beam-time) linear infinite;\n}\n.${PREFIX}-ring-head { stroke: var(--zrl-beam-head); }\n.${PREFIX}-ring-halo {\n stroke-width: calc(var(--zrl-glow-core) * 2 + var(--zrl-beam-line));\n filter: blur(var(--zrl-glow-blur));\n}\n@keyframes ${PREFIX}-dash {\n from { stroke-dashoffset: var(--zrl-s, 0px); }\n to { stroke-dashoffset: calc(var(--zrl-s, 0px) - var(--zrl-l)); }\n}\n@media (prefers-reduced-motion: reduce) {\n .${PREFIX}-ring-svg rect { animation: none; stroke-dasharray: none; opacity: 0.45; }\n .${PREFIX}-ring-halo, .${PREFIX}-ring-head { display: none; }\n}\n\n\n@media (prefers-reduced-motion: reduce) {\n .${PREFIX}-scrim,\n .${PREFIX}-card,\n .${PREFIX}-qr-overlay { animation: none }\n .${PREFIX}-dot i:first-child { animation: none; opacity: 0.35 }\n .${PREFIX}-qr,\n .${PREFIX}-cancel,\n .${PREFIX}-close,\n .${PREFIX}-timer { transition: none }\n /* No travelling light; the edge keeps its dim static glow instead. */\n .${PREFIX}-qr-beam::after,\n .${PREFIX}-qr-beam-glow-band::after { animation: none; opacity: 0 }\n .${PREFIX}-qr-beam::before { opacity: 0.55 }\n .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7 }\n}\n`;\n\n/**\n * Injected at module scope on first import in a DOM, not per render: the tag is\n * idempotent by id, so a host with two provider instances (or a hot reload)\n * still ends up with exactly one.\n */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ELEMENT_ID)) return;\n const el = document.createElement('style');\n el.id = STYLE_ELEMENT_ID;\n el.textContent = CSS;\n document.head.appendChild(el);\n}\n","/**\n * The pairing modal, in plain DOM.\n *\n * Same dialog as @zoreal/oauth2-react's, built without a framework so the core\n * package can put the QR on screen by itself. On desktop a QR sign-in cannot\n * complete unless something renders the pairing code, and leaving that to every\n * caller is how a QR login ships with no QR on it.\n *\n * Nothing here is exported as a component: `startLogin` mounts it, updates it\n * from the same state it hands `onState`, and unmounts it when the flow\n * settles. Callers who want their own UI pass `pairingUI: 'none'`.\n */\n\nimport { interpolate, isRtl, strings } from './i18n';\nimport { titleFor } from './intent';\nimport { cx, ensureStyles } from './styles';\nimport type { LoginIntent, PairingState, ZorealTheme } from './types';\n\n/** Our own cap on how long a pairing sits on screen. See `pairingTimeoutMs`. */\nexport const DEFAULT_PAIRING_TIMEOUT_MS = 120_000;\n\n/** Below this the countdown changes colour: background information becomes a prompt to hurry. */\nconst URGENT_SECONDS = 20;\n\nfunction mmss(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = totalSeconds % 60;\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className?: string,\n text?: string\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\n/**\n * Icons and the lockup are built with createElementNS rather than innerHTML.\n * This package renders on someone else's sign-in page; assigning markup here\n * would be an injection surface for no benefit.\n */\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\nfunction svg(viewBox: string, attrs: Record<string, string> = {}): SVGSVGElement {\n const node = document.createElementNS(SVG_NS, 'svg');\n node.setAttribute('viewBox', viewBox);\n node.setAttribute('focusable', 'false');\n node.setAttribute('aria-hidden', 'true');\n for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, v);\n return node;\n}\n\nfunction path(d: string, attrs: Record<string, string> = {}): SVGPathElement {\n const node = document.createElementNS(SVG_NS, 'path');\n node.setAttribute('d', d);\n for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, v);\n return node;\n}\n\nfunction strokeIcon(size: number, ds: string[], width = '2'): SVGSVGElement {\n const node = svg('0 0 24 24', {\n width: String(size),\n height: String(size),\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': width,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n });\n for (const d of ds) node.appendChild(path(d));\n return node;\n}\n\nconst ZOREAL_BLUE = '#00b4d9';\n\n/** Wordmark paths, from zoreal-web's zoreal-lockup.svg. */\nconst WORDMARK =\n 'M205,40.5h15.3v-3.5h-11.5v-18.4h-3.8v21.8ZM157,22.2v5.6h10.9v3.5h-10.9v5.8h12.5v3.5h-16.3v-21.8h16.2v3.5h-12.4ZM141.4,25.8c0,1.1-.4,2-1.2,2.7-.8.7-1.9,1-3.3,1h-5.6v-7.3h5.6c1.4,0,2.6.3,3.4.9.8.6,1.2,1.5,1.2,2.7ZM146,40.5l-5.9-8.3c.8-.2,1.4-.5,2.1-.9s1.2-.9,1.7-1.4c.4-.5.8-1.2,1.1-1.9s.4-1.5.4-2.4-.1-2-.5-2.9c-.4-.9-.9-1.6-1.7-2.2-.6-.6-1.5-1-2.5-1.4-1-.3-2.2-.4-3.4-.4h-9.8v21.8h3.8v-7.6h4.8l5.4,7.6h4.5ZM115.7,29.7c0,1.1-.1,2.1-.5,3s-.9,1.7-1.5,2.4-1.4,1.2-2.4,1.7c-.9.4-1.9.6-3,.6s-2.1-.1-3-.6-1.7-1-2.4-1.7-1.2-1.5-1.5-2.4-.5-1.9-.5-3,.1-2.1.5-3,.9-1.7,1.5-2.4,1.4-1.2,2.4-1.7c.9-.4,1.9-.6,3-.6s2.1.2,3,.6c.9.4,1.7,1,2.3,1.7.6.6,1.2,1.5,1.6,2.4s.5,1.9.5,3ZM119.7,29.6c0-1.5-.3-3-.8-4.4-.6-1.4-1.4-2.5-2.4-3.6-1-1-2.2-1.8-3.6-2.4-1.4-.6-3-.9-4.6-.9s-3.2.4-4.6.9c-1.4.6-2.7,1.4-3.7,2.4s-1.8,2.2-2.4,3.6c-.5,1.4-.8,2.8-.8,4.4s.3,3,.8,4.4c.6,1.4,1.4,2.5,2.4,3.6,1,1,2.2,1.8,3.6,2.4,1.4.6,3,.9,4.6.9s3.2-.4,4.6-.9c1.4-.6,2.6-1.4,3.7-2.4,1-1,1.8-2.2,2.4-3.6.5-1.4.8-2.9.8-4.4ZM86.1,22.1l-13,15.6v2.8h17.9v-3.4h-12.9l12.9-15.6v-2.8h-17.5v3.4h12.5ZM188.5,18.5h-3.5l-9.6,22h4c3.7-8.8,3.4-8,7.4-17.4,3.7,8.8,3.9,9.1,7.4,17.4h4l-9.6-22Z';\n\nconst MARK_PATHS = [\n 'M52,25.7c.4-2-.5-4.2-2.5-5.4l-11.8-6.8,3.4-2,10.1,5.9c3.6,2,5.1,6.3,3.8,10.1-.2.5-.4,1-.7,1.6-.3.5-.6.9-1,1.4-.9.9-1.9,1.7-3,2.2-2.4,1-5.2.9-7.6-.5l-5.9-3.4c-1.9-1.1-4.3-.9-5.9.5-.4.4-.8.8-1.1,1.3-.3.5-.5,1.1-.6,1.7-.4,2,.6,4.2,2.5,5.4l11.8,6.8-3.4,2-10.1-5.9c-3.6-2-5.1-6.3-3.8-10.1.1-.5.4-1,.7-1.6.3-.5.7-.9,1-1.4.9-.9,1.9-1.7,3.1-2.2,2.4-1,5.2-.9,7.6.4l5.9,3.4c1.9,1.1,4.3.9,5.9-.5.4-.4.8-.8,1.1-1.3.3-.5.5-1.1.6-1.7Z',\n 'M60.3,33.1c-.5.8-1.5,1.1-2.3.6-.9-.5-1.1-1.5-.7-2.3.5-.9,1.5-1.1,2.3-.7.9.5,1.1,1.5.7,2.4Z',\n 'M31.9,18c3.4-.5,6.9,0,10,1.9l.8.5h0c.8.5,1,1.5.6,2.3s-1.5,1.1-2.3.6l-.8-.5c-2.8-1.6-6-1.9-8.9-1.2-1,.3-2,.7-3,1.2h0c-.8.5-1.8.2-2.3-.7-.5-.8-.2-1.8.6-2.3,0,0,0,0,.2,0,.2,0,.4-.2.7-.3,1.4-.7,2.9-1.1,4.4-1.4Z',\n 'M21.4,24.9c.5-.8,1.6-1,2.3-.5.8.5,1,1.6.5,2.3-.5.8-1.6,1-2.4.5-.8-.5-1-1.6-.5-2.3Z',\n 'M45.5,23.9c.5-.9,1.5-1.1,2.3-.7.8.5,1.1,1.5.6,2.4-.5.8-1.5,1.1-2.3.6s-1.1-1.5-.6-2.3Z',\n 'M49.4,39.9c-3.3.6-6.9,0-10-1.8l-.8-.5h0c-.8-.5-1.1-1.5-.6-2.3s1.5-1.1,2.3-.6l.8.5c2.8,1.6,6,1.9,9,1.2,1-.3,2-.7,3-1.2h0c.8-.5,1.8-.2,2.3.6.5.9.2,1.9-.6,2.4,0,0-.1,0-.2,0-.2.1-.5.3-.7.4-1.4.7-2.8,1.1-4.4,1.4Z',\n 'M35.8,34c-.5.8-1.5,1.1-2.3.6s-1.1-1.5-.6-2.3,1.5-1.1,2.3-.6,1.1,1.5.6,2.3Z',\n];\n\n/**\n * The full lockup. The wordmark is `currentColor` rather than the master's\n * near-black, because the dialog renders in either theme and a fixed dark\n * wordmark disappears on a dark card. The mark keeps the brand blue in both:\n * it reads on either ground, and it is the part that says whose sign-in this\n * is.\n */\nfunction lockup(height: number): SVGSVGElement {\n const node = svg('0 0 240 58.5', {\n height: String(height),\n width: String(Math.round(height * (240 / 58.5))),\n });\n node.removeAttribute('aria-hidden');\n node.setAttribute('role', 'img');\n node.setAttribute('aria-label', 'ZOREAL');\n node.appendChild(path(WORDMARK, { fill: 'currentColor' }));\n const g = document.createElementNS(SVG_NS, 'g');\n g.setAttribute('fill', ZOREAL_BLUE);\n g.setAttribute('fill-rule', 'evenodd');\n for (const d of MARK_PATHS) g.appendChild(path(d));\n node.appendChild(g);\n return node;\n}\n\nexport interface PairingModalOptions {\n onCancel: () => void;\n locale?: string;\n theme?: ZorealTheme;\n timeoutMs?: number;\n /** Which title the dialog opens with. Defaults to the sign-in wording. */\n intent?: LoginIntent;\n}\n\nexport interface PairingModalHandle {\n /** Re-render from a new pairing state. */\n update: (state: PairingState) => void;\n /** Remove the dialog and release everything it held. Idempotent. */\n close: () => void;\n}\n\n/**\n * Mounts the dialog and returns the two controls the flow needs. Returns null\n * outside a browser, so importing this package on a server is inert.\n */\nexport function mountPairingModal(\n state: PairingState,\n options: PairingModalOptions\n): PairingModalHandle | null {\n if (typeof document === 'undefined') return null;\n ensureStyles();\n\n const t = strings(options.locale);\n const timeoutMs = options.timeoutMs ?? DEFAULT_PAIRING_TIMEOUT_MS;\n let closed = false;\n\n const scrim = el('div', `${cx('root')} ${cx('scrim')}`);\n scrim.dataset.theme = options.theme ?? 'auto';\n\n const card = el('div', cx('card'));\n card.setAttribute('role', 'dialog');\n card.setAttribute('aria-modal', 'true');\n card.dir = isRtl(options.locale) ? 'rtl' : 'ltr';\n\n const titleId = `zrl-title-${Math.random().toString(36).slice(2, 9)}`;\n card.setAttribute('aria-labelledby', titleId);\n\n const closeBtn = el('button', cx('close'));\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', t.close);\n closeBtn.appendChild(strokeIcon(16, ['M18 6 6 18M6 6l12 12']));\n\n const body = el('div', cx('body'));\n const mark = lockup(44);\n mark.classList.add(cx('lockup'));\n\n const title = el('h2', cx('title'));\n title.id = titleId;\n const bodyText = el('p', cx('body-text'));\n\n // The light on the well's edge is a set of masked overlays inside the well,\n // drawn first so the spent badge, a later sibling, stays above them. The\n // well carries the spent flag for them: a stylesheet cannot look back from\n // the image to a sibling before it.\n const well = el('div', cx('qr-well'));\n const glow = el('span', cx('qr-beam-glow'));\n glow.setAttribute('aria-hidden', 'true');\n glow.appendChild(el('span', cx('qr-beam-glow-band')));\n const beam = el('span', cx('qr-beam'));\n beam.setAttribute('aria-hidden', 'true');\n well.append(glow, beam);\n const qr = el('img', cx('qr'));\n qr.alt = t.qrAlt;\n qr.width = 180;\n qr.height = 180;\n if (state.qrUrl) qr.src = state.qrUrl;\n\n const overlay = el('span', cx('qr-overlay'));\n const badge = el('span', cx('qr-badge'));\n badge.appendChild(strokeIcon(24, ['M8.5 2h7a2.5 2.5 0 0 1 2.5 2.5v15a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 6 19.5v-15A2.5 2.5 0 0 1 8.5 2Z', 'M11 18.5h2'], '1.8'));\n overlay.appendChild(badge);\n well.append(qr, overlay);\n\n const status = el('div', cx('status'));\n const dot = el('span', cx('dot'));\n dot.append(el('i'), el('i'));\n const statusLabel = el('span');\n status.append(dot, statusLabel);\n\n const timer = el('p', cx('timer'));\n\n body.append(mark, title, bodyText, well, status, timer);\n\n // The QR is on screen because this person is being asked to use a phone app,\n // and some of them do not have it yet. Without this the panel reads as \"scan\n // this with something I do not have\", and the flow dead-ends at the one\n // moment it can still be recovered: the same code installs the app.\n const help = el('div', cx('help'));\n help.append(el('p', cx('help-title'), t.noIdTitle), el('p', cx('help-body'), t.noIdBody));\n\n const footer = el('div', cx('footer'));\n const cancelBtn = el('button', cx('cancel'), t.cancel);\n cancelBtn.type = 'button';\n // The line at the foot is a link to ZOREAL itself, in a new tab so the\n // login on this page is not abandoned. The referrer is passed on purpose.\n const secured = el('a', cx('secured'));\n secured.href = 'https://zoreal.com';\n secured.target = '_blank';\n secured.rel = 'noopener';\n secured.append(strokeIcon(13, ['M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z', 'm9 12 2 2 4-4']), document.createTextNode(t.secured));\n footer.append(cancelBtn, secured);\n\n card.append(closeBtn, body, help, footer);\n scrim.appendChild(card);\n\n // A deadline, not a decremented counter: background tabs throttle timers, so\n // a counter that subtracts one per tick comes back lying about the time left.\n const serverMs = typeof state.expiresIn === 'number' ? state.expiresIn * 1000 : Infinity;\n const deadline = Date.now() + Math.min(timeoutMs, serverMs);\n\n // The frame swap. The provider renders a new code every few seconds and each\n // state can carry a new qrUrl. Assigning it straight to the visible <img>\n // blanks the image until the new bytes arrive, and on a slow link that is a\n // flicker on the one thing the person is trying to scan. So the next frame\n // loads off screen first and is swapped in once it has arrived; the browser\n // serves the swap from the fetch it just made while the preload is still\n // held. A frame that fails to load is dropped, the next state brings\n // another. A frame superseded while still loading is dropped too: the newer\n // one is the current code. Once the code is spent (data-spent) nothing\n // swaps any more; the blurred image behind the phone glyph is the last one.\n let shown = state.qrUrl;\n let loading: HTMLImageElement | null = null;\n const spent = () => qr.dataset.spent === 'true';\n\n const showFrame = (url: string) => {\n if (url === shown || spent()) return;\n if (!shown) {\n // Nothing on screen yet, so there is no flash to avoid.\n qr.src = url;\n shown = url;\n return;\n }\n const next = new Image();\n loading = next;\n next.onload = () => {\n if (loading !== next || spent()) return;\n loading = null;\n qr.src = url;\n shown = url;\n };\n next.onerror = () => {\n if (loading === next) loading = null;\n };\n next.src = url;\n };\n\n const paint = (s: PairingState) => {\n // `claimed` = the request is waiting in the holder's app; `enrolling` = a\n // first-time holder finishing setup. In both the QR is spent and the action\n // has moved to the phone.\n const settled = s.status === 'claimed' || s.status === 'enrolling';\n title.textContent = settled ? t.titleApprove : titleFor(t, options.intent ?? 'sign-in');\n bodyText.textContent =\n s.status === 'enrolling' ? t.bodyEnrolling : settled ? t.bodyApprove : t.bodyScan;\n statusLabel.textContent = settled ? t.waitingApproval : t.waiting;\n qr.dataset.spent = String(settled);\n well.dataset.spent = String(settled);\n overlay.style.display = settled ? '' : 'none';\n if (s.qrUrl) showFrame(s.qrUrl);\n };\n\n const tick = () => {\n const left = Math.max(0, Math.ceil((deadline - Date.now()) / 1000));\n timer.textContent = interpolate(t.expiresIn, mmss(left));\n timer.dataset.urgent = String(left <= URGENT_SECONDS);\n if (left === 0) {\n // Stop the tick before cancelling: close() clears it too, but a timer\n // still firing cancel once a second in between is a race to inherit.\n window.clearInterval(interval);\n options.onCancel();\n }\n };\n\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') options.onCancel();\n };\n\n const close = () => {\n if (closed) return;\n closed = true;\n loading = null;\n window.clearInterval(interval);\n document.removeEventListener('keydown', onKey);\n document.body.style.overflow = previousOverflow;\n scrim.remove();\n };\n\n // Every dismissal is the same behaviour: abort the poll, close. An orphaned\n // poll is how a request gets cancelled for over-polling.\n closeBtn.addEventListener('click', options.onCancel);\n cancelBtn.addEventListener('click', options.onCancel);\n scrim.addEventListener('click', (e) => {\n if (e.target === scrim) options.onCancel();\n });\n document.addEventListener('keydown', onKey);\n\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n\n paint(state);\n tick();\n const interval = window.setInterval(tick, 1000);\n\n document.body.appendChild(scrim);\n closeBtn.focus();\n\n return { update: paint, close };\n}\n","/**\n * PKCE, S256 only: mandatory for every client, confidential ones included.\n * There is no plain fallback and there must never be one; a provider seeing\n * method=plain is seeing a bug or an attack.\n */\n\nconst VERIFIER_BYTES = 32; // 43 base64url chars, the RFC 7636 minimum length\n\nconst base64url = (bytes: Uint8Array): string =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\nexport function generateVerifier(): string {\n const bytes = new Uint8Array(VERIFIER_BYTES);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\nexport async function challengeS256(verifier: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return base64url(new Uint8Array(digest));\n}\n\n/**\n * The same challenge, computed synchronously.\n *\n * The same-device sign-in is a navigation the browser must see as the\n * person's own tap, and an `await` between the tap and the navigation is\n * what breaks that: WebCrypto only digests asynchronously, so the digest is\n * done here by hand. SHA-256 as in FIPS 180-4, verified against the RFC\n * 7636 vector and against WebCrypto in the tests.\n */\nexport function challengeS256Sync(verifier: string): string {\n return base64url(sha256(new TextEncoder().encode(verifier)));\n}\n\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst rotr = (x: number, n: number): number => (x >>> n) | (x << (32 - n));\n\nexport function sha256(message: Uint8Array): Uint8Array {\n const H = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n ]);\n const length = message.length;\n const padded = new Uint8Array(((length + 9 + 63) >> 6) << 6);\n padded.set(message);\n padded[length] = 0x80;\n const view = new DataView(padded.buffer);\n const bits = length * 8;\n view.setUint32(padded.length - 8, Math.floor(bits / 0x100000000));\n view.setUint32(padded.length - 4, bits >>> 0);\n\n const W = new Uint32Array(64);\n for (let offset = 0; offset < padded.length; offset += 64) {\n for (let i = 0; i < 16; i++) W[i] = view.getUint32(offset + i * 4);\n for (let i = 16; i < 64; i++) {\n const w15 = W[i - 15];\n const w2 = W[i - 2];\n const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);\n const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);\n W[i] = (W[i - 16] + s0 + W[i - 7] + s1) >>> 0;\n }\n let [a, b, c, d, e, f, g, h] = H;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (h + S1 + ch + K[i] + W[i]) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (S0 + maj) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + t1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) >>> 0;\n }\n H[0] = (H[0] + a) >>> 0;\n H[1] = (H[1] + b) >>> 0;\n H[2] = (H[2] + c) >>> 0;\n H[3] = (H[3] + d) >>> 0;\n H[4] = (H[4] + e) >>> 0;\n H[5] = (H[5] + f) >>> 0;\n H[6] = (H[6] + g) >>> 0;\n H[7] = (H[7] + h) >>> 0;\n }\n const out = new Uint8Array(32);\n const outView = new DataView(out.buffer);\n for (let i = 0; i < 8; i++) outView.setUint32(i * 4, H[i]);\n return out;\n}\n\nexport function generateState(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\n/**\n * A pairing token of this package's own choosing, for the same-device\n * navigation: the provider answers a navigation with nothing the page could\n * read, so the page names the pairing it will poll. Same shape as a token\n * the provider mints, 32 letters and digits from the CSPRNG.\n */\nconst TOKEN_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\nexport function generateRequestId(): string {\n let out = '';\n const bytes = new Uint8Array(64);\n while (out.length < 32) {\n crypto.getRandomValues(bytes);\n for (const byte of bytes) {\n // Rejection sampling: 62 does not divide 256, so bytes past the last\n // full multiple are thrown away rather than folded, which would bias.\n if (byte >= 248 || out.length === 32) continue;\n out += TOKEN_ALPHABET[byte % 62];\n }\n }\n return out;\n}\n","/**\n * The one flow, as an imperative handle. This is the same state machine the\n * React SDK's hook runs, without the React: start a pairing, surface it for\n * rendering through onState, poll, and finish per mode. Browser-direct\n * exchanges the code here (public client, PKCE, no secret) and hands over an\n * ID token; auth-code hands the code and the PKCE verifier to the caller,\n * whose backend does the exchange with its client authentication.\n *\n * A framework wrapper owns exactly two things: calling startLogin on the\n * user's gesture, and rendering what onState carries. Everything else -\n * PKCE, state, nonce, cadence, cancellation - lives here.\n */\n\nimport { unsafeClaims } from './jwt';\nimport {\n FlowAbandonedError,\n OAuthFlowError,\n exchangeCode,\n isMobileUserAgent,\n pollUntilApproved,\n sameDeviceStartUrl,\n startPairing,\n} from './pairing';\nimport { resolveIntent } from './intent';\nimport { mountPairingModal, type PairingModalHandle } from './modal';\nimport {\n challengeS256,\n challengeS256Sync,\n generateRequestId,\n generateState,\n generateVerifier,\n} from './pkce';\nimport { DEFAULT_ISSUER, DEFAULT_QR_REFRESH_SECONDS } from './wire';\nimport type {\n AcrValue,\n AuthCodeLoginOptions,\n BrowserDirectLoginOptions,\n LoginHandle,\n PairingState,\n SelectBy,\n ZorealCodeResponse,\n ZorealCredentialResponse,\n} from './types';\n\nexport function startLogin(\n options: BrowserDirectLoginOptions\n): LoginHandle<ZorealCredentialResponse>;\nexport function startLogin(options: AuthCodeLoginOptions): LoginHandle<ZorealCodeResponse>;\nexport function startLogin(\n options: BrowserDirectLoginOptions | AuthCodeLoginOptions\n): LoginHandle<ZorealCredentialResponse> | LoginHandle<ZorealCodeResponse> {\n if ('ux_mode' in options && options.ux_mode === 'redirect') {\n // The popup shape only: the code and PKCE verifier resolve the promise\n // and go from there to your backend over TLS. A redirect would have to\n // carry the verifier in a URL, which is a credential in every access\n // log on the path. Refused loudly rather than implemented badly.\n throw new Error(\n \"@zoreal/oauth2-js: ux_mode 'redirect' is not supported. Use the default \" +\n \"'popup' shape and post the code and code_verifier from the resolved \" +\n 'promise to your backend.'\n );\n }\n\n const flow = options.flow ?? 'browser-direct';\n const issuer = options.issuer ?? DEFAULT_ISSUER;\n const controller = new AbortController();\n\n // Decided before the pairing is created, not after: the provider binds the\n // surface at creation, either moving QR frames or a start token that only\n // the opened link carries, and will not serve the other one later. It\n // depends only on the options and the user agent, so there is nothing to\n // wait for.\n const useAppLink =\n options.display === 'link' || (options.display !== 'qr' && isMobileUserAgent());\n const intent = resolveIntent(options.intent, options.scope, options.acr_values);\n\n const surface: {\n requestId?: string;\n pairUrl?: string;\n qrUrl?: string;\n appLink?: boolean;\n } = {};\n\n const cancel = () => controller.abort();\n\n // Mounted lazily once the provider has created a pairing, and torn down on\n // every exit from `run` below: resolution, refusal, and cancel alike.\n let modal: PairingModalHandle | null = null;\n // The QR frame refresh, once there is one. Stopped on every exit from `run`,\n // on cancel, and the moment the pairing leaves `pending`: from then on the\n // code is spent and a moving image would only distract.\n let stopRefresh: () => void = () => {};\n controller.signal.addEventListener('abort', () => stopRefresh());\n const teardown = () => {\n stopRefresh();\n modal?.close();\n modal = null;\n };\n\n const run = async (): Promise<ZorealCredentialResponse | ZorealCodeResponse> => {\n const verifier = generateVerifier();\n const state = generateState();\n const nonce = generateState();\n\n try {\n let code: string;\n let selectBy: SelectBy = 'device';\n\n if (useAppLink && typeof window !== 'undefined') {\n // THE TAP IS THE NAVIGATION. Nothing is awaited between the caller's\n // click and the assignment below: a browser hands a universal link to\n // an app only inside a navigation the person began, and an await here\n // would put the navigation outside it, where the link loads as a web\n // page instead (see sameDeviceStartUrl). The provider creates the\n // pairing and redirects to the link; the page stays and polls the\n // token it chose, tolerating \"no such pairing\" for as long as the\n // provider may still be answering the navigation. No modal: there is\n // no code to scan and the page is the button that was tapped.\n const requestId = generateRequestId();\n const startUrl = sameDeviceStartUrl(issuer, {\n client_id: options.clientId,\n scope: options.scope ?? 'openid',\n state,\n nonce,\n code_challenge: challengeS256Sync(verifier),\n redirect_uri:\n flow === 'auth-code' ? (options as AuthCodeLoginOptions).redirect_uri : undefined,\n acr_values: Array.isArray(options.acr_values)\n ? options.acr_values.join(' ')\n : options.acr_values,\n max_age: options.max_age,\n prompt: options.prompt,\n locale: options.locale,\n request_id: requestId,\n origin: window.location.origin,\n });\n selectBy = 'app_link';\n surface.requestId = requestId;\n surface.pairUrl = startUrl;\n surface.appLink = true;\n const withSurface = (s: PairingState): PairingState => ({\n ...s,\n pairUrl: startUrl,\n appLink: true,\n intent,\n cancel,\n });\n options.onState?.(withSurface({ status: 'pending' }));\n window.location.assign(startUrl);\n\n code = await pollUntilApproved(\n issuer,\n requestId,\n (s) => options.onState?.(withSurface(s)),\n controller.signal,\n { tolerateUnknownUntil: Date.now() + 15_000 }\n );\n } else {\n const started = await startPairing(\n issuer,\n {\n client_id: options.clientId,\n scope: options.scope ?? 'openid',\n state,\n nonce,\n code_challenge: await challengeS256(verifier),\n redirect_uri:\n flow === 'auth-code' ? (options as AuthCodeLoginOptions).redirect_uri : undefined,\n acr_values: Array.isArray(options.acr_values)\n ? options.acr_values.join(' ')\n : options.acr_values,\n max_age: options.max_age,\n prompt: options.prompt,\n locale: options.locale,\n display: 'qr',\n },\n controller.signal\n );\n\n if ('code' in started) {\n // prompt=none resolved silently: consented sector, live session.\n code = started.code;\n selectBy = 'session';\n } else {\n selectBy = 'qr';\n\n const requestId = started.request_id;\n const qrBase = `${issuer}/pair/${encodeURIComponent(requestId)}/qr.svg`;\n\n // The image moves while the pairing is pending: the provider renders\n // a new frame every few seconds and refuses an old one, which is what\n // makes a screenshot of the code useless. This package only has to\n // re-fetch it on time. Nothing to move on an app-link hand-off, and\n // nothing to move when the provider says it bound the static code.\n const animated = started.display !== 'legacy';\n const qrRefreshSeconds = !animated\n ? undefined\n : typeof started.qr_refresh_seconds === 'number' && started.qr_refresh_seconds > 0\n ? started.qr_refresh_seconds\n : DEFAULT_QR_REFRESH_SECONDS;\n\n surface.requestId = requestId;\n surface.pairUrl = started.pair_url;\n surface.qrUrl = qrBase;\n surface.appLink = false;\n\n // Everything a pairing UI needs, on every state it sees. The modal\n // below renders from it, and so does a caller who has opted out with\n // pairingUI: 'none'. Read at call time rather than captured once,\n // because qrUrl changes underneath while the pairing is pending.\n const withSurface = (s: PairingState): PairingState => ({\n ...s,\n pairUrl: surface.pairUrl,\n qrUrl: surface.qrUrl,\n qrRefreshSeconds,\n appLink: false,\n intent,\n cancel,\n });\n\n // The last state the provider reported, so a frame refresh can emit\n // it again with only the image changed.\n let lastPolled: PairingState = { status: 'pending', expiresIn: started.expires_in };\n const initial = withSurface(lastPolled);\n\n // The initial state, immediately: the first poll response is one\n // round-trip away, and a UI that waits for it opens visibly empty.\n options.onState?.(initial);\n\n if ((options.pairingUI ?? 'modal') === 'modal') {\n modal = mountPairingModal(initial, {\n onCancel: cancel,\n intent,\n locale: options.locale,\n theme: options.theme,\n timeoutMs: options.pairingTimeoutMs,\n });\n }\n\n\n if (qrRefreshSeconds !== undefined) {\n // A deadline and a setTimeout chain, not setInterval. Background\n // tabs throttle timers, and an interval that comes back from a\n // throttled minute fires its backlog in one burst: several frames\n // in one tick, for nothing. Here each frame is stamped with the\n // clock when it is emitted, the next deadline is set from that\n // moment, and a tab becoming visible with its deadline already past\n // gets a current frame at once rather than whenever the throttled\n // timer gets around to it.\n const periodMs = qrRefreshSeconds * 1000;\n let due = Date.now() + periodMs;\n let timer: ReturnType<typeof setTimeout> | undefined;\n // A flag, not just a cleared timer: a caller may cancel() from\n // inside the onState below, and the stop then lands in the middle of\n // emit. Without this, the line after it would schedule the next\n // frame and the loop would outlive the login that ended it.\n let stopped = false;\n\n const emit = () => {\n timer = undefined;\n surface.qrUrl = `${qrBase}?t=${Date.now()}`;\n const next = withSurface(lastPolled);\n modal?.update(next);\n options.onState?.(next);\n if (stopped) return;\n due = Date.now() + periodMs;\n timer = setTimeout(emit, periodMs);\n };\n const onVisible = () => {\n if (document.visibilityState === 'visible' && timer !== undefined && Date.now() >= due) {\n clearTimeout(timer);\n emit();\n }\n };\n const hasDocument = typeof document !== 'undefined';\n if (hasDocument) document.addEventListener('visibilitychange', onVisible);\n\n stopRefresh = () => {\n stopped = true;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n if (hasDocument) document.removeEventListener('visibilitychange', onVisible);\n stopRefresh = () => {};\n };\n timer = setTimeout(emit, Math.max(0, due - Date.now()));\n }\n\n code = await pollUntilApproved(\n issuer,\n requestId,\n (s) => {\n lastPolled = s;\n // Anything but pending means the code is spent: claimed and\n // enrolling have moved the action to the phone, the rest are\n // terminal. Stop before emitting so no frame lands after this.\n if (s.status !== 'pending') stopRefresh();\n const next = withSurface(s);\n modal?.update(next);\n options.onState?.(next);\n },\n controller.signal\n );\n }\n\n teardown();\n\n }\n\n if (flow === 'auth-code') {\n const response: ZorealCodeResponse = {\n code,\n scope: options.scope ?? 'openid',\n app_state: options.app_state,\n code_verifier: verifier,\n nonce,\n };\n return response;\n }\n\n const tokens = await exchangeCode(issuer, {\n code,\n code_verifier: verifier,\n client_id: options.clientId,\n });\n const claims = unsafeClaims(tokens.id_token);\n const response: ZorealCredentialResponse = {\n credential: tokens.id_token,\n clientId: options.clientId,\n select_by: selectBy,\n acr: (claims.acr as AcrValue) ?? 'zoreal.device',\n };\n return response;\n } catch (e) {\n teardown();\n // The taxonomy the promise rejects with, and nothing else:\n // OAuthFlowError the provider refused; reason verbatim\n // FlowAbandonedError a human outcome, or a failure that never\n // reached the provider (network, unknown)\n // AbortError the caller's own cancel()\n if (e instanceof DOMException && e.name === 'AbortError') throw e;\n if (e instanceof OAuthFlowError || e instanceof FlowAbandonedError) throw e;\n throw new FlowAbandonedError({\n type: 'unknown',\n description: e instanceof Error ? e.message : String(e),\n });\n }\n };\n\n const promise = run();\n // A caller driving everything from onState and cancel() may never attach a\n // rejection handler; this no-op one keeps a cancelled login from surfacing\n // as an unhandled rejection. The caller's own catch still sees the error.\n promise.catch(() => {});\n\n return {\n promise: promise as Promise<ZorealCredentialResponse> & Promise<ZorealCodeResponse>,\n cancel,\n get requestId() {\n return surface.requestId;\n },\n get pairUrl() {\n return surface.pairUrl;\n },\n get qrUrl() {\n return surface.qrUrl;\n },\n get appLink() {\n return surface.appLink;\n },\n };\n}\n"],"mappings":"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","names":["el","response"]}
1
+ {"version":3,"sources":["../src/jwt.ts","../src/wire.ts","../src/pairing.ts","../src/intent.ts","../src/i18n.ts","../src/styles.ts","../src/modal.ts","../src/pkce.ts","../src/login.ts"],"sourcesContent":["/**\n * Reads claims OUT of an ID token without verifying it.\n *\n * That is not a shortcut, it is the design: this code runs in a browser the\n * threat model assumes is attacker-controlled, so a signature check here\n * proves nothing to anyone. The token is verified where verification means\n * something: server-side against the JWKS. What this parser feeds is\n * convenience fields (acr on the response object) that the types document as\n * convenience, with the token staying the authority.\n */\n\nexport function unsafeClaims(idToken: string): Record<string, unknown> {\n try {\n const payload = idToken.split('.')[1] ?? '';\n const b64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);\n return JSON.parse(\n new TextDecoder().decode(Uint8Array.from(atob(padded), (c) => c.charCodeAt(0)))\n );\n } catch {\n return {};\n }\n}\n","/**\n * The wire protocol between this package and the ZOREAL OpenID Provider.\n *\n * VERSIONED: a shipped version keeps working until the provider explicitly\n * refuses it, and when it does, the reason is surfaced verbatim. Both the wire\n * version and the package version travel on every pairing request so a refusal\n * can be precise.\n *\n * Endpoints, all relative to the issuer and all CORS-gated on the client's\n * authorized JavaScript origins (the dashboard):\n *\n * POST /pair start a pairing request. Body carries the\n * authorize parameters plus PKCE challenge,\n * and `display`: which pairing surface this\n * package is about to show, 'qr' or 'link',\n * decided before the request is made. Returns\n * { request_id, pair_url, expires_in, display,\n * qr_refresh_seconds } or, for prompt=none\n * with a live consented session, { code }\n * immediately. `display` is echoed as the\n * provider bound it ('legacy' for a request\n * that sent none, which gets the static code\n * older versions of this package showed).\n * `qr_refresh_seconds` comes with 'qr' and is\n * how often to re-fetch the image; 3 today.\n * A 'link' pairing's pair_url carries\n * ?t=<start_token>: it can only be claimed by\n * the app that opened that exact link, and\n * the provider never renders a QR for it, so\n * nobody can turn a same-device link into a\n * static code to relay.\n * GET /pair/start the same-device sign-in as a NAVIGATION:\n * the /pair parameters as a query, plus\n * request_id (the page's own token) and\n * origin; answered with a redirect to the\n * pairing's universal link, inside the tap\n * GET /pair/:id/status poll: pending | claimed |\n * approved (with code) | denied | expired |\n * enrolling. Over-polling cancels the request\n * rather than throttling it, so the cadence\n * below is not a suggestion.\n * GET /pair/:id/qr.svg the QR image, rendered by the provider so\n * this package draws nothing and keeps zero\n * dependencies. For a 'qr' pairing it encodes\n * the CURRENT FRAME, the pairing URL with\n * ?f=<time>.<hmac>: `time` is whole seconds\n * since the pairing was created on the\n * provider's clock, `hmac` is keyed with a\n * secret only the provider holds. Served\n * Cache-Control: no-store, only while the\n * pairing is pending. The app sends the frame\n * it scanned with its claim, and the provider\n * refuses a frame older than 30 seconds, so a\n * screenshot of the code is dead on arrival.\n * This package re-fetches the image every\n * `qr_refresh_seconds` with a cache-busting\n * ?t=<Date.now()>. A 'link' pairing has no\n * image (404).\n * POST /token the code exchange. Browser-direct mode uses\n * it directly with PKCE and no client secret;\n * auth-code mode leaves it to the RP backend.\n */\n\nexport const WIRE_VERSION = 1;\nexport const SDK_VERSION = '0.1.19';\nexport const SDK_NAME = '@zoreal/oauth2-js';\nexport const DEFAULT_ISSUER = 'https://id.zoreal.com';\n\n/** Pending TTL is short. Poll gently; over-polling cancels the request. */\nexport const POLL_INTERVAL_MS = 2000;\n/** Enrolling extends the window well beyond a normal login; poll slower. */\nexport const POLL_INTERVAL_ENROLLING_MS = 5000;\n/**\n * How often the QR image is re-fetched while a pairing is pending, when the\n * provider does not say. The provider's own `qr_refresh_seconds` wins when\n * present. Refreshing is what makes the code on screen move, and a frame the\n * provider refuses after 30 seconds is what makes a screenshot of it useless.\n */\nexport const DEFAULT_QR_REFRESH_SECONDS = 3;\n\n/** The pairing surface this package will show, sent on POST /pair. */\nexport type PairDisplay = 'qr' | 'link';\n\nexport interface PairCreated {\n request_id: string;\n /**\n * https://zoreal.com/login/<request_id>. For a 'link' pairing the URL also\n * carries ?t=<start_token>, and only the app that opens that exact link can\n * claim it. Navigate to it verbatim.\n */\n pair_url: string;\n expires_in: number;\n /**\n * The surface the provider bound, echoed back. 'legacy' means the request\n * sent no `display` (an older version of this package) and got the static\n * code. Absent from a provider that predates the field, which also serves\n * the static code.\n */\n display?: PairDisplay | 'legacy';\n /** 'qr' pairings only: how often to re-fetch qr.svg. Defaults to 3 when absent. */\n qr_refresh_seconds?: number;\n}\n\nexport interface PairImmediate {\n /** prompt=none resolved silently: consented sector, live session. */\n code: string;\n}\n\nexport type PairStartResponse = PairCreated | PairImmediate;\n\nexport interface PairStatusResponse {\n status: 'pending' | 'claimed' | 'approved' | 'denied' | 'expired' | 'cancelled' | 'enrolling';\n code?: string;\n expires_in?: number;\n enrolment_deadline?: number;\n /**\n * The provider's reason on denial or refusal. Surfaced verbatim, never\n * rewritten: it is also how a site's own policy reaches the person, such\n * as a sign-in refused because the phone that approved it was in a\n * different country than the browser.\n */\n error?: string;\n error_description?: string;\n}\n\nexport interface TokenResponse {\n id_token: string;\n access_token?: string;\n token_type?: string;\n expires_in?: number;\n scope?: string;\n error?: string;\n error_description?: string;\n}\n","/**\n * The pairing channel, client side. wire.ts pins the endpoints.\n *\n * The browser polls; the phone never talks to the browser. Everything here is\n * therefore plain fetch against the issuer, CORS-gated on the client's\n * authorized origins, with the poll cadence fixed: the provider cancels an\n * over-polling request rather than throttling it, so a \"retry\n * faster on error\" strategy here would kill the login it is trying to save.\n */\n\nimport {\n POLL_INTERVAL_ENROLLING_MS,\n POLL_INTERVAL_MS,\n SDK_NAME,\n SDK_VERSION,\n WIRE_VERSION,\n type PairDisplay,\n type PairStartResponse,\n type PairStatusResponse,\n type TokenResponse,\n} from './wire';\nimport type { ErrorCode, NonOAuthError, PairingState } from './types';\n\nexport class OAuthFlowError extends Error {\n constructor(\n public error: ErrorCode,\n public description?: string\n ) {\n super(description ?? error);\n }\n}\n\nexport class FlowAbandonedError extends Error {\n constructor(public reason: NonOAuthError) {\n super(reason.description ?? reason.type);\n }\n}\n\nexport interface StartPairingParams {\n client_id: string;\n scope: string;\n state: string;\n nonce: string;\n code_challenge: string;\n redirect_uri?: string;\n acr_values?: string;\n max_age?: number;\n prompt?: string;\n locale?: string;\n /**\n * Which surface the caller will show: 'qr' binds the pairing to moving QR\n * frames, 'link' to a start token only the opened link carries. Decided\n * before the request, because the provider binds it at creation and will\n * not serve the other surface afterwards. Omitting it gets the static code.\n */\n display?: PairDisplay;\n}\n\nasync function parseJson(response: Response): Promise<Record<string, unknown>> {\n try {\n return (await response.json()) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport async function startPairing(\n issuer: string,\n params: StartPairingParams,\n signal?: AbortSignal\n): Promise<PairStartResponse> {\n const response = await fetch(`${issuer}/pair`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...params,\n code_challenge_method: 'S256',\n wire_version: WIRE_VERSION,\n sdk: `${SDK_NAME}/${SDK_VERSION}`,\n }),\n signal,\n });\n\n const body = await parseJson(response);\n if (!response.ok) {\n // The provider's words, verbatim. A refused package version arrives here,\n // and rewriting its reason would hide the only signal telling an integrator\n // to upgrade.\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n (body.error_description as string) ?? `The provider refused the request (${response.status})`\n );\n }\n return body as unknown as PairStartResponse;\n}\n\n/**\n * The same-device sign-in, as a URL to NAVIGATE to, not to fetch.\n *\n * A phone's browser hands a universal link to an app only inside a\n * navigation the person began, and a page that sets its location after a\n * network round trip has left that navigation behind: the link then loads\n * as a web page. So on a phone this package fetches nothing on the tap. The\n * tap itself navigates to the provider's start endpoint with what /pair\n * would have been sent, the provider creates the link pairing and answers\n * with a redirect to its universal link, still inside the person's\n * navigation, and the app opens. The page is not unloaded when it does, and\n * polls the pairing by the `request_id` it chose here. With no app installed\n * the same redirect lands on the page that installs it.\n */\nexport function sameDeviceStartUrl(\n issuer: string,\n params: StartPairingParams & { request_id: string; origin: string }\n): string {\n const query = new URLSearchParams();\n const all: Record<string, unknown> = {\n ...params,\n code_challenge_method: 'S256',\n wire_version: WIRE_VERSION,\n sdk: `${SDK_NAME}/${SDK_VERSION}`,\n };\n for (const [key, value] of Object.entries(all)) {\n if (value === undefined || value === null || value === '') continue;\n query.set(key, String(value));\n }\n return `${issuer}/pair/start?${query.toString()}`;\n}\n\nconst sleep = (ms: number, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new DOMException('aborted', 'AbortError'));\n return;\n }\n // The listener comes off when the sleep ends. One poll is one sleep, and a\n // pairing is dozens of polls on the SAME signal, so a listener left behind\n // per sleep accumulates for as long as the login is open.\n const onAbort = () => {\n clearTimeout(t);\n reject(new DOMException('aborted', 'AbortError'));\n };\n const t = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/**\n * Polls until the request resolves. Returns the authorization code.\n * Throws FlowAbandonedError for the human outcomes (denied, expired,\n * enrolment abandoned) and OAuthFlowError for protocol ones.\n */\n/** How long a same-device navigation is given to begin before the first poll. */\nconst SETTLE_MS = 1500;\n/** Consecutive network failures a poll rides out before it is a failure. */\nconst NETWORK_FAILURES_TOLERATED = 4;\n\nexport async function pollUntilApproved(\n issuer: string,\n requestId: string,\n onState?: (state: PairingState) => void,\n signal?: AbortSignal,\n options: {\n /**\n * Same-device navigation only. The page starts polling while the\n * provider is still answering the navigation that creates the pairing,\n * so a \"no such pairing\" answer before this instant (epoch ms) is the\n * pairing not existing YET, and is read as pending.\n */\n tolerateUnknownUntil?: number;\n } = {}\n): Promise<string> {\n const settlingUntil = options.tolerateUnknownUntil ?? 0;\n let networkFailures = 0;\n let last: PairingState = { status: 'pending' };\n // Let a same-device navigation begin before the first poll, so the poll\n // is not the request the navigation cancels.\n if (settlingUntil > Date.now()) await sleep(SETTLE_MS, signal);\n for (;;) {\n let response: Response;\n try {\n response = await fetch(`${issuer}/pair/${encodeURIComponent(requestId)}/status`, {\n signal,\n });\n networkFailures = 0;\n } catch (e) {\n if (e instanceof DOMException && e.name === 'AbortError') throw e;\n // A navigation cancels the page's requests while it is in flight, and\n // the same-device sign-in IS a navigation: the first poll after the tap\n // is killed by it (Safari reports \"Load failed\"), even though the tab\n // stays once the app has taken the link. A network failure while the\n // navigation settles is therefore not an outcome, and one in the\n // background, on a phone that has just switched apps, seldom is either:\n // only a run of them is.\n networkFailures += 1;\n if (settlingUntil > Date.now() || networkFailures <= NETWORK_FAILURES_TOLERATED) {\n onState?.(last);\n await sleep(POLL_INTERVAL_MS, signal);\n continue;\n }\n throw e;\n }\n const body = (await parseJson(response)) as unknown as PairStatusResponse;\n\n if (response.status === 404 && (options.tolerateUnknownUntil ?? 0) > Date.now()) {\n // Same-device navigation: the pairing is being created by the\n // navigation this page is polling ahead of; not there YET is pending.\n onState?.({ status: 'pending' });\n await sleep(POLL_INTERVAL_MS, signal);\n continue;\n }\n\n if (!response.ok) {\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n body.error_description ?? `Pairing status failed (${response.status})`\n );\n }\n\n last = {\n status: body.status,\n expiresIn: body.expires_in,\n enrolmentDeadline: body.enrolment_deadline,\n };\n onState?.(last);\n\n switch (body.status) {\n case 'approved':\n if (!body.code) {\n throw new OAuthFlowError('server_error', 'approved with no authorization code');\n }\n return body.code;\n case 'denied':\n throw new FlowAbandonedError({ type: 'request_denied', description: body.error_description });\n case 'expired':\n throw new FlowAbandonedError({ type: 'request_expired', description: body.error_description });\n case 'cancelled':\n // The provider cancels an over-polled or abandoned request outright\n // (its pairing rows have a real cancelled state); a poll that treats\n // it as unknown spins on a dead request forever.\n throw new FlowAbandonedError({\n type: 'request_expired',\n description: body.error_description ?? 'the provider cancelled the pairing request',\n });\n case 'enrolling':\n await sleep(POLL_INTERVAL_ENROLLING_MS, signal);\n break;\n default:\n await sleep(POLL_INTERVAL_MS, signal);\n }\n }\n}\n\n/**\n * The code exchange, browser-direct mode only: a public client, PKCE and no\n * secret. What comes back can only ever be the pseudonymous tier, by\n * construction rather than by rule: personal data lives at /userinfo behind an\n * access token this mode is never issued, because personal-data scopes are\n * refused for public clients at the pairing step.\n */\nexport async function exchangeCode(\n issuer: string,\n input: { code: string; code_verifier: string; client_id: string }\n): Promise<TokenResponse> {\n const response = await fetch(`${issuer}/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: input.code,\n code_verifier: input.code_verifier,\n client_id: input.client_id,\n }),\n });\n\n const body = (await parseJson(response)) as unknown as TokenResponse;\n if (!response.ok || body.error) {\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n body.error_description ?? `Token exchange failed (${response.status})`\n );\n }\n return body;\n}\n\n/** A mobile user agent gets the app link, not a QR of its own screen. */\nexport function isMobileUserAgent(): boolean {\n if (typeof navigator === 'undefined') return false;\n return /android|iphone|ipad|ipod/i.test(navigator.userAgent);\n}\n","import type { PairingStrings } from './i18n';\nimport type { LoginIntent } from './types';\n\n/**\n * The scopes a relying party asks for in order to know who is signing in:\n * the identifier, and how to reach and address the person. Anything beyond\n * these is an attribute read from the identity document, and the dialog\n * should say that it is about to be shared rather than call it a sign-in.\n */\nconst SIGN_IN_SCOPES = new Set(['openid', 'email', 'profile.name']);\n\n/**\n * What the pairing dialog says it is for. An explicit intent wins. Otherwise\n * a request for document attributes is an identification; a request for the\n * identifier alone with a liveness capture is a presence check, since nothing\n * is being logged into; everything else is a sign-in.\n */\nexport function resolveIntent(\n intent: LoginIntent | undefined,\n scope: string | undefined,\n acrValues: string | readonly string[] | undefined\n): LoginIntent {\n if (intent) return intent;\n const scopes = (scope ?? 'openid').split(/\\s+/).filter(Boolean);\n if (scopes.some((s) => !SIGN_IN_SCOPES.has(s))) return 'identify';\n const acr = typeof acrValues === 'string' ? acrValues.split(/\\s+/) : (acrValues ?? []);\n if (scopes.every((s) => s === 'openid') && acr.includes('zoreal.live')) return 'presence';\n return 'sign-in';\n}\n\n/** The unscanned-code title for an intent. */\nexport function titleFor(t: PairingStrings, intent: LoginIntent): string {\n if (intent === 'identify') return t.titleIdentify;\n if (intent === 'presence') return t.titlePresence;\n return t.title;\n}\n","/**\n * Pairing-modal copy, carried by the SDK.\n *\n * The modal is rendered by this package, so its strings have to ship with it:\n * an integrator cannot translate a component they never write, and asking every\n * one of them to re-supply the same fifteen strings is how a sign-in screen\n * ends up half-English in production.\n *\n * No i18n runtime. A frozen record and one `{time}` substitution is the whole\n * requirement, and a dependency here would be inherited by every host app.\n *\n * Locales match the set ZOREAL's own pairing page serves, so the phone and the\n * browser say the same thing in the same language. `strings()` resolves BCP 47\n * down to that set; anything unknown falls back to English rather than\n * rendering a key.\n */\n\nexport interface PairingStrings {\n /** Dialog title while the code is still unscanned. */\n title: string;\n /** Dialog title when the request is for verified identity attributes. */\n titleIdentify: string;\n /** Dialog title when the request is a presence check and not a login. */\n titlePresence: string;\n /** Dialog title once the request is waiting in the app. */\n titleApprove: string;\n bodyScan: string;\n bodyApprove: string;\n bodyEnrolling: string;\n waiting: string;\n waitingApproval: string;\n /** Carries `{time}`, substituted with mm:ss. */\n expiresIn: string;\n secured: string;\n noIdTitle: string;\n noIdBody: string;\n cancel: string;\n close: string;\n qrAlt: string;\n /** The default label of the sign-in button. */\n buttonContinue: string;\n}\n\nconst en: PairingStrings = {\n title: 'Scan to sign in',\n titleIdentify: 'Scan to verify your identity',\n titlePresence: 'Scan to prove you are a real human',\n titleApprove: 'Approve on your phone',\n bodyScan: 'Scan with your phone camera or the ZOREAL ID app.',\n bodyApprove: 'Approve the login in your ZOREAL ID app.',\n bodyEnrolling: 'Finish setting up ZOREAL ID on your phone, then approve the login.',\n waiting: 'Waiting for scan',\n waitingApproval: 'Waiting for approval',\n expiresIn: 'Expires in {time}',\n secured: 'Proof-of-Human verification by ZOREAL',\n noIdTitle: 'No ZOREAL ID yet?',\n noIdBody: 'Scan the same code to download the app and create one for free. It only takes a minute.',\n cancel: 'Cancel',\n close: 'Close',\n qrAlt: 'QR code to sign in with ZOREAL',\n buttonContinue: 'Continue with ZOREAL',\n};\n\nconst TRANSLATIONS: Record<string, PairingStrings> = {\n en,\n sv: {\n title: 'Skanna för att logga in',\n titleIdentify: 'Skanna för att verifiera din identitet',\n titlePresence: 'Skanna för att bevisa att du är en riktig människa',\n titleApprove: 'Godkänn på telefonen',\n bodyScan: 'Skanna med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkänn inloggningen i ZOREAL ID-appen.',\n bodyEnrolling: 'Slutför konfigurationen av ZOREAL ID på telefonen och godkänn sedan inloggningen.',\n waiting: 'Väntar på skanning',\n waitingApproval: 'Väntar på godkännande',\n expiresIn: 'Upphör om {time}',\n secured: 'Proof-of-Human-verifiering av ZOREAL',\n noIdTitle: 'Har du inget ZOREAL ID?',\n noIdBody: 'Skanna samma kod för att ladda ner appen och skapa ett gratis. Det tar bara en minut.',\n cancel: 'Avbryt',\n close: 'Stäng',\n qrAlt: 'QR-kod för att logga in med ZOREAL',\n buttonContinue: 'Fortsätt med ZOREAL',\n },\n es: {\n title: 'Escanea para iniciar sesión',\n titleIdentify: 'Escanea para verificar tu identidad',\n titlePresence: 'Escanea para demostrar que eres una persona real',\n titleApprove: 'Apruébalo en tu teléfono',\n bodyScan: 'Escanea con la cámara de tu teléfono o con la app ZOREAL ID.',\n bodyApprove: 'Aprueba el inicio de sesión en tu app ZOREAL ID.',\n bodyEnrolling: 'Termina de configurar ZOREAL ID en tu teléfono y luego aprueba el inicio de sesión.',\n waiting: 'Esperando el escaneo',\n waitingApproval: 'Esperando aprobación',\n expiresIn: 'Caduca en {time}',\n secured: 'Verificación Proof-of-Human de ZOREAL',\n noIdTitle: '¿Aún no tienes ZOREAL ID?',\n noIdBody: 'Escanea el mismo código para descargar la app y crear una gratis. Solo toma un minuto.',\n cancel: 'Cancelar',\n close: 'Cerrar',\n qrAlt: 'Código QR para iniciar sesión con ZOREAL',\n buttonContinue: 'Continuar con ZOREAL',\n },\n pt: {\n title: 'Digitalize para entrar',\n titleIdentify: 'Digitalize para verificar a sua identidade',\n titlePresence: 'Digitalize para provar que é uma pessoa real',\n titleApprove: 'Aprove no seu telefone',\n bodyScan: 'Digitalize com a câmara do seu telefone ou com a app ZOREAL ID.',\n bodyApprove: 'Aprove o login no app ZOREAL ID.',\n bodyEnrolling: 'Termine de configurar o ZOREAL ID no seu telefone e depois aprove o login.',\n waiting: 'Aguardando digitalização',\n waitingApproval: 'Aguardando aprovação',\n expiresIn: 'Expira em {time}',\n secured: 'Verificação Proof-of-Human da ZOREAL',\n noIdTitle: 'Ainda não tem ZOREAL ID?',\n noIdBody: 'Digitalize o mesmo código para baixar o app e criar uma conta grátis. Leva só um minuto.',\n cancel: 'Cancelar',\n close: 'Fechar',\n qrAlt: 'Código QR para entrar com ZOREAL',\n buttonContinue: 'Continuar com ZOREAL',\n },\n fr: {\n title: 'Scannez pour vous connecter',\n titleIdentify: 'Scannez pour vérifier votre identité',\n titlePresence: 'Scannez pour prouver que vous êtes bien un humain',\n titleApprove: 'Approuvez sur votre téléphone',\n bodyScan: \"Scannez avec l'appareil photo de votre téléphone ou l'app ZOREAL ID.\",\n bodyApprove: 'Approuvez la connexion dans votre app ZOREAL ID.',\n bodyEnrolling: 'Terminez la configuration de ZOREAL ID sur votre téléphone, puis approuvez la connexion.',\n waiting: 'En attente du scan',\n waitingApproval: \"En attente d'approbation\",\n expiresIn: 'Expire dans {time}',\n secured: 'Vérification Proof-of-Human par ZOREAL',\n noIdTitle: \"Pas encore de ZOREAL ID ?\",\n noIdBody: \"Scannez le même code pour télécharger l'app et en créer un gratuitement. Cela prend une minute.\",\n cancel: 'Annuler',\n close: 'Fermer',\n qrAlt: 'Code QR pour se connecter avec ZOREAL',\n buttonContinue: 'Continuer avec ZOREAL',\n },\n de: {\n title: 'Zum Anmelden scannen',\n titleIdentify: 'Scannen, um Ihre Identität zu verifizieren',\n titlePresence: 'Scannen, um zu beweisen, dass Sie ein echter Mensch sind',\n titleApprove: 'Auf dem Handy bestätigen',\n bodyScan: 'Mit der Handykamera oder der ZOREAL ID App scannen.',\n bodyApprove: 'Anmeldung in der ZOREAL ID App bestätigen.',\n bodyEnrolling: 'ZOREAL ID auf dem Handy fertig einrichten und dann die Anmeldung bestätigen.',\n waiting: 'Warten auf Scan',\n waitingApproval: 'Warten auf Bestätigung',\n expiresIn: 'Läuft ab in {time}',\n secured: 'Proof-of-Human-Verifizierung von ZOREAL',\n noIdTitle: 'Noch keine ZOREAL ID?',\n noIdBody: 'Denselben Code scannen, um die App zu laden und kostenlos eine zu erstellen. Dauert nur eine Minute.',\n cancel: 'Abbrechen',\n close: 'Schließen',\n qrAlt: 'QR-Code für die Anmeldung mit ZOREAL',\n buttonContinue: 'Weiter mit ZOREAL',\n },\n ru: {\n title: 'Отсканируйте, чтобы войти',\n titleIdentify: 'Отсканируйте, чтобы подтвердить личность',\n titlePresence: 'Отсканируйте, чтобы доказать, что вы реальный человек',\n titleApprove: 'Подтвердите на телефоне',\n bodyScan: 'Отсканируйте камерой телефона или через приложение ZOREAL ID.',\n bodyApprove: 'Подтвердите вход в приложении ZOREAL ID.',\n bodyEnrolling: 'Завершите настройку ZOREAL ID на телефоне, затем подтвердите вход.',\n waiting: 'Ожидание сканирования',\n waitingApproval: 'Ожидание подтверждения',\n expiresIn: 'Истекает через {time}',\n secured: 'Проверка Proof-of-Human от ZOREAL',\n noIdTitle: 'Ещё нет ZOREAL ID?',\n noIdBody: 'Отсканируйте тот же код, чтобы скачать приложение и создать его бесплатно. Это займёт минуту.',\n cancel: 'Отмена',\n close: 'Закрыть',\n qrAlt: 'QR-код для входа через ZOREAL',\n buttonContinue: 'Продолжить с ZOREAL',\n },\n ja: {\n title: 'スキャンしてログイン',\n titleIdentify: 'スキャンして本人確認',\n titlePresence: 'スキャンして実在の人物であることを証明',\n titleApprove: 'スマートフォンで承認',\n bodyScan: 'スマートフォンのカメラまたはZOREAL IDアプリでスキャンしてください。',\n bodyApprove: 'ZOREAL IDアプリでログインを承認してください。',\n bodyEnrolling: 'スマートフォンでZOREAL IDの設定を完了し、ログインを承認してください。',\n waiting: 'スキャン待ち',\n waitingApproval: '承認待ち',\n expiresIn: '有効期限まで {time}',\n secured: 'ZOREALによるProof-of-Human認証',\n noIdTitle: 'ZOREAL IDをお持ちでないですか?',\n noIdBody: '同じコードをスキャンしてアプリをダウンロードし、無料で作成できます。1分ほどで完了します。',\n cancel: 'キャンセル',\n close: '閉じる',\n qrAlt: 'ZOREALでログインするためのQRコード',\n buttonContinue: 'ZOREALで続行',\n },\n hi: {\n title: 'साइन इन करने के लिए स्कैन करें',\n titleIdentify: 'अपनी पहचान सत्यापित करने के लिए स्कैन करें',\n titlePresence: 'यह साबित करने के लिए स्कैन करें कि आप एक वास्तविक इंसान हैं',\n titleApprove: 'अपने फोन पर स्वीकृत करें',\n bodyScan: 'अपने फोन के कैमरे या ZOREAL ID ऐप से स्कैन करें।',\n bodyApprove: 'अपने ZOREAL ID ऐप में लॉगिन स्वीकृत करें।',\n bodyEnrolling: 'अपने फोन पर ZOREAL ID सेटअप पूरा करें, फिर लॉगिन स्वीकृत करें।',\n waiting: 'स्कैन की प्रतीक्षा है',\n waitingApproval: 'स्वीकृति की प्रतीक्षा है',\n expiresIn: '{time} में समाप्त',\n secured: 'ZOREAL द्वारा Proof-of-Human सत्यापन',\n noIdTitle: 'अभी तक ZOREAL ID नहीं है?',\n noIdBody: 'ऐप डाउनलोड करने और मुफ्त में एक बनाने के लिए वही कोड स्कैन करें। इसमें बस एक मिनट लगता है।',\n cancel: 'रद्द करें',\n close: 'बंद करें',\n qrAlt: 'ZOREAL से साइन इन करने के लिए QR कोड',\n buttonContinue: 'ZOREAL के साथ जारी रखें',\n },\n zhs: {\n title: '扫码登录',\n titleIdentify: '扫码验证身份',\n titlePresence: '扫码证明您是真人',\n titleApprove: '在手机上批准',\n bodyScan: '使用手机相机或 ZOREAL ID 应用扫描。',\n bodyApprove: '请在 ZOREAL ID 应用中批准登录。',\n bodyEnrolling: '请在手机上完成 ZOREAL ID 设置,然后批准登录。',\n waiting: '等待扫描',\n waitingApproval: '等待批准',\n expiresIn: '{time} 后失效',\n secured: '由 ZOREAL 提供的 Proof-of-Human 验证',\n noIdTitle: '还没有 ZOREAL ID?',\n noIdBody: '扫描同一个二维码即可下载应用并免费创建,只需一分钟。',\n cancel: '取消',\n close: '关闭',\n qrAlt: '使用 ZOREAL 登录的二维码',\n buttonContinue: '使用 ZOREAL 继续',\n },\n zht: {\n title: '掃碼登入',\n titleIdentify: '掃碼驗證身分',\n titlePresence: '掃碼證明您是真人',\n titleApprove: '在手機上核准',\n bodyScan: '使用手機相機或 ZOREAL ID 應用程式掃描。',\n bodyApprove: '請在 ZOREAL ID 應用程式中核准登入。',\n bodyEnrolling: '請在手機上完成 ZOREAL ID 設定,然後核准登入。',\n waiting: '等待掃描',\n waitingApproval: '等待核准',\n expiresIn: '{time} 後失效',\n secured: '由 ZOREAL 提供的 Proof-of-Human 驗證',\n noIdTitle: '還沒有 ZOREAL ID?',\n noIdBody: '掃描同一個 QR code 即可下載應用程式並免費建立,只需一分鐘。',\n cancel: '取消',\n close: '關閉',\n qrAlt: '使用 ZOREAL 登入的 QR code',\n buttonContinue: '使用 ZOREAL 繼續',\n },\n ar: {\n title: 'امسح لتسجيل الدخول',\n titleIdentify: 'امسح للتحقق من هويتك',\n titlePresence: 'امسح لإثبات أنك إنسان حقيقي',\n titleApprove: 'وافق على هاتفك',\n bodyScan: 'امسح باستخدام كاميرا هاتفك أو تطبيق ZOREAL ID.',\n bodyApprove: 'وافق على تسجيل الدخول في تطبيق ZOREAL ID.',\n bodyEnrolling: 'أكمل إعداد ZOREAL ID على هاتفك، ثم وافق على تسجيل الدخول.',\n waiting: 'في انتظار المسح',\n waitingApproval: 'في انتظار الموافقة',\n expiresIn: 'تنتهي الصلاحية خلال {time}',\n secured: 'التحقق من Proof-of-Human بواسطة ZOREAL',\n noIdTitle: 'ليس لديك ZOREAL ID بعد؟',\n noIdBody: 'امسح الرمز نفسه لتنزيل التطبيق وإنشاء حساب مجاني. يستغرق الأمر دقيقة واحدة فقط.',\n cancel: 'إلغاء',\n close: 'إغلاق',\n qrAlt: 'رمز QR لتسجيل الدخول باستخدام ZOREAL',\n buttonContinue: 'المتابعة باستخدام ZOREAL',\n },\n ko: {\n title: '스캔하여 로그인',\n titleIdentify: '스캔하여 신원 확인',\n titlePresence: '스캔하여 실제 사람임을 증명',\n titleApprove: '휴대폰에서 승인',\n bodyScan: '휴대폰 카메라 또는 ZOREAL ID 앱으로 스캔하세요.',\n bodyApprove: 'ZOREAL ID 앱에서 로그인을 승인하세요.',\n bodyEnrolling: '휴대폰에서 ZOREAL ID 설정을 완료한 후 로그인을 승인하세요.',\n waiting: '스캔 대기 중',\n waitingApproval: '승인 대기 중',\n expiresIn: '{time} 후 만료',\n secured: 'ZOREAL의 Proof-of-Human 인증',\n noIdTitle: '아직 ZOREAL ID가 없으신가요?',\n noIdBody: '같은 코드를 스캔해 앱을 내려받고 무료로 만드세요. 1분이면 됩니다.',\n cancel: '취소',\n close: '닫기',\n qrAlt: 'ZOREAL로 로그인하기 위한 QR 코드',\n buttonContinue: 'ZOREAL로 계속',\n },\n // Български\n bg: {\n title: 'Сканирайте за вход',\n titleIdentify: 'Сканирайте, за да потвърдите самоличността си',\n titlePresence: 'Сканирайте, за да докажете, че сте истински човек',\n titleApprove: 'Потвърдете на телефона си',\n bodyScan: 'Сканирайте с камерата на телефона или с приложението ZOREAL ID.',\n bodyApprove: 'Потвърдете входа в приложението ZOREAL ID.',\n bodyEnrolling: 'Довършете настройката на ZOREAL ID на телефона си, след което потвърдете входа.',\n waiting: 'Изчакване на сканиране',\n waitingApproval: 'Изчакване на потвърждение',\n expiresIn: 'Изтича след {time}',\n secured: 'Проверка Proof-of-Human от ZOREAL',\n noIdTitle: 'Все още нямате ZOREAL ID?',\n noIdBody: 'Сканирайте същия код, за да изтеглите приложението и да си създадете безплатен акаунт. Отнема само минута.',\n cancel: 'Отказ',\n close: 'Затвори',\n qrAlt: 'QR код за вход със ZOREAL',\n buttonContinue: 'Продължи със ZOREAL',\n },\n // বাংলা\n bn: {\n title: 'সাইন ইন করতে স্ক্যান করুন',\n titleIdentify: 'আপনার পরিচয় যাচাই করতে স্ক্যান করুন',\n titlePresence: 'আপনি একজন প্রকৃত মানুষ তা প্রমাণ করতে স্ক্যান করুন',\n titleApprove: 'আপনার ফোনে অনুমোদন করুন',\n bodyScan: 'আপনার ফোনের ক্যামেরা বা ZOREAL ID অ্যাপ দিয়ে স্ক্যান করুন।',\n bodyApprove: 'আপনার ZOREAL ID অ্যাপে লগইন অনুমোদন করুন।',\n bodyEnrolling: 'আপনার ফোনে ZOREAL ID সেটআপ সম্পূর্ণ করুন, তারপর লগইন অনুমোদন করুন।',\n waiting: 'স্ক্যানের অপেক্ষায়',\n waitingApproval: 'অনুমোদনের অপেক্ষায়',\n expiresIn: '{time} পরে মেয়াদ শেষ হবে',\n secured: 'ZOREAL দ্বারা Proof-of-Human যাচাইকরণ',\n noIdTitle: 'এখনো ZOREAL ID নেই?',\n noIdBody: 'অ্যাপ ডাউনলোড করে বিনামূল্যে একটি তৈরি করতে একই কোড স্ক্যান করুন। এতে মাত্র এক মিনিট সময় লাগে।',\n cancel: 'বাতিল',\n close: 'বন্ধ',\n qrAlt: 'ZOREAL দিয়ে সাইন ইন করার জন্য QR কোড',\n buttonContinue: 'ZOREAL দিয়ে চালিয়ে যান',\n },\n // Bosanski\n bs: {\n title: 'Skenirajte za prijavu',\n titleIdentify: 'Skenirajte da potvrdite svoj identitet',\n titlePresence: 'Skenirajte da dokažete da ste stvarna osoba',\n titleApprove: 'Odobrite na svom telefonu',\n bodyScan: 'Skenirajte kamerom svog telefona ili aplikacijom ZOREAL ID.',\n bodyApprove: 'Odobrite prijavu u aplikaciji ZOREAL ID.',\n bodyEnrolling: 'Završite podešavanje ZOREAL ID-a na svom telefonu, a zatim odobrite prijavu.',\n waiting: 'Čeka se skeniranje',\n waitingApproval: 'Čeka se odobrenje',\n expiresIn: 'Ističe za {time}',\n secured: 'ZOREAL Proof-of-Human verifikacija',\n noIdTitle: 'Nemate ZOREAL ID?',\n noIdBody: 'Skenirajte isti kod da preuzmete aplikaciju i besplatno ga napravite. Traje samo minutu.',\n cancel: 'Otkaži',\n close: 'Zatvori',\n qrAlt: 'QR kod za prijavu putem ZOREAL-a',\n buttonContinue: 'Nastavi sa ZOREAL-om',\n },\n // Čeština\n cs: {\n title: 'Přihlaste se naskenováním',\n titleIdentify: 'Naskenujte pro ověření totožnosti',\n titlePresence: 'Naskenujte a prokažte, že jste skutečný člověk',\n titleApprove: 'Potvrďte v telefonu',\n bodyScan: 'Naskenujte fotoaparátem telefonu nebo aplikací ZOREAL ID.',\n bodyApprove: 'Potvrďte přihlášení v aplikaci ZOREAL ID.',\n bodyEnrolling: 'Dokončete nastavení ZOREAL ID v telefonu a poté potvrďte přihlášení.',\n waiting: 'Čekání na naskenování',\n waitingApproval: 'Čekání na potvrzení',\n expiresIn: 'Vyprší za {time}',\n secured: 'Ověření Proof-of-Human od ZOREAL',\n noIdTitle: 'Ještě nemáte ZOREAL ID?',\n noIdBody: 'Naskenováním stejného kódu si stáhnete aplikaci a zdarma vytvoříte ZOREAL ID. Zabere to jen minutu.',\n cancel: 'Zrušit',\n close: 'Zavřít',\n qrAlt: 'QR kód pro přihlášení pomocí ZOREAL',\n buttonContinue: 'Pokračovat se ZOREAL',\n },\n // Dansk\n da: {\n title: 'Scan for at logge ind',\n titleIdentify: 'Scan for at bekræfte din identitet',\n titlePresence: 'Scan for at bevise, at du er et rigtigt menneske',\n titleApprove: 'Godkend på din telefon',\n bodyScan: 'Scan med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkend login i din ZOREAL ID-app.',\n bodyEnrolling: 'Færdiggør opsætningen af ZOREAL ID på din telefon, og godkend derefter login.',\n waiting: 'Venter på scanning',\n waitingApproval: 'Venter på godkendelse',\n expiresIn: 'Udløber om {time}',\n secured: 'Proof-of-Human-verificering af ZOREAL',\n noIdTitle: 'Har du ikke et ZOREAL ID endnu?',\n noIdBody: 'Scan den samme kode for at hente appen og oprette et gratis. Det tager kun et minut.',\n cancel: 'Annuller',\n close: 'Luk',\n qrAlt: 'QR-kode til at logge ind med ZOREAL',\n buttonContinue: 'Fortsæt med ZOREAL',\n },\n // Ελληνικά\n el: {\n title: 'Σάρωση για σύνδεση',\n titleIdentify: 'Σάρωση για επαλήθευση ταυτότητας',\n titlePresence: 'Σάρωση για να αποδείξετε ότι είστε πραγματικός άνθρωπος',\n titleApprove: 'Έγκριση από το κινητό σας',\n bodyScan: 'Σαρώστε με την κάμερα του κινητού σας ή την εφαρμογή ZOREAL ID.',\n bodyApprove: 'Εγκρίνετε τη σύνδεση στην εφαρμογή ZOREAL ID.',\n bodyEnrolling: 'Ολοκληρώστε τη ρύθμιση του ZOREAL ID στο κινητό σας και έπειτα εγκρίνετε τη σύνδεση.',\n waiting: 'Αναμονή σάρωσης',\n waitingApproval: 'Αναμονή έγκρισης',\n expiresIn: 'Λήγει σε {time}',\n secured: 'Επαλήθευση Proof-of-Human από τη ZOREAL',\n noIdTitle: 'Δεν έχετε ακόμα ZOREAL ID;',\n noIdBody: 'Σαρώστε τον ίδιο κωδικό για να κατεβάσετε την εφαρμογή και να δημιουργήσετε ένα δωρεάν. Χρειάζεται μόνο ένα λεπτό.',\n cancel: 'Άκυρο',\n close: 'Κλείσιμο',\n qrAlt: 'Κωδικός QR για σύνδεση με ZOREAL',\n buttonContinue: 'Συνέχεια με ZOREAL',\n },\n // Español (LA)\n 'es-419': {\n title: 'Escanea para iniciar sesión',\n titleIdentify: 'Escanea para verificar tu identidad',\n titlePresence: 'Escanea para demostrar que eres una persona real',\n titleApprove: 'Aprueba desde tu celular',\n bodyScan: 'Escanea con la cámara de tu celular o con la app ZOREAL ID.',\n bodyApprove: 'Aprueba el inicio de sesión en tu app ZOREAL ID.',\n bodyEnrolling: 'Termina de configurar ZOREAL ID en tu celular y luego aprueba el inicio de sesión.',\n waiting: 'Esperando escaneo',\n waitingApproval: 'Esperando aprobación',\n expiresIn: 'Expira en {time}',\n secured: 'Verificación Proof-of-Human de ZOREAL',\n noIdTitle: '¿Todavía no tienes ZOREAL ID?',\n noIdBody: 'Escanea el mismo código para descargar la app y crear uno gratis. Solo toma un minuto.',\n cancel: 'Cancelar',\n close: 'Cerrar',\n qrAlt: 'Código QR para iniciar sesión con ZOREAL',\n buttonContinue: 'Continuar con ZOREAL',\n },\n // Suomi\n fi: {\n title: 'Kirjaudu sisään skannaamalla',\n titleIdentify: 'Vahvista henkilöllisyytesi skannaamalla',\n titlePresence: 'Todista skannaamalla, että olet oikea ihminen',\n titleApprove: 'Hyväksy puhelimessasi',\n bodyScan: 'Skannaa puhelimesi kameralla tai ZOREAL ID -sovelluksella.',\n bodyApprove: 'Hyväksy kirjautuminen ZOREAL ID -sovelluksessasi.',\n bodyEnrolling: 'Viimeistele ZOREAL ID -sovelluksen käyttöönotto puhelimellasi ja hyväksy sitten kirjautuminen.',\n waiting: 'Odotetaan skannausta',\n waitingApproval: 'Odotetaan hyväksyntää',\n expiresIn: 'Vanhenee {time} kuluttua',\n secured: 'ZOREALin Proof-of-Human-vahvistus',\n noIdTitle: 'Eikö sinulla ole vielä ZOREAL ID:tä?',\n noIdBody: 'Skannaa sama koodi ladataksesi sovelluksen ja luodaksesi tunnuksen ilmaiseksi. Se vie vain minuutin.',\n cancel: 'Peruuta',\n close: 'Sulje',\n qrAlt: 'QR-koodi ZOREAL-kirjautumista varten',\n buttonContinue: 'Jatka ZOREALilla',\n },\n // עברית\n he: {\n title: 'סרוק כדי להתחבר',\n titleIdentify: 'סרוק כדי לאמת את זהותך',\n titlePresence: 'סרוק כדי להוכיח שאתה אדם אמיתי',\n titleApprove: 'אשר בטלפון שלך',\n bodyScan: 'סרוק באמצעות מצלמת הטלפון שלך או אפליקציית ZOREAL ID.',\n bodyApprove: 'אשר את ההתחברות באפליקציית ZOREAL ID שלך.',\n bodyEnrolling: 'סיים להגדיר את ZOREAL ID בטלפון שלך, ואז אשר את ההתחברות.',\n waiting: 'ממתין לסריקה',\n waitingApproval: 'ממתין לאישור',\n expiresIn: 'יפוג בעוד {time}',\n secured: 'אימות Proof-of-Human מבית ZOREAL',\n noIdTitle: 'עדיין אין לך ZOREAL ID?',\n noIdBody: 'סרוק את אותו הקוד כדי להוריד את האפליקציה וליצור אחד בחינם. זה לוקח רק דקה.',\n cancel: 'ביטול',\n close: 'סגור',\n qrAlt: 'קוד QR להתחברות עם ZOREAL',\n buttonContinue: 'המשך עם ZOREAL',\n },\n // Hrvatski\n hr: {\n title: 'Skenirajte za prijavu',\n titleIdentify: 'Skenirajte za potvrdu identiteta',\n titlePresence: 'Skenirajte kako biste dokazali da ste stvarna osoba',\n titleApprove: 'Odobrite na svom mobitelu',\n bodyScan: 'Skenirajte kamerom svog mobitela ili aplikacijom ZOREAL ID.',\n bodyApprove: 'Odobrite prijavu u aplikaciji ZOREAL ID.',\n bodyEnrolling: 'Dovršite postavljanje ZOREAL ID-a na svom mobitelu, a zatim odobrite prijavu.',\n waiting: 'Čeka se skeniranje',\n waitingApproval: 'Čeka se odobrenje',\n expiresIn: 'Ističe za {time}',\n secured: 'ZOREAL Proof-of-Human provjera',\n noIdTitle: 'Nemate ZOREAL ID?',\n noIdBody: 'Skenirajte isti kod da preuzmete aplikaciju i besplatno ga izradite. Traje samo minutu.',\n cancel: 'Odustani',\n close: 'Zatvori',\n qrAlt: 'QR kod za prijavu putem ZOREAL-a',\n buttonContinue: 'Nastavi sa ZOREAL-om',\n },\n // Magyar\n hu: {\n title: 'Bejelentkezés beolvasással',\n titleIdentify: 'Olvassa be a személyazonossága igazolásához',\n titlePresence: 'Olvassa be annak igazolásához, hogy valódi ember',\n titleApprove: 'Jóváhagyás a telefonján',\n bodyScan: 'Olvassa be a telefonja kamerájával, vagy a ZOREAL ID alkalmazással.',\n bodyApprove: 'Hagyja jóvá a bejelentkezést a ZOREAL ID alkalmazásban.',\n bodyEnrolling: 'Fejezze be a ZOREAL ID beállítását a telefonján, majd hagyja jóvá a bejelentkezést.',\n waiting: 'Várakozás beolvasásra',\n waitingApproval: 'Várakozás jóváhagyásra',\n expiresIn: 'Lejár {time} múlva',\n secured: 'Proof-of-Human hitelesítés a ZOREAL-tól',\n noIdTitle: 'Még nincs ZOREAL ID-je?',\n noIdBody: 'Olvassa be ugyanazt a kódot az alkalmazás letöltéséhez, és hozzon létre egyet ingyenesen. Mindössze egy percet vesz igénybe.',\n cancel: 'Mégse',\n close: 'Bezárás',\n qrAlt: 'QR-kód a ZOREAL-lal való bejelentkezéshez',\n buttonContinue: 'Folytatás a ZOREAL-lal',\n },\n // Bahasa Indonesia\n id: {\n title: 'Pindai untuk masuk',\n titleIdentify: 'Pindai untuk memverifikasi identitas Anda',\n titlePresence: 'Pindai untuk membuktikan bahwa Anda manusia sungguhan',\n titleApprove: 'Setujui di ponsel Anda',\n bodyScan: 'Pindai dengan kamera ponsel atau aplikasi ZOREAL ID.',\n bodyApprove: 'Setujui proses masuk di aplikasi ZOREAL ID Anda.',\n bodyEnrolling: 'Selesaikan pengaturan ZOREAL ID di ponsel Anda, lalu setujui proses masuk.',\n waiting: 'Menunggu pemindaian',\n waitingApproval: 'Menunggu persetujuan',\n expiresIn: 'Berakhir dalam {time}',\n secured: 'Verifikasi Proof-of-Human oleh ZOREAL',\n noIdTitle: 'Belum punya ZOREAL ID?',\n noIdBody: 'Pindai kode yang sama untuk mengunduh aplikasi dan membuat akun secara gratis. Hanya butuh waktu satu menit.',\n cancel: 'Batal',\n close: 'Tutup',\n qrAlt: 'Kode QR untuk masuk dengan ZOREAL',\n buttonContinue: 'Lanjutkan dengan ZOREAL',\n },\n // Italiano\n it: {\n title: 'Scansiona per accedere',\n titleIdentify: 'Scansiona per verificare la tua identità',\n titlePresence: 'Scansiona per dimostrare di essere una persona reale',\n titleApprove: 'Approva sul tuo telefono',\n bodyScan: 'Scansiona con la fotocamera del telefono o con l\\'app ZOREAL ID.',\n bodyApprove: 'Approva l\\'accesso nell\\'app ZOREAL ID.',\n bodyEnrolling: 'Completa la configurazione di ZOREAL ID sul telefono, poi approva l\\'accesso.',\n waiting: 'In attesa della scansione',\n waitingApproval: 'In attesa di approvazione',\n expiresIn: 'Scade tra {time}',\n secured: 'Verifica Proof-of-Human di ZOREAL',\n noIdTitle: 'Non hai ancora uno ZOREAL ID?',\n noIdBody: 'Scansiona lo stesso codice per scaricare l\\'app e crearne uno gratis. Basta un minuto.',\n cancel: 'Annulla',\n close: 'Chiudi',\n qrAlt: 'Codice QR per accedere con ZOREAL',\n buttonContinue: 'Continua con ZOREAL',\n },\n // Bahasa Melayu\n ms: {\n title: 'Imbas untuk log masuk',\n titleIdentify: 'Imbas untuk mengesahkan identiti anda',\n titlePresence: 'Imbas untuk membuktikan anda manusia sebenar',\n titleApprove: 'Luluskan di telefon anda',\n bodyScan: 'Imbas dengan kamera telefon atau aplikasi ZOREAL ID.',\n bodyApprove: 'Luluskan log masuk dalam aplikasi ZOREAL ID anda.',\n bodyEnrolling: 'Selesaikan persediaan ZOREAL ID di telefon anda, kemudian luluskan log masuk.',\n waiting: 'Menunggu imbasan',\n waitingApproval: 'Menunggu kelulusan',\n expiresIn: 'Tamat tempoh dalam {time}',\n secured: 'Pengesahan Proof-of-Human oleh ZOREAL',\n noIdTitle: 'Belum ada ZOREAL ID?',\n noIdBody: 'Imbas kod yang sama untuk memuat turun aplikasi dan cipta satu secara percuma. Hanya mengambil masa seminit.',\n cancel: 'Batal',\n close: 'Tutup',\n qrAlt: 'Kod QR untuk log masuk dengan ZOREAL',\n buttonContinue: 'Teruskan dengan ZOREAL',\n },\n // Nederlands\n nl: {\n title: 'Scan om in te loggen',\n titleIdentify: 'Scan om je identiteit te verifiëren',\n titlePresence: 'Scan om te bewijzen dat je een echt mens bent',\n titleApprove: 'Keur goed op je telefoon',\n bodyScan: 'Scan met de camera van je telefoon of de ZOREAL ID-app.',\n bodyApprove: 'Keur de aanmelding goed in je ZOREAL ID-app.',\n bodyEnrolling: 'Rond het instellen van ZOREAL ID op je telefoon af en keur daarna de aanmelding goed.',\n waiting: 'Wachten op scan',\n waitingApproval: 'Wachten op goedkeuring',\n expiresIn: 'Verloopt over {time}',\n secured: 'Proof-of-Human-verificatie door ZOREAL',\n noIdTitle: 'Nog geen ZOREAL ID?',\n noIdBody: 'Scan dezelfde code om de app te downloaden en gratis een account aan te maken. Dit duurt maar een minuut.',\n cancel: 'Annuleren',\n close: 'Sluiten',\n qrAlt: 'QR-code om in te loggen met ZOREAL',\n buttonContinue: 'Doorgaan met ZOREAL',\n },\n // Norsk\n no: {\n title: 'Skann for å logge inn',\n titleIdentify: 'Skann for å bekrefte identiteten din',\n titlePresence: 'Skann for å bevise at du er et ekte menneske',\n titleApprove: 'Godkjenn på telefonen din',\n bodyScan: 'Skann med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkjenn innloggingen i ZOREAL ID-appen din.',\n bodyEnrolling: 'Fullfør oppsettet av ZOREAL ID på telefonen din, og godkjenn deretter innloggingen.',\n waiting: 'Venter på skanning',\n waitingApproval: 'Venter på godkjenning',\n expiresIn: 'Utløper om {time}',\n secured: 'Proof-of-Human-verifisering av ZOREAL',\n noIdTitle: 'Har du ikke ZOREAL ID ennå?',\n noIdBody: 'Skann den samme koden for å laste ned appen og opprette en gratis. Det tar bare et minutt.',\n cancel: 'Avbryt',\n close: 'Lukk',\n qrAlt: 'QR-kode for å logge inn med ZOREAL',\n buttonContinue: 'Fortsett med ZOREAL',\n },\n // Polski\n pl: {\n title: 'Zeskanuj, aby się zalogować',\n titleIdentify: 'Zeskanuj, aby zweryfikować swoją tożsamość',\n titlePresence: 'Zeskanuj, aby udowodnić, że jesteś prawdziwym człowiekiem',\n titleApprove: 'Zatwierdź w telefonie',\n bodyScan: 'Zeskanuj aparatem telefonu lub aplikacją ZOREAL ID.',\n bodyApprove: 'Zatwierdź logowanie w aplikacji ZOREAL ID.',\n bodyEnrolling: 'Dokończ konfigurację ZOREAL ID w telefonie, a następnie zatwierdź logowanie.',\n waiting: 'Czekanie na skan',\n waitingApproval: 'Czekanie na zatwierdzenie',\n expiresIn: 'Wygasa za {time}',\n secured: 'Weryfikacja Proof-of-Human od ZOREAL',\n noIdTitle: 'Nie masz jeszcze ZOREAL ID?',\n noIdBody: 'Zeskanuj ten sam kod, aby pobrać aplikację i bezpłatnie utworzyć ZOREAL ID. Zajmie to tylko minutę.',\n cancel: 'Anuluj',\n close: 'Zamknij',\n qrAlt: 'Kod QR do logowania za pomocą ZOREAL',\n buttonContinue: 'Kontynuuj z ZOREAL',\n },\n // Português (BR)\n 'pt-br': {\n title: 'Escaneie para entrar',\n titleIdentify: 'Escaneie para verificar sua identidade',\n titlePresence: 'Escaneie para provar que você é uma pessoa real',\n titleApprove: 'Aprove no seu celular',\n bodyScan: 'Escaneie com a câmera do seu celular ou com o app ZOREAL ID.',\n bodyApprove: 'Aprove o login no app ZOREAL ID.',\n bodyEnrolling: 'Termine de configurar o ZOREAL ID no seu celular e depois aprove o login.',\n waiting: 'Aguardando escaneamento',\n waitingApproval: 'Aguardando aprovação',\n expiresIn: 'Expira em {time}',\n secured: 'Verificação Proof-of-Human da ZOREAL',\n noIdTitle: 'Ainda não tem um ZOREAL ID?',\n noIdBody: 'Escaneie o mesmo código para baixar o app e criar um de graça. Leva só um minuto.',\n cancel: 'Cancelar',\n close: 'Fechar',\n qrAlt: 'Código QR para entrar com ZOREAL',\n buttonContinue: 'Continuar com ZOREAL',\n },\n // Română\n ro: {\n title: 'Scanați pentru conectare',\n titleIdentify: 'Scanați pentru a vă verifica identitatea',\n titlePresence: 'Scanați pentru a dovedi că sunteți o persoană reală',\n titleApprove: 'Aprobați de pe telefon',\n bodyScan: 'Scanați cu camera telefonului sau cu aplicația ZOREAL ID.',\n bodyApprove: 'Aprobați conectarea în aplicația ZOREAL ID.',\n bodyEnrolling: 'Finalizați configurarea ZOREAL ID pe telefon, apoi aprobați conectarea.',\n waiting: 'Se așteaptă scanarea',\n waitingApproval: 'Se așteaptă aprobarea',\n expiresIn: 'Expiră în {time}',\n secured: 'Verificare Proof-of-Human de la ZOREAL',\n noIdTitle: 'Nu aveți încă un ZOREAL ID?',\n noIdBody: 'Scanați același cod pentru a descărca aplicația și a crea unul gratuit. Durează doar un minut.',\n cancel: 'Anulează',\n close: 'Închide',\n qrAlt: 'Cod QR pentru conectare cu ZOREAL',\n buttonContinue: 'Continuați cu ZOREAL',\n },\n // Српски\n sr: {\n title: 'Скенирајте за пријаву',\n titleIdentify: 'Скенирајте да потврдите свој идентитет',\n titlePresence: 'Скенирајте да докажете да сте права особа',\n titleApprove: 'Одобрите на свом телефону',\n bodyScan: 'Скенирајте камером свог телефона или апликацијом ZOREAL ID.',\n bodyApprove: 'Одобрите пријаву у апликацији ZOREAL ID.',\n bodyEnrolling: 'Довршите подешавање ZOREAL ID-а на свом телефону, па одобрите пријаву.',\n waiting: 'Чека се скенирање',\n waitingApproval: 'Чека се одобрење',\n expiresIn: 'Истиче за {time}',\n secured: 'ZOREAL Proof-of-Human верификација',\n noIdTitle: 'Немате ZOREAL ID?',\n noIdBody: 'Скенирајте исти код да преузмете апликацију и бесплатно га направите. Траје само минут.',\n cancel: 'Откажи',\n close: 'Затвори',\n qrAlt: 'QR код за пријаву преко ZOREAL-а',\n buttonContinue: 'Настави са ZOREAL-ом',\n },\n // ไทย\n th: {\n title: 'สแกนเพื่อเข้าสู่ระบบ',\n titleIdentify: 'สแกนเพื่อยืนยันตัวตนของคุณ',\n titlePresence: 'สแกนเพื่อพิสูจน์ว่าคุณเป็นมนุษย์จริง',\n titleApprove: 'อนุมัติบนโทรศัพท์ของคุณ',\n bodyScan: 'สแกนด้วยกล้องโทรศัพท์หรือแอป ZOREAL ID',\n bodyApprove: 'อนุมัติการเข้าสู่ระบบในแอป ZOREAL ID ของคุณ',\n bodyEnrolling: 'ตั้งค่า ZOREAL ID บนโทรศัพท์ของคุณให้เสร็จสิ้น แล้วอนุมัติการเข้าสู่ระบบ',\n waiting: 'รอการสแกน',\n waitingApproval: 'รอการอนุมัติ',\n expiresIn: 'หมดอายุใน {time}',\n secured: 'การยืนยันตัวตน Proof-of-Human โดย ZOREAL',\n noIdTitle: 'ยังไม่มี ZOREAL ID ใช่ไหม',\n noIdBody: 'สแกนโค้ดเดียวกันเพื่อดาวน์โหลดแอปและสร้างบัญชีฟรี ใช้เวลาเพียงนาทีเดียว',\n cancel: 'ยกเลิก',\n close: 'ปิด',\n qrAlt: 'คิวอาร์โค้ดสำหรับเข้าสู่ระบบด้วย ZOREAL',\n buttonContinue: 'ดำเนินการต่อด้วย ZOREAL',\n },\n // Tagalog\n tl: {\n title: 'I-scan para mag-sign in',\n titleIdentify: 'I-scan para i-verify ang iyong pagkakakilanlan',\n titlePresence: 'I-scan para patunayang tunay kang tao',\n titleApprove: 'I-approve sa iyong telepono',\n bodyScan: 'I-scan gamit ang camera ng iyong telepono o ang ZOREAL ID app.',\n bodyApprove: 'I-approve ang login sa iyong ZOREAL ID app.',\n bodyEnrolling: 'Tapusin muna ang pag-set up ng ZOREAL ID sa iyong telepono, pagkatapos ay i-approve ang login.',\n waiting: 'Naghihintay ng scan',\n waitingApproval: 'Naghihintay ng approval',\n expiresIn: 'Mag-e-expire sa {time}',\n secured: 'Proof-of-Human verification mula sa ZOREAL',\n noIdTitle: 'Wala ka pang ZOREAL ID?',\n noIdBody: 'I-scan ang parehong code para i-download ang app at gumawa ng iyong ZOREAL ID nang libre. Isang minuto lang ito.',\n cancel: 'Kanselahin',\n close: 'Isara',\n qrAlt: 'QR code para mag-sign in gamit ang ZOREAL',\n buttonContinue: 'Magpatuloy gamit ang ZOREAL',\n },\n // Türkçe\n tr: {\n title: 'Giriş için tarayın',\n titleIdentify: 'Kimliğinizi doğrulamak için tarayın',\n titlePresence: 'Gerçek bir insan olduğunuzu kanıtlamak için tarayın',\n titleApprove: 'Telefonunuzdan onaylayın',\n bodyScan: 'Telefonunuzun kamerasıyla veya ZOREAL ID uygulamasıyla tarayın.',\n bodyApprove: 'Girişi ZOREAL ID uygulamanızdan onaylayın.',\n bodyEnrolling: 'Telefonunuzda ZOREAL ID kurulumunu tamamlayın, ardından girişi onaylayın.',\n waiting: 'Tarama bekleniyor',\n waitingApproval: 'Onay bekleniyor',\n expiresIn: '{time} içinde sona erer',\n secured: 'ZOREAL tarafından Proof-of-Human doğrulaması',\n noIdTitle: 'Henüz ZOREAL ID\\'niz yok mu?',\n noIdBody: 'Uygulamayı indirmek ve ücretsiz bir tane oluşturmak için aynı kodu tarayın. Sadece bir dakikanızı alır.',\n cancel: 'İptal',\n close: 'Kapat',\n qrAlt: 'ZOREAL ile giriş yapmak için QR kodu',\n buttonContinue: 'ZOREAL ile devam et',\n },\n // Українська\n uk: {\n title: 'Скануйте для входу',\n titleIdentify: 'Скануйте, щоб підтвердити особу',\n titlePresence: 'Скануйте, щоб довести, що ви справжня людина',\n titleApprove: 'Підтвердьте на телефоні',\n bodyScan: 'Скануйте камерою телефону або додатком ZOREAL ID.',\n bodyApprove: 'Підтвердьте вхід у додатку ZOREAL ID.',\n bodyEnrolling: 'Завершіть налаштування ZOREAL ID на телефоні, а потім підтвердьте вхід.',\n waiting: 'Очікування сканування',\n waitingApproval: 'Очікування підтвердження',\n expiresIn: 'Спливає через {time}',\n secured: 'Перевірка Proof-of-Human від ZOREAL',\n noIdTitle: 'Ще немає ZOREAL ID?',\n noIdBody: 'Скануйте той самий код, щоб завантажити додаток і безкоштовно створити його. Це займе лише хвилину.',\n cancel: 'Скасувати',\n close: 'Закрити',\n qrAlt: 'QR-код для входу через ZOREAL',\n buttonContinue: 'Продовжити з ZOREAL',\n },\n // اردو\n ur: {\n title: 'لاگ اِن کرنے کے لیے اسکین کریں',\n titleIdentify: 'اپنی شناخت کی تصدیق کے لیے اسکین کریں',\n titlePresence: 'یہ ثابت کرنے کے لیے اسکین کریں کہ آپ ایک حقیقی انسان ہیں',\n titleApprove: 'اپنے فون پر منظوری دیں',\n bodyScan: 'اپنے فون کے کیمرے یا ZOREAL ID ایپ سے اسکین کریں۔',\n bodyApprove: 'اپنی ZOREAL ID ایپ میں لاگ اِن کی منظوری دیں۔',\n bodyEnrolling: 'اپنے فون پر ZOREAL ID کی سیٹ اپ مکمل کریں، پھر لاگ اِن کی منظوری دیں۔',\n waiting: 'اسکین کا انتظار',\n waitingApproval: 'منظوری کا انتظار',\n expiresIn: '{time} میں ختم ہوگا',\n secured: 'ZOREAL کی جانب سے Proof-of-Human تصدیق',\n noIdTitle: 'ابھی تک ZOREAL ID نہیں ہے؟',\n noIdBody: 'ایپ ڈاؤن لوڈ کرنے اور مفت میں ایک بنانے کے لیے وہی کوڈ اسکین کریں۔ اس میں صرف ایک منٹ لگتا ہے۔',\n cancel: 'منسوخ کریں',\n close: 'بند کریں',\n qrAlt: 'ZOREAL کے ساتھ لاگ اِن کرنے کے لیے QR کوڈ',\n buttonContinue: 'ZOREAL کے ساتھ جاری رکھیں',\n },\n // Tiếng Việt\n vi: {\n title: 'Quét để đăng nhập',\n titleIdentify: 'Quét để xác minh danh tính của bạn',\n titlePresence: 'Quét để chứng minh bạn là người thật',\n titleApprove: 'Phê duyệt trên điện thoại của bạn',\n bodyScan: 'Quét bằng camera điện thoại hoặc ứng dụng ZOREAL ID.',\n bodyApprove: 'Phê duyệt đăng nhập trong ứng dụng ZOREAL ID của bạn.',\n bodyEnrolling: 'Hoàn tất thiết lập ZOREAL ID trên điện thoại, sau đó phê duyệt đăng nhập.',\n waiting: 'Đang chờ quét mã',\n waitingApproval: 'Đang chờ phê duyệt',\n expiresIn: 'Hết hạn sau {time}',\n secured: 'Xác minh Proof-of-Human bởi ZOREAL',\n noIdTitle: 'Chưa có ZOREAL ID?',\n noIdBody: 'Quét cùng mã này để tải ứng dụng và tạo tài khoản miễn phí. Chỉ mất một phút.',\n cancel: 'Hủy',\n close: 'Đóng',\n qrAlt: 'Mã QR để đăng nhập bằng ZOREAL',\n buttonContinue: 'Tiếp tục với ZOREAL',\n },\n};\n\n/** Locales whose script runs right to left, so the dialog flips with `dir`. */\n// Only languages we actually carry. Listing an RTL language we do not\n// translate would flip the dialog for someone who is then shown the English\n// fallback — LTR text in an RTL container, which is worse than either alone.\nconst RTL = new Set(['ar', 'he', 'iw', 'ur']);\n\n/**\n * One BCP 47 tag to a translation, or undefined if we do not carry it.\n *\n * Chinese is the only case needing more than the primary subtag: `zh-Hans` /\n * `zh-CN` / `zh-SG` are Simplified, everything else `zh` is treated as\n * Traditional, matching how the pairing page splits them.\n */\n/**\n * Primary subtags that reach the same table under another name: superseded ISO\n * codes some platforms still emit, and the written standards we carry one entry\n * for. Without these a Norwegian browser sending `nb` gets English while `no`\n * sits right there in the table.\n */\nconst ALIASES: Record<string, string> = {\n nb: 'no', // Bokmål — what we actually wrote\n nn: 'no', // Nynorsk reader, served Bokmål: closer than English\n fil: 'tl', // Filipino / Tagalog\n iw: 'he', // superseded code for Hebrew, still emitted by some platforms\n in: 'id', // superseded code for Indonesian\n};\n\n/**\n * Spanish and Portuguese ship two variants each, and the split that matters is\n * not the language but the side of the Atlantic. A `es-MX` browser resolving to\n * peninsular Spanish is the kind of near-miss that reads as nobody having\n * thought about it, so the Latin American regions are named explicitly.\n */\nconst LATAM = new Set([\n 'ar', 'bo', 'cl', 'co', 'cr', 'cu', 'do', 'ec', 'gt', 'hn',\n 'mx', 'ni', 'pa', 'pe', 'pr', 'py', 'sv', 'uy', 've', '419',\n]);\n\nfunction lookup(locale: string): PairingStrings | undefined {\n const tag = locale.toLowerCase().replace(/_/g, '-');\n const parts = tag.split('-');\n const primary = ALIASES[parts[0]] ?? parts[0];\n const region = parts[1];\n\n // Script, not region, is what separates these two.\n if (primary === 'zh') {\n const simplified = /(^|-)(hans|cn|sg|my)(-|$)/.test(tag);\n return TRANSLATIONS[simplified ? 'zhs' : 'zht'];\n }\n if (primary === 'es' && region && LATAM.has(region)) return TRANSLATIONS['es-419'];\n if (primary === 'pt' && region === 'br') return TRANSLATIONS['pt-br'];\n\n return TRANSLATIONS[tag] ?? TRANSLATIONS[primary];\n}\n\n/**\n * What the browser says the person reads, best first. `languages` is the whole\n * ordered preference list, which matters: someone whose first choice we do not\n * carry may well have a second we do, and falling straight to English would\n * skip it.\n */\nfunction browserLocales(): string[] {\n if (typeof navigator === 'undefined') return [];\n const nav = navigator as Navigator & { languages?: readonly string[] };\n if (nav.languages && nav.languages.length) return [...nav.languages];\n return nav.language ? [nav.language] : [];\n}\n\n/**\n * The strings to render.\n *\n * An explicit `locale` (from the provider) wins outright: the host app knows\n * which language it is currently showing, and the modal must not disagree with\n * the page it opened on. With none given we follow the browser's own preference\n * list, so an integrator who never sets `locale` still gets a translated modal\n * instead of English-by-default. Anything we do not carry falls back to English\n * rather than rendering a key.\n */\nexport function strings(locale?: string): PairingStrings {\n if (locale) return lookup(locale) ?? en;\n for (const candidate of browserLocales()) {\n const hit = lookup(candidate);\n if (hit) return hit;\n }\n return en;\n}\n\nexport function isRtl(locale?: string): boolean {\n const tag = locale ?? browserLocales()[0];\n if (!tag) return false;\n return RTL.has(tag.toLowerCase().replace(/_/g, '-').split('-')[0]);\n}\n\n/** The one substitution the copy needs. */\nexport function interpolate(template: string, time: string): string {\n return template.replace('{time}', time);\n}\n","/**\n * The pairing modal's stylesheet, injected once on first mount.\n *\n * Why a stylesheet and not inline styles: the modal needs hover, focus-visible,\n * keyframes, `prefers-color-scheme` and `prefers-reduced-motion`. None of those\n * exist as inline style properties, and a component that silently drops its\n * focus ring and its reduced-motion fallback is not shippable in a sign-in\n * flow.\n *\n * Why injected and not a `.css` file the integrator imports: a required import\n * step is a required support ticket. Plenty of hosts (Next.js app dir, CRA,\n * plain Vite, an app with no CSS pipeline at all) treat package CSS\n * differently, and the modal has to look the same in all of them.\n *\n * Every selector is prefixed `zrl-` and every declaration is scoped under one\n * of those classes, so nothing here can reach the host's markup. Values are\n * literal rather than inherited for the same reason: a host page with an\n * aggressive reset must not be able to break the layout of a dialog the person\n * is being asked to authenticate in. Font family is the one exception — it\n * inherits the host's UI font so the modal belongs to the page it opens on.\n */\n\nconst PREFIX = 'zrl';\nexport const cx = (name: string) => `${PREFIX}-${name}`;\n\nexport const STYLE_ELEMENT_ID = 'zoreal-pairing-styles';\n\n/**\n * Palette. `light`/`dark` force a theme, `auto` follows the OS. The tokens are\n * defined three times rather than once with overrides so a forced theme never\n * depends on media-query specificity to win.\n */\nconst LIGHT = `\n --zrl-scrim: rgba(16, 18, 27, 0.45);\n --zrl-surface: #ffffff;\n --zrl-surface-sunken: #f6f7f9;\n --zrl-ink: #16181c;\n --zrl-ink-soft: #4a4f57;\n --zrl-ink-mute: #6b7078;\n --zrl-line: #e4e6ea;\n --zrl-line-soft: #eef0f3;\n --zrl-accent: #00b4d9;\n --zrl-accent-soft: #dcf3fa;\n --zrl-accent-ink: #04698a;\n --zrl-urgent: #b4761a;\n --zrl-qr-bg: #ffffff;\n --zrl-qr-filter: none;\n --zrl-qr-spent-filter: blur(3px);\n --zrl-qr-blend: normal;\n --zrl-shadow: 0 1px 2px rgba(16, 18, 27, 0.06), 0 20px 50px -12px rgba(16, 18, 27, 0.3);\n --zrl-ring: rgba(16, 18, 27, 0.07);\n /* The light on the QR well's edge. Brand blue on both grounds, a lighter\n tint at the head; only its strength is themed, see the dark block. */\n --zrl-beam: #00b4d9;\n --zrl-beam-head: #7fe0f4;\n --zrl-beam-line: 2px;\n --zrl-glow-core: 4px;\n --zrl-glow-reach: 24px;\n --zrl-glow-blur: 8px;\n --zrl-glow-opacity: 0.6;\n`;\n\nconst DARK = `\n --zrl-scrim: rgba(0, 0, 0, 0.62);\n --zrl-surface: #17191d;\n --zrl-surface-sunken: #1f2226;\n --zrl-ink: #f4f5f7;\n --zrl-ink-soft: #b3b8c0;\n --zrl-ink-mute: #8b9199;\n --zrl-line: #2c3036;\n --zrl-line-soft: #24272c;\n --zrl-accent: #34c9e8;\n --zrl-accent-soft: #0d3b47;\n --zrl-accent-ink: #7fdcf0;\n --zrl-urgent: #e0a952;\n /* The code is drawn light on the dark surface: the panel is transparent\n and the image is inverted and screened, so only the modules and the\n mark show. */\n --zrl-qr-bg: transparent;\n --zrl-qr-filter: invert(1);\n --zrl-qr-spent-filter: invert(1) blur(3px);\n --zrl-qr-blend: screen;\n --zrl-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 50px -12px rgba(0, 0, 0, 0.65);\n --zrl-ring: rgba(255, 255, 255, 0.1);\n /* A glow that reads on a white card disappears on a dark one: the light\n here is brighter and wider, and its halo reaches further out. */\n --zrl-beam: #22c8ec;\n --zrl-beam-head: #c2f3fc;\n --zrl-beam-line: 3px;\n --zrl-glow-core: 6px;\n --zrl-glow-reach: 32px;\n --zrl-glow-blur: 10px;\n --zrl-glow-opacity: 0.85;\n`;\n\nexport const CSS = `\n.${PREFIX}-root { ${LIGHT} }\n.${PREFIX}-root[data-theme=\"dark\"] { ${DARK} }\n@media (prefers-color-scheme: dark) {\n .${PREFIX}-root[data-theme=\"auto\"] { ${DARK} }\n}\n\n.${PREFIX}-scrim {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n display: grid;\n place-items: center;\n overflow-y: auto;\n padding: 16px;\n background: var(--zrl-scrim);\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n font-family: inherit;\n animation: ${PREFIX}-fade 200ms ease-out both;\n}\n\n.${PREFIX}-card {\n position: relative;\n box-sizing: border-box;\n width: 100%;\n max-width: 380px;\n border-radius: 16px;\n background: var(--zrl-surface);\n color: var(--zrl-ink);\n box-shadow: var(--zrl-shadow);\n outline: 1px solid var(--zrl-ring);\n outline-offset: -1px;\n text-align: center;\n animation: ${PREFIX}-rise 300ms cubic-bezier(0.23, 1, 0.32, 1) both;\n}\n\n.${PREFIX}-body { padding: 28px 24px 20px; }\n\n.${PREFIX}-lockup { display: block; margin: 0 auto; color: var(--zrl-ink); }\n\n.${PREFIX}-title {\n margin: 18px 0 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n line-height: 1.3;\n color: var(--zrl-ink);\n}\n\n.${PREFIX}-body-text {\n margin: 6px auto 0;\n max-width: 30ch;\n font-size: 14px;\n line-height: 1.55;\n color: var(--zrl-ink-soft);\n}\n\n.${PREFIX}-qr-well {\n position: relative;\n display: grid;\n place-items: center;\n box-sizing: border-box;\n width: 204px;\n height: 204px;\n margin: 20px auto 0;\n padding: 12px;\n border: 1px solid var(--zrl-line);\n border-radius: var(--zrl-radius);\n background: var(--zrl-qr-bg);\n /* The light on the edge takes its shape from here and its colour and\n strength from the theme tokens above. One lap in 4s on every tier. */\n --zrl-radius: 16px;\n --zrl-beam-time: 4s;\n}\n\n/* The light on the well's edge: a short comet running along the border, with\n a soft glow outside it. Three overlays inside the well, each masked so the\n comet can only ever paint where its mask allows, and the white interior lies\n outside every mask: nothing here can reach the quiet zone a camera needs,\n whatever the comet is doing. The mask is the padding box cut out of the\n border box, a transparent layer clipped to the padding box intersected\n with a solid one clipped to the border box. The prefixed form is for Chrome\n before 120 and Safari before 15.4; the unprefixed one, declared after it,\n wins everywhere else.\n\n qr-beam keeps a thin ring on the border line: the comet itself.\n qr-beam-glow is the glow: a wide band outside the well that blurs whatever\n is inside it, and inside it qr-beam-glow-band keeps a 3px ring with a\n second copy of the comet. The blur has to sit on the parent because a\n filter is applied before a mask: blurred on the band itself, the glow\n would be cut back to the band's own edge. On the parent it runs after the\n band has clipped the comet thin and before the parent's mask cuts away the\n inward half, which is what makes it fade outward and never over the QR.\n All three share one containing block, the well's padding box, so the two\n comets ride the same path; the spent badge is a later sibling and paints\n above them. */\n.${PREFIX}-qr-beam,\n.${PREFIX}-qr-beam-glow,\n.${PREFIX}-qr-beam-glow-band {\n position: absolute;\n inset: calc(0px - var(--zrl-beam-line));\n border: var(--zrl-beam-line) solid transparent;\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-beam-line));\n pointer-events: none;\n -webkit-mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);\n -webkit-mask-clip: padding-box, border-box;\n -webkit-mask-composite: source-in;\n mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);\n mask-clip: padding-box, border-box;\n mask-composite: intersect;\n}\n.${PREFIX}-qr-beam-glow {\n inset: calc(0px - var(--zrl-glow-reach));\n border-width: var(--zrl-glow-reach);\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-reach));\n filter: blur(var(--zrl-glow-blur));\n opacity: var(--zrl-glow-opacity);\n will-change: filter;\n}\n.${PREFIX}-qr-beam-glow-band {\n inset: calc(0px - var(--zrl-glow-core));\n border-width: var(--zrl-glow-core);\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-core));\n}\n\n/* At rest the edge holds a dim, even blue: a 1px line on the border and, from\n the glow band, a soft halo outside it. Hidden while the comet runs, so the\n border reads as the well's own line with a light passing over it; shown\n once the light has stopped. Every path below ends here, which is what\n makes them look the same at rest. */\n.${PREFIX}-qr-beam::before,\n.${PREFIX}-qr-beam-glow-band::before {\n content: '';\n position: absolute;\n inset: -50%;\n background: var(--zrl-beam);\n opacity: 0;\n transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* The moving light: an oversized square carrying a conic sweep, rotated\n whole. A transform animation runs on the compositor, so the light keeps\n moving while the page is busy; animating the gradient angle instead\n repaints every frame on the main thread and stutters. */\n.${PREFIX}-qr-beam::after,\n.${PREFIX}-qr-beam-glow-band::after {\n content: '';\n position: absolute;\n inset: -50%;\n background: conic-gradient(\n from 0deg,\n transparent 0deg 220deg,\n var(--zrl-beam) 330deg,\n var(--zrl-beam-head) 348deg,\n transparent 356deg 360deg\n );\n animation: ${PREFIX}-orbit var(--zrl-beam-time) linear infinite;\n will-change: transform;\n transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* Spent: the light stops where it is and fades, and the edge settles to the\n dim glow. Paused rather than removed, so it does not jump back to its start\n on the way out. */\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam::after,\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam-glow-band::after {\n animation-play-state: paused;\n opacity: 0;\n}\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam::before { opacity: 0.55; }\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7; }\n\n.${PREFIX}-qr {\n display: block;\n width: 100%;\n height: 100%;\n border-radius: 8px;\n filter: var(--zrl-qr-filter);\n mix-blend-mode: var(--zrl-qr-blend);\n transition: filter 300ms cubic-bezier(0.23, 1, 0.32, 1),\n opacity 300ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 300ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* Once the code is claimed the QR is spent. Blurring it out rather than\n swapping it keeps one object on screen through the state change, so the eye\n reads a transformation instead of two things trading places. */\n.${PREFIX}-qr[data-spent=\"true\"] { opacity: 0.2; filter: var(--zrl-qr-spent-filter); transform: scale(0.96); }\n\n.${PREFIX}-qr-overlay {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n animation: ${PREFIX}-fade 200ms ease-out both;\n}\n\n.${PREFIX}-qr-badge {\n display: grid;\n place-items: center;\n width: 56px;\n height: 56px;\n border-radius: 999px;\n background: var(--zrl-accent-soft);\n color: var(--zrl-accent-ink);\n}\n\n.${PREFIX}-status {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin-top: 20px;\n font-size: 14px;\n font-weight: 500;\n color: var(--zrl-ink);\n}\n\n.${PREFIX}-dot { position: relative; display: grid; place-items: center; width: 8px; height: 8px; }\n.${PREFIX}-dot i {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 999px;\n background: var(--zrl-accent);\n font-style: normal;\n}\n.${PREFIX}-dot i:first-child { animation: ${PREFIX}-ping 1.8s cubic-bezier(0.23, 1, 0.32, 1) infinite; }\n\n.${PREFIX}-timer {\n margin: 4px 0 0;\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n color: var(--zrl-ink-mute);\n transition: color 200ms ease-out;\n}\n.${PREFIX}-timer[data-urgent=\"true\"] { color: var(--zrl-urgent); }\n\n.${PREFIX}-help {\n padding: 14px 24px;\n border-top: 1px solid var(--zrl-line-soft);\n background: var(--zrl-surface-sunken);\n border-radius: 0;\n}\n.${PREFIX}-help-title { margin: 0; font-size: 12px; font-weight: 600; color: var(--zrl-ink); }\n.${PREFIX}-help-body {\n margin: 4px auto 0;\n max-width: 34ch;\n font-size: 12px;\n line-height: 1.55;\n color: var(--zrl-ink-soft);\n}\n\n.${PREFIX}-footer { padding: 12px; border-top: 1px solid var(--zrl-line-soft); }\n\n.${PREFIX}-cancel {\n display: block;\n width: 100%;\n padding: 10px;\n border: 0;\n border-radius: 12px;\n background: transparent;\n font: inherit;\n font-size: 14px;\n font-weight: 500;\n color: var(--zrl-ink-soft);\n cursor: pointer;\n transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;\n}\n.${PREFIX}-cancel:hover { background: var(--zrl-surface-sunken); color: var(--zrl-ink); }\n.${PREFIX}-cancel:active { transform: scale(0.99); }\n\n.${PREFIX}-secured {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n margin: 6px 0 0;\n font-size: 12px;\n color: var(--zrl-ink-mute);\n text-decoration: none;\n border-radius: 6px;\n transition: color 150ms ease-out;\n}\n.${PREFIX}-secured:hover { color: var(--zrl-ink); }\n\n.${PREFIX}-close {\n position: absolute;\n top: 12px;\n inset-inline-end: 12px;\n display: grid;\n place-items: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--zrl-ink-mute);\n cursor: pointer;\n transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;\n}\n.${PREFIX}-close:hover { background: var(--zrl-surface-sunken); color: var(--zrl-ink); }\n.${PREFIX}-close:active { transform: scale(0.95); }\n\n.${PREFIX}-card :focus-visible {\n outline: 2px solid var(--zrl-accent);\n outline-offset: 2px;\n}\n\n@keyframes ${PREFIX}-fade { from { opacity: 0 } to { opacity: 1 } }\n@keyframes ${PREFIX}-rise {\n from { opacity: 0; transform: translateY(10px) scale(0.98) }\n to { opacity: 1; transform: none }\n}\n@keyframes ${PREFIX}-ping {\n 0% { transform: scale(1); opacity: 0.5 }\n 70%, 100% { transform: scale(2.6); opacity: 0 }\n}\n\n@keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }\n/* THE BUSY RING. The light of the QR well, around any control that is\n waiting on the provider: the button on a phone between the tap and the\n hand-over to the app. The well's sweep is a cone from the centre, which is\n even on a square and useless on a wide button: it crawls along the long\n sides and lights two edges at once near the ends. So here the light is a\n dash on an SVG outline, which moves at one speed the whole way round\n whatever the shape, drawn with the well's tokens: its colour and head\n tint, its line width, its halo, its four second lap. The outline's length\n is measured by the component and set as --zrl-ring-len, and every dash\n and offset is a fraction of it, because pathLength does not scale dash\n values given from CSS. A stroke cannot fade along its length, so the tail\n is a stack of dashes sharing one head, each shorter and more opaque than\n the one under it, with opacities chosen so the stack composes to a\n straight fade from the head to nothing three tenths of the way back; the\n component sets each layer's length, offset and opacity. Shown only while\n busy. */\n.${PREFIX}-ring {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n --zrl-beam-time: 4s;\n}\n.${PREFIX}-ring-svg {\n position: absolute;\n inset: -4px;\n width: calc(100% + 8px);\n height: calc(100% + 8px);\n overflow: visible;\n pointer-events: none;\n opacity: 0;\n transition: opacity 200ms ease-out;\n}\n.${PREFIX}-ring[data-busy=\"true\"] > .${PREFIX}-ring-svg { opacity: 1; }\n.${PREFIX}-ring-svg rect {\n --zrl-l: var(--zrl-ring-len, 600px);\n x: 2px;\n y: 2px;\n width: calc(100% - 4px);\n height: calc(100% - 4px);\n fill: none;\n stroke: var(--zrl-beam);\n stroke-width: var(--zrl-beam-line);\n stroke-linecap: round;\n stroke-dashoffset: var(--zrl-s, 0px);\n animation: ${PREFIX}-dash var(--zrl-beam-time) linear infinite;\n}\n.${PREFIX}-ring-head { stroke: var(--zrl-beam-head); }\n.${PREFIX}-ring-halo {\n stroke-width: calc(var(--zrl-glow-core) * 2 + var(--zrl-beam-line));\n filter: blur(var(--zrl-glow-blur));\n}\n@keyframes ${PREFIX}-dash {\n from { stroke-dashoffset: var(--zrl-s, 0px); }\n to { stroke-dashoffset: calc(var(--zrl-s, 0px) - var(--zrl-l)); }\n}\n@media (prefers-reduced-motion: reduce) {\n .${PREFIX}-ring-svg rect { animation: none; stroke-dasharray: none; opacity: 0.45; }\n .${PREFIX}-ring-halo, .${PREFIX}-ring-head { display: none; }\n}\n\n\n@media (prefers-reduced-motion: reduce) {\n .${PREFIX}-scrim,\n .${PREFIX}-card,\n .${PREFIX}-qr-overlay { animation: none }\n .${PREFIX}-dot i:first-child { animation: none; opacity: 0.35 }\n .${PREFIX}-qr,\n .${PREFIX}-cancel,\n .${PREFIX}-close,\n .${PREFIX}-timer { transition: none }\n /* No travelling light; the edge keeps its dim static glow instead. */\n .${PREFIX}-qr-beam::after,\n .${PREFIX}-qr-beam-glow-band::after { animation: none; opacity: 0 }\n .${PREFIX}-qr-beam::before { opacity: 0.55 }\n .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7 }\n}\n`;\n\n/**\n * Injected at module scope on first import in a DOM, not per render: the tag is\n * idempotent by id, so a host with two provider instances (or a hot reload)\n * still ends up with exactly one.\n */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ELEMENT_ID)) return;\n const el = document.createElement('style');\n el.id = STYLE_ELEMENT_ID;\n el.textContent = CSS;\n document.head.appendChild(el);\n}\n","/**\n * The pairing modal, in plain DOM.\n *\n * Same dialog as @zoreal/oauth2-react's, built without a framework so the core\n * package can put the QR on screen by itself. On desktop a QR sign-in cannot\n * complete unless something renders the pairing code, and leaving that to every\n * caller is how a QR login ships with no QR on it.\n *\n * Nothing here is exported as a component: `startLogin` mounts it, updates it\n * from the same state it hands `onState`, and unmounts it when the flow\n * settles. Callers who want their own UI pass `pairingUI: 'none'`.\n */\n\nimport { interpolate, isRtl, strings } from './i18n';\nimport { titleFor } from './intent';\nimport { cx, ensureStyles } from './styles';\nimport type { LoginIntent, PairingState, ZorealTheme } from './types';\n\n/** Our own cap on how long a pairing sits on screen. See `pairingTimeoutMs`. */\nexport const DEFAULT_PAIRING_TIMEOUT_MS = 120_000;\n\n/** Below this the countdown changes colour: background information becomes a prompt to hurry. */\nconst URGENT_SECONDS = 20;\n\nfunction mmss(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = totalSeconds % 60;\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className?: string,\n text?: string\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\n/**\n * Icons and the lockup are built with createElementNS rather than innerHTML.\n * This package renders on someone else's sign-in page; assigning markup here\n * would be an injection surface for no benefit.\n */\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\nfunction svg(viewBox: string, attrs: Record<string, string> = {}): SVGSVGElement {\n const node = document.createElementNS(SVG_NS, 'svg');\n node.setAttribute('viewBox', viewBox);\n node.setAttribute('focusable', 'false');\n node.setAttribute('aria-hidden', 'true');\n for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, v);\n return node;\n}\n\nfunction path(d: string, attrs: Record<string, string> = {}): SVGPathElement {\n const node = document.createElementNS(SVG_NS, 'path');\n node.setAttribute('d', d);\n for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, v);\n return node;\n}\n\nfunction strokeIcon(size: number, ds: string[], width = '2'): SVGSVGElement {\n const node = svg('0 0 24 24', {\n width: String(size),\n height: String(size),\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': width,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n });\n for (const d of ds) node.appendChild(path(d));\n return node;\n}\n\nconst ZOREAL_BLUE = '#00b4d9';\n\n/** Wordmark paths, from zoreal-web's zoreal-lockup.svg. */\nconst WORDMARK =\n 'M205,40.5h15.3v-3.5h-11.5v-18.4h-3.8v21.8ZM157,22.2v5.6h10.9v3.5h-10.9v5.8h12.5v3.5h-16.3v-21.8h16.2v3.5h-12.4ZM141.4,25.8c0,1.1-.4,2-1.2,2.7-.8.7-1.9,1-3.3,1h-5.6v-7.3h5.6c1.4,0,2.6.3,3.4.9.8.6,1.2,1.5,1.2,2.7ZM146,40.5l-5.9-8.3c.8-.2,1.4-.5,2.1-.9s1.2-.9,1.7-1.4c.4-.5.8-1.2,1.1-1.9s.4-1.5.4-2.4-.1-2-.5-2.9c-.4-.9-.9-1.6-1.7-2.2-.6-.6-1.5-1-2.5-1.4-1-.3-2.2-.4-3.4-.4h-9.8v21.8h3.8v-7.6h4.8l5.4,7.6h4.5ZM115.7,29.7c0,1.1-.1,2.1-.5,3s-.9,1.7-1.5,2.4-1.4,1.2-2.4,1.7c-.9.4-1.9.6-3,.6s-2.1-.1-3-.6-1.7-1-2.4-1.7-1.2-1.5-1.5-2.4-.5-1.9-.5-3,.1-2.1.5-3,.9-1.7,1.5-2.4,1.4-1.2,2.4-1.7c.9-.4,1.9-.6,3-.6s2.1.2,3,.6c.9.4,1.7,1,2.3,1.7.6.6,1.2,1.5,1.6,2.4s.5,1.9.5,3ZM119.7,29.6c0-1.5-.3-3-.8-4.4-.6-1.4-1.4-2.5-2.4-3.6-1-1-2.2-1.8-3.6-2.4-1.4-.6-3-.9-4.6-.9s-3.2.4-4.6.9c-1.4.6-2.7,1.4-3.7,2.4s-1.8,2.2-2.4,3.6c-.5,1.4-.8,2.8-.8,4.4s.3,3,.8,4.4c.6,1.4,1.4,2.5,2.4,3.6,1,1,2.2,1.8,3.6,2.4,1.4.6,3,.9,4.6.9s3.2-.4,4.6-.9c1.4-.6,2.6-1.4,3.7-2.4,1-1,1.8-2.2,2.4-3.6.5-1.4.8-2.9.8-4.4ZM86.1,22.1l-13,15.6v2.8h17.9v-3.4h-12.9l12.9-15.6v-2.8h-17.5v3.4h12.5ZM188.5,18.5h-3.5l-9.6,22h4c3.7-8.8,3.4-8,7.4-17.4,3.7,8.8,3.9,9.1,7.4,17.4h4l-9.6-22Z';\n\nconst MARK_PATHS = [\n 'M52,25.7c.4-2-.5-4.2-2.5-5.4l-11.8-6.8,3.4-2,10.1,5.9c3.6,2,5.1,6.3,3.8,10.1-.2.5-.4,1-.7,1.6-.3.5-.6.9-1,1.4-.9.9-1.9,1.7-3,2.2-2.4,1-5.2.9-7.6-.5l-5.9-3.4c-1.9-1.1-4.3-.9-5.9.5-.4.4-.8.8-1.1,1.3-.3.5-.5,1.1-.6,1.7-.4,2,.6,4.2,2.5,5.4l11.8,6.8-3.4,2-10.1-5.9c-3.6-2-5.1-6.3-3.8-10.1.1-.5.4-1,.7-1.6.3-.5.7-.9,1-1.4.9-.9,1.9-1.7,3.1-2.2,2.4-1,5.2-.9,7.6.4l5.9,3.4c1.9,1.1,4.3.9,5.9-.5.4-.4.8-.8,1.1-1.3.3-.5.5-1.1.6-1.7Z',\n 'M60.3,33.1c-.5.8-1.5,1.1-2.3.6-.9-.5-1.1-1.5-.7-2.3.5-.9,1.5-1.1,2.3-.7.9.5,1.1,1.5.7,2.4Z',\n 'M31.9,18c3.4-.5,6.9,0,10,1.9l.8.5h0c.8.5,1,1.5.6,2.3s-1.5,1.1-2.3.6l-.8-.5c-2.8-1.6-6-1.9-8.9-1.2-1,.3-2,.7-3,1.2h0c-.8.5-1.8.2-2.3-.7-.5-.8-.2-1.8.6-2.3,0,0,0,0,.2,0,.2,0,.4-.2.7-.3,1.4-.7,2.9-1.1,4.4-1.4Z',\n 'M21.4,24.9c.5-.8,1.6-1,2.3-.5.8.5,1,1.6.5,2.3-.5.8-1.6,1-2.4.5-.8-.5-1-1.6-.5-2.3Z',\n 'M45.5,23.9c.5-.9,1.5-1.1,2.3-.7.8.5,1.1,1.5.6,2.4-.5.8-1.5,1.1-2.3.6s-1.1-1.5-.6-2.3Z',\n 'M49.4,39.9c-3.3.6-6.9,0-10-1.8l-.8-.5h0c-.8-.5-1.1-1.5-.6-2.3s1.5-1.1,2.3-.6l.8.5c2.8,1.6,6,1.9,9,1.2,1-.3,2-.7,3-1.2h0c.8-.5,1.8-.2,2.3.6.5.9.2,1.9-.6,2.4,0,0-.1,0-.2,0-.2.1-.5.3-.7.4-1.4.7-2.8,1.1-4.4,1.4Z',\n 'M35.8,34c-.5.8-1.5,1.1-2.3.6s-1.1-1.5-.6-2.3,1.5-1.1,2.3-.6,1.1,1.5.6,2.3Z',\n];\n\n/**\n * The full lockup. The wordmark is `currentColor` rather than the master's\n * near-black, because the dialog renders in either theme and a fixed dark\n * wordmark disappears on a dark card. The mark keeps the brand blue in both:\n * it reads on either ground, and it is the part that says whose sign-in this\n * is.\n */\nfunction lockup(height: number): SVGSVGElement {\n const node = svg('0 0 240 58.5', {\n height: String(height),\n width: String(Math.round(height * (240 / 58.5))),\n });\n node.removeAttribute('aria-hidden');\n node.setAttribute('role', 'img');\n node.setAttribute('aria-label', 'ZOREAL');\n node.appendChild(path(WORDMARK, { fill: 'currentColor' }));\n const g = document.createElementNS(SVG_NS, 'g');\n g.setAttribute('fill', ZOREAL_BLUE);\n g.setAttribute('fill-rule', 'evenodd');\n for (const d of MARK_PATHS) g.appendChild(path(d));\n node.appendChild(g);\n return node;\n}\n\nexport interface PairingModalOptions {\n onCancel: () => void;\n locale?: string;\n theme?: ZorealTheme;\n timeoutMs?: number;\n /** Which title the dialog opens with. Defaults to the sign-in wording. */\n intent?: LoginIntent;\n}\n\nexport interface PairingModalHandle {\n /** Re-render from a new pairing state. */\n update: (state: PairingState) => void;\n /** Remove the dialog and release everything it held. Idempotent. */\n close: () => void;\n}\n\n/**\n * Mounts the dialog and returns the two controls the flow needs. Returns null\n * outside a browser, so importing this package on a server is inert.\n */\nexport function mountPairingModal(\n state: PairingState,\n options: PairingModalOptions\n): PairingModalHandle | null {\n if (typeof document === 'undefined') return null;\n ensureStyles();\n\n const t = strings(options.locale);\n const timeoutMs = options.timeoutMs ?? DEFAULT_PAIRING_TIMEOUT_MS;\n let closed = false;\n\n const scrim = el('div', `${cx('root')} ${cx('scrim')}`);\n scrim.dataset.theme = options.theme ?? 'auto';\n\n const card = el('div', cx('card'));\n card.setAttribute('role', 'dialog');\n card.setAttribute('aria-modal', 'true');\n card.dir = isRtl(options.locale) ? 'rtl' : 'ltr';\n\n const titleId = `zrl-title-${Math.random().toString(36).slice(2, 9)}`;\n card.setAttribute('aria-labelledby', titleId);\n\n const closeBtn = el('button', cx('close'));\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', t.close);\n closeBtn.appendChild(strokeIcon(16, ['M18 6 6 18M6 6l12 12']));\n\n const body = el('div', cx('body'));\n const mark = lockup(44);\n mark.classList.add(cx('lockup'));\n\n const title = el('h2', cx('title'));\n title.id = titleId;\n const bodyText = el('p', cx('body-text'));\n\n // The light on the well's edge is a set of masked overlays inside the well,\n // drawn first so the spent badge, a later sibling, stays above them. The\n // well carries the spent flag for them: a stylesheet cannot look back from\n // the image to a sibling before it.\n const well = el('div', cx('qr-well'));\n const glow = el('span', cx('qr-beam-glow'));\n glow.setAttribute('aria-hidden', 'true');\n glow.appendChild(el('span', cx('qr-beam-glow-band')));\n const beam = el('span', cx('qr-beam'));\n beam.setAttribute('aria-hidden', 'true');\n well.append(glow, beam);\n const qr = el('img', cx('qr'));\n qr.alt = t.qrAlt;\n qr.width = 180;\n qr.height = 180;\n if (state.qrUrl) qr.src = state.qrUrl;\n\n const overlay = el('span', cx('qr-overlay'));\n const badge = el('span', cx('qr-badge'));\n badge.appendChild(strokeIcon(24, ['M8.5 2h7a2.5 2.5 0 0 1 2.5 2.5v15a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 6 19.5v-15A2.5 2.5 0 0 1 8.5 2Z', 'M11 18.5h2'], '1.8'));\n overlay.appendChild(badge);\n well.append(qr, overlay);\n\n const status = el('div', cx('status'));\n const dot = el('span', cx('dot'));\n dot.append(el('i'), el('i'));\n const statusLabel = el('span');\n status.append(dot, statusLabel);\n\n const timer = el('p', cx('timer'));\n\n body.append(mark, title, bodyText, well, status, timer);\n\n // The QR is on screen because this person is being asked to use a phone app,\n // and some of them do not have it yet. Without this the panel reads as \"scan\n // this with something I do not have\", and the flow dead-ends at the one\n // moment it can still be recovered: the same code installs the app.\n const help = el('div', cx('help'));\n help.append(el('p', cx('help-title'), t.noIdTitle), el('p', cx('help-body'), t.noIdBody));\n\n const footer = el('div', cx('footer'));\n const cancelBtn = el('button', cx('cancel'), t.cancel);\n cancelBtn.type = 'button';\n // The line at the foot is a link to ZOREAL itself, in a new tab so the\n // login on this page is not abandoned. The referrer is passed on purpose.\n const secured = el('a', cx('secured'));\n secured.href = 'https://zoreal.com';\n secured.target = '_blank';\n secured.rel = 'noopener';\n secured.append(strokeIcon(13, ['M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z', 'm9 12 2 2 4-4']), document.createTextNode(t.secured));\n footer.append(cancelBtn, secured);\n\n card.append(closeBtn, body, help, footer);\n scrim.appendChild(card);\n\n // A deadline, not a decremented counter: background tabs throttle timers, so\n // a counter that subtracts one per tick comes back lying about the time left.\n const serverMs = typeof state.expiresIn === 'number' ? state.expiresIn * 1000 : Infinity;\n const deadline = Date.now() + Math.min(timeoutMs, serverMs);\n\n // The frame swap. The provider renders a new code every few seconds and each\n // state can carry a new qrUrl. Assigning it straight to the visible <img>\n // blanks the image until the new bytes arrive, and on a slow link that is a\n // flicker on the one thing the person is trying to scan. So the next frame\n // loads off screen first and is swapped in once it has arrived; the browser\n // serves the swap from the fetch it just made while the preload is still\n // held. A frame that fails to load is dropped, the next state brings\n // another. A frame superseded while still loading is dropped too: the newer\n // one is the current code. Once the code is spent (data-spent) nothing\n // swaps any more; the blurred image behind the phone glyph is the last one.\n let shown = state.qrUrl;\n let loading: HTMLImageElement | null = null;\n const spent = () => qr.dataset.spent === 'true';\n\n const showFrame = (url: string) => {\n if (url === shown || spent()) return;\n if (!shown) {\n // Nothing on screen yet, so there is no flash to avoid.\n qr.src = url;\n shown = url;\n return;\n }\n const next = new Image();\n loading = next;\n next.onload = () => {\n if (loading !== next || spent()) return;\n loading = null;\n qr.src = url;\n shown = url;\n };\n next.onerror = () => {\n if (loading === next) loading = null;\n };\n next.src = url;\n };\n\n const paint = (s: PairingState) => {\n // `claimed` = the request is waiting in the holder's app; `enrolling` = a\n // first-time holder finishing setup. In both the QR is spent and the action\n // has moved to the phone.\n const settled = s.status === 'claimed' || s.status === 'enrolling';\n title.textContent = settled ? t.titleApprove : titleFor(t, options.intent ?? 'sign-in');\n bodyText.textContent =\n s.status === 'enrolling' ? t.bodyEnrolling : settled ? t.bodyApprove : t.bodyScan;\n statusLabel.textContent = settled ? t.waitingApproval : t.waiting;\n qr.dataset.spent = String(settled);\n well.dataset.spent = String(settled);\n overlay.style.display = settled ? '' : 'none';\n if (s.qrUrl) showFrame(s.qrUrl);\n };\n\n const tick = () => {\n const left = Math.max(0, Math.ceil((deadline - Date.now()) / 1000));\n timer.textContent = interpolate(t.expiresIn, mmss(left));\n timer.dataset.urgent = String(left <= URGENT_SECONDS);\n if (left === 0) {\n // Stop the tick before cancelling: close() clears it too, but a timer\n // still firing cancel once a second in between is a race to inherit.\n window.clearInterval(interval);\n options.onCancel();\n }\n };\n\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') options.onCancel();\n };\n\n const close = () => {\n if (closed) return;\n closed = true;\n loading = null;\n window.clearInterval(interval);\n document.removeEventListener('keydown', onKey);\n document.body.style.overflow = previousOverflow;\n scrim.remove();\n };\n\n // Every dismissal is the same behaviour: abort the poll, close. An orphaned\n // poll is how a request gets cancelled for over-polling.\n closeBtn.addEventListener('click', options.onCancel);\n cancelBtn.addEventListener('click', options.onCancel);\n scrim.addEventListener('click', (e) => {\n if (e.target === scrim) options.onCancel();\n });\n document.addEventListener('keydown', onKey);\n\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n\n paint(state);\n tick();\n const interval = window.setInterval(tick, 1000);\n\n document.body.appendChild(scrim);\n closeBtn.focus();\n\n return { update: paint, close };\n}\n","/**\n * PKCE, S256 only: mandatory for every client, confidential ones included.\n * There is no plain fallback and there must never be one; a provider seeing\n * method=plain is seeing a bug or an attack.\n */\n\nconst VERIFIER_BYTES = 32; // 43 base64url chars, the RFC 7636 minimum length\n\nconst base64url = (bytes: Uint8Array): string =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\nexport function generateVerifier(): string {\n const bytes = new Uint8Array(VERIFIER_BYTES);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\nexport async function challengeS256(verifier: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return base64url(new Uint8Array(digest));\n}\n\n/**\n * The same challenge, computed synchronously.\n *\n * The same-device sign-in is a navigation the browser must see as the\n * person's own tap, and an `await` between the tap and the navigation is\n * what breaks that: WebCrypto only digests asynchronously, so the digest is\n * done here by hand. SHA-256 as in FIPS 180-4, verified against the RFC\n * 7636 vector and against WebCrypto in the tests.\n */\nexport function challengeS256Sync(verifier: string): string {\n return base64url(sha256(new TextEncoder().encode(verifier)));\n}\n\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst rotr = (x: number, n: number): number => (x >>> n) | (x << (32 - n));\n\nexport function sha256(message: Uint8Array): Uint8Array {\n const H = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n ]);\n const length = message.length;\n const padded = new Uint8Array(((length + 9 + 63) >> 6) << 6);\n padded.set(message);\n padded[length] = 0x80;\n const view = new DataView(padded.buffer);\n const bits = length * 8;\n view.setUint32(padded.length - 8, Math.floor(bits / 0x100000000));\n view.setUint32(padded.length - 4, bits >>> 0);\n\n const W = new Uint32Array(64);\n for (let offset = 0; offset < padded.length; offset += 64) {\n for (let i = 0; i < 16; i++) W[i] = view.getUint32(offset + i * 4);\n for (let i = 16; i < 64; i++) {\n const w15 = W[i - 15];\n const w2 = W[i - 2];\n const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);\n const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);\n W[i] = (W[i - 16] + s0 + W[i - 7] + s1) >>> 0;\n }\n let [a, b, c, d, e, f, g, h] = H;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (h + S1 + ch + K[i] + W[i]) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (S0 + maj) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + t1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) >>> 0;\n }\n H[0] = (H[0] + a) >>> 0;\n H[1] = (H[1] + b) >>> 0;\n H[2] = (H[2] + c) >>> 0;\n H[3] = (H[3] + d) >>> 0;\n H[4] = (H[4] + e) >>> 0;\n H[5] = (H[5] + f) >>> 0;\n H[6] = (H[6] + g) >>> 0;\n H[7] = (H[7] + h) >>> 0;\n }\n const out = new Uint8Array(32);\n const outView = new DataView(out.buffer);\n for (let i = 0; i < 8; i++) outView.setUint32(i * 4, H[i]);\n return out;\n}\n\nexport function generateState(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\n/**\n * A pairing token of this package's own choosing, for the same-device\n * navigation: the provider answers a navigation with nothing the page could\n * read, so the page names the pairing it will poll. Same shape as a token\n * the provider mints, 32 letters and digits from the CSPRNG.\n */\nconst TOKEN_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\nexport function generateRequestId(): string {\n let out = '';\n const bytes = new Uint8Array(64);\n while (out.length < 32) {\n crypto.getRandomValues(bytes);\n for (const byte of bytes) {\n // Rejection sampling: 62 does not divide 256, so bytes past the last\n // full multiple are thrown away rather than folded, which would bias.\n if (byte >= 248 || out.length === 32) continue;\n out += TOKEN_ALPHABET[byte % 62];\n }\n }\n return out;\n}\n","/**\n * The one flow, as an imperative handle. This is the same state machine the\n * React SDK's hook runs, without the React: start a pairing, surface it for\n * rendering through onState, poll, and finish per mode. Browser-direct\n * exchanges the code here (public client, PKCE, no secret) and hands over an\n * ID token; auth-code hands the code and the PKCE verifier to the caller,\n * whose backend does the exchange with its client authentication.\n *\n * A framework wrapper owns exactly two things: calling startLogin on the\n * user's gesture, and rendering what onState carries. Everything else -\n * PKCE, state, nonce, cadence, cancellation - lives here.\n */\n\nimport { unsafeClaims } from './jwt';\nimport {\n FlowAbandonedError,\n OAuthFlowError,\n exchangeCode,\n isMobileUserAgent,\n pollUntilApproved,\n sameDeviceStartUrl,\n startPairing,\n} from './pairing';\nimport { resolveIntent } from './intent';\nimport { mountPairingModal, type PairingModalHandle } from './modal';\nimport {\n challengeS256,\n challengeS256Sync,\n generateRequestId,\n generateState,\n generateVerifier,\n} from './pkce';\nimport { DEFAULT_ISSUER, DEFAULT_QR_REFRESH_SECONDS } from './wire';\nimport type {\n AcrValue,\n AuthCodeLoginOptions,\n BrowserDirectLoginOptions,\n LoginHandle,\n PairingState,\n SelectBy,\n ZorealCodeResponse,\n ZorealCredentialResponse,\n} from './types';\n\nexport function startLogin(\n options: BrowserDirectLoginOptions\n): LoginHandle<ZorealCredentialResponse>;\nexport function startLogin(options: AuthCodeLoginOptions): LoginHandle<ZorealCodeResponse>;\nexport function startLogin(\n options: BrowserDirectLoginOptions | AuthCodeLoginOptions\n): LoginHandle<ZorealCredentialResponse> | LoginHandle<ZorealCodeResponse> {\n if ('ux_mode' in options && options.ux_mode === 'redirect') {\n // The popup shape only: the code and PKCE verifier resolve the promise\n // and go from there to your backend over TLS. A redirect would have to\n // carry the verifier in a URL, which is a credential in every access\n // log on the path. Refused loudly rather than implemented badly.\n throw new Error(\n \"@zoreal/oauth2-js: ux_mode 'redirect' is not supported. Use the default \" +\n \"'popup' shape and post the code and code_verifier from the resolved \" +\n 'promise to your backend.'\n );\n }\n\n const flow = options.flow ?? 'browser-direct';\n const issuer = options.issuer ?? DEFAULT_ISSUER;\n const controller = new AbortController();\n\n // Decided before the pairing is created, not after: the provider binds the\n // surface at creation, either moving QR frames or a start token that only\n // the opened link carries, and will not serve the other one later. It\n // depends only on the options and the user agent, so there is nothing to\n // wait for.\n const useAppLink =\n options.display === 'link' || (options.display !== 'qr' && isMobileUserAgent());\n const intent = resolveIntent(options.intent, options.scope, options.acr_values);\n\n const surface: {\n requestId?: string;\n pairUrl?: string;\n qrUrl?: string;\n appLink?: boolean;\n } = {};\n\n const cancel = () => controller.abort();\n\n // Mounted lazily once the provider has created a pairing, and torn down on\n // every exit from `run` below: resolution, refusal, and cancel alike.\n let modal: PairingModalHandle | null = null;\n // The QR frame refresh, once there is one. Stopped on every exit from `run`,\n // on cancel, and the moment the pairing leaves `pending`: from then on the\n // code is spent and a moving image would only distract.\n let stopRefresh: () => void = () => {};\n controller.signal.addEventListener('abort', () => stopRefresh());\n const teardown = () => {\n stopRefresh();\n modal?.close();\n modal = null;\n };\n\n const run = async (): Promise<ZorealCredentialResponse | ZorealCodeResponse> => {\n const verifier = generateVerifier();\n const state = generateState();\n const nonce = generateState();\n\n try {\n let code: string;\n let selectBy: SelectBy = 'device';\n\n if (useAppLink && typeof window !== 'undefined') {\n // THE TAP IS THE NAVIGATION. Nothing is awaited between the caller's\n // click and the assignment below: a browser hands a universal link to\n // an app only inside a navigation the person began, and an await here\n // would put the navigation outside it, where the link loads as a web\n // page instead (see sameDeviceStartUrl). The provider creates the\n // pairing and redirects to the link; the page stays and polls the\n // token it chose, tolerating \"no such pairing\" for as long as the\n // provider may still be answering the navigation. No modal: there is\n // no code to scan and the page is the button that was tapped.\n const requestId = generateRequestId();\n const startUrl = sameDeviceStartUrl(issuer, {\n client_id: options.clientId,\n scope: options.scope ?? 'openid',\n state,\n nonce,\n code_challenge: challengeS256Sync(verifier),\n redirect_uri:\n flow === 'auth-code' ? (options as AuthCodeLoginOptions).redirect_uri : undefined,\n acr_values: Array.isArray(options.acr_values)\n ? options.acr_values.join(' ')\n : options.acr_values,\n max_age: options.max_age,\n prompt: options.prompt,\n locale: options.locale,\n request_id: requestId,\n origin: window.location.origin,\n });\n selectBy = 'app_link';\n surface.requestId = requestId;\n surface.pairUrl = startUrl;\n surface.appLink = true;\n const withSurface = (s: PairingState): PairingState => ({\n ...s,\n pairUrl: startUrl,\n appLink: true,\n intent,\n cancel,\n });\n options.onState?.(withSurface({ status: 'pending' }));\n window.location.assign(startUrl);\n\n code = await pollUntilApproved(\n issuer,\n requestId,\n (s) => options.onState?.(withSurface(s)),\n controller.signal,\n { tolerateUnknownUntil: Date.now() + 15_000 }\n );\n } else {\n const started = await startPairing(\n issuer,\n {\n client_id: options.clientId,\n scope: options.scope ?? 'openid',\n state,\n nonce,\n code_challenge: await challengeS256(verifier),\n redirect_uri:\n flow === 'auth-code' ? (options as AuthCodeLoginOptions).redirect_uri : undefined,\n acr_values: Array.isArray(options.acr_values)\n ? options.acr_values.join(' ')\n : options.acr_values,\n max_age: options.max_age,\n prompt: options.prompt,\n locale: options.locale,\n display: 'qr',\n },\n controller.signal\n );\n\n if ('code' in started) {\n // prompt=none resolved silently: consented sector, live session.\n code = started.code;\n selectBy = 'session';\n } else {\n selectBy = 'qr';\n\n const requestId = started.request_id;\n const qrBase = `${issuer}/pair/${encodeURIComponent(requestId)}/qr.svg`;\n\n // The image moves while the pairing is pending: the provider renders\n // a new frame every few seconds and refuses an old one, which is what\n // makes a screenshot of the code useless. This package only has to\n // re-fetch it on time. Nothing to move on an app-link hand-off, and\n // nothing to move when the provider says it bound the static code.\n const animated = started.display !== 'legacy';\n const qrRefreshSeconds = !animated\n ? undefined\n : typeof started.qr_refresh_seconds === 'number' && started.qr_refresh_seconds > 0\n ? started.qr_refresh_seconds\n : DEFAULT_QR_REFRESH_SECONDS;\n\n surface.requestId = requestId;\n surface.pairUrl = started.pair_url;\n surface.qrUrl = qrBase;\n surface.appLink = false;\n\n // Everything a pairing UI needs, on every state it sees. The modal\n // below renders from it, and so does a caller who has opted out with\n // pairingUI: 'none'. Read at call time rather than captured once,\n // because qrUrl changes underneath while the pairing is pending.\n const withSurface = (s: PairingState): PairingState => ({\n ...s,\n pairUrl: surface.pairUrl,\n qrUrl: surface.qrUrl,\n qrRefreshSeconds,\n appLink: false,\n intent,\n cancel,\n });\n\n // The last state the provider reported, so a frame refresh can emit\n // it again with only the image changed.\n let lastPolled: PairingState = { status: 'pending', expiresIn: started.expires_in };\n const initial = withSurface(lastPolled);\n\n // The initial state, immediately: the first poll response is one\n // round-trip away, and a UI that waits for it opens visibly empty.\n options.onState?.(initial);\n\n if ((options.pairingUI ?? 'modal') === 'modal') {\n modal = mountPairingModal(initial, {\n onCancel: cancel,\n intent,\n locale: options.locale,\n theme: options.theme,\n timeoutMs: options.pairingTimeoutMs,\n });\n }\n\n\n if (qrRefreshSeconds !== undefined) {\n // A deadline and a setTimeout chain, not setInterval. Background\n // tabs throttle timers, and an interval that comes back from a\n // throttled minute fires its backlog in one burst: several frames\n // in one tick, for nothing. Here each frame is stamped with the\n // clock when it is emitted, the next deadline is set from that\n // moment, and a tab becoming visible with its deadline already past\n // gets a current frame at once rather than whenever the throttled\n // timer gets around to it.\n const periodMs = qrRefreshSeconds * 1000;\n let due = Date.now() + periodMs;\n let timer: ReturnType<typeof setTimeout> | undefined;\n // A flag, not just a cleared timer: a caller may cancel() from\n // inside the onState below, and the stop then lands in the middle of\n // emit. Without this, the line after it would schedule the next\n // frame and the loop would outlive the login that ended it.\n let stopped = false;\n\n const emit = () => {\n timer = undefined;\n surface.qrUrl = `${qrBase}?t=${Date.now()}`;\n const next = withSurface(lastPolled);\n modal?.update(next);\n options.onState?.(next);\n if (stopped) return;\n due = Date.now() + periodMs;\n timer = setTimeout(emit, periodMs);\n };\n const onVisible = () => {\n if (document.visibilityState === 'visible' && timer !== undefined && Date.now() >= due) {\n clearTimeout(timer);\n emit();\n }\n };\n const hasDocument = typeof document !== 'undefined';\n if (hasDocument) document.addEventListener('visibilitychange', onVisible);\n\n stopRefresh = () => {\n stopped = true;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n if (hasDocument) document.removeEventListener('visibilitychange', onVisible);\n stopRefresh = () => {};\n };\n timer = setTimeout(emit, Math.max(0, due - Date.now()));\n }\n\n code = await pollUntilApproved(\n issuer,\n requestId,\n (s) => {\n lastPolled = s;\n // Anything but pending means the code is spent: claimed and\n // enrolling have moved the action to the phone, the rest are\n // terminal. Stop before emitting so no frame lands after this.\n if (s.status !== 'pending') stopRefresh();\n const next = withSurface(s);\n modal?.update(next);\n options.onState?.(next);\n },\n controller.signal\n );\n }\n\n teardown();\n\n }\n\n if (flow === 'auth-code') {\n const response: ZorealCodeResponse = {\n code,\n scope: options.scope ?? 'openid',\n app_state: options.app_state,\n code_verifier: verifier,\n nonce,\n };\n return response;\n }\n\n const tokens = await exchangeCode(issuer, {\n code,\n code_verifier: verifier,\n client_id: options.clientId,\n });\n const claims = unsafeClaims(tokens.id_token);\n const response: ZorealCredentialResponse = {\n credential: tokens.id_token,\n clientId: options.clientId,\n select_by: selectBy,\n acr: (claims.acr as AcrValue) ?? 'zoreal.device',\n };\n return response;\n } catch (e) {\n teardown();\n // The taxonomy the promise rejects with, and nothing else:\n // OAuthFlowError the provider refused; reason verbatim\n // FlowAbandonedError a human outcome, or a failure that never\n // reached the provider (network, unknown)\n // AbortError the caller's own cancel()\n if (e instanceof DOMException && e.name === 'AbortError') throw e;\n if (e instanceof OAuthFlowError || e instanceof FlowAbandonedError) throw e;\n throw new FlowAbandonedError({\n type: 'unknown',\n description: e instanceof Error ? e.message : String(e),\n });\n }\n };\n\n const promise = run();\n // A caller driving everything from onState and cancel() may never attach a\n // rejection handler; this no-op one keeps a cancelled login from surfacing\n // as an unhandled rejection. The caller's own catch still sees the error.\n promise.catch(() => {});\n\n return {\n promise: promise as Promise<ZorealCredentialResponse> & Promise<ZorealCodeResponse>,\n cancel,\n get requestId() {\n return surface.requestId;\n },\n get pairUrl() {\n return surface.pairUrl;\n },\n get qrUrl() {\n return surface.qrUrl;\n },\n get appLink() {\n return surface.appLink;\n },\n };\n}\n"],"mappings":"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","names":["el","response"]}