@zitadel/components 1.0.0-alpha.21 → 1.0.0-alpha.23

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.
@@ -1,5 +1,5 @@
1
- import { C as focusVisibleStyles, E as emit, S as __decorate, T as t, d as hookName, w as baseHostStyles } from "./atoms-DSxjEHca.mjs";
2
- import { i as tokensCss, t as THEME_SELECTORS } from "./tokens-T4N9VJz3.mjs";
1
+ import { C as focusVisibleStyles, E as emit, S as __decorate, T as t, d as hookName, w as baseHostStyles } from "./atoms-DbcPXAO0.mjs";
2
+ import { i as tokensCss, r as tokens, t as THEME_SELECTORS } from "./tokens-T4N9VJz3.mjs";
3
3
  import { manifestRegistry } from "./manifests.mjs";
4
4
  import { t as default_default } from "./default-Dnjq9iBt.mjs";
5
5
  import { LitElement, css, html, nothing } from "lit";
@@ -331,18 +331,28 @@ function validateBranding(input, context = {}) {
331
331
  out.layout = CreateFlow201BrandingLayout.centered;
332
332
  }
333
333
  out.logo_url = sanitiseUrl(out.logo_url, "logo_url", issues, { allowed: allowLoopbackHttp });
334
- out.font_url = sanitiseUrl(out.font_url, "font_url", issues);
334
+ if (out.typography?.font_url) out.typography = {
335
+ ...out.typography,
336
+ font_url: sanitiseUrl(out.typography.font_url, "typography.font_url", issues)
337
+ };
335
338
  out.hero_url = sanitiseUrl(out.hero_url, "hero_url", issues, { allowed: allowLoopbackHttp });
336
- if (out.assets) out.assets = {
337
- logo_dark: sanitiseUrl(out.assets.logo_dark, "assets.logo_dark", issues),
338
- favicon: sanitiseUrl(out.assets.favicon, "assets.favicon", issues),
339
- background_image: sanitiseUrl(out.assets.background_image, "assets.background_image", issues)
339
+ if (out.theme?.light || out.theme?.dark) out.theme = {
340
+ ...out.theme,
341
+ ...sanitiseSideLogo(out.theme.light, "light", issues, allowLoopbackHttp),
342
+ ...sanitiseSideLogo(out.theme.dark, "dark", issues, allowLoopbackHttp)
340
343
  };
341
344
  return {
342
345
  branding: out,
343
346
  issues
344
347
  };
345
348
  }
349
+ function sanitiseSideLogo(side, name, issues, allowLoopbackHttp) {
350
+ if (!side) return {};
351
+ return { [name]: {
352
+ ...side,
353
+ logo_url: sanitiseUrl(side.logo_url, `theme.${name}.logo_url`, issues, { allowed: allowLoopbackHttp })
354
+ } };
355
+ }
346
356
  function sanitiseUrl(value, field, issues, loopbackHttp) {
347
357
  if (value == null || value === "") return;
348
358
  try {
@@ -360,6 +370,34 @@ function sanitiseUrl(value, field, issues, loopbackHttp) {
360
370
  }
361
371
  }
362
372
  //#endregion
373
+ //#region src/orchestrator/branding.ts
374
+ /**
375
+ * Which sides the revision actually publishes. A side is published when the
376
+ * revision names it at all — an empty side object still says "this surface is
377
+ * mine", it just takes the maintained defaults for every key.
378
+ */
379
+ function publishedSides(branding) {
380
+ const theme = branding?.theme;
381
+ const sides = [];
382
+ if (theme?.light) sides.push("light");
383
+ if (theme?.dark) sides.push("dark");
384
+ return sides;
385
+ }
386
+ /**
387
+ * The mark for the resolved surface. A logo is pixels and is never recoloured,
388
+ * so a side without its own file shows no mark rather than borrowing the other
389
+ * side's. The legacy top-level `logo_url` is a single-mark fallback and applies
390
+ * only when neither side names one.
391
+ */
392
+ function resolveLogoUrl(branding, theme) {
393
+ if (!branding) return void 0;
394
+ const sides = branding.theme;
395
+ const sideLogo = theme === "light" ? sides?.light?.logo_url : sides?.dark?.logo_url;
396
+ if (sideLogo) return sideLogo;
397
+ if (sides?.light?.logo_url || sides?.dark?.logo_url) return void 0;
398
+ return branding.logo_url;
399
+ }
400
+ //#endregion
363
401
  //#region src/orchestrator/exportparts.ts
364
402
  /**
365
403
  * Forwards atom-internal CSS Shadow Parts through the orchestrator boundary.
@@ -816,7 +854,7 @@ const en = {
816
854
  "password.action.register.lead": "Don't have an account? ",
817
855
  "password.action.register.link": "Sign up",
818
856
  "register.title": "Create your account",
819
- "register.description": "",
857
+ "register.description": "Enter your details to get started",
820
858
  "register.field.email": "Email",
821
859
  "register.field.email.placeholder": "you@example.com",
822
860
  "register.field.password": "Password",
@@ -864,7 +902,7 @@ const en = {
864
902
  "error.password_required": "Please enter a password",
865
903
  "error.password_incorrect": "Wrong email or password.",
866
904
  "error.email_exists": "An account with this email already exists.",
867
- /** Figma sign-in error `6602:180268` — inline on password field. */
905
+ /** Sign-in error, inline on the password field. */
868
906
  "error.invalid_credentials": "Wrong email or password.",
869
907
  "error.required": "This field is required.",
870
908
  "error.field_required": "{0} is required.",
@@ -933,7 +971,7 @@ const de = {
933
971
  "password.action.register.lead": "Noch kein Konto? ",
934
972
  "password.action.register.link": "Registrieren",
935
973
  "register.title": "Konto erstellen",
936
- "register.description": "",
974
+ "register.description": "Gib deine Daten ein, um loszulegen",
937
975
  "register.field.email": "E-Mail",
938
976
  "register.field.email.placeholder": "du@beispiel.de",
939
977
  "register.field.password": "Passwort",
@@ -1049,7 +1087,7 @@ const it = {
1049
1087
  "password.action.register.lead": "Non hai un account? ",
1050
1088
  "password.action.register.link": "Registrati",
1051
1089
  "register.title": "Crea il tuo account",
1052
- "register.description": "",
1090
+ "register.description": "Inserisci i tuoi dati per iniziare",
1053
1091
  "register.field.email": "E-mail",
1054
1092
  "register.field.email.placeholder": "tu@esempio.com",
1055
1093
  "register.field.password": "Password",
@@ -1187,22 +1225,24 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1187
1225
  createFlowDefinition: () => createFlowDefinition,
1188
1226
  createGrant: () => createGrant,
1189
1227
  createHandoff: () => createHandoff,
1228
+ createIdp: () => createIdp,
1190
1229
  createProject: () => createProject,
1191
1230
  createRelease: () => createRelease,
1192
1231
  createSchema: () => createSchema,
1193
1232
  createSession: () => createSession,
1194
1233
  createTeam: () => createTeam,
1195
1234
  createUser: () => createUser,
1196
- deleteFlowDefinition: () => deleteFlowDefinition,
1197
1235
  deleteGrant: () => deleteGrant,
1198
1236
  deleteTeam: () => deleteTeam,
1199
1237
  deleteUserByID: () => deleteUserByID,
1238
+ deleteVariable: () => deleteVariable,
1200
1239
  exchangeHandoff: () => exchangeHandoff,
1201
1240
  finishUserPasskeyRegistration: () => finishUserPasskeyRegistration,
1202
1241
  getAuthAttempt: () => getAuthAttempt,
1203
1242
  getBeginUserPasskeyRegistrationUrl: () => getBeginUserPasskeyRegistrationUrl,
1204
1243
  getBrandingById: () => getBrandingById,
1205
1244
  getClaimStatus: () => getClaimStatus,
1245
+ getClaimWindow: () => getClaimWindow,
1206
1246
  getCompleteClaimUrl: () => getCompleteClaimUrl,
1207
1247
  getCreateAuthAttemptUrl: () => getCreateAuthAttemptUrl,
1208
1248
  getCreateBrandingUrl: () => getCreateBrandingUrl,
@@ -1210,16 +1250,17 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1210
1250
  getCreateFlowUrl: () => getCreateFlowUrl,
1211
1251
  getCreateGrantUrl: () => getCreateGrantUrl,
1212
1252
  getCreateHandoffUrl: () => getCreateHandoffUrl,
1253
+ getCreateIdpUrl: () => getCreateIdpUrl,
1213
1254
  getCreateProjectUrl: () => getCreateProjectUrl,
1214
1255
  getCreateReleaseUrl: () => getCreateReleaseUrl,
1215
1256
  getCreateSchemaUrl: () => getCreateSchemaUrl,
1216
1257
  getCreateSessionUrl: () => getCreateSessionUrl,
1217
1258
  getCreateTeamUrl: () => getCreateTeamUrl,
1218
1259
  getCreateUserUrl: () => getCreateUserUrl,
1219
- getDeleteFlowDefinitionUrl: () => getDeleteFlowDefinitionUrl,
1220
1260
  getDeleteGrantUrl: () => getDeleteGrantUrl,
1221
1261
  getDeleteTeamUrl: () => getDeleteTeamUrl,
1222
1262
  getDeleteUserByIDUrl: () => getDeleteUserByIDUrl,
1263
+ getDeleteVariableUrl: () => getDeleteVariableUrl,
1223
1264
  getEnvironmentByName: () => getEnvironmentByName,
1224
1265
  getEvent: () => getEvent,
1225
1266
  getExchangeHandoffUrl: () => getExchangeHandoffUrl,
@@ -1229,12 +1270,15 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1229
1270
  getGetAuthAttemptUrl: () => getGetAuthAttemptUrl,
1230
1271
  getGetBrandingByIdUrl: () => getGetBrandingByIdUrl,
1231
1272
  getGetClaimStatusUrl: () => getGetClaimStatusUrl,
1273
+ getGetClaimWindowUrl: () => getGetClaimWindowUrl,
1232
1274
  getGetEnvironmentByNameUrl: () => getGetEnvironmentByNameUrl,
1233
1275
  getGetEventUrl: () => getGetEventUrl,
1234
1276
  getGetFlowDefinitionUrl: () => getGetFlowDefinitionUrl,
1235
1277
  getGetFlowStepUrl: () => getGetFlowStepUrl,
1236
1278
  getGetGrantUrl: () => getGetGrantUrl,
1237
1279
  getGetHealthUrl: () => getGetHealthUrl,
1280
+ getGetIdpByIdUrl: () => getGetIdpByIdUrl,
1281
+ getGetIdpRevisionByIdUrl: () => getGetIdpRevisionByIdUrl,
1238
1282
  getGetLiveUrl: () => getGetLiveUrl,
1239
1283
  getGetMySessionUrl: () => getGetMySessionUrl,
1240
1284
  getGetMyUserUrl: () => getGetMyUserUrl,
@@ -1245,14 +1289,20 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1245
1289
  getGetSessionUrl: () => getGetSessionUrl,
1246
1290
  getGetTeamUrl: () => getGetTeamUrl,
1247
1291
  getGetUserByIDUrl: () => getGetUserByIDUrl,
1292
+ getGetVariableUrl: () => getGetVariableUrl,
1293
+ getGetVariablesUrl: () => getGetVariablesUrl,
1248
1294
  getGrant: () => getGrant,
1249
1295
  getHealth: () => getHealth,
1296
+ getIdpById: () => getIdpById,
1297
+ getIdpRevisionById: () => getIdpRevisionById,
1250
1298
  getInitClaimUrl: () => getInitClaimUrl,
1251
1299
  getIssueChallengeUrl: () => getIssueChallengeUrl,
1252
1300
  getListBrandingUrl: () => getListBrandingUrl,
1253
1301
  getListEnvironmentsUrl: () => getListEnvironmentsUrl,
1254
1302
  getListEventsUrl: () => getListEventsUrl,
1255
1303
  getListFlowDefinitionsUrl: () => getListFlowDefinitionsUrl,
1304
+ getListIdpRevisionsUrl: () => getListIdpRevisionsUrl,
1305
+ getListMyProjectsUrl: () => getListMyProjectsUrl,
1256
1306
  getListReleasesUrl: () => getListReleasesUrl,
1257
1307
  getListSchemasUrl: () => getListSchemasUrl,
1258
1308
  getListUserPasskeysUrl: () => getListUserPasskeysUrl,
@@ -1260,8 +1310,12 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1260
1310
  getLive: () => getLive,
1261
1311
  getMySession: () => getMySession,
1262
1312
  getMyUser: () => getMyUser,
1313
+ getPatchMyUserUrl: () => getPatchMyUserUrl,
1263
1314
  getPatchProjectUrl: () => getPatchProjectUrl,
1315
+ getPatchUserByIDUrl: () => getPatchUserByIDUrl,
1264
1316
  getProject: () => getProject,
1317
+ getQueryGrantsUrl: () => getQueryGrantsUrl,
1318
+ getQueryIdpsUrl: () => getQueryIdpsUrl,
1265
1319
  getQueryProjectsUrl: () => getQueryProjectsUrl,
1266
1320
  getQuerySessionsUrl: () => getQuerySessionsUrl,
1267
1321
  getQueryTeamsUrl: () => getQueryTeamsUrl,
@@ -1275,9 +1329,11 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1275
1329
  getSetUserPasswordUrl: () => getSetUserPasswordUrl,
1276
1330
  getSubmitFlowStepUrl: () => getSubmitFlowStepUrl,
1277
1331
  getTeam: () => getTeam,
1278
- getUpdateFlowDefinitionUrl: () => getUpdateFlowDefinitionUrl,
1279
1332
  getUpdateTeamUrl: () => getUpdateTeamUrl,
1333
+ getUpdateVariablesUrl: () => getUpdateVariablesUrl,
1280
1334
  getUserByID: () => getUserByID,
1335
+ getVariable: () => getVariable,
1336
+ getVariables: () => getVariables,
1281
1337
  getVerifyChallengeProofUrl: () => getVerifyChallengeProofUrl,
1282
1338
  initClaim: () => initClaim,
1283
1339
  issueChallenge: () => issueChallenge,
@@ -1285,11 +1341,17 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1285
1341
  listEnvironments: () => listEnvironments,
1286
1342
  listEvents: () => listEvents,
1287
1343
  listFlowDefinitions: () => listFlowDefinitions,
1344
+ listIdpRevisions: () => listIdpRevisions,
1345
+ listMyProjects: () => listMyProjects,
1288
1346
  listReleases: () => listReleases,
1289
1347
  listSchemas: () => listSchemas,
1290
1348
  listUserPasskeys: () => listUserPasskeys,
1291
1349
  listUserTeams: () => listUserTeams,
1350
+ patchMyUser: () => patchMyUser,
1292
1351
  patchProject: () => patchProject,
1352
+ patchUserByID: () => patchUserByID,
1353
+ queryGrants: () => queryGrants,
1354
+ queryIdps: () => queryIdps,
1293
1355
  queryProjects: () => queryProjects,
1294
1356
  querySessions: () => querySessions,
1295
1357
  queryTeams: () => queryTeams,
@@ -1298,8 +1360,8 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1298
1360
  revokeSession: () => revokeSession,
1299
1361
  setUserPassword: () => setUserPassword,
1300
1362
  submitFlowStep: () => submitFlowStep,
1301
- updateFlowDefinition: () => updateFlowDefinition,
1302
1363
  updateTeam: () => updateTeam,
1364
+ updateVariables: () => updateVariables,
1303
1365
  verifyChallengeProof: () => verifyChallengeProof
1304
1366
  });
1305
1367
  const getGetHealthUrl = () => {
@@ -1369,9 +1431,14 @@ const getQueryUsersUrl = () => {
1369
1431
  /**
1370
1432
  * Returns the users of a project, paginated with a cursor.
1371
1433
 
1372
- The project comes from the credential, not from a parameter: the operation
1373
- is bound to the token's own project by construction. This is why it takes
1374
- no `project_id`, unlike the other query endpoints.
1434
+ The project comes from the credential, not from a parameter: the
1435
+ operation is bound to the credential's home project by construction
1436
+ (oauth2 secret or user-bound session). This is why it takes no
1437
+ `project_id`, unlike the other query endpoints.
1438
+
1439
+ Accepts either a project secret (`oauth2`) or a user-bound Console
1440
+ session cookie (`nextgenSession`). CSRF/Origin for cookie mutations
1441
+ is a follow-up (#1140).
1375
1442
 
1376
1443
  * @summary Query users
1377
1444
  */
@@ -1403,6 +1470,28 @@ const getUserByID = async (userId, params, options) => {
1403
1470
  method: "GET"
1404
1471
  });
1405
1472
  };
1473
+ const getPatchUserByIDUrl = (userId) => {
1474
+ return `${getProxyPath()}/users/${userId}`;
1475
+ };
1476
+ /**
1477
+ * Partially updates a user's schema-defined attributes, and optionally
1478
+ moves the user to another registered schema. The merged result is
1479
+ validated against the schema before commit. Concurrent writes are
1480
+ last-write-wins.
1481
+
1482
+ * @summary Update user by ID
1483
+ */
1484
+ const patchUserByID = async (userId, patchUserByIDBody, options) => {
1485
+ return customFetch(getPatchUserByIDUrl(userId), {
1486
+ ...options,
1487
+ method: "PATCH",
1488
+ headers: {
1489
+ "Content-Type": "application/json",
1490
+ ...options?.headers
1491
+ },
1492
+ body: JSON.stringify(patchUserByIDBody)
1493
+ });
1494
+ };
1406
1495
  const getDeleteUserByIDUrl = (userId) => {
1407
1496
  return `${getProxyPath()}/users/${userId}`;
1408
1497
  };
@@ -1533,6 +1622,52 @@ const getMyUser = async (options) => {
1533
1622
  method: "GET"
1534
1623
  });
1535
1624
  };
1625
+ const getPatchMyUserUrl = () => {
1626
+ return `${getProxyPath()}/users/me`;
1627
+ };
1628
+ /**
1629
+ * Partially updates the caller's own schema-defined attributes. The merged
1630
+ result is validated against the user's schema before commit. Concurrent
1631
+ writes are last-write-wins.
1632
+
1633
+ * @summary Update my user information
1634
+ */
1635
+ const patchMyUser = async (patchMyUserBody, options) => {
1636
+ return customFetch(getPatchMyUserUrl(), {
1637
+ ...options,
1638
+ method: "PATCH",
1639
+ headers: {
1640
+ "Content-Type": "application/json",
1641
+ ...options?.headers
1642
+ },
1643
+ body: JSON.stringify(patchMyUserBody)
1644
+ });
1645
+ };
1646
+ const getListMyProjectsUrl = (params) => {
1647
+ const normalizedParams = new URLSearchParams();
1648
+ Object.entries(params || {}).forEach(([key, value]) => {
1649
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1650
+ });
1651
+ const stringifiedParams = normalizedParams.toString();
1652
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users/me/projects?${stringifiedParams}` : `${getProxyPath()}/users/me/projects`;
1653
+ };
1654
+ /**
1655
+ * The projects the signed-in user holds an active grant on, either directly or
1656
+ through a team they are a member of. Ordered by project id and paged with an
1657
+ opaque cursor.
1658
+
1659
+ This is not the same question as `queryProjects`, which answers with the one
1660
+ project the calling credential is bound to. Here the session is the caller
1661
+ and the grants are the answer, so the list spans projects.
1662
+
1663
+ * @summary List the projects I can act on
1664
+ */
1665
+ const listMyProjects = async (params, options) => {
1666
+ return customFetch(getListMyProjectsUrl(params), {
1667
+ ...options,
1668
+ method: "GET"
1669
+ });
1670
+ };
1536
1671
  const getCreateFlowUrl = () => {
1537
1672
  return `${getProxyPath()}/flow`;
1538
1673
  };
@@ -1839,7 +1974,11 @@ const getGetClaimStatusUrl = (projectId, params) => {
1839
1974
  * Polled by the CLI while a browser completes the claim. Authorized by the
1840
1975
  project secret that initiated the challenge. Returns `pending`, or
1841
1976
  `completed` with the owning team, the claim timestamp, and the dashboard
1842
- URL once the browser leg has finished.
1977
+ URL once the project is claimed. The claim grant, not the polled
1978
+ challenge, is the source of truth: a project claimed through another
1979
+ concurrent challenge also reports `completed`, and a completed claim
1980
+ keeps reporting `completed` past this challenge's expiry and past the
1981
+ project's claim window.
1843
1982
 
1844
1983
  * @summary Get claim status
1845
1984
  */
@@ -1871,6 +2010,30 @@ const completeClaim = async (projectId, completeClaimBody, options) => {
1871
2010
  body: JSON.stringify(completeClaimBody)
1872
2011
  });
1873
2012
  };
2013
+ const getGetClaimWindowUrl = (projectId, params) => {
2014
+ const normalizedParams = new URLSearchParams();
2015
+ Object.entries(params || {}).forEach(([key, value]) => {
2016
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2017
+ });
2018
+ const stringifiedParams = normalizedParams.toString();
2019
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/projects/${projectId}/claim/window?${stringifiedParams}` : `${getProxyPath()}/projects/${projectId}/claim/window`;
2020
+ };
2021
+ /**
2022
+ * Read by the claim page in the browser to show how long is left to claim the
2023
+ project. Unauthenticated by design: the claim page runs this before the
2024
+ developer has signed in, and holding the `challenge_id` from the claim URL
2025
+ is the authorization — the same capability `claim/complete` accepts. It
2026
+ reveals only the window, never the project itself, and unlike
2027
+ `claim/complete` it spends nothing, so a reload is free.
2028
+
2029
+ * @summary Get the claim window for a project
2030
+ */
2031
+ const getClaimWindow = async (projectId, params, options) => {
2032
+ return customFetch(getGetClaimWindowUrl(projectId, params), {
2033
+ ...options,
2034
+ method: "GET"
2035
+ });
2036
+ };
1874
2037
  const getCreateSessionUrl = () => {
1875
2038
  return `${getProxyPath()}/sessions`;
1876
2039
  };
@@ -2132,12 +2295,17 @@ const getCreateFlowDefinitionUrl = () => {
2132
2295
  return `${getProxyPath()}/flow_definitions`;
2133
2296
  };
2134
2297
  /**
2135
- * Creates a new flow definition.
2298
+ * Publishes a new flow definition revision.
2136
2299
  Flow definitions are templates that define the sequence of steps (capabilities)
2137
2300
  for a particular user journey (e.g., registration, login, password reset).
2138
2301
 
2139
2302
  Flow definitions are created based on the flow definition schema, which includes the flow's purpose, audience, and the steps involved.
2140
2303
 
2304
+ Every call allocates a new opaque id. Revisions of one flow share its
2305
+ `name`; posting a definition under an existing `name` publishes a new
2306
+ revision of that flow, it is not a conflict. List with `name` to see a
2307
+ flow's revisions, newest by creation time first.
2308
+
2141
2309
  * @summary Create a new flow definition
2142
2310
  */
2143
2311
  const createFlowDefinition = async (createFlowDefinitionBody, options) => {
@@ -2160,8 +2328,9 @@ const getListFlowDefinitionsUrl = (params) => {
2160
2328
  return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions?${stringifiedParams}` : `${getProxyPath()}/flow_definitions`;
2161
2329
  };
2162
2330
  /**
2163
- * Retrieves a list of all flow definitions.
2331
+ * Retrieves a list of all flow definitions, newest by creation time first.
2164
2332
  This endpoint can be used to view existing flow definitions and their configurations.
2333
+ Filter by `name` to list the revisions of one flow.
2165
2334
 
2166
2335
  * @summary List flow definitions
2167
2336
  */
@@ -2184,42 +2353,6 @@ const getFlowDefinition = async (id, options) => {
2184
2353
  method: "GET"
2185
2354
  });
2186
2355
  };
2187
- const getUpdateFlowDefinitionUrl = (id) => {
2188
- return `${getProxyPath()}/flow_definitions/${id}`;
2189
- };
2190
- /**
2191
- * Update a flow definition by id. This endpoint replaces the existing flow definition.
2192
- If `flow_definition.status` is omitted, the current status is preserved
2193
-
2194
- * @summary Update a flow definition by id
2195
- */
2196
- const updateFlowDefinition = async (id, updateFlowDefinitionBody, options) => {
2197
- return customFetch(getUpdateFlowDefinitionUrl(id), {
2198
- ...options,
2199
- method: "PUT",
2200
- headers: {
2201
- "Content-Type": "application/json",
2202
- ...options?.headers
2203
- },
2204
- body: JSON.stringify(updateFlowDefinitionBody)
2205
- });
2206
- };
2207
- const getDeleteFlowDefinitionUrl = (id) => {
2208
- return `${getProxyPath()}/flow_definitions/${id}`;
2209
- };
2210
- /**
2211
- * Delete a flow definition by id.
2212
- If the flow definition is currently being used by a flow, the deletion will fail.
2213
- If the flow definition is the last active flow definition for a given purpose, the deletion will fail to prevent disruption of new flows being started for that purpose.
2214
-
2215
- * @summary Delete a flow definition by id
2216
- */
2217
- const deleteFlowDefinition = async (id, options) => {
2218
- return customFetch(getDeleteFlowDefinitionUrl(id), {
2219
- ...options,
2220
- method: "DELETE"
2221
- });
2222
- };
2223
2356
  const getCreateTeamUrl = (params) => {
2224
2357
  const normalizedParams = new URLSearchParams();
2225
2358
  Object.entries(params || {}).forEach(([key, value]) => {
@@ -2293,6 +2426,11 @@ users whose lifecycle it owns are deactivated with it.
2293
2426
  The request is idempotent. Deleting a team that is already deactivated
2294
2427
  or doesn't exist succeeds without changing anything.
2295
2428
 
2429
+ A team that still owns a project is refused with `409
2430
+ team.owns_project`: deactivating it would leave the project owned by a
2431
+ dead team and so unmanageable. No endpoint releases that ownership yet,
2432
+ so such a team cannot currently be deactivated through the API.
2433
+
2296
2434
  * @summary Delete team
2297
2435
  */
2298
2436
  const deleteTeam = async (teamId, options) => {
@@ -2372,8 +2510,7 @@ const getCreateBrandingUrl = (params) => {
2372
2510
  /**
2373
2511
  * Publishes a new immutable branding revision for the project. Branding
2374
2512
  revisions cannot be updated or deleted; every edit publishes a new
2375
- revision, and flow responses resolve the latest revision per project
2376
- (see ADR 040).
2513
+ revision, and flow responses resolve the latest revision per project.
2377
2514
 
2378
2515
  The `liquid_template` is validated lexically on save (size, encoding,
2379
2516
  banned patterns such as `<script>` tags, inline event handlers, and the
@@ -2404,9 +2541,9 @@ const getListBrandingUrl = (params) => {
2404
2541
  /**
2405
2542
  * Lists branding revisions for the project, newest first, capped at the
2406
2543
  100 most recent. The first entry is the revision flow responses
2407
- currently resolve. Deliberately unpaginated in v1 — list endpoints
2408
- gain a real query mechanism together (ADR 031); advertising pagination
2409
- parameters the server ignores would be worse than none.
2544
+ currently resolve. Deliberately unpaginated in v1 — list endpoints gain a
2545
+ real query mechanism together; advertising pagination parameters the
2546
+ server ignores would be worse than none.
2410
2547
 
2411
2548
  * @summary List branding revisions
2412
2549
  */
@@ -2429,6 +2566,142 @@ const getBrandingById = async (id, options) => {
2429
2566
  method: "GET"
2430
2567
  });
2431
2568
  };
2569
+ const getCreateIdpUrl = (params) => {
2570
+ const normalizedParams = new URLSearchParams();
2571
+ Object.entries(params || {}).forEach(([key, value]) => {
2572
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2573
+ });
2574
+ const stringifiedParams = normalizedParams.toString();
2575
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/idps?${stringifiedParams}` : `${getProxyPath()}/idps`;
2576
+ };
2577
+ /**
2578
+ * Creates or revises a connection document. If the slug does not already
2579
+ exist, a new connection is created. If a connection with that slug already
2580
+ exists, a revision is created.
2581
+
2582
+ A revision gets a new `revision_id` and does not modify the connection
2583
+ `id`, so identity links that reference the connection keep resolving while
2584
+ releases and auth attempts stay pinned to the revision they captured.
2585
+
2586
+ Identity fields are fixed for the life of the connection and a revision
2587
+ that changes one is rejected: `protocol`, `subject_claim`, and the
2588
+ endpoints that name the authority (`issuer` for OIDC, `token_endpoint`
2589
+ and `userinfo_endpoint` for OAuth 2.0). Their values decide which provider
2590
+ account a stored subject belongs to, so changing one would silently
2591
+ repoint existing identities at a different provider.
2592
+
2593
+ `subject_claim` is optional for OIDC and required for OAuth 2.0. For an
2594
+ OIDC connection, `sub` is used as the default value. If a revision updates
2595
+ `subject_claim` to a different value than the one set during creation, the
2596
+ request is rejected.
2597
+
2598
+ The document is validated against the `idp-connection.json` schema before
2599
+ anything is stored.
2600
+
2601
+ * @summary Create or revise an identity provider connection
2602
+ */
2603
+ const createIdp = async (createIdpBody, params, options) => {
2604
+ return customFetch(getCreateIdpUrl(params), {
2605
+ ...options,
2606
+ method: "POST",
2607
+ headers: {
2608
+ "Content-Type": "application/json",
2609
+ ...options?.headers
2610
+ },
2611
+ body: JSON.stringify(createIdpBody)
2612
+ });
2613
+ };
2614
+ const getQueryIdpsUrl = (params) => {
2615
+ const normalizedParams = new URLSearchParams();
2616
+ Object.entries(params || {}).forEach(([key, value]) => {
2617
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2618
+ });
2619
+ const stringifiedParams = normalizedParams.toString();
2620
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/idps/query?${stringifiedParams}` : `${getProxyPath()}/idps/query`;
2621
+ };
2622
+ /**
2623
+ * Returns the identity provider connections of a project, paginated with a
2624
+ cursor. One row per connection, carrying its newest revision.
2625
+
2626
+ * @summary Query identity provider connections
2627
+ */
2628
+ const queryIdps = async (queryIdpsBody, params, options) => {
2629
+ return customFetch(getQueryIdpsUrl(params), {
2630
+ ...options,
2631
+ method: "POST",
2632
+ headers: {
2633
+ "Content-Type": "application/json",
2634
+ ...options?.headers
2635
+ },
2636
+ body: JSON.stringify(queryIdpsBody)
2637
+ });
2638
+ };
2639
+ const getGetIdpByIdUrl = (id, params) => {
2640
+ const normalizedParams = new URLSearchParams();
2641
+ Object.entries(params || {}).forEach(([key, value]) => {
2642
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2643
+ });
2644
+ const stringifiedParams = normalizedParams.toString();
2645
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/idps/${id}?${stringifiedParams}` : `${getProxyPath()}/idps/${id}`;
2646
+ };
2647
+ /**
2648
+ * Reads a connection by its id, at its newest revision.
2649
+ `GET /idps/{id}/revisions` lists every revision of the connection.
2650
+
2651
+ The lookup is scoped to the project in `project_id`.
2652
+
2653
+ * @summary Get an identity provider connection by id
2654
+ */
2655
+ const getIdpById = async (id, params, options) => {
2656
+ return customFetch(getGetIdpByIdUrl(id, params), {
2657
+ ...options,
2658
+ method: "GET"
2659
+ });
2660
+ };
2661
+ const getListIdpRevisionsUrl = (id, params) => {
2662
+ const normalizedParams = new URLSearchParams();
2663
+ Object.entries(params || {}).forEach(([key, value]) => {
2664
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2665
+ });
2666
+ const stringifiedParams = normalizedParams.toString();
2667
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/idps/${id}/revisions?${stringifiedParams}` : `${getProxyPath()}/idps/${id}/revisions`;
2668
+ };
2669
+ /**
2670
+ * Returns every revision of one connection, newest first, paginated with a
2671
+ cursor. The order is fixed, so there is no `sorting`.
2672
+
2673
+ The lookup is scoped to the project in `project_id`.
2674
+
2675
+ * @summary List the revisions of an identity provider connection
2676
+ */
2677
+ const listIdpRevisions = async (id, params, options) => {
2678
+ return customFetch(getListIdpRevisionsUrl(id, params), {
2679
+ ...options,
2680
+ method: "GET"
2681
+ });
2682
+ };
2683
+ const getGetIdpRevisionByIdUrl = (revisionId, params) => {
2684
+ const normalizedParams = new URLSearchParams();
2685
+ Object.entries(params || {}).forEach(([key, value]) => {
2686
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2687
+ });
2688
+ const stringifiedParams = normalizedParams.toString();
2689
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/idps/revisions/${revisionId}?${stringifiedParams}` : `${getProxyPath()}/idps/revisions/${revisionId}`;
2690
+ };
2691
+ /**
2692
+ * Reads one revision of a connection by its `revision_id`, the value an auth
2693
+ attempt or a release pins.
2694
+
2695
+ The lookup is scoped to the project in `project_id`.
2696
+
2697
+ * @summary Get one revision of an identity provider connection
2698
+ */
2699
+ const getIdpRevisionById = async (revisionId, params, options) => {
2700
+ return customFetch(getGetIdpRevisionByIdUrl(revisionId, params), {
2701
+ ...options,
2702
+ method: "GET"
2703
+ });
2704
+ };
2432
2705
  const getCreateReleaseUrl = (params) => {
2433
2706
  const normalizedParams = new URLSearchParams();
2434
2707
  Object.entries(params || {}).forEach(([key, value]) => {
@@ -2528,10 +2801,23 @@ const getCreateGrantUrl = (params) => {
2528
2801
  /**
2529
2802
  * Bind a user or team to `project.viewer`, `project.editor`, or
2530
2803
  `project.admin` on the project identified by the `project-id` header.
2531
- IDs are `asgn_<opaque>`. Owning-team (`project.team`) grants are not
2532
- created here — claim owns that path. An unrevoked grant with the same
2533
- principal and relation occupies the unique key even after `expires_at`;
2534
- DELETE it before re-creating.
2804
+ Name the principal with `user` (`user_id` or `identifier`) or `team`
2805
+ (`team_id` or `name`). IDs are `asgn_<opaque>`. Owning-team
2806
+ (`project.team`) grants are not created here — claim owns that path. An
2807
+ unrevoked grant with the same principal and relation occupies the unique
2808
+ key even after `expires_at`; DELETE it before re-creating.
2809
+ Create does not accept `expand`. The `user_id` and team locators
2810
+ return 201, whose `user` / `team` carry only `user_id` / `team_id`,
2811
+ and still 404 / 409 when the principal is missing or the tuple
2812
+ already exists. Creating by `user.identifier` answers 202 with no
2813
+ body on every outcome. Granting the session caller's own user is
2814
+ `grant.invalid` on either user locator. Read the grant back for
2815
+ identifier and display.
2816
+
2817
+ Accepts either a project secret (`oauth2`) or a user-bound Console
2818
+ session cookie (`nextgenSession`). Session callers are authorized as
2819
+ the human against the target project (home may differ). CSRF/Origin
2820
+ for cookie mutations is a follow-up (#1140).
2535
2821
 
2536
2822
  * @summary Create grant
2537
2823
  */
@@ -2546,6 +2832,45 @@ const createGrant = async (createGrantBody, params, options) => {
2546
2832
  body: JSON.stringify(createGrantBody)
2547
2833
  });
2548
2834
  };
2835
+ const getQueryGrantsUrl = (params) => {
2836
+ const normalizedParams = new URLSearchParams();
2837
+ Object.entries(params || {}).forEach(([key, value]) => {
2838
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2839
+ });
2840
+ const stringifiedParams = normalizedParams.toString();
2841
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/grants/query?${stringifiedParams}` : `${getProxyPath()}/grants/query`;
2842
+ };
2843
+ /**
2844
+ * Returns the collaboration grants of a project, paginated with a cursor.
2845
+ Only unrevoked grants this API manages are listed (user or team bound to
2846
+ viewer, editor, or admin), including expired grants so a client can
2847
+ DELETE before re-granting. Project-secret setup (`sk_proj`) and
2848
+ owning-team (`relation=team`) rows are not returned. Grants are not in
2849
+ `resource_scope_index`; project scope is required on the query (same as
2850
+ get). Requires `project.read`. `expand: ["principal"]` adds envelope
2851
+ fields on `user` / `team` and additionally requires `user.read` and
2852
+ `team.read` for project secrets (documented on the expand enum; those
2853
+ scopes cannot be ANDed onto this security block because they are
2854
+ body-conditional). A user-bound Console session that already passed
2855
+ the project Check may expand without those scopes.
2856
+
2857
+ Accepts either a project secret (`oauth2`) or a user-bound Console
2858
+ session cookie (`nextgenSession`). CSRF/Origin for cookie mutations
2859
+ is a follow-up (#1140).
2860
+
2861
+ * @summary Query grants
2862
+ */
2863
+ const queryGrants = async (queryGrantsBody, params, options) => {
2864
+ return customFetch(getQueryGrantsUrl(params), {
2865
+ ...options,
2866
+ method: "POST",
2867
+ headers: {
2868
+ "Content-Type": "application/json",
2869
+ ...options?.headers
2870
+ },
2871
+ body: JSON.stringify(queryGrantsBody)
2872
+ });
2873
+ };
2549
2874
  const getGetGrantUrl = (id, params) => {
2550
2875
  const normalizedParams = new URLSearchParams();
2551
2876
  Object.entries(params || {}).forEach(([key, value]) => {
@@ -2563,6 +2888,14 @@ still ignores expired grants. Grants are not registered in
2563
2888
  `resource_scope_index`; project scope is required on the query (same as
2564
2889
  events). Misses, revoked rows, project-secret setup (`sk_proj`),
2565
2890
  owning-team (`relation=team`) rows, and cross-project ids return 404.
2891
+ `expand=principal` adds envelope fields on `user` or `team` and requires
2892
+ `user.read` and `team.read` in addition to `project.read` for project
2893
+ secrets. A user-bound Console session that already passed the project
2894
+ Check may expand without those scopes.
2895
+
2896
+ Accepts either a project secret (`oauth2`) or a user-bound Console
2897
+ session cookie (`nextgenSession`). CSRF/Origin for cookie mutations
2898
+ is a follow-up (#1140).
2566
2899
 
2567
2900
  * @summary Get grant
2568
2901
  */
@@ -2586,6 +2919,10 @@ Already-revoked, missing, project-secret setup, and owning-team grants
2586
2919
  return 404. The row is not un-revoked. Expired grants can still be
2587
2920
  revoked so the unique binding can be reused.
2588
2921
 
2922
+ Accepts either a project secret (`oauth2`) or a user-bound Console
2923
+ session cookie (`nextgenSession`). CSRF/Origin for cookie mutations
2924
+ is a follow-up (#1140).
2925
+
2589
2926
  * @summary Revoke grant
2590
2927
  */
2591
2928
  const deleteGrant = async (id, params, options) => {
@@ -2652,6 +2989,122 @@ const getEvent = async (id, params, options) => {
2652
2989
  method: "GET"
2653
2990
  });
2654
2991
  };
2992
+ const getGetVariablesUrl = (params) => {
2993
+ const normalizedParams = new URLSearchParams();
2994
+ Object.entries(params || {}).forEach(([key, value]) => {
2995
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2996
+ });
2997
+ const stringifiedParams = normalizedParams.toString();
2998
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/variables?${stringifiedParams}` : `${getProxyPath()}/variables`;
2999
+ };
3000
+ /**
3001
+ * Returns the variables entered at the owner this request addresses, keyed by
3002
+ name — one environment of the project with `environment_name`, the project
3003
+ level itself without it.
3004
+
3005
+ Owners are separate, not a ladder: an environment does not inherit the
3006
+ project's variables and the project does not see its environments'. Reading
3007
+ everything a project holds therefore means reading each owner in turn.
3008
+
3009
+ Secret values are not returned. A secret appears as `{"secret": true}`,
3010
+ which says a value is held without disclosing it.
3011
+
3012
+ * @summary Get variables
3013
+ */
3014
+ const getVariables = async (params, options) => {
3015
+ return customFetch(getGetVariablesUrl(params), {
3016
+ ...options,
3017
+ method: "GET"
3018
+ });
3019
+ };
3020
+ const getUpdateVariablesUrl = (params) => {
3021
+ const normalizedParams = new URLSearchParams();
3022
+ Object.entries(params || {}).forEach(([key, value]) => {
3023
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
3024
+ });
3025
+ const stringifiedParams = normalizedParams.toString();
3026
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/variables?${stringifiedParams}` : `${getProxyPath()}/variables`;
3027
+ };
3028
+ /**
3029
+ * Enters, replaces and removes variables at the owner this request
3030
+ addresses.
3031
+
3032
+ Every name in the body is applied at exactly that owner — the project, or
3033
+ the environment named by `environment_name` — and reaches no other. Names
3034
+ not in the body are untouched.
3035
+
3036
+ A bare scalar enters a non-secret value. `{"value": …, "secret": true}`
3037
+ stores the value encrypted under the project's active `secret` key, after
3038
+ which it can be referenced but not read back. `null` removes the name from
3039
+ this owner (RFC 7386), which is how several variables are removed in one
3040
+ request; removing a name this owner does not hold is a no-op rather than an
3041
+ error.
3042
+
3043
+ The body is applied whole or not at all, so a rejected request leaves the
3044
+ owner exactly as it was. Writing the same name and owner twice replaces the
3045
+ value rather than duplicating it, which makes a retry safe.
3046
+
3047
+ * @summary Update variables
3048
+ */
3049
+ const updateVariables = async (updateVariablesBody, params, options) => {
3050
+ return customFetch(getUpdateVariablesUrl(params), {
3051
+ ...options,
3052
+ method: "PATCH",
3053
+ headers: {
3054
+ "Content-Type": "application/json",
3055
+ ...options?.headers
3056
+ },
3057
+ body: JSON.stringify(updateVariablesBody)
3058
+ });
3059
+ };
3060
+ const getGetVariableUrl = (variableName, params) => {
3061
+ const normalizedParams = new URLSearchParams();
3062
+ Object.entries(params || {}).forEach(([key, value]) => {
3063
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
3064
+ });
3065
+ const stringifiedParams = normalizedParams.toString();
3066
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/variables/${variableName}?${stringifiedParams}` : `${getProxyPath()}/variables/${variableName}`;
3067
+ };
3068
+ /**
3069
+ * Reads one variable by name from the owner this request addresses — one
3070
+ environment of the project with `environment_name`, the project level
3071
+ itself without it.
3072
+
3073
+ A name that owner has not entered answers `var.not_found`, even when
3074
+ another owner of the same project holds it: nothing is inherited. A secret
3075
+ is found but not disclosed: the response is `{"secret": true}`.
3076
+
3077
+ * @summary Get a variable
3078
+ */
3079
+ const getVariable = async (variableName, params, options) => {
3080
+ return customFetch(getGetVariableUrl(variableName, params), {
3081
+ ...options,
3082
+ method: "GET"
3083
+ });
3084
+ };
3085
+ const getDeleteVariableUrl = (variableName, params) => {
3086
+ const normalizedParams = new URLSearchParams();
3087
+ Object.entries(params || {}).forEach(([key, value]) => {
3088
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
3089
+ });
3090
+ const stringifiedParams = normalizedParams.toString();
3091
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/variables/${variableName}?${stringifiedParams}` : `${getProxyPath()}/variables/${variableName}`;
3092
+ };
3093
+ /**
3094
+ * Removes the variable this owner entered under this name.
3095
+
3096
+ A variable is deletable only by the owner that entered it. Deleting a name
3097
+ another owner of the same project holds answers `var.not_found` and leaves
3098
+ that owner's value standing.
3099
+
3100
+ * @summary Delete a variable
3101
+ */
3102
+ const deleteVariable = async (variableName, params, options) => {
3103
+ return customFetch(getDeleteVariableUrl(variableName, params), {
3104
+ ...options,
3105
+ method: "DELETE"
3106
+ });
3107
+ };
2655
3108
  //#endregion
2656
3109
  //#region ../api/dist/runtime/api-factory.mjs
2657
3110
  /**
@@ -2967,9 +3420,10 @@ function createSanitiser() {
2967
3420
  * the `Branding` JSON into a CSSStyleSheet of `:host { --zl-* }` declarations
2968
3421
  * and applies it via `shadowRoot.adoptedStyleSheets`.
2969
3422
  *
2970
- * Dark-mode overrides are emitted as `:host([data-theme="dark"]) { ... }`
2971
- * (matching `docs/design/branding/tokens.md`). Resolution between
2972
- * `light | dark | auto` happens in `<zitadel-login>`.
3423
+ * Light and dark are independent surfaces: each side's palette is emitted under
3424
+ * its own `data-theme` selector only, so a key the revision left unset on one
3425
+ * side takes the maintained default for that side rather than the other side's
3426
+ * colour. Resolution between `light | dark | auto` happens in `<zitadel-login>`.
2973
3427
  *
2974
3428
  * Base layer: the design-tokens package ships the full `--zl-*` set as both a
2975
3429
  * `.css` file (for host pages) and a `tokensCss` string (for shadow roots and
@@ -2977,13 +3431,32 @@ function createSanitiser() {
2977
3431
  * paint correctly even when the host page didn't `@import` tokens.css — the
2978
3432
  * orchestrator is meant to drop into any page.
2979
3433
  */
2980
- const RADIUS_MAP = {
2981
- none: "0",
2982
- sm: "0.25rem",
2983
- md: "0.5rem",
2984
- lg: "0.75rem",
2985
- full: "9999px"
2986
- };
3434
+ const RADIUS_STEPS = [
3435
+ "xs",
3436
+ "sm",
3437
+ "md",
3438
+ "lg",
3439
+ "xl"
3440
+ ];
3441
+ const RADIUS_RATIOS = (() => {
3442
+ const base = remToNumber(tokens.radius.md);
3443
+ const ratios = {};
3444
+ for (const step of RADIUS_STEPS) ratios[step] = remToNumber(tokens.radius[step]) / base;
3445
+ return ratios;
3446
+ })();
3447
+ const RADIUS_PRESETS = {
3448
+ none: 0,
3449
+ sm: .25,
3450
+ md: .5,
3451
+ lg: .75
3452
+ };
3453
+ const TEXT_STEPS = [
3454
+ "xs",
3455
+ "sm",
3456
+ "base",
3457
+ "lg",
3458
+ "xl"
3459
+ ];
2987
3460
  const DENSITY_MAP = {
2988
3461
  compact: {
2989
3462
  "--zl-spacing-4": "0.75rem",
@@ -3035,34 +3508,35 @@ const PALETTE_MAP = {
3035
3508
  * the moment before a theme resolves.
3036
3509
  */
3037
3510
  const BRANDING_SELECTOR = ":host, :host([data-theme=\"light\"]), :host([data-theme=\"dark\"])";
3038
- /** Dark-only overrides. Emitted after {@link BRANDING_SELECTOR}, so it wins on order. */
3511
+ /** Per-side palettes. Emitted after {@link BRANDING_SELECTOR}, so they win on order. */
3512
+ const LIGHT_SELECTOR = ":host([data-theme=\"light\"])";
3039
3513
  const DARK_SELECTOR = ":host([data-theme=\"dark\"])";
3040
3514
  /**
3041
3515
  * Build a CSS string of `:host { --zl-* }` declarations from a Branding
3042
- * payload. Light values land on `:host`; dark overrides land on
3043
- * `:host([data-theme="dark"])`.
3516
+ * payload. Shape and typography are shared across the sides and land on the
3517
+ * shared selector; each side's palette lands only under its own `data-theme`
3518
+ * selector, and nowhere else.
3519
+ *
3520
+ * No copy of the resolved side goes on the shared selector. That copy would
3521
+ * reach `[data-theme="light"]` as well, so a key set on the dark side and not
3522
+ * the light one would paint on both, with nothing in the light block to
3523
+ * override it. Callers stamp `data-theme` on the host — `applySurfaceTheme`
3524
+ * does it in the same update that adopts this sheet — so the per-side block is
3525
+ * already matching by the time anything paints.
3044
3526
  */
3045
- function buildBrandingStylesheet(branding, options = {}) {
3046
- const lightDecls = collectDeclarations(branding);
3047
- const darkPalette = branding?.theme?.dark?.palette;
3048
- const darkDecls = darkPalette ? mapPalette(darkPalette) : {};
3527
+ function buildBrandingStylesheet(branding) {
3528
+ const shared = collectDeclarations(branding);
3529
+ const light = mapPalette(branding?.theme?.light?.palette);
3530
+ const dark = mapPalette(branding?.theme?.dark?.palette);
3049
3531
  const blocks = [];
3050
- if (Object.keys(lightDecls).length > 0) blocks.push(formatBlock(BRANDING_SELECTOR, lightDecls));
3051
- if (Object.keys(darkDecls).length > 0) blocks.push(formatBlock(DARK_SELECTOR, darkDecls));
3052
- if (options.resolvedTheme === "dark" && darkPalette) {
3053
- const merged = {
3054
- ...lightDecls,
3055
- ...darkDecls
3056
- };
3057
- blocks.length = 0;
3058
- blocks.push(formatBlock(BRANDING_SELECTOR, merged));
3059
- }
3532
+ if (Object.keys(shared).length > 0) blocks.push(formatBlock(BRANDING_SELECTOR, shared));
3533
+ if (Object.keys(light).length > 0) blocks.push(formatBlock(LIGHT_SELECTOR, light));
3534
+ if (Object.keys(dark).length > 0) blocks.push(formatBlock(DARK_SELECTOR, dark));
3060
3535
  return blocks.join("\n");
3061
3536
  }
3062
3537
  function collectDeclarations(branding) {
3063
3538
  if (!branding) return {};
3064
3539
  const decls = {};
3065
- Object.assign(decls, mapPalette(branding.palette));
3066
3540
  Object.assign(decls, mapTypography(branding.typography));
3067
3541
  Object.assign(decls, mapShape(branding.shape));
3068
3542
  return decls;
@@ -3083,24 +3557,52 @@ function mapTypography(typography) {
3083
3557
  out["--zl-font-family-sans"] = typography.font_family;
3084
3558
  out["--zl-font-family-heading"] = typography.font_family;
3085
3559
  }
3086
- if (typography.font_family_heading) out["--zl-font-family-heading"] = typography.font_family_heading;
3087
- if (typography.font_family_mono) out["--zl-font-family-mono"] = typography.font_family_mono;
3088
3560
  const scale = clamp(typography.scale ?? 1, .75, 1.25);
3089
- if (scale !== 1) out["--zl-font-scale"] = `${scale}`;
3561
+ if (scale !== 1) for (const step of TEXT_STEPS) {
3562
+ out[`--zl-text-${step}-size`] = scaleRem(tokens.text[step].size, scale);
3563
+ out[`--zl-text-${step}-leading`] = scaleRem(tokens.text[step].leading, scale);
3564
+ }
3090
3565
  return out;
3091
3566
  }
3092
3567
  function mapShape(shape) {
3093
3568
  if (!shape) return {};
3094
3569
  const out = {};
3095
- if (shape.radius && RADIUS_MAP[shape.radius]) {
3096
- const radius = RADIUS_MAP[shape.radius];
3097
- out["--zl-radius-md"] = radius;
3098
- out["--zl-radius-lg"] = radius === "0" ? "0" : `calc(${radius} * 1.25)`;
3099
- out["--zl-radius-xl"] = radius === "0" ? "0" : `calc(${radius} * 1.75)`;
3100
- }
3570
+ Object.assign(out, mapRadius(shape.radius));
3101
3571
  if (shape.density) Object.assign(out, DENSITY_MAP[shape.density]);
3572
+ const logoScale = shape.logo_scale;
3573
+ if (typeof logoScale === "number" && Number.isFinite(logoScale)) out["--zl-logo-scale"] = `${clamp(logoScale, .5, 2)}`;
3574
+ return out;
3575
+ }
3576
+ /**
3577
+ * A brand picks one corner value and the whole ramp follows it in proportion,
3578
+ * which is what keeps the card rounder than the controls inside it. Both forms
3579
+ * the revision accepts land here: a preset name, or an integer number of pixels
3580
+ * for the brand that has a specific value.
3581
+ */
3582
+ function mapRadius(radius) {
3583
+ if (radius == null) return {};
3584
+ const out = {};
3585
+ if (radius === "full") {
3586
+ for (const step of RADIUS_STEPS) out[`--zl-radius-${step}`] = tokens.radius.full;
3587
+ return out;
3588
+ }
3589
+ const [base, unit] = typeof radius === "number" ? [radius, "px"] : radius in RADIUS_PRESETS ? [RADIUS_PRESETS[radius], "rem"] : [NaN, ""];
3590
+ if (Number.isNaN(base)) return {};
3591
+ for (const step of RADIUS_STEPS) {
3592
+ const value = base * RADIUS_RATIOS[step];
3593
+ out[`--zl-radius-${step}`] = value === 0 ? "0" : `${round(value)}${unit}`;
3594
+ }
3102
3595
  return out;
3103
3596
  }
3597
+ function scaleRem(value, scale) {
3598
+ return `${round(remToNumber(value) * scale)}rem`;
3599
+ }
3600
+ function remToNumber(value) {
3601
+ return Number.parseFloat(value);
3602
+ }
3603
+ function round(value) {
3604
+ return Math.round(value * 1e4) / 1e4;
3605
+ }
3104
3606
  function formatBlock(selector, decls) {
3105
3607
  return `${selector} {\n${Object.entries(decls).map(([prop, value]) => ` ${prop}: ${value};`).join("\n")}\n}`;
3106
3608
  }
@@ -3140,8 +3642,8 @@ function applyBaseTokens(shadowRoot) {
3140
3642
  * Callers should run `applyBaseTokens(shadowRoot)` first (once per shadow
3141
3643
  * root) so the base values exist before branding patches them.
3142
3644
  */
3143
- function applyBrandingTokens(shadowRoot, branding, resolvedTheme) {
3144
- const css = buildBrandingStylesheet(branding, { resolvedTheme });
3645
+ function applyBrandingTokens(shadowRoot, branding) {
3646
+ const css = buildBrandingStylesheet(branding);
3145
3647
  if (typeof CSSStyleSheet === "undefined") return;
3146
3648
  const sheet = new CSSStyleSheet();
3147
3649
  sheet.replaceSync(css);
@@ -3156,9 +3658,11 @@ function applyBrandingTokens(shadowRoot, branding, resolvedTheme) {
3156
3658
  * and a variant-derived fallback on top of the same branding input.
3157
3659
  *
3158
3660
  * Defaults to dark: the design system's primary surface, and the mode a
3159
- * hosted login page renders when a tenant states no preference.
3661
+ * hosted login page renders when a revision states no preference.
3160
3662
  */
3161
3663
  function resolveTheme(branding) {
3664
+ const [only, second] = publishedSides(branding);
3665
+ if (only && !second) return only;
3162
3666
  const mode = branding?.theme?.mode ?? "dark";
3163
3667
  if (mode === "light") return "light";
3164
3668
  if (mode === "dark") return "dark";
@@ -3176,7 +3680,7 @@ function resolveTheme(branding) {
3176
3680
  * (Arimo, named first in `--zl-font-family-sans`). The component ships this
3177
3681
  * so the auth screens render in the brand font out of the box, even when the
3178
3682
  * server returns no branding. This is the "default users can override" layer.
3179
- * - The **tenant override** (`applyFontUrl`) — `branding.font_url`, resolved
3683
+ * - The **tenant override** (`applyFontUrl`) — `branding.typography.font_url`, resolved
3180
3684
  * server-side from the app → team → project branding hierarchy.
3181
3685
  *
3182
3686
  * Both must live at document level, not inside the shadow root: browsers ignore
@@ -3205,7 +3709,7 @@ const DEFAULT_LINK_ID = "zl-default-font-link";
3205
3709
  * Design-system default brand font. Arimo is the Figma body face and leads the
3206
3710
  * `--zl-font-family-sans` stack; loading it here makes the unbranded auth UI
3207
3711
  * paint in the brand font instead of the system fallback. Tenants override it
3208
- * with `branding.font_url`. Exported so a privacy- or offline-sensitive
3712
+ * with `branding.typography.font_url`. Exported so a privacy- or offline-sensitive
3209
3713
  * deployment can self-host the same face and swap this single URL.
3210
3714
  */
3211
3715
  const DEFAULT_BRAND_FONT_HREF = "https://fonts.googleapis.com/css2?family=Arimo:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap";
@@ -3225,7 +3729,7 @@ const DEFAULT_BRAND_FONT_HREF = "https://fonts.googleapis.com/css2?family=Arimo:
3225
3729
  /** Per-document, per-link-id map of each owner's desired href. */
3226
3730
  const registries = /* @__PURE__ */ new WeakMap();
3227
3731
  /**
3228
- * Inject the tenant's `branding.font_url` stylesheet. Pass `null`/`undefined`
3732
+ * Inject the tenant's `branding.typography.font_url` stylesheet. Pass `null`/`undefined`
3229
3733
  * to withdraw this surface's registration (e.g. when a flow resolves with no
3230
3734
  * tenant font); the link disappears once no surface registers a URL.
3231
3735
  */
@@ -3235,7 +3739,7 @@ function applyFontUrl(shadowRoot, fontUrl) {
3235
3739
  /**
3236
3740
  * Inject the design-system default font stylesheet. Defaults to
3237
3741
  * {@link DEFAULT_BRAND_FONT_HREF}; pass `null` to withdraw this surface's
3238
- * registration (the orchestrator does this when a tenant `font_url` takes
3742
+ * registration (the orchestrator does this when a tenant font stylesheet takes
3239
3743
  * over, to avoid a redundant request).
3240
3744
  */
3241
3745
  function applyDefaultFont(shadowRoot, href = DEFAULT_BRAND_FONT_HREF) {
@@ -3302,7 +3806,10 @@ var ThemeController = class {
3302
3806
  * element nor the branding payload names a mode. Precedence, strongest
3303
3807
  * first: element `theme` property → `branding.theme.mode` → fallback. The
3304
3808
  * element wins because the page embedding the widget knows its own surface
3305
- * better than the tenant's stored branding does.
3809
+ * better than the tenant's stored branding does — but it selects among the
3810
+ * sides the revision published, and cannot reach one it did not. A revision
3811
+ * with a single published side therefore ignores this property: an embedder
3812
+ * pinning `theme="light"` against a dark-only revision gets dark.
3306
3813
  */
3307
3814
  setModePreference(explicit, fallback) {
3308
3815
  if (this.explicitMode === explicit && this.fallbackMode === fallback) return;
@@ -3317,6 +3824,12 @@ var ThemeController = class {
3317
3824
  this.detach();
3318
3825
  }
3319
3826
  refresh() {
3827
+ const [only, second] = publishedSides(this.branding);
3828
+ if (only && !second) {
3829
+ this.detach();
3830
+ this.update(only);
3831
+ return;
3832
+ }
3320
3833
  const mode = this.explicitMode ?? this.branding?.theme?.mode ?? this.fallbackMode;
3321
3834
  if (mode === "auto") {
3322
3835
  this.attach();
@@ -3395,9 +3908,10 @@ var ZitadelSurface = class extends LitElement {
3395
3908
  const root = this.shadowRoot;
3396
3909
  if (root) {
3397
3910
  applyBaseTokens(root);
3398
- applyBrandingTokens(root, branding, this.themeController.theme);
3399
- const tenantFontUrl = branding?.font_url ?? null;
3400
- applyDefaultFont(root, this.variant === "page" && !tenantFontUrl ? void 0 : null);
3911
+ applyBrandingTokens(root, branding);
3912
+ const page = this.variant === "page";
3913
+ const tenantFontUrl = page ? branding?.typography?.font_url ?? null : null;
3914
+ applyDefaultFont(root, page && !tenantFontUrl ? void 0 : null);
3401
3915
  applyFontUrl(root, tenantFontUrl);
3402
3916
  }
3403
3917
  this.dataset.theme = this.themeController.theme;
@@ -3433,7 +3947,7 @@ const TEMPLATE_NAMES = {
3433
3947
  };
3434
3948
  //#endregion
3435
3949
  //#region src/orchestrator/templates/layout-chrome.css?inline
3436
- var layout_chrome_default = ":host {\n color: var(--zl-foreground);\n font-family: var(--zl-font-family-sans);\n background: none;\n display: block;\n}\n\n:host([variant=\"page\"]) {\n background: var(--zl-background);\n min-height: 100%;\n}\n\nimg[data-zl-asset-broken] {\n display: none;\n}\n\n.zl-mount {\n min-height: var(--zl-page-min-height, auto);\n background: inherit;\n color: inherit;\n flex-direction: column;\n display: flex;\n container-type: inline-size;\n}\n\n:host([variant=\"page\"]) .zl-mount {\n min-height: var(--zl-page-min-height, 100vh);\n}\n\n.zl-mount > zl-page-shell, .zl-mount > [data-zl-template-root] {\n flex: auto;\n}\n\n.zl-attribution {\n background: none;\n justify-content: center;\n align-items: center;\n display: flex;\n}\n\n.zl-attribution[hidden] {\n display: none;\n}\n\n.zl-trustmark {\n gap: var(--zl-spacing-2-5);\n flex-wrap: wrap;\n}\n\n.zl-trustmark__mark {\n align-items: center;\n gap: var(--zl-spacing-2);\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n color: var(--zl-foreground);\n text-decoration: none;\n display: inline-flex;\n}\n\n.zl-trustmark__mark:hover {\n text-underline-offset: 4px;\n text-decoration: underline;\n}\n\n.zl-trustmark__mark:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n border-radius: var(--zl-radius-sm);\n}\n\n.zl-trustmark__logotype {\n flex-shrink: 0;\n width: 65px;\n height: 16px;\n line-height: 0;\n display: block;\n}\n\n.zl-trustmark__logotype-svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n.zl-card-logo {\n width: auto;\n max-width: min(12rem, 60%);\n max-height: var(--zl-card-logo-max-height, 2.5rem);\n object-fit: contain;\n}\n\n.zl-card-title {\n font-family: var(--zl-font-family-heading);\n font-size: var(--zl-text-xl-size);\n font-weight: var(--zl-font-weight-medium);\n color: var(--zl-card-foreground);\n text-align: left;\n margin: 0;\n line-height: 1;\n}\n\n.zl-card-subtitle {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-muted-foreground);\n margin: 0;\n}\n\n.zl-field-group {\n gap: var(--zl-spacing-7);\n flex-direction: column;\n display: flex;\n}\n\n.zl-field-group:not(:has(*)) {\n display: none;\n}\n\n.zl-card-actions {\n gap: var(--zl-spacing-3);\n flex-direction: column;\n display: flex;\n}\n\n[data-suppress-header] .zl-card-title, [data-suppress-header] .zl-card-subtitle {\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n position: absolute;\n overflow: hidden;\n}\n\n.zl-card-nav {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-muted-foreground);\n text-align: center;\n margin: 0;\n}\n\n.zl-card-nav__link {\n font: inherit;\n line-height: inherit;\n color: var(--zl-link);\n text-underline-offset: 4px;\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: underline;\n display: inline;\n}\n\n.zl-card-nav__link:hover {\n color: var(--zl-foreground);\n}\n\n.zl-card-nav__link:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n}\n\n.zl-card-forgot {\n text-align: right;\n margin: 0;\n}\n\n.zl-card-forgot__link {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-link);\n text-underline-offset: 4px;\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: none;\n display: inline;\n}\n\n.zl-card-forgot__link:hover {\n text-decoration: underline;\n}\n\n.zl-card-forgot__link:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n}\n\n.zl-split {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n align-items: var(--zl-split-align, center);\n gap: var(--zl-spacing-8);\n width: 100%;\n min-height: 100%;\n display: grid;\n}\n\n.zl-split__brand {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-6);\n padding: var(--zl-spacing-8) var(--zl-spacing-4);\n flex-direction: column;\n display: flex;\n}\n\n.zl-split__logo {\n max-width: min(16rem, 60%);\n max-height: var(--zl-split-logo-max-height, 16rem);\n}\n\n.zl-split__logo:has( + .zl-split__hero) {\n max-height: var(--zl-split-logo-max-height, 8rem);\n}\n\n.zl-split__logo:has( + .zl-split__hero) + .zl-split__hero {\n max-height: min(var(--zl-split-hero-max-height, 22.5rem),\n max(6rem,\n calc(100vh - 104px - var(--zl-spacing-8) - var(--zl-spacing-8) -\n 2.5rem - var(--zl-spacing-6) - var(--zl-spacing-6) -\n var(--zl-split-logo-max-height, 8rem))));\n}\n\n.zl-split__hero {\n width: 100%;\n max-height: var(--zl-split-hero-max-height, min(22.5rem, 60vh));\n border-radius: var(--zl-radius-lg);\n object-fit: cover;\n}\n\n.zl-split__placeholder {\n aspect-ratio: 4 / 5;\n border-radius: var(--zl-radius-lg);\n background: radial-gradient(120% 120% at 18% 12%,\n var(--zl-gradient-lavender-start) 0%,\n transparent 60%),\n radial-gradient(130% 130% at 85% 92%,\n var(--zl-gradient-rose-start) 0%,\n transparent 60%),\n var(--zl-card);\n width: min(18rem, 85%);\n}\n\n.zl-split__form {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-6);\n flex-direction: column;\n padding: 16px;\n display: flex;\n}\n\n.zl-split__compact {\n max-width: min(12rem, 60%);\n display: none;\n}\n\nimg.zl-split__compact {\n max-height: 2.5rem;\n}\n\nimg.zl-split__compact--hero {\n border-radius: var(--zl-radius-lg);\n object-fit: cover;\n width: 100%;\n max-width: 100%;\n max-height: 6rem;\n}\n\n.zl-split--right .zl-split__brand {\n order: 2;\n}\n\n.zl-split--right .zl-split__form {\n order: 1;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n gap: var(--zl-spacing-8);\n width: 100%;\n display: grid;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution > * {\n grid-column: 2;\n justify-self: center;\n}\n\nzl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution > * {\n grid-column: 1;\n}\n\n@container (width <= 48rem) {\n .zl-split {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .zl-split__brand {\n display: var(--zl-split-brand-mobile, none);\n }\n\n .zl-split__compact {\n display: block;\n }\n\n zl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution {\n grid-template-columns: minmax(0, 1fr);\n }\n\n zl-page-shell:has(.zl-split) .zl-attribution > *, zl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution > * {\n grid-column: 1;\n }\n}\n\n.zl-hero {\n text-align: left;\n width: 100%;\n max-width: 34rem;\n color: var(--zl-foreground);\n font-family: var(--zl-font-family-sans);\n flex-direction: column;\n display: flex;\n}\n\n.zl-hero__nav {\n margin-bottom: var(--zl-spacing-8);\n justify-content: space-between;\n align-items: center;\n display: flex;\n}\n\n.zl-hero__brand {\n align-items: center;\n gap: var(--zl-spacing-2);\n letter-spacing: -.01em;\n font-size: 1.125rem;\n font-weight: 700;\n display: inline-flex;\n}\n\n.zl-hero__brand-logo {\n max-height: 1.75rem;\n}\n\n.zl-hero__brand-mark {\n border-radius: var(--zl-radius-md);\n background: linear-gradient(135deg,\n var(--zl-gradient-lavender-start),\n var(--zl-gradient-rose-start));\n width: 1.75rem;\n height: 1.75rem;\n display: inline-flex;\n}\n\n.zl-hero__compact-brand {\n overflow-wrap: anywhere;\n letter-spacing: -.01em;\n max-width: 100%;\n color: var(--zl-foreground);\n margin: 0;\n font-size: 1.125rem;\n font-weight: 700;\n}\n\n.zl-hero__nav-links {\n gap: var(--zl-spacing-6);\n color: var(--zl-muted-foreground);\n font-size: .9375rem;\n display: flex;\n}\n\n.zl-hero__nav-links a {\n color: inherit;\n text-decoration: none;\n}\n\n.zl-hero__nav-links a:hover {\n text-decoration: underline;\n}\n\n.zl-hero__eyebrow {\n margin: 0 0 var(--zl-spacing-4);\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--zl-muted-foreground);\n font-size: .8125rem;\n font-weight: 600;\n}\n\n.zl-hero__headline {\n margin: 0 0 var(--zl-spacing-4);\n font-family: var(--zl-font-family-heading);\n letter-spacing: -.02em;\n font-size: 2.75rem;\n font-weight: 700;\n line-height: 1.1;\n}\n\n.zl-hero__sub {\n margin: 0 0 var(--zl-spacing-8);\n max-width: 30rem;\n color: var(--zl-muted-foreground);\n font-size: 1.0625rem;\n line-height: 1.6;\n}\n\n.zl-hero__bullets {\n gap: var(--zl-spacing-2);\n margin: 0 0 var(--zl-spacing-8);\n flex-direction: column;\n padding: 0;\n list-style: none;\n display: flex;\n}\n\n.zl-hero__bullets li {\n align-items: center;\n gap: var(--zl-spacing-2);\n font-size: 1rem;\n display: flex;\n}\n\n.zl-hero__bullets li:before {\n content: \"✓\";\n color: var(--zl-success);\n font-weight: 700;\n}\n\n.zl-hero__footnote {\n padding-top: var(--zl-spacing-6);\n border-top: 1px solid var(--zl-border);\n color: var(--zl-muted-foreground);\n margin: 0;\n font-size: .8125rem;\n}\n\n.zl-hero__footnote strong {\n color: var(--zl-muted-foreground);\n font-weight: 600;\n}\n\n.zl-minimal {\n gap: var(--zl-spacing-6);\n flex-direction: column;\n width: min(24rem, 100%);\n margin-inline: auto;\n display: flex;\n}\n\n:host([variant=\"page\"]) .zl-minimal {\n padding: 52px 16px;\n}\n";
3950
+ var layout_chrome_default = ":host {\n color: var(--zl-foreground);\n font-family: var(--zl-font-family-sans);\n background: none;\n display: block;\n}\n\n:host([variant=\"page\"]) {\n background: var(--zl-background);\n min-height: 100%;\n}\n\nimg[data-zl-asset-broken] {\n display: none;\n}\n\n.zl-mount {\n min-height: var(--zl-page-min-height, auto);\n background: inherit;\n color: inherit;\n flex-direction: column;\n display: flex;\n container-type: inline-size;\n}\n\n:host([variant=\"page\"]) .zl-mount {\n min-height: var(--zl-page-min-height, 100vh);\n}\n\n.zl-mount > zl-page-shell, .zl-mount > [data-zl-template-root] {\n flex: auto;\n}\n\n.zl-attribution {\n background: none;\n justify-content: center;\n align-items: center;\n display: flex;\n}\n\n.zl-attribution[hidden] {\n display: none;\n}\n\n.zl-trustmark {\n gap: var(--zl-spacing-2-5);\n flex-wrap: wrap;\n}\n\n.zl-trustmark__mark {\n align-items: center;\n gap: var(--zl-spacing-2);\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n color: var(--zl-foreground);\n text-decoration: none;\n display: inline-flex;\n}\n\n.zl-trustmark__mark:hover {\n text-underline-offset: 4px;\n text-decoration: underline;\n}\n\n.zl-trustmark__mark:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n border-radius: var(--zl-radius-sm);\n}\n\n.zl-trustmark__logotype {\n flex-shrink: 0;\n width: 65px;\n height: 16px;\n line-height: 0;\n display: block;\n}\n\n.zl-trustmark__logotype-svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n.zl-card-logo {\n width: auto;\n max-width: min(12rem, 60%);\n max-height: calc(var(--zl-card-logo-max-height, 2.5rem) * var(--zl-logo-scale, 1));\n object-fit: contain;\n}\n\n.zl-card-title {\n font-family: var(--zl-font-family-heading);\n font-size: var(--zl-text-xl-size);\n font-weight: var(--zl-font-weight-medium);\n color: var(--zl-card-foreground);\n text-align: left;\n margin: 0;\n line-height: 1;\n}\n\n.zl-card-subtitle {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-muted-foreground);\n margin: 0;\n}\n\n.zl-field-group {\n gap: var(--zl-spacing-7);\n flex-direction: column;\n display: flex;\n}\n\n.zl-field-group:not(:has(*)) {\n display: none;\n}\n\n.zl-card-actions {\n gap: var(--zl-spacing-3);\n flex-direction: column;\n display: flex;\n}\n\n[data-suppress-header] .zl-card-title, [data-suppress-header] .zl-card-subtitle {\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n position: absolute;\n overflow: hidden;\n}\n\n.zl-card-nav {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-muted-foreground);\n text-align: center;\n margin: 0;\n}\n\n.zl-card-nav__link {\n font: inherit;\n line-height: inherit;\n color: var(--zl-link);\n text-underline-offset: 4px;\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: underline;\n display: inline;\n}\n\n.zl-card-nav__link:hover {\n color: var(--zl-foreground);\n}\n\n.zl-card-nav__link:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n}\n\n.zl-card-forgot {\n text-align: right;\n margin: 0;\n}\n\n.zl-card-forgot__link {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-link);\n text-underline-offset: 4px;\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: none;\n display: inline;\n}\n\n.zl-card-forgot__link:hover {\n text-decoration: underline;\n}\n\n.zl-card-forgot__link:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n}\n\n.zl-split {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n align-items: var(--zl-split-align, center);\n gap: var(--zl-spacing-8);\n width: 100%;\n min-height: 100%;\n display: grid;\n}\n\n.zl-split__brand {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-6);\n padding: var(--zl-spacing-8) var(--zl-spacing-4);\n flex-direction: column;\n display: flex;\n}\n\n.zl-split__logo {\n max-width: min(16rem, 60%);\n max-height: calc(var(--zl-split-logo-max-height, 16rem) * var(--zl-logo-scale, 1));\n}\n\n.zl-split__logo:has( + .zl-split__hero) {\n max-height: calc(var(--zl-split-logo-max-height, 8rem) * var(--zl-logo-scale, 1));\n}\n\n.zl-split__logo:has( + .zl-split__hero) + .zl-split__hero {\n max-height: min(var(--zl-split-hero-max-height, 22.5rem),\n max(6rem,\n calc(100vh - 104px - var(--zl-spacing-8) - var(--zl-spacing-8) -\n 2.5rem - var(--zl-spacing-6) - var(--zl-spacing-6) -\n calc(var(--zl-split-logo-max-height, 8rem) * var(--zl-logo-scale, 1)))));\n}\n\n.zl-split__hero {\n width: 100%;\n max-height: var(--zl-split-hero-max-height, min(22.5rem, 60vh));\n border-radius: var(--zl-radius-lg);\n object-fit: cover;\n}\n\n.zl-split__placeholder {\n aspect-ratio: 4 / 5;\n border-radius: var(--zl-radius-lg);\n background: radial-gradient(120% 120% at 18% 12%,\n var(--zl-gradient-lavender-start) 0%,\n transparent 60%),\n radial-gradient(130% 130% at 85% 92%,\n var(--zl-gradient-rose-start) 0%,\n transparent 60%),\n var(--zl-card);\n width: min(18rem, 85%);\n}\n\n.zl-split__form {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-6);\n flex-direction: column;\n padding: 16px;\n display: flex;\n}\n\n.zl-split__compact {\n max-width: min(12rem, 60%);\n display: none;\n}\n\nimg.zl-split__compact {\n max-height: calc(2.5rem * var(--zl-logo-scale, 1));\n}\n\nimg.zl-split__compact--hero {\n border-radius: var(--zl-radius-lg);\n object-fit: cover;\n width: 100%;\n max-width: 100%;\n max-height: 6rem;\n}\n\n.zl-split--right .zl-split__brand {\n order: 2;\n}\n\n.zl-split--right .zl-split__form {\n order: 1;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n gap: var(--zl-spacing-8);\n width: 100%;\n display: grid;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution > * {\n grid-column: 2;\n justify-self: center;\n}\n\nzl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution > * {\n grid-column: 1;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution ::slotted(*) {\n grid-column: 2;\n justify-self: center;\n}\n\nzl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution ::slotted(*) {\n grid-column: 1;\n}\n\n@container (width <= 48rem) {\n .zl-split {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .zl-split__brand {\n display: var(--zl-split-brand-mobile, none);\n }\n\n .zl-split__compact {\n display: block;\n }\n\n zl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution {\n grid-template-columns: minmax(0, 1fr);\n }\n\n zl-page-shell:has(.zl-split) .zl-attribution > *, zl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution > * {\n grid-column: 1;\n }\n}\n\n.zl-hero {\n text-align: left;\n width: 100%;\n max-width: 34rem;\n color: var(--zl-foreground);\n font-family: var(--zl-font-family-sans);\n flex-direction: column;\n display: flex;\n}\n\n.zl-hero__nav {\n margin-bottom: var(--zl-spacing-8);\n justify-content: space-between;\n align-items: center;\n display: flex;\n}\n\n.zl-hero__brand {\n align-items: center;\n gap: var(--zl-spacing-2);\n letter-spacing: -.01em;\n font-size: 1.125rem;\n font-weight: 700;\n display: inline-flex;\n}\n\n.zl-hero__brand-logo {\n max-height: calc(1.75rem * var(--zl-logo-scale, 1));\n}\n\n.zl-hero__brand-mark {\n border-radius: var(--zl-radius-md);\n background: linear-gradient(135deg,\n var(--zl-gradient-lavender-start),\n var(--zl-gradient-rose-start));\n width: 1.75rem;\n height: 1.75rem;\n display: inline-flex;\n}\n\n.zl-hero__compact-brand {\n overflow-wrap: anywhere;\n letter-spacing: -.01em;\n max-width: 100%;\n color: var(--zl-foreground);\n margin: 0;\n font-size: 1.125rem;\n font-weight: 700;\n}\n\n.zl-hero__nav-links {\n gap: var(--zl-spacing-6);\n color: var(--zl-muted-foreground);\n font-size: .9375rem;\n display: flex;\n}\n\n.zl-hero__nav-links a {\n color: inherit;\n text-decoration: none;\n}\n\n.zl-hero__nav-links a:hover {\n text-decoration: underline;\n}\n\n.zl-hero__eyebrow {\n margin: 0 0 var(--zl-spacing-4);\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--zl-muted-foreground);\n font-size: .8125rem;\n font-weight: 600;\n}\n\n.zl-hero__headline {\n margin: 0 0 var(--zl-spacing-4);\n font-family: var(--zl-font-family-heading);\n letter-spacing: -.02em;\n font-size: 2.75rem;\n font-weight: 700;\n line-height: 1.1;\n}\n\n.zl-hero__sub {\n margin: 0 0 var(--zl-spacing-8);\n max-width: 30rem;\n color: var(--zl-muted-foreground);\n font-size: 1.0625rem;\n line-height: 1.6;\n}\n\n.zl-hero__bullets {\n gap: var(--zl-spacing-2);\n margin: 0 0 var(--zl-spacing-8);\n flex-direction: column;\n padding: 0;\n list-style: none;\n display: flex;\n}\n\n.zl-hero__bullets li {\n align-items: center;\n gap: var(--zl-spacing-2);\n font-size: 1rem;\n display: flex;\n}\n\n.zl-hero__bullets li:before {\n content: \"✓\";\n color: var(--zl-success);\n font-weight: 700;\n}\n\n.zl-hero__footnote {\n padding-top: var(--zl-spacing-6);\n border-top: 1px solid var(--zl-border);\n color: var(--zl-muted-foreground);\n margin: 0;\n font-size: .8125rem;\n}\n\n.zl-hero__footnote strong {\n color: var(--zl-muted-foreground);\n font-weight: 600;\n}\n\n.zl-minimal {\n gap: var(--zl-spacing-6);\n flex-direction: column;\n width: min(24rem, 100%);\n margin-inline: auto;\n display: flex;\n}\n\n:host([variant=\"page\"]) .zl-minimal {\n padding: 52px 16px;\n}\n";
3437
3951
  //#endregion
3438
3952
  //#region src/orchestrator/zitadel-login.ts
3439
3953
  /** Narrow a rendered named element to the `formValue` field-atom contract. */
@@ -3542,6 +4056,13 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3542
4056
  set loading(value) {
3543
4057
  this.#_loading_accessor_storage = value;
3544
4058
  }
4059
+ #_completing_accessor_storage = false;
4060
+ get completing() {
4061
+ return this.#_completing_accessor_storage;
4062
+ }
4063
+ set completing(value) {
4064
+ this.#_completing_accessor_storage = value;
4065
+ }
3545
4066
  #_startupError_accessor_storage = null;
3546
4067
  get startupError() {
3547
4068
  return this.#_startupError_accessor_storage;
@@ -3557,6 +4078,13 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3557
4078
  this.#_formValues_accessor_storage = value;
3558
4079
  }
3559
4080
  engine = null;
4081
+ /**
4082
+ * The theme the last commit rendered with. The template output carries the
4083
+ * resolved side's mark, so a theme change rewrites the string and
4084
+ * `unsafeHTML` rebuilds the form — which drops what the visitor had typed
4085
+ * unless it is put back.
4086
+ */
4087
+ lastRenderedTheme = null;
3560
4088
  sanitise = createSanitiser();
3561
4089
  /**
3562
4090
  * Cached compiled tenant template, keyed by source string. Re-rendering on
@@ -3655,9 +4183,14 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3655
4183
  }
3656
4184
  for (const card of this.shadowRoot.querySelectorAll("zl-card")) card.toggleAttribute("data-suppress-header", this.suppressHeader);
3657
4185
  }
4186
+ const previousTheme = this.lastRenderedTheme;
4187
+ this.lastRenderedTheme = this.themeController.theme;
3658
4188
  const props = changed;
3659
- if (!props.has("response")) return;
3660
- this.hydrateStepAfterRender(props.get("response") == null);
4189
+ if (props.has("response")) {
4190
+ this.hydrateStepAfterRender(props.get("response") == null);
4191
+ return;
4192
+ }
4193
+ if (previousTheme !== null && previousTheme !== this.lastRenderedTheme) this.restoreValuesAfterRender();
3661
4194
  }
3662
4195
  /**
3663
4196
  * Apply captured values and move focus once the new step has fully
@@ -3672,11 +4205,19 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3672
4205
  * swaps are user-initiated, so focus moves in both modes.
3673
4206
  */
3674
4207
  async hydrateStepAfterRender(initial = false) {
4208
+ await this.restoreValuesAfterRender();
4209
+ if (!initial || this.variant === "page") this.moveFocusToFirstField(initial && this.variant === "page");
4210
+ }
4211
+ /**
4212
+ * Put captured values back once the rebuilt subtree has rendered. Awaits the
4213
+ * child atoms' own first render before touching them, rather than guessing a
4214
+ * frame with `requestAnimationFrame`.
4215
+ */
4216
+ async restoreValuesAfterRender() {
3675
4217
  await this.updateComplete;
3676
4218
  const atoms = this.shadowRoot?.querySelectorAll("zl-field, zl-select, zl-checkbox, zl-button");
3677
4219
  if (atoms) await Promise.all(Array.from(atoms).map((atom) => atom.updateComplete));
3678
4220
  this.applyValuesToFields();
3679
- if (!initial || this.variant === "page") this.moveFocusToFirstField(initial && this.variant === "page");
3680
4221
  }
3681
4222
  render() {
3682
4223
  if (this.startupError) return html`<form class="zl-mount" novalidate>
@@ -3686,7 +4227,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3686
4227
  </zl-card>
3687
4228
  </zl-page-shell>
3688
4229
  </form>`;
3689
- if (!this.response || !this.engine) return html`<slot name="loader"></slot>`;
4230
+ if (!this.response || !this.engine || this.completing) return html`<slot name="loader"></slot>`;
3690
4231
  const rendered = this.injectAttribution(this.renderStep(this.response.step, this.engine));
3691
4232
  return html`<form
3692
4233
  class="zl-mount"
@@ -3718,6 +4259,20 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3718
4259
  return rendered + html;
3719
4260
  }
3720
4261
  /**
4262
+ * The branding a template renders against, with `logo_url` already resolved
4263
+ * to the mark for the active side. Templates read one logo field and get the
4264
+ * right file per surface; the per-side URLs stay on `theme` for a template
4265
+ * that wants to reach them itself.
4266
+ */
4267
+ brandingForTemplate() {
4268
+ if (!this.branding) return {};
4269
+ const logoUrl = resolveLogoUrl(this.branding, this.themeController.theme);
4270
+ return {
4271
+ ...this.branding,
4272
+ logo_url: logoUrl
4273
+ };
4274
+ }
4275
+ /**
3721
4276
  * "Secured with Zitadel" attribution chrome injected into every
3722
4277
  * template's page-shell footer slot. Controlled by
3723
4278
  * `branding.attribution.show_zitadel` — defaults to `true` for
@@ -3730,7 +4285,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3730
4285
  const custom = attribution?.custom_link;
3731
4286
  if (!show && !custom) return "";
3732
4287
  const mark = custom ? `<a class="zl-trustmark__mark" part="attribution-mark" href="${escapeHtml(String(custom.href))}">${escapeHtml(String(custom.label))}</a>` : `<a class="zl-trustmark__mark" part="attribution-mark" href="https://zitadel.com" aria-label="Secured with Zitadel">${zitadelTrustmarkInnerHtml()}</a>`;
3733
- return `<div${placement === "footer" ? ` slot="footer"` : ""} part="attribution" class="zl-attribution zl-trustmark">${mark}</div>`;
4288
+ return `<div${placement === "footer" ? ` slot="footer"` : ""} part="attribution" class="zl-attribution zl-trustmark">${mark}<slot name="attribution-trailing"></slot></div>`;
3734
4289
  }
3735
4290
  /** Declarative config read from this element's attributes. */
3736
4291
  get projectAttrs() {
@@ -3778,6 +4333,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3778
4333
  }
3779
4334
  applyResponse(wire) {
3780
4335
  this.stepErrorDismissed = false;
4336
+ this.completing = navigatesOnComplete(wire, this.postSignInUrl);
3781
4337
  this.response = wire;
3782
4338
  const { branding, issues } = validateBranding(wire.branding, { renderingOrigin: this.ownerDocument.location.origin });
3783
4339
  this.branding = branding;
@@ -3797,7 +4353,11 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3797
4353
  this.armed = true;
3798
4354
  } else if (!hasBack && this.armed) {
3799
4355
  this.armed = false;
3800
- if (history.state?.zl === true) {
4356
+ if (history.state?.zl === true) if (this.completing) history.replaceState({
4357
+ ...history.state,
4358
+ zl: false
4359
+ }, "");
4360
+ else {
3801
4361
  this.ignoreNextPop = true;
3802
4362
  history.back();
3803
4363
  }
@@ -3869,7 +4429,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3869
4429
  messages: [],
3870
4430
  identity: this.deriveIdentity(),
3871
4431
  errors,
3872
- branding: this.branding ?? {},
4432
+ branding: this.brandingForTemplate(),
3873
4433
  loading: this.loading
3874
4434
  };
3875
4435
  let raw;
@@ -4249,6 +4809,7 @@ __decorate([property({ attribute: false })], ZitadelLogin.prototype, "locales",
4249
4809
  __decorate([state()], ZitadelLogin.prototype, "response", null);
4250
4810
  __decorate([state()], ZitadelLogin.prototype, "branding", null);
4251
4811
  __decorate([state()], ZitadelLogin.prototype, "loading", null);
4812
+ __decorate([state()], ZitadelLogin.prototype, "completing", null);
4252
4813
  __decorate([state()], ZitadelLogin.prototype, "startupError", null);
4253
4814
  __decorate([state()], ZitadelLogin.prototype, "formValues", null);
4254
4815
  ZitadelLogin = __decorate([customElement("zitadel-login")], ZitadelLogin);
@@ -4262,6 +4823,19 @@ ZitadelLogin = __decorate([customElement("zitadel-login")], ZitadelLogin);
4262
4823
  function isAllowedSelectValue(field, value) {
4263
4824
  return field.validation?.enum?.includes(value) ?? false;
4264
4825
  }
4826
+ /**
4827
+ * Whether a terminal step ends in a navigation rather than a screen: a
4828
+ * `redirect` with a URI, or a `show` whose handoff the widget exchanges before
4829
+ * sending the browser to `post-sign-in-url`. A `show` without a
4830
+ * `post-sign-in-url` is the host handling the handoff itself, and that screen
4831
+ * is the flow's own last word — it still renders.
4832
+ */
4833
+ function navigatesOnComplete(wire, postSignInUrl) {
4834
+ const behavior = wire.step.complete;
4835
+ if (behavior === "redirect") return Boolean(wire.redirect_uri);
4836
+ if (behavior === "show") return Boolean(wire.handoff_token && postSignInUrl);
4837
+ return false;
4838
+ }
4265
4839
  function collectInitialValues(step) {
4266
4840
  const values = {};
4267
4841
  if (!step.fields) return values;
@@ -4519,7 +5093,7 @@ let ZitadelLogout = class ZitadelLogout extends LitElement {
4519
5093
  const root = this.shadowRoot;
4520
5094
  if (root && !this.templateMode) {
4521
5095
  applyBaseTokens(root);
4522
- applyBrandingTokens(root, void 0, this.themeController.theme);
5096
+ applyBrandingTokens(root, void 0);
4523
5097
  }
4524
5098
  this.dataset.theme = this.themeController.theme;
4525
5099
  this.toggleAttribute("data-theme-dark", this.themeController.theme === "dark");
@@ -5003,6 +5577,6 @@ __decorate([state()], ZitadelSession.prototype, "loading", null);
5003
5577
  __decorate([state()], ZitadelSession.prototype, "errorMessage", null);
5004
5578
  ZitadelSession = __decorate([customElement("zitadel-session")], ZitadelSession);
5005
5579
  //#endregion
5006
- export { submitStep as C, startFlow as S, zitadelTrustmarkInnerHtml as T, MANDATORY_GATES_MARKER as _, TEMPLATE_NAMES as a, validateBranding as b, applyBrandingTokens as c, createSanitiser as d, builtinLocales as f, en as g, de as h, layout_chrome_default as i, buildBrandingStylesheet as l, it as m, ZitadelLogout as n, ThemeController as o, businessLocales as p, ZitadelLogin as r, applyFontUrl as s, ZitadelSession as t, resolveTheme as u, mandatoryGatesMarkerComment as v, ZITADEL_ATTRIBUTION_LOGOTYPE_SVG as w, getCurrentStep as x, patchMandatoryGates as y };
5580
+ export { getCurrentStep as C, zitadelTrustmarkInnerHtml as D, ZITADEL_ATTRIBUTION_LOGOTYPE_SVG as E, validateBranding as S, submitStep as T, MANDATORY_GATES_MARKER as _, TEMPLATE_NAMES as a, publishedSides as b, applyBrandingTokens as c, createSanitiser as d, builtinLocales as f, en as g, de as h, layout_chrome_default as i, buildBrandingStylesheet as l, it as m, ZitadelLogout as n, ThemeController as o, businessLocales as p, ZitadelLogin as r, applyFontUrl as s, ZitadelSession as t, resolveTheme as u, mandatoryGatesMarkerComment as v, startFlow as w, resolveLogoUrl as x, patchMandatoryGates as y };
5007
5581
 
5008
- //# sourceMappingURL=orchestrator-DG-YwFFE.mjs.map
5582
+ //# sourceMappingURL=orchestrator-BVmKaCqz.mjs.map