import { DeesElement, html, property, state, customElement, css, type TemplateResult } from '@design.estate/dees-element';
import { idpElementStyles } from './tokens.js';
import './idp-badge.js';
import './idp-icon.js';
import './idp-data-table.js';

declare global {
  interface HTMLElementTagNameMap {
    'idp-admin-shell': IdpAdminShell;
  }
}

type TNavItem = {
  id: TIdpAdminPage;
  label: string;
  icon: string;
  badge?: string;
};

type TKpi = {
  label: string;
  value: string;
  unit?: string;
  delta: string;
  deltaKind: 'up' | 'live';
  sub: string;
  accent: string;
  spark: number[];
  sparkColor: string;
};

export type TIdpAdminPage =
  | 'overview'
  | 'profile'
  | 'security'
  | 'sessions'
  | 'apps'
  | 'org-general'
  | 'org-settings'
  | 'org-members'
  | 'org-apps'
  | 'support'
  | 'ga-users'
  | 'ga-orgs'
  | 'ga-apps';

export interface IIdpAdminUser {
  name: string;
  email: string;
  initials?: string;
  username?: string;
  mobileNumber?: string;
  status?: string;
}

export interface IIdpAdminOrg {
  id: string;
  name: string;
  slug: string;
  myRole?: string;
}

export interface IIdpAdminNavigateEventDetail {
  page: TIdpAdminPage;
}

export interface IIdpAdminOrgSelectEventDetail {
  orgId: string;
  org: IIdpAdminOrg | null;
}

export interface IIdpAdminSession {
  id: string;
  deviceName: string;
  browser: string;
  os: string;
  ip: string;
  lastActive: number;
  createdAt: number;
  isCurrent: boolean;
}

export interface IIdpAdminActivity {
  id: string;
  action: string;
  description: string;
  timestamp: number;
  ip?: string;
  targetType?: string;
}

export interface IIdpAdminApp {
  id: string;
  name: string;
  description?: string;
  logoUrl?: string;
  appUrl?: string;
  category?: string;
  type?: string;
  status?: string;
  isConnected?: boolean;
  connectionCount?: number;
  clientId?: string;
  scopes?: string[];
  grants?: string[];
  roleMappings?: IIdpAdminAppRoleMapping[];
}

export interface IIdpAdminOrgRoleDefinition {
  key: string;
  name: string;
  description?: string;
  createdAt?: number;
  updatedAt?: number;
}

export interface IIdpAdminAppRoleMapping {
  orgRoleKey: string;
  appRoles: string[];
  permissions: string[];
  scopes: string[];
}

export interface IIdpAdminMember {
  userId: string;
  name: string;
  email: string;
  roles: string[];
  isCurrentUser?: boolean;
}

export interface IIdpAdminInvitation {
  id: string;
  email: string;
  roles: string[];
  invitedAt: number;
  expiresAt: number;
  status?: string;
}

export interface IIdpAdminPassportDevice {
  id: string;
  label: string;
  platform: string;
  status: string;
  capabilities?: {
    gps?: boolean;
    nfc?: boolean;
    push?: boolean;
  };
  appVersion?: string;
  createdAt: number;
  lastSeenAt?: number;
  lastChallengeAt?: number;
  pushRegistered?: boolean;
}

export interface IIdpAdminPassportEnrollment {
  challengeId: string;
  pairingToken: string;
  pairingPayload: string;
  signingPayload: string;
  expiresAt: number;
}

export interface IIdpAdminPasskey {
  id: string;
  label: string;
  credentialId?: string;
  status: string;
  backedUp?: boolean;
  deviceType?: string;
  transports?: string[];
  createdAt: number;
  lastUsedAt?: number | null;
}

export interface IIdpAdminTotpEnrollment {
  credentialId: string;
  secret: string;
  otpauthUrl: string;
}

export interface IIdpAdminSessionEventDetail {
  sessionId: string;
}

export interface IIdpAdminAppToggleEventDetail {
  appId: string;
  connected: boolean;
}

export interface IIdpAdminMemberEventDetail {
  userId: string;
}

export interface IIdpAdminMemberRolesEventDetail {
  userId: string;
  roles: string[];
}

export interface IIdpAdminInvitationEventDetail {
  invitationId: string;
}

export interface IIdpAdminOrgUpdateEventDetail {
  organizationId: string;
  name: string;
  slug: string;
  confirmationText: string;
}

export interface IIdpAdminOrgTransferEventDetail {
  organizationId: string;
  newOwnerId: string;
  confirmationText: string;
}

export interface IIdpAdminOrgDeleteEventDetail {
  organizationId: string;
  confirmationText: string;
}

export interface IIdpAdminOrgRoleUpsertEventDetail {
  organizationId: string;
  roleDefinition: {
    key: string;
    name: string;
    description?: string;
  };
}

export interface IIdpAdminOrgRoleDeleteEventDetail {
  organizationId: string;
  roleKey: string;
  confirmationText: string;
}

export interface IIdpAdminAppRoleMappingsEventDetail {
  organizationId: string;
  appId: string;
  roleMappings: IIdpAdminAppRoleMapping[];
}

export interface IIdpAdminPasswordChangeEventDetail {
  currentPassword: string;
  newPassword: string;
}

export interface IIdpAdminPassportEnrollmentEventDetail {
  deviceLabel: string;
}

export interface IIdpAdminPassportDeviceEventDetail {
  deviceId: string;
}

export interface IIdpAdminTotpVerifyEventDetail {
  credentialId: string;
  code: string;
}

export interface IIdpAdminTotpCodeEventDetail {
  code: string;
}

export interface IIdpAdminPasskeyRegistrationEventDetail {
  label: string;
}

export interface IIdpAdminPasskeyEventDetail {
  passkeyId: string;
}

@customElement('idp-admin-shell')
export class IdpAdminShell extends DeesElement {
  public static demo = () => html`<idp-admin-shell global-admin></idp-admin-shell>`;
  public static demoGroups = ['idp.global v3 full pages'];

  public static styles = [
    ...idpElementStyles,
    css`
      :host {
        display: block;
        height: 100vh;
        max-height: 100vh;
        min-height: 0;
        overflow: hidden;
        color: var(--idp-fg);
      }
      .shell {
        height: 100%;
        min-height: 0;
        display: grid;
        grid-template-columns: 220px minmax(0, 1fr);
        overflow: hidden;
        border: 0;
        background: var(--idp-bg);
      }
      .sidebar {
        height: 100%;
        min-height: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        border-right: 1px solid var(--idp-border);
        background: var(--idp-bg);
      }
      .logo-block {
        padding: 14px 16px 10px;
        border-bottom: 1px solid var(--idp-border);
      }
      .logo {
        display: flex;
        align-items: center;
        gap: 8px;
      }
      .logo-icon {
        width: 26px;
        height: 26px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        border-radius: 7px;
        background: var(--idp-primary);
        color: var(--idp-primary-fg);
      }
      .logo-text {
        color: var(--idp-fg);
        font-size: 13px;
        font-weight: 700;
        letter-spacing: -0.3px;
      }
      .nav-wrap {
        flex: 1;
        min-height: 0;
        overflow: auto;
        padding: 10px 8px;
      }
      .state-card {
        padding: 42px 24px;
        border: 1px solid var(--idp-border);
        border-radius: var(--idp-radius);
        background: var(--idp-card);
        color: var(--idp-muted-fg);
        text-align: center;
      }
      .state-icon {
        width: 38px;
        height: 38px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        margin-bottom: 12px;
        border: 1px solid var(--idp-border);
        border-radius: 10px;
        background: var(--idp-muted);
        color: var(--idp-muted-fg);
      }
      .state-title {
        margin-bottom: 4px;
        color: var(--idp-fg);
        font-size: 14px;
        font-weight: 600;
      }
      .state-description {
        max-width: 420px;
        margin: 0 auto;
        font-size: 12px;
        line-height: 1.5;
      }
      .notice-card {
        display: flex;
        align-items: flex-start;
        gap: 12px;
        padding: 14px 16px;
        border: 1px solid var(--idp-info-border);
        border-radius: var(--idp-radius);
        background: var(--idp-info-bg);
        color: var(--idp-info);
      }
      .notice-card .muted {
        color: var(--idp-muted-fg);
      }
      .nav-section {
        margin-bottom: 20px;
      }
      .nav-title {
        padding: 0 8px;
        margin-bottom: 4px;
        color: var(--idp-muted-fg);
        font-size: 10px;
        font-weight: 600;
        letter-spacing: 0.8px;
        text-transform: uppercase;
      }
      .nav-list {
        display: grid;
      }
      .nav-item,
      .org-switch,
      .plain-button {
        border: 0;
        font-family: inherit;
      }
      .nav-item {
        width: 100%;
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 6px 8px;
        border-radius: 7px;
        background: transparent;
        color: var(--idp-muted-fg);
        font-size: 13px;
        font-weight: 400;
        text-align: left;
      }
      .nav-item.active {
        background: var(--idp-muted);
        color: var(--idp-fg);
        font-weight: 500;
      }
      .nav-item idp-icon {
        color: currentColor;
      }
      .nav-item.active idp-icon {
        color: var(--idp-accent);
      }
      .org-switch-wrap {
        position: relative;
        margin-bottom: 2px;
      }
      .org-switch {
        width: 100%;
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 6px 8px;
        border-radius: 7px;
        background: transparent;
        color: var(--idp-muted-fg);
        cursor: pointer;
      }
      .org-switch:hover {
        background: var(--idp-muted);
      }
      .org-switch.open {
        background: var(--idp-muted);
      }
      .org-switch.open .org-name {
        color: var(--idp-accent);
      }
      .org-switch.open idp-icon {
        transform: rotate(180deg);
      }
      .org-menu {
        position: absolute;
        top: calc(100% + 4px);
        left: 0;
        right: 0;
        z-index: 40;
        overflow: hidden;
        border: 1px solid var(--idp-border);
        border-radius: 10px;
        background: var(--idp-card);
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
      }
      .org-menu-title {
        padding: 6px 8px 3px;
        color: var(--idp-muted-fg);
        font-size: 10px;
        font-weight: 600;
        letter-spacing: 0.7px;
        text-transform: uppercase;
      }
      .org-menu-item,
      .org-create {
        width: 100%;
        display: flex;
        align-items: center;
        gap: 8px;
        border: 0;
        background: transparent;
        font-family: inherit;
        text-align: left;
      }
      .org-menu-item {
        padding: 6px 8px;
        cursor: pointer;
      }
      .org-menu-item.selected {
        background: var(--idp-accent-soft);
      }
      .org-menu-item .org-avatar {
        width: 20px;
        height: 20px;
        font-size: 7px;
      }
      .org-menu-name {
        overflow: hidden;
        color: var(--idp-fg);
        font-size: 12px;
        font-weight: 400;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .org-menu-item.selected .org-menu-name {
        color: var(--idp-accent);
      }
      .org-menu-role {
        color: var(--idp-muted-fg);
        font-size: 10px;
      }
      .org-menu-divider {
        height: 1px;
        margin: 3px 0;
        background: var(--idp-border);
      }
      .org-create {
        padding: 6px 8px 8px;
        color: var(--idp-muted-fg);
        cursor: pointer;
        font-size: 12px;
      }
      .org-create-icon {
        width: 20px;
        height: 20px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        border: 1.5px dashed var(--idp-border);
        border-radius: 5px;
      }
      .org-name {
        flex: 1;
        min-width: 0;
        overflow: hidden;
        color: var(--idp-muted-fg);
        font-size: 13px;
        font-weight: 400;
        text-align: left;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .org-avatar,
      .user-avatar {
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        font-family: var(--idp-mono);
        font-weight: 700;
      }
      .org-avatar {
        width: 14px;
        height: 14px;
        border: 1px solid var(--idp-border);
        border-radius: 6px;
        background: var(--idp-accent-soft);
        color: var(--idp-accent);
        font-size: 5px;
      }
      .user-footer {
        display: flex;
        align-items: center;
        gap: 9px;
        padding: 10px 12px;
        border-top: 1px solid var(--idp-border);
      }
      .user-avatar {
        width: 26px;
        height: 26px;
        border-radius: 50%;
        background: oklch(0.85 0.08 240);
        color: oklch(0.3 0.08 240);
        font-size: 9px;
      }
      .user-meta {
        flex: 1;
        min-width: 0;
      }
      .user-name,
      .user-email {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .user-name {
        color: var(--idp-fg);
        font-size: 12px;
        font-weight: 500;
      }
      .user-email {
        color: var(--idp-muted-fg);
        font-size: 10px;
      }
      main {
        height: 100%;
        min-height: 0;
        min-width: 0;
        overflow: auto;
        background: var(--idp-bg);
      }
      .page-head {
        display: flex;
        align-items: flex-end;
        justify-content: space-between;
        gap: 16px;
        flex-wrap: wrap;
        padding: 24px 28px 18px;
        border-bottom: 1px solid var(--idp-border);
        background: var(--idp-bg);
      }
      .eyebrow-row {
        display: flex;
        align-items: center;
        gap: 8px;
        margin-bottom: 4px;
      }
      .eyebrow {
        color: var(--idp-muted-fg);
        font-family: var(--idp-mono);
        font-size: 10px;
        font-weight: 600;
        letter-spacing: 0.08em;
        text-transform: uppercase;
      }
      .live-pill {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        padding: 1px 7px;
        border: 1px solid var(--idp-ok-border);
        border-radius: 999px;
        background: var(--idp-ok-bg);
        color: var(--idp-ok);
        font-family: var(--idp-mono);
        font-size: 10px;
        font-weight: 500;
      }
      .live-dot,
      .feed-dot {
        width: 6px;
        height: 6px;
        border-radius: 50%;
        background: var(--idp-ok);
        box-shadow: 0 0 6px var(--idp-ok);
        animation: idp-blink 1.6s ease-in-out infinite;
      }
      @keyframes idp-blink {
        0%, 100% { opacity: 1; }
        50% { opacity: 0.35; }
      }
      h1,
      h2 {
        margin: 0;
        color: var(--idp-fg);
        font-family: var(--idp-display);
        letter-spacing: -0.025em;
      }
      h1 {
        font-size: 26px;
        font-weight: 700;
        line-height: 1.1;
      }
      .lead {
        margin-top: 4px;
        color: var(--idp-muted-fg);
        font-size: 13px;
      }
      .lead code,
      .mono {
        color: var(--idp-info);
        font-family: var(--idp-mono);
      }
      .page-actions {
        display: flex;
        align-items: center;
        gap: 12px;
      }
      .tabs {
        display: inline-flex;
        gap: 1px;
        padding: 2px;
        border: 1px solid var(--idp-border);
        border-radius: 6px;
        background: var(--idp-bg-2);
      }
      .tab {
        padding: 4px 10px;
        border-radius: 4px;
        color: var(--idp-muted-fg);
        font-family: var(--idp-mono);
        font-size: 11px;
        font-weight: 500;
      }
      .tab.active {
        background: var(--idp-muted);
        color: var(--idp-fg);
      }
      .plain-button {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        height: 28px;
        padding: 5px 10px;
        border-radius: 8px;
        background: transparent;
        color: var(--idp-fg);
        font-size: 12px;
        font-weight: 500;
        white-space: nowrap;
      }
      .plain-button.outline {
        border: 1px solid var(--idp-border);
      }
      .plain-button.primary {
        background: var(--idp-accent);
        color: var(--idp-accent-fg);
      }
      .plain-button.destructive {
        background: var(--idp-destructive);
        color: var(--idp-accent-fg);
      }
      .plain-button.ghost {
        color: var(--idp-fg);
      }
      .plain-button:disabled {
        opacity: 0.45;
        cursor: not-allowed;
      }
      .body {
        display: flex;
        flex-direction: column;
        gap: 16px;
        padding: 20px 28px 32px;
      }
      .narrow-body {
        max-width: 700px;
      }
      .wide-body {
        max-width: 860px;
      }
      .section-card {
        margin-bottom: 16px;
        padding: 20px;
        border: 1px solid var(--idp-border);
        border-radius: var(--idp-radius);
        background: var(--idp-card);
      }
      .section-card.compact {
        padding: 14px;
      }
      .section-headline {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: 16px;
        margin-bottom: 16px;
      }
      .section-title {
        color: var(--idp-fg);
        font-size: 14px;
        font-weight: 600;
      }
      .section-description,
      .muted {
        color: var(--idp-muted-fg);
        font-size: 12px;
        line-height: 1.5;
      }
      .form-row {
        display: grid;
        grid-template-columns: 180px 1fr;
        gap: 16px;
        align-items: start;
        padding: 12px 0;
        border-bottom: 1px solid var(--idp-border);
      }
      .form-row:last-child {
        border-bottom: 0;
      }
      .form-label {
        display: flex;
        gap: 3px;
        color: var(--idp-fg);
        font-size: 13px;
        font-weight: 500;
      }
      .form-hint {
        margin-top: 2px;
        color: var(--idp-muted-fg);
        font-size: 12px;
        line-height: 1.4;
      }
      .input,
      .select,
      .textarea {
        box-sizing: border-box;
        width: 100%;
        border: 1px solid var(--idp-border);
        border-radius: 8px;
        outline: none;
        background: var(--idp-bg);
        color: var(--idp-fg);
        font-family: inherit;
        font-size: 13px;
      }
      .input,
      .select {
        height: 34px;
        padding: 0 10px;
      }
      .textarea {
        min-height: 96px;
        padding: 8px 10px;
        resize: vertical;
      }
      .input-group {
        display: flex;
        overflow: hidden;
        border: 1px solid var(--idp-border);
        border-radius: 8px;
        background: var(--idp-bg);
      }
      .input-prefix {
        height: 34px;
        display: flex;
        align-items: center;
        padding: 0 8px;
        border-right: 1px solid var(--idp-border);
        background: var(--idp-muted);
        color: var(--idp-muted-fg);
        font-family: var(--idp-mono);
        font-size: 12px;
        white-space: nowrap;
      }
      .input-group .input {
        border: 0;
        border-radius: 0;
        background: transparent;
      }
      .divider {
        height: 1px;
        margin: 16px 0;
        background: var(--idp-border);
      }
      .code-block {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 6px 10px;
        border: 1px solid var(--idp-border);
        border-radius: 7px;
        background: var(--idp-muted);
      }
      .code-block code {
        flex: 1;
        overflow-wrap: anywhere;
        color: var(--idp-fg);
        font-family: var(--idp-mono);
        font-size: 12px;
      }
      .avatar,
      .app-avatar,
      .icon-tile {
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
      }
      .avatar {
        border-radius: 50%;
        background: oklch(0.85 0.08 240);
        color: oklch(0.3 0.08 240);
        font-family: var(--idp-mono);
        font-weight: 600;
      }
      .app-avatar,
      .icon-tile {
        border: 1px solid var(--idp-border);
        border-radius: 10px;
        background: var(--idp-muted);
        color: var(--idp-muted-fg);
      }
      .app-avatar {
        width: 40px;
        height: 40px;
        background: var(--idp-accent-soft);
        color: var(--idp-accent);
        font-size: 14px;
        font-weight: 700;
      }
      .icon-tile {
        width: 38px;
        height: 38px;
      }
      .list-stack {
        display: grid;
        gap: 12px;
      }
      .row-card {
        display: flex;
        align-items: flex-start;
        gap: 14px;
        padding: 16px;
        border: 1px solid var(--idp-border);
        border-radius: var(--idp-radius);
        background: var(--idp-card);
      }
      .split-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 16px;
      }
      .chip-row {
        display: flex;
        flex-wrap: wrap;
        gap: 4px;
      }
      .switch {
        width: 36px;
        height: 20px;
        position: relative;
        flex-shrink: 0;
        border-radius: 10px;
        background: var(--idp-accent);
      }
      .switch::after {
        content: '';
        position: absolute;
        top: 2px;
        left: 18px;
        width: 16px;
        height: 16px;
        border-radius: 8px;
        background: #fff;
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
      }
      .sub-tabs {
        width: fit-content;
        display: flex;
        gap: 2px;
        margin-bottom: 16px;
        padding: 3px;
        border-radius: 8px;
        background: var(--idp-muted);
      }
      .sub-tab {
        padding: 5px 12px;
        border-radius: 6px;
        color: var(--idp-muted-fg);
        font-size: 12px;
      }
      .sub-tab.active {
        background: var(--idp-bg);
        color: var(--idp-fg);
        font-weight: 500;
        box-shadow: 0 1px 3px rgba(0,0,0,0.06);
      }
      .kpis {
        display: grid;
        grid-template-columns: repeat(4, 1fr);
        gap: 12px;
      }
      .kpi,
      .card {
        border: 1px solid var(--idp-border);
        background: var(--idp-card);
      }
      .kpi {
        position: relative;
        min-height: 132px;
        display: flex;
        flex-direction: column;
        gap: 4px;
        overflow: hidden;
        padding: 18px 20px 14px;
        border-radius: 10px;
      }
      .kpi::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: 2px;
        background: var(--kpi-accent);
      }
      .kpi-label {
        color: var(--idp-muted-fg);
        font-size: 11.5px;
        font-weight: 500;
        letter-spacing: 0.02em;
      }
      .kpi-value {
        color: var(--idp-fg);
        font-family: var(--idp-display);
        font-size: 30px;
        font-weight: 700;
        font-variant-numeric: tabular-nums;
        letter-spacing: -0.025em;
        line-height: 1.1;
      }
      .kpi-value span {
        margin-left: 2px;
        color: var(--idp-muted-fg);
        font-size: 14px;
        font-weight: 500;
      }
      .kpi-sub {
        color: var(--idp-fg-3);
        font-family: var(--idp-mono);
        font-size: 11px;
      }
      .kpi-foot {
        display: flex;
        align-items: flex-end;
        justify-content: space-between;
        gap: 8px;
        margin-top: auto;
      }
      .delta {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        color: var(--idp-ok);
        font-family: var(--idp-mono);
        font-size: 11px;
        font-weight: 600;
      }
      .sparkline {
        width: 84px;
        opacity: 0.85;
      }
      .primary-grid,
      .secondary-grid {
        display: grid;
        grid-template-columns: minmax(0, 1.6fr) minmax(300px, 1fr);
        gap: 16px;
      }
      .card {
        overflow: hidden;
        border-radius: 8px;
      }
      .card-head {
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 12px 16px;
        border-bottom: 1px solid var(--idp-border-soft);
      }
      .card-title {
        color: var(--idp-fg);
        font-size: 13px;
        font-weight: 600;
      }
      .feed-dot {
        display: inline-block;
        width: 8px;
        height: 8px;
        border-radius: 4px;
        background: var(--dot-color);
        box-shadow: var(--dot-glow, none);
      }
      .feed-meta {
        color: var(--idp-muted-fg);
        font-family: var(--idp-mono);
        font-size: 11.5px;
      }
      .feed-item {
        display: grid;
        grid-template-columns: 14px 1fr auto;
        gap: 12px;
        align-items: center;
        padding: 11px 16px;
        border-bottom: 1px solid var(--idp-border-soft);
      }
      .feed-item:last-child {
        border-bottom: 0;
      }
      .feed-text {
        color: var(--idp-fg-2);
        font-size: 12.5px;
      }
      .feed-text strong {
        color: var(--idp-fg);
        font-weight: 600;
      }
      .dialog-backdrop {
        position: fixed;
        inset: 0;
        z-index: 100;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 20px;
        background: rgba(0, 0, 0, 0.42);
      }
      .dialog-card {
        width: min(720px, 100%);
        max-height: min(760px, calc(100vh - 40px));
        display: flex;
        flex-direction: column;
        overflow: hidden;
        border: 1px solid var(--idp-border);
        border-radius: 12px;
        background: var(--idp-card);
        box-shadow: 0 24px 80px rgba(0, 0, 0, 0.28);
      }
      .dialog-card.wide {
        width: min(980px, 100%);
      }
      .dialog-head {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: 16px;
        padding: 18px 20px 14px;
        border-bottom: 1px solid var(--idp-border);
      }
      .dialog-title {
        color: var(--idp-fg);
        font-size: 15px;
        font-weight: 700;
      }
      .dialog-body {
        display: grid;
        gap: 14px;
        overflow: auto;
        padding: 18px 20px;
      }
      .dialog-actions {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: 10px;
        padding: 14px 20px;
        border-top: 1px solid var(--idp-border);
      }
      .role-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
        gap: 8px;
      }
      .role-option {
        display: flex;
        align-items: flex-start;
        gap: 8px;
        padding: 9px;
        border: 1px solid var(--idp-border);
        border-radius: 8px;
        background: var(--idp-bg);
      }
      .role-option input {
        margin-top: 2px;
      }
      .mapping-row {
        display: grid;
        grid-template-columns: 160px repeat(3, minmax(0, 1fr));
        gap: 8px;
        align-items: start;
        padding: 10px;
        border: 1px solid var(--idp-border);
        border-radius: 8px;
        background: var(--idp-bg);
      }
      .mapping-role {
        color: var(--idp-fg);
        font-size: 12px;
        font-weight: 600;
      }
      .mapping-role .muted {
        display: block;
        margin-top: 2px;
      }
      .mapping-row .form-row {
        display: block;
        padding: 0;
        border-bottom: 0;
      }
      .mapping-row .form-label {
        margin-bottom: 4px;
      }
      @media (max-width: 1120px) {
        .shell {
          grid-template-columns: 1fr;
        }
        .sidebar {
          display: none;
        }
        .kpis {
          grid-template-columns: repeat(2, 1fr);
        }
        .primary-grid,
        .secondary-grid {
          grid-template-columns: 1fr;
        }
      }
      @media (max-width: 720px) {
        .page-head,
        .body {
          padding-left: 20px;
          padding-right: 20px;
        }
        .page-actions {
          flex-wrap: wrap;
        }
        .kpis {
          grid-template-columns: 1fr;
        }
        .mapping-row {
          grid-template-columns: 1fr;
        }
      }
    `,
  ];

  @property({ type: String })
  public accessor page: TIdpAdminPage = 'overview';

  @property({ type: Object })
  public accessor user: IIdpAdminUser = {
    name: '',
    email: '',
  };

  @property({ type: Array })
  public accessor orgs: IIdpAdminOrg[] = [];

  @property({ type: String, attribute: 'selected-org-id' })
  public accessor selectedOrgId = '';

  @property({ type: Boolean, attribute: 'global-admin', reflect: true })
  public accessor globalAdmin = false;

  @property({ type: Boolean, attribute: 'data-loading', reflect: true })
  public accessor dataLoading = false;

  @property({ type: String, attribute: 'data-error' })
  public accessor dataError = '';

  @property({ type: Array })
  public accessor sessions: IIdpAdminSession[] = [];

  @property({ type: Array })
  public accessor activities: IIdpAdminActivity[] = [];

  @property({ type: Array })
  public accessor orgMembers: IIdpAdminMember[] = [];

  @property({ type: Array })
  public accessor orgInvitations: IIdpAdminInvitation[] = [];

  @property({ type: Array })
  public accessor orgRoleDefinitions: IIdpAdminOrgRoleDefinition[] = [];

  @property({ type: Array })
  public accessor orgApps: IIdpAdminApp[] = [];

  @property({ type: Array })
  public accessor accountApps: IIdpAdminApp[] = [];

  @property({ type: Array })
  public accessor adminApps: IIdpAdminApp[] = [];

  @property({ type: Array })
  public accessor passportDevices: IIdpAdminPassportDevice[] = [];

  @property({ type: Object })
  public accessor passportEnrollment: IIdpAdminPassportEnrollment | null = null;

  @property({ type: Boolean, attribute: 'totp-enabled' })
  public accessor totpEnabled = false;

  @property({ type: Number, attribute: 'backup-codes-remaining' })
  public accessor backupCodesRemaining = 0;

  @property({ type: Object })
  public accessor totpEnrollment: IIdpAdminTotpEnrollment | null = null;

  @property({ type: Array })
  public accessor passkeys: IIdpAdminPasskey[] = [];

  @property({ type: String, attribute: 'credential-message' })
  public accessor credentialMessage = '';

  @state()
  private accessor orgMenuOpen = false;

  @state()
  private accessor currentPassword = '';

  @state()
  private accessor newPassword = '';

  @state()
  private accessor confirmPassword = '';

  @state()
  private accessor credentialError = '';

  @state()
  private accessor orgSettingsOrgId = '';

  @state()
  private accessor orgNameDraft = '';

  @state()
  private accessor orgSlugDraft = '';

  @state()
  private accessor orgSettingsConfirmation = '';

  @state()
  private accessor transferOwnerId = '';

  @state()
  private accessor transferConfirmation = '';

  @state()
  private accessor deleteConfirmation = '';

  @state()
  private accessor orgSettingsError = '';

  @state()
  private accessor dialogMode: 'none' | 'role-upsert' | 'role-delete' | 'member-roles' | 'app-role-mappings' = 'none';

  @state()
  private accessor dialogError = '';

  @state()
  private accessor dialogRoleKey = '';

  @state()
  private accessor dialogRoleName = '';

  @state()
  private accessor dialogRoleDescription = '';

  @state()
  private accessor dialogRoleDeleteConfirmation = '';

  @state()
  private accessor dialogMember: IIdpAdminMember | null = null;

  @state()
  private accessor dialogMemberRoles: string[] = [];

  @state()
  private accessor dialogApp: IIdpAdminApp | null = null;

  @state()
  private accessor dialogAppMappings: IIdpAdminAppRoleMapping[] = [];

  private workspaceNav: TNavItem[] = [
    { id: 'overview', label: 'Overview', icon: 'grid' },
  ];

  private accountNav: TNavItem[] = [
    { id: 'profile', label: 'Profile', icon: 'user' },
    { id: 'security', label: 'Security', icon: 'shield' },
    { id: 'sessions', label: 'Sessions & Devices', icon: 'monitor' },
    { id: 'apps', label: 'Connected Apps', icon: 'grid' },
  ];

  private orgNav: TNavItem[] = [
    { id: 'org-general', label: 'General', icon: 'building' },
    { id: 'org-settings', label: 'Settings', icon: 'settings' },
    { id: 'org-members', label: 'Members', icon: 'users' },
    { id: 'org-apps', label: 'OAuth Apps', icon: 'box' },
  ];

  private supportNav: TNavItem[] = [
    { id: 'support', label: 'Support', icon: 'shield' },
  ];

  private adminNav: TNavItem[] = [
    { id: 'ga-users', label: 'All Users', icon: 'users' },
    { id: 'ga-orgs', label: 'All Organisations', icon: 'building' },
    { id: 'ga-apps', label: 'Platform Apps', icon: 'globe' },
  ];

  private get safeOrgs(): IIdpAdminOrg[] {
    return Array.isArray(this.orgs) ? this.orgs.filter(Boolean) : [];
  }

  private get currentOrg(): IIdpAdminOrg {
    const orgs = this.safeOrgs;
    return orgs.find((orgArg) => orgArg.id === this.selectedOrgId) || orgs[0] || {
      id: '',
      name: 'No organisation',
      slug: '',
      myRole: '',
    };
  }

  private get userInitials(): string {
    if (this.user?.initials) {
      return this.user.initials;
    }

    const source = this.user?.name || this.user?.email || '?';
    return source
      .split(/\s+|@/)
      .filter(Boolean)
      .map((partArg) => partArg[0])
      .slice(0, 2)
      .join('')
      .toUpperCase();
  }

  private getInitials(valueArg?: string): string {
    const source = valueArg || '?';
    return source
      .split(/\s+|@|-|\./)
      .filter(Boolean)
      .map((partArg) => partArg[0])
      .slice(0, 2)
      .join('')
      .toUpperCase();
  }

  private setPasswordField(fieldArg: 'current' | 'new' | 'confirm', eventArg: Event) {
    const value = (eventArg.target as HTMLInputElement).value;
    if (fieldArg === 'current') this.currentPassword = value;
    if (fieldArg === 'new') this.newPassword = value;
    if (fieldArg === 'confirm') this.confirmPassword = value;
    this.credentialError = '';
  }

  private submitPasswordChange() {
    if (!this.currentPassword || !this.newPassword || !this.confirmPassword) {
      this.credentialError = 'Enter your current password and confirm the new password.';
      return;
    }

    if (this.newPassword.length < 12) {
      this.credentialError = 'Use at least 12 characters for the new password.';
      return;
    }

    if (this.newPassword !== this.confirmPassword) {
      this.credentialError = 'New password and confirmation do not match.';
      return;
    }

    this.dispatchShellEvent<IIdpAdminPasswordChangeEventDetail>('idp-admin-password-change', {
      currentPassword: this.currentPassword,
      newPassword: this.newPassword,
    });
  }

  private syncOrgSettingsState() {
    const org = this.currentOrg;
    if (this.orgSettingsOrgId === org.id) {
      return;
    }

    this.orgSettingsOrgId = org.id;
    this.orgNameDraft = org.name || '';
    this.orgSlugDraft = org.slug || '';
    this.orgSettingsConfirmation = '';
    this.transferOwnerId = '';
    this.transferConfirmation = '';
    this.deleteConfirmation = '';
    this.orgSettingsError = '';
  }

  private setOrgSettingsField(
    fieldArg: 'name' | 'slug' | 'settingsConfirmation' | 'transferOwnerId' | 'transferConfirmation' | 'deleteConfirmation',
    eventArg: Event
  ) {
    const value = (eventArg.target as HTMLInputElement | HTMLSelectElement).value;
    if (fieldArg === 'name') this.orgNameDraft = value;
    if (fieldArg === 'slug') this.orgSlugDraft = value.trim().toLowerCase();
    if (fieldArg === 'settingsConfirmation') this.orgSettingsConfirmation = value;
    if (fieldArg === 'transferOwnerId') this.transferOwnerId = value;
    if (fieldArg === 'transferConfirmation') this.transferConfirmation = value;
    if (fieldArg === 'deleteConfirmation') this.deleteConfirmation = value;
    this.orgSettingsError = '';
  }

  private submitOrgSettingsUpdate() {
    const org = this.currentOrg;
    const name = this.orgNameDraft.trim();
    const slug = this.orgSlugDraft.trim().toLowerCase();
    if (!org.id) {
      this.orgSettingsError = 'Select an organisation before updating settings.';
      return;
    }
    if (!name || !slug) {
      this.orgSettingsError = 'Organisation name and slug are required.';
      return;
    }
    if (name === org.name && slug === org.slug) {
      this.orgSettingsError = 'Change the organisation name or slug before applying settings.';
      return;
    }
    if (this.orgSettingsConfirmation.trim() !== org.slug) {
      this.orgSettingsError = `Type ${org.slug} to confirm organisation settings changes.`;
      return;
    }

    this.dispatchShellEvent<IIdpAdminOrgUpdateEventDetail>('idp-admin-org-update', {
      organizationId: org.id,
      name,
      slug,
      confirmationText: this.orgSettingsConfirmation.trim(),
    });
  }

  private submitOrgTransfer() {
    const org = this.currentOrg;
    const expectedText = `transfer ${org.slug}`;
    if (!org.id) {
      this.orgSettingsError = 'Select an organisation before transferring ownership.';
      return;
    }
    if (!this.transferOwnerId) {
      this.orgSettingsError = 'Select the member who should become the new owner.';
      return;
    }
    if (this.transferConfirmation.trim() !== expectedText) {
      this.orgSettingsError = `Type ${expectedText} to confirm ownership transfer.`;
      return;
    }

    this.dispatchShellEvent<IIdpAdminOrgTransferEventDetail>('idp-admin-org-transfer', {
      organizationId: org.id,
      newOwnerId: this.transferOwnerId,
      confirmationText: this.transferConfirmation.trim(),
    });
  }

  private submitOrgDelete() {
    const org = this.currentOrg;
    const expectedText = `delete ${org.slug}`;
    if (!org.id) {
      this.orgSettingsError = 'Select an organisation before deleting it.';
      return;
    }
    if (this.deleteConfirmation.trim() !== expectedText) {
      this.orgSettingsError = `Type ${expectedText} to confirm organisation deletion.`;
      return;
    }

    this.dispatchShellEvent<IIdpAdminOrgDeleteEventDetail>('idp-admin-org-delete', {
      organizationId: org.id,
      confirmationText: this.deleteConfirmation.trim(),
    });
  }

  private requestPassportEnrollment() {
    const fallbackLabel = typeof navigator !== 'undefined'
      ? navigator.userAgent.includes('Mobile') ? 'Mobile passport device' : 'Desktop passport device'
      : 'Passport device';
    const deviceLabel = globalThis.prompt?.('Device label', fallbackLabel)?.trim();
    if (!deviceLabel) {
      return;
    }

    this.dispatchShellEvent<IIdpAdminPassportEnrollmentEventDetail>('idp-admin-passport-enroll', {
      deviceLabel,
    });
  }

  private requestTotpEnrollment() {
    this.dispatchShellEvent('idp-admin-totp-start', {});
  }

  private verifyTotpEnrollment() {
    if (!this.totpEnrollment) {
      return;
    }
    const code = globalThis.prompt?.('Authenticator code')?.trim();
    if (!code) {
      return;
    }
    this.dispatchShellEvent<IIdpAdminTotpVerifyEventDetail>('idp-admin-totp-verify', {
      credentialId: this.totpEnrollment.credentialId,
      code,
    });
  }

  private disableTotp() {
    const code = globalThis.prompt?.('Enter your current authenticator code to disable TOTP')?.trim();
    if (!code) {
      return;
    }
    this.dispatchShellEvent<IIdpAdminTotpCodeEventDetail>('idp-admin-totp-disable', { code });
  }

  private regenerateBackupCodes() {
    const code = globalThis.prompt?.('Enter your current authenticator code to regenerate backup codes')?.trim();
    if (!code) {
      return;
    }
    this.dispatchShellEvent<IIdpAdminTotpCodeEventDetail>('idp-admin-backup-codes-regenerate', { code });
  }

  private requestPasskeyRegistration() {
    const fallbackLabel = typeof navigator !== 'undefined'
      ? navigator.userAgent.includes('Mobile') ? 'Mobile passkey' : 'Desktop passkey'
      : 'Passkey';
    const label = globalThis.prompt?.('Passkey label', fallbackLabel)?.trim();
    if (!label) {
      return;
    }
    this.dispatchShellEvent<IIdpAdminPasskeyRegistrationEventDetail>('idp-admin-passkey-register', { label });
  }

  private setPage(pageArg: TIdpAdminPage) {
    this.page = pageArg;
    this.orgMenuOpen = false;
    this.dispatchEvent(new CustomEvent<IIdpAdminNavigateEventDetail>('idp-admin-navigate', {
      detail: { page: pageArg },
      bubbles: true,
      composed: true,
    }));
  }

  private selectOrg(orgIdArg: string) {
    this.selectedOrgId = orgIdArg;
    this.orgMenuOpen = false;
    this.page = 'org-general';
    this.dispatchEvent(new CustomEvent<IIdpAdminOrgSelectEventDetail>('idp-admin-org-select', {
      detail: {
        orgId: orgIdArg,
        org: this.orgs.find((orgArg) => orgArg.id === orgIdArg) || null,
      },
      bubbles: true,
      composed: true,
    }));
  }

  private requestOrgCreate() {
    this.orgMenuOpen = false;
    this.dispatchEvent(new CustomEvent('idp-admin-org-create', {
      bubbles: true,
      composed: true,
    }));
  }

  private dispatchShellEvent<TDetail>(eventNameArg: string, detailArg: TDetail) {
    this.dispatchEvent(new CustomEvent<TDetail>(eventNameArg, {
      detail: detailArg,
      bubbles: true,
      composed: true,
    }));
  }

  private formatTimeAgo(timestampArg?: number): string {
    if (!timestampArg) {
      return 'unknown';
    }

    const diff = Date.now() - timestampArg;
    const minutes = Math.floor(diff / 60000);
    const hours = Math.floor(diff / 3600000);
    const days = Math.floor(diff / 86400000);

    if (minutes < 1) return 'just now';
    if (minutes < 60) return `${minutes}m ago`;
    if (hours < 24) return `${hours}h ago`;
    if (days < 7) return `${days}d ago`;
    return new Date(timestampArg).toLocaleDateString();
  }

  private renderStateCard(titleArg: string, descriptionArg: string, iconArg = 'box'): TemplateResult {
    return html`
      <div class="state-card">
        <span class="state-icon"><idp-icon name=${iconArg as any} size="17"></idp-icon></span>
        <div class="state-title">${titleArg}</div>
        <div class="state-description">${descriptionArg}</div>
      </div>
    `;
  }

  private renderDataState(emptyTitleArg: string, emptyDescriptionArg: string, iconArg = 'box'): TemplateResult | null {
    if (this.dataLoading) {
      return this.renderStateCard('Loading data', 'Fetching the latest account and organisation data.', 'cloud');
    }

    if (this.dataError) {
      return this.renderStateCard('Data unavailable', this.dataError, 'alert');
    }

    return this.renderStateCard(emptyTitleArg, emptyDescriptionArg, iconArg);
  }

  private roleVariant(roleArg: string): 'default' | 'accent' | 'ok' | 'warn' | 'error' | 'outline' {
    if (roleArg === 'owner') return 'accent';
    if (roleArg === 'admin') return 'warn';
    if (roleArg === 'editor') return 'ok';
    if (roleArg === 'outlaw') return 'error';
    return 'outline';
  }

  private get platformRoles(): IIdpAdminOrgRoleDefinition[] {
    return [
      { key: 'owner', name: 'Owner', description: 'Protected idp.global owner role.' },
      { key: 'admin', name: 'Admin', description: 'Protected idp.global admin role.' },
      { key: 'editor', name: 'Editor', description: 'Standard organisation role.' },
      { key: 'viewer', name: 'Viewer', description: 'Standard organisation role.' },
      { key: 'guest', name: 'Guest', description: 'Standard organisation role.' },
      { key: 'outlaw', name: 'Outlaw', description: 'Restricted organisation role.' },
    ];
  }

  private get availableOrgRoles(): IIdpAdminOrgRoleDefinition[] {
    const customRoles = this.orgRoleDefinitions || [];
    const customRoleKeys = new Set(customRoles.map((roleArg) => roleArg.key));
    return [
      ...this.platformRoles.filter((roleArg) => !customRoleKeys.has(roleArg.key)),
      ...customRoles,
    ];
  }

  private parseCsv(valueArg: string): string[] {
    return [...new Set(valueArg.split(',').map((entryArg) => entryArg.trim()).filter(Boolean))];
  }

  private normalizeRoleKey(valueArg: string): string {
    return valueArg.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
  }

  private closeDialog() {
    this.dialogMode = 'none';
    this.dialogError = '';
    this.dialogRoleKey = '';
    this.dialogRoleName = '';
    this.dialogRoleDescription = '';
    this.dialogRoleDeleteConfirmation = '';
    this.dialogMember = null;
    this.dialogMemberRoles = [];
    this.dialogApp = null;
    this.dialogAppMappings = [];
  }

  private openRoleUpsertDialog(roleArg?: IIdpAdminOrgRoleDefinition) {
    this.dialogMode = 'role-upsert';
    this.dialogError = '';
    this.dialogRoleKey = roleArg?.key || '';
    this.dialogRoleName = roleArg?.name || '';
    this.dialogRoleDescription = roleArg?.description || '';
  }

  private openRoleDeleteDialog(roleArg: IIdpAdminOrgRoleDefinition) {
    this.dialogMode = 'role-delete';
    this.dialogError = '';
    this.dialogRoleKey = roleArg.key;
    this.dialogRoleName = roleArg.name;
    this.dialogRoleDescription = roleArg.description || '';
    this.dialogRoleDeleteConfirmation = '';
  }

  private openMemberRolesDialog(memberArg: IIdpAdminMember) {
    this.dialogMode = 'member-roles';
    this.dialogError = '';
    this.dialogMember = memberArg;
    this.dialogMemberRoles = [...memberArg.roles];
  }

  private openAppRoleMappingsDialog(appArg: IIdpAdminApp) {
    this.dialogMode = 'app-role-mappings';
    this.dialogError = '';
    this.dialogApp = appArg;
    this.dialogAppMappings = this.availableOrgRoles.map((roleArg) => {
      const existingMapping = (appArg.roleMappings || []).find((mappingArg) => mappingArg.orgRoleKey === roleArg.key);
      return {
        orgRoleKey: roleArg.key,
        appRoles: [...(existingMapping?.appRoles || [])],
        permissions: [...(existingMapping?.permissions || [])],
        scopes: [...(existingMapping?.scopes || [])],
      };
    });
  }

  private setDialogRoleField(fieldArg: 'key' | 'name' | 'description' | 'deleteConfirmation', eventArg: Event) {
    const value = (eventArg.target as HTMLInputElement).value;
    if (fieldArg === 'key') this.dialogRoleKey = this.normalizeRoleKey(value);
    if (fieldArg === 'name') {
      this.dialogRoleName = value;
      if (!this.dialogRoleKey) {
        this.dialogRoleKey = this.normalizeRoleKey(value);
      }
    }
    if (fieldArg === 'description') this.dialogRoleDescription = value;
    if (fieldArg === 'deleteConfirmation') this.dialogRoleDeleteConfirmation = value;
    this.dialogError = '';
  }

  private toggleDialogMemberRole(roleKeyArg: string, checkedArg: boolean) {
    const nextRoles = new Set(this.dialogMemberRoles);
    if (checkedArg) {
      nextRoles.add(roleKeyArg);
    } else {
      nextRoles.delete(roleKeyArg);
    }
    this.dialogMemberRoles = [...nextRoles];
    this.dialogError = '';
  }

  private setDialogMappingField(roleKeyArg: string, fieldArg: 'appRoles' | 'permissions' | 'scopes', eventArg: Event) {
    const value = (eventArg.target as HTMLInputElement).value;
    this.dialogAppMappings = this.dialogAppMappings.map((mappingArg) => mappingArg.orgRoleKey === roleKeyArg
      ? { ...mappingArg, [fieldArg]: this.parseCsv(value) }
      : mappingArg
    );
    this.dialogError = '';
  }

  private submitRoleUpsertDialog() {
    const key = this.normalizeRoleKey(this.dialogRoleKey);
    const name = this.dialogRoleName.trim();
    if (!key || !name) {
      this.dialogError = 'Role key and name are required.';
      return;
    }
    this.dispatchShellEvent<IIdpAdminOrgRoleUpsertEventDetail>('idp-admin-org-role-upsert', {
      organizationId: this.currentOrg.id,
      roleDefinition: {
        key,
        name,
        description: this.dialogRoleDescription.trim(),
      },
    });
    this.closeDialog();
  }

  private submitRoleDeleteDialog() {
    const expectedText = `delete role ${this.dialogRoleKey}`;
    if (this.dialogRoleDeleteConfirmation.trim() !== expectedText) {
      this.dialogError = `Type ${expectedText} to confirm role deletion.`;
      return;
    }
    this.dispatchShellEvent<IIdpAdminOrgRoleDeleteEventDetail>('idp-admin-org-role-delete', {
      organizationId: this.currentOrg.id,
      roleKey: this.dialogRoleKey,
      confirmationText: this.dialogRoleDeleteConfirmation.trim(),
    });
    this.closeDialog();
  }

  private submitMemberRolesDialog() {
    if (!this.dialogMember) return;
    if (!this.dialogMemberRoles.length) {
      this.dialogError = 'At least one role is required.';
      return;
    }
    this.dispatchShellEvent<IIdpAdminMemberRolesEventDetail>('idp-admin-member-roles-update', {
      userId: this.dialogMember.userId,
      roles: this.dialogMemberRoles,
    });
    this.closeDialog();
  }

  private submitAppRoleMappingsDialog() {
    if (!this.dialogApp) return;
    const roleMappings = this.dialogAppMappings
      .map((mappingArg) => ({
        orgRoleKey: mappingArg.orgRoleKey,
        appRoles: [...mappingArg.appRoles],
        permissions: [...mappingArg.permissions],
        scopes: [...mappingArg.scopes],
      }))
      .filter((mappingArg) => mappingArg.appRoles.length || mappingArg.permissions.length || mappingArg.scopes.length);
    const allowedScopes = this.dialogApp.scopes || [];
    const invalidScopes = roleMappings.flatMap((mappingArg) => mappingArg.scopes).filter((scopeArg) => !allowedScopes.includes(scopeArg));
    if (invalidScopes.length) {
      this.dialogError = `Unsupported app scopes: ${[...new Set(invalidScopes)].join(', ')}.`;
      return;
    }
    this.dispatchShellEvent<IIdpAdminAppRoleMappingsEventDetail>('idp-admin-app-role-mappings-update', {
      organizationId: this.currentOrg.id,
      appId: this.dialogApp.id,
      roleMappings,
    });
    this.closeDialog();
  }

  private renderDialogError(): TemplateResult {
    return this.dialogError
      ? html`<div class="notice-card" style="border-color:var(--idp-error-border);background:var(--idp-error-bg)"><idp-icon name="alert" size="16" style="color:var(--idp-error)"></idp-icon><div><div class="section-title" style="color:var(--idp-error)">Action blocked</div><div class="muted">${this.dialogError}</div></div></div>`
      : html``;
  }

  private renderRoleUpsertDialog(): TemplateResult {
    const isEdit = this.orgRoleDefinitions.some((roleArg) => roleArg.key === this.dialogRoleKey);
    return html`
      <div class="dialog-card" @click=${(eventArg: Event) => eventArg.stopPropagation()}>
        <div class="dialog-head">
          <div><div class="dialog-title">${isEdit ? 'Edit custom role' : 'Add custom role'}</div><div class="muted">Custom roles model organisation-specific business access.</div></div>
          <button class="plain-button ghost" @click=${() => this.closeDialog()}>Close</button>
        </div>
        <div class="dialog-body">
          ${this.renderDialogError()}
          ${this.renderFormRow('Role name', 'Readable label shown to admins.', html`<input class="input" .value=${this.dialogRoleName} @input=${(eventArg: Event) => this.setDialogRoleField('name', eventArg)} />`, true)}
          ${this.renderFormRow('Role key', 'Lowercase stable identifier used in assignments and app mappings.', html`<input class="input" .value=${this.dialogRoleKey} @input=${(eventArg: Event) => this.setDialogRoleField('key', eventArg)} ?disabled=${isEdit} />`, true)}
          ${this.renderFormRow('Description', 'Optional admin note describing when this role should be used.', html`<textarea class="textarea" .value=${this.dialogRoleDescription} @input=${(eventArg: Event) => this.setDialogRoleField('description', eventArg)}></textarea>`)}
        </div>
        <div class="dialog-actions">
          <button class="plain-button outline" @click=${() => this.closeDialog()}>Cancel</button>
          <button class="plain-button primary" @click=${() => this.submitRoleUpsertDialog()}>Save role</button>
        </div>
      </div>
    `;
  }

  private renderRoleDeleteDialog(): TemplateResult {
    const expectedText = `delete role ${this.dialogRoleKey}`;
    return html`
      <div class="dialog-card" @click=${(eventArg: Event) => eventArg.stopPropagation()}>
        <div class="dialog-head">
          <div><div class="dialog-title">Delete custom role</div><div class="muted">This removes the role from member assignments and app mappings after backend confirmation.</div></div>
          <button class="plain-button ghost" @click=${() => this.closeDialog()}>Close</button>
        </div>
        <div class="dialog-body">
          ${this.renderDialogError()}
          <div class="notice-card" style="border-color:var(--idp-error-border);background:var(--idp-error-bg)"><idp-icon name="alert" size="16" style="color:var(--idp-error)"></idp-icon><div><div class="section-title" style="color:var(--idp-error)">${this.dialogRoleName}</div><div class="muted">Type <span class="mono">${expectedText}</span> to confirm deletion.</div></div></div>
          ${this.renderFormRow('Confirmation', `Type ${expectedText}`, html`<input class="input" .value=${this.dialogRoleDeleteConfirmation} @input=${(eventArg: Event) => this.setDialogRoleField('deleteConfirmation', eventArg)} />`, true)}
        </div>
        <div class="dialog-actions">
          <button class="plain-button outline" @click=${() => this.closeDialog()}>Cancel</button>
          <button class="plain-button destructive" @click=${() => this.submitRoleDeleteDialog()}>Delete role</button>
        </div>
      </div>
    `;
  }

  private renderMemberRolesDialog(): TemplateResult {
    const member = this.dialogMember;
    return html`
      <div class="dialog-card" @click=${(eventArg: Event) => eventArg.stopPropagation()}>
        <div class="dialog-head">
          <div><div class="dialog-title">Edit member roles</div><div class="muted">${member?.name || member?.email || 'Member'} receives the selected organisation roles.</div></div>
          <button class="plain-button ghost" @click=${() => this.closeDialog()}>Close</button>
        </div>
        <div class="dialog-body">
          ${this.renderDialogError()}
          <div class="role-grid">
            ${this.availableOrgRoles.map((roleArg) => {
              const checked = this.dialogMemberRoles.includes(roleArg.key);
              return html`
                <label class="role-option">
                  <input type="checkbox" .checked=${checked} @change=${(eventArg: Event) => this.toggleDialogMemberRole(roleArg.key, (eventArg.target as HTMLInputElement).checked)} />
                  <span><span class="section-title">${roleArg.name}</span><span class="muted" style="display:block">${roleArg.description || roleArg.key}</span></span>
                </label>
              `;
            })}
          </div>
        </div>
        <div class="dialog-actions">
          <button class="plain-button outline" @click=${() => this.closeDialog()}>Cancel</button>
          <button class="plain-button primary" @click=${() => this.submitMemberRolesDialog()}>Save roles</button>
        </div>
      </div>
    `;
  }

  private renderAppRoleMappingsDialog(): TemplateResult {
    const app = this.dialogApp;
    return html`
      <div class="dialog-card wide" @click=${(eventArg: Event) => eventArg.stopPropagation()}>
        <div class="dialog-head">
          <div><div class="dialog-title">Map organisation roles</div><div class="muted">Map ${this.currentOrg.name} roles to app-specific roles, permissions, and OAuth scopes for ${app?.name || 'this app'}.</div></div>
          <button class="plain-button ghost" @click=${() => this.closeDialog()}>Close</button>
        </div>
        <div class="dialog-body">
          ${this.renderDialogError()}
          ${app?.scopes?.length ? html`<div class="muted">Available OAuth scopes: <span class="mono">${app.scopes.join(', ')}</span></div>` : html`<div class="muted">This app has no declared OAuth scopes. Role and permission mappings are still supported.</div>`}
          ${this.dialogAppMappings.map((mappingArg) => {
            const role = this.availableOrgRoles.find((roleArg) => roleArg.key === mappingArg.orgRoleKey);
            return html`
              <div class="mapping-row">
                <div class="mapping-role">${role?.name || mappingArg.orgRoleKey}<span class="muted">${mappingArg.orgRoleKey}</span></div>
                <div>${this.renderFormRow('App roles', '', html`<input class="input" .value=${mappingArg.appRoles.join(', ')} @input=${(eventArg: Event) => this.setDialogMappingField(mappingArg.orgRoleKey, 'appRoles', eventArg)} />`)}</div>
                <div>${this.renderFormRow('Permissions', '', html`<input class="input" .value=${mappingArg.permissions.join(', ')} @input=${(eventArg: Event) => this.setDialogMappingField(mappingArg.orgRoleKey, 'permissions', eventArg)} />`)}</div>
                <div>${this.renderFormRow('Scopes', '', html`<input class="input" .value=${mappingArg.scopes.join(', ')} @input=${(eventArg: Event) => this.setDialogMappingField(mappingArg.orgRoleKey, 'scopes', eventArg)} />`)}</div>
              </div>
            `;
          })}
        </div>
        <div class="dialog-actions">
          <button class="plain-button outline" @click=${() => this.closeDialog()}>Cancel</button>
          <button class="plain-button primary" @click=${() => this.submitAppRoleMappingsDialog()}>Save mappings</button>
        </div>
      </div>
    `;
  }

  private renderDialog(): TemplateResult {
    if (this.dialogMode === 'none') {
      return html``;
    }

    const dialog = this.dialogMode === 'role-upsert'
      ? this.renderRoleUpsertDialog()
      : this.dialogMode === 'role-delete'
        ? this.renderRoleDeleteDialog()
        : this.dialogMode === 'member-roles'
          ? this.renderMemberRolesDialog()
          : this.renderAppRoleMappingsDialog();
    return html`<div class="dialog-backdrop" @click=${() => this.closeDialog()}>${dialog}</div>`;
  }

  private renderNavGroup(items: TNavItem[], active = ''): TemplateResult {
    return html`
      <div class="nav-list">
        ${items.map((item) => html`
          <button class="nav-item ${item.id === active ? 'active' : ''}" @click=${() => this.setPage(item.id)}>
            <idp-icon name=${item.icon as any} size="14"></idp-icon>
            ${item.label}
            ${item.badge ? html`<idp-badge variant="accent" style="margin-left:auto">${item.badge}</idp-badge>` : html``}
          </button>
        `)}
      </div>
    `;
  }

  private renderSidebar(): TemplateResult {
    const currentOrg = this.currentOrg;
    return html`
      <aside class="sidebar">
        <div class="logo-block">
          <div class="logo">
            <div class="logo-icon" aria-hidden="true">
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
              </svg>
            </div>
            <span class="logo-text">idp.global</span>
          </div>
        </div>
        <div class="nav-wrap">
          <div class="nav-section"><div class="nav-title">Workspace</div>${this.renderNavGroup(this.workspaceNav, this.page)}</div>
          <div class="nav-section"><div class="nav-title">My Account</div>${this.renderNavGroup(this.accountNav, this.page)}</div>
          <div class="nav-section">
            <div class="nav-title">Organisation</div>
            <div class="org-switch-wrap">
              <button class="org-switch ${this.orgMenuOpen ? 'open' : ''}" @click=${() => { this.orgMenuOpen = !this.orgMenuOpen; }}>
                <span class="org-avatar">${currentOrg.name.slice(0, 2).toUpperCase()}</span>
                <span class="org-name">${currentOrg.name}</span>
                <idp-icon name="chevron-down" size="10"></idp-icon>
              </button>
              ${this.orgMenuOpen ? html`
                <div class="org-menu">
                  <div class="org-menu-title">Switch organisation</div>
                  ${this.safeOrgs.map((orgArg) => html`
                    <button class="org-menu-item ${orgArg.id === this.selectedOrgId ? 'selected' : ''}" @click=${() => this.selectOrg(orgArg.id)}>
                      <span class="org-avatar">${orgArg.name.slice(0, 2).toUpperCase()}</span>
                      <span style="flex:1;min-width:0"><span class="org-menu-name">${orgArg.name}</span><span class="org-menu-role">${orgArg.myRole || 'member'}</span></span>
                      ${orgArg.id === this.selectedOrgId ? html`<idp-icon name="check" size="11"></idp-icon>` : html``}
                    </button>
                  `)}
                  <div class="org-menu-divider"></div>
                  <button class="org-create" @click=${() => this.requestOrgCreate()}><span class="org-create-icon"><idp-icon name="plus" size="9"></idp-icon></span>Create organisation</button>
                </div>
              ` : html``}
            </div>
            ${this.renderNavGroup(this.orgNav, this.page)}
          </div>
          <div class="nav-section"><div class="nav-title">Support</div>${this.renderNavGroup(this.supportNav, this.page)}</div>
          ${this.globalAdmin ? html`<div class="nav-section"><div class="nav-title">Global Admin</div>${this.renderNavGroup(this.adminNav, this.page)}</div>` : html``}
        </div>
        <div class="user-footer">
          <span class="user-avatar">${this.userInitials}</span>
          <div class="user-meta"><div class="user-name">${this.user?.name || 'Unknown User'}</div><div class="user-email">${this.user?.email || 'No email'}</div></div>
        </div>
      </aside>
    `;
  }

  private renderSparkline(data: number[], color: string): TemplateResult {
    const max = Math.max(...data);
    const min = Math.min(...data);
    const range = max - min || 1;
    const width = 100;
    const height = 22;
    const points = data.map((valueArg, indexArg) => {
      const x = (indexArg / (data.length - 1)) * width;
      const y = height - ((valueArg - min) / range) * (height - 4) - 2;
      return `${x},${y}`;
    }).join(' ');
    const area = `0,${height} ${points} ${width},${height}`;

    return html`
      <svg viewBox="0 0 ${width} ${height}" preserveAspectRatio="none">
        <polygon points=${area} fill=${color} opacity="0.12"></polygon>
        <polyline points=${points} fill="none" stroke=${color} stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></polyline>
      </svg>
    `;
  }

  private renderKpi(kpi: TKpi): TemplateResult {
    return html`
      <div class="kpi" style="--kpi-accent:${kpi.accent}">
        <div class="kpi-label">${kpi.label}</div>
        <div class="kpi-value">${kpi.value}${kpi.unit ? html`<span>${kpi.unit}</span>` : html``}</div>
        <div class="kpi-sub">${kpi.sub}</div>
        <div class="kpi-foot">
          <span class="delta">${kpi.deltaKind === 'live' ? html`<span class="live-dot"></span>` : html``}${kpi.delta}</span>
          <div class="sparkline">${this.renderSparkline(kpi.spark, kpi.sparkColor)}</div>
        </div>
      </div>
    `;
  }

  private renderPageHeader(titleArg: string, descriptionArg: string, actionArg?: TemplateResult): TemplateResult {
    return html`
      <header class="page-head">
        <div><h1>${titleArg}</h1><div class="lead">${descriptionArg}</div></div>
        ${actionArg ? html`<div class="page-actions">${actionArg}</div>` : html``}
      </header>
    `;
  }

  private renderSectionCard(titleArg: string, descriptionArg: string, contentArg: TemplateResult, actionArg?: TemplateResult): TemplateResult {
    return html`
      <section class="section-card">
        ${titleArg || actionArg ? html`
          <div class="section-headline">
            <div><div class="section-title">${titleArg}</div>${descriptionArg ? html`<div class="section-description">${descriptionArg}</div>` : html``}</div>
            ${actionArg}
          </div>
        ` : html``}
        ${contentArg}
      </section>
    `;
  }

  private renderFormRow(labelArg: string, hintArg: string, contentArg: TemplateResult, requiredArg = false): TemplateResult {
    return html`
      <div class="form-row">
        <div><div class="form-label">${labelArg}${requiredArg ? html`<span style="color:var(--idp-error)">*</span>` : html``}</div>${hintArg ? html`<div class="form-hint">${hintArg}</div>` : html``}</div>
        <div>${contentArg}</div>
      </div>
    `;
  }

  private renderCodeBlock(valueArg: string): TemplateResult {
    return html`<div class="code-block"><code>${valueArg}</code><idp-icon name="copy" size="13"></idp-icon></div>`;
  }

  private renderOverview(): TemplateResult {
    const firstName = this.user?.name?.split(' ')[0] || 'there';
    const activeSessionCount = this.sessions.filter((sessionArg) => sessionArg.isCurrent).length || this.sessions.length;
    const activePassportCount = this.passportDevices.filter((deviceArg) => deviceArg.status === 'active').length;
    const kpis: TKpi[] = [
      {
        label: 'Organisations',
        value: String(this.orgs.length),
        delta: 'live',
        deltaKind: 'live',
        spark: [1, 1, 1, 1, 1, 1, 1, Math.max(this.orgs.length, 1)],
        sparkColor: 'var(--idp-spark-info)',
        accent: 'var(--idp-chart-1)',
        sub: 'Memberships visible to this account',
      },
      {
        label: 'Sessions',
        value: String(this.sessions.length),
        delta: `${activeSessionCount} active`,
        deltaKind: 'up',
        spark: [1, 2, 1, 2, 3, 2, Math.max(this.sessions.length, 1)],
        sparkColor: 'var(--idp-spark-up)',
        accent: 'var(--idp-chart-2)',
        sub: this.sessions.length ? `Latest ${this.formatTimeAgo(Math.max(...this.sessions.map((sessionArg) => sessionArg.lastActive)))}` : 'No active sessions loaded',
      },
      {
        label: 'Passport devices',
        value: String(this.passportDevices.length),
        delta: `${activePassportCount} active`,
        deltaKind: 'live',
        spark: [1, 1, 1, Math.max(this.passportDevices.length, 1)],
        sparkColor: 'var(--idp-spark-info)',
        accent: 'var(--idp-chart-5)',
        sub: 'Cryptographic credentials',
      },
      {
        label: 'Activity',
        value: String(this.activities.length),
        delta: 'live',
        deltaKind: 'live',
        spark: [1, 2, 2, 3, Math.max(this.activities.length, 1)],
        sparkColor: 'var(--idp-spark-info)',
        accent: 'var(--idp-info)',
        sub: 'Recent account events',
      },
    ];
    return html`
      <header class="page-head">
        <div>
          <div class="eyebrow-row"><span class="eyebrow">Account - Overview</span><span class="live-pill"><span class="live-dot"></span>live</span></div>
          <h1>Good morning, ${firstName}.</h1>
          <div class="lead">Account-wide operational snapshot using live identity data.</div>
        </div>
        <div class="page-actions"><button class="plain-button outline" @click=${() => this.setPage('sessions')}>Review sessions</button><button class="plain-button primary" @click=${() => this.setPage('security')}>Manage security</button></div>
      </header>
      <div class="body">
        <div class="kpis">${kpis.map((kpiArg) => this.renderKpi(kpiArg))}</div>
        ${this.dataLoading || this.dataError ? this.renderDataState('No overview data', 'The console has not received live account data yet.', 'activity') : html``}
        <div class="primary-grid">
          <div class="card">
            <div class="card-head"><span class="card-title">Recent activity</span><idp-badge>${this.activities.length} events</idp-badge></div>
            ${this.activities.length ? this.activities.slice(0, 8).map((activityArg) => html`
              <div class="feed-item"><span class="feed-dot" style="--dot-color:var(--idp-info)"></span><div class="feed-text"><strong>${activityArg.action.replace(/_/g, ' ')}</strong> - ${activityArg.description}</div><span class="feed-meta">${this.formatTimeAgo(activityArg.timestamp)}</span></div>
            `) : this.renderStateCard('No activity yet', 'Activity events will appear here after logins, app changes, and organisation updates.', 'activity')}
          </div>
          <div class="card">
            <div class="card-head"><span class="card-title">Current sessions</span><idp-badge>${this.sessions.length} total</idp-badge></div>
            ${this.sessions.length ? this.sessions.slice(0, 5).map((sessionArg) => html`
              <div class="feed-item"><span class="feed-dot" style="--dot-color:${sessionArg.isCurrent ? 'var(--idp-ok)' : 'var(--idp-muted-fg)'}"></span><div class="feed-text"><strong>${sessionArg.deviceName || 'Unknown device'}</strong> - <span class="mono">${sessionArg.browser} ${sessionArg.os}</span></div><span class="feed-meta">${this.formatTimeAgo(sessionArg.lastActive)}</span></div>
            `) : this.renderStateCard('No sessions loaded', 'Active session telemetry is unavailable or there are no sessions.', 'monitor')}
          </div>
        </div>
      </div>
    `;
  }

  private renderProfile(): TemplateResult {
    const username = this.user?.username || this.user?.email?.split('@')[0] || '';
    const status = this.user?.status || 'active';
    return html`
      ${this.renderPageHeader('Profile', 'Your personal identity details visible to connected apps.')}
      <div class="body narrow-body">
        ${this.renderSectionCard('Avatar', 'Shown to apps that request your profile.', html`<div class="split-row" style="justify-content:flex-start"><span class="avatar" style="width:56px;height:56px;font-size:20px">${this.userInitials}</span><div><div class="section-title">${this.user?.name || 'Unknown User'}</div><div class="muted" style="margin-top:5px">Profile update endpoints are not exposed in this console yet.</div></div></div>`)}
        ${this.renderSectionCard('Personal information', '', html`
          ${this.renderFormRow('Full name', '', html`<input class="input" .value=${this.user?.name || ''} disabled />`, true)}
          ${this.renderFormRow('Username', 'Used in your public profile URL', html`<div class="input-group"><span class="input-prefix">idp.global/user/</span><input class="input" .value=${username} disabled /></div>`)}
          ${this.renderFormRow('Email', 'Primary address for login and notifications', html`<input class="input" .value=${this.user?.email || ''} disabled />`)}
          ${this.renderFormRow('Mobile number', 'Used for SMS verification', html`<input class="input" .value=${this.user?.mobileNumber || ''} disabled />`)}
        `)}
        ${this.renderSectionCard('Account status', '', html`<div class="split-row"><div><div class="section-title">Status</div><div class="muted">Your account is currently ${status}.</div></div><idp-badge variant=${status === 'active' ? 'ok' : status === 'suspended' ? 'error' : 'warn'}>${status}</idp-badge></div>${this.globalAdmin ? html`<div class="divider"></div><div class="split-row"><div><div class="section-title">Global admin</div><div class="muted">You have platform-wide administrative access.</div></div><idp-badge variant="accent">Admin</idp-badge></div>` : html``}`)}
      </div>
    `;
  }

  private renderSecurity(): TemplateResult {
    const passportRows = this.passportDevices.map((deviceArg) => ({
      cells: [
        html`
          <div class="identity-cell">
            <span class="identity-avatar">${this.getInitials(deviceArg.label)}</span>
            <div>
              <div class="identity-primary">${deviceArg.label}</div>
              <div class="identity-secondary">${deviceArg.platform}${deviceArg.appVersion ? ` - ${deviceArg.appVersion}` : ''}</div>
            </div>
          </div>
        `,
        html`<idp-badge variant=${deviceArg.status === 'active' ? 'ok' : 'error'}>${deviceArg.status}</idp-badge>`,
        [
          deviceArg.capabilities?.push ? 'push' : '',
          deviceArg.capabilities?.nfc ? 'nfc' : '',
          deviceArg.capabilities?.gps ? 'gps' : '',
        ].filter(Boolean).join(', ') || '-',
        deviceArg.lastSeenAt ? this.formatTimeAgo(deviceArg.lastSeenAt) : 'never',
        html`<div class="cell-actions"><button class="table-action destructive" @click=${() => this.dispatchShellEvent<IIdpAdminPassportDeviceEventDetail>('idp-admin-passport-revoke', { deviceId: deviceArg.id })}>Revoke</button></div>`,
      ],
    }));
    const passkeyRows = this.passkeys.map((passkeyArg) => ({
      cells: [
        html`
          <div class="identity-cell">
            <span class="identity-avatar">${this.getInitials(passkeyArg.label)}</span>
            <div>
              <div class="identity-primary">${passkeyArg.label}</div>
              <div class="identity-secondary">${passkeyArg.deviceType || 'passkey'}${passkeyArg.backedUp ? ' - backed up' : ''}</div>
            </div>
          </div>
        `,
        html`<idp-badge variant=${passkeyArg.status === 'active' ? 'ok' : 'error'}>${passkeyArg.status}</idp-badge>`,
        (passkeyArg.transports || []).join(', ') || '-',
        passkeyArg.lastUsedAt ? this.formatTimeAgo(passkeyArg.lastUsedAt) : 'never',
        html`<div class="cell-actions"><button class="table-action destructive" @click=${() => this.dispatchShellEvent<IIdpAdminPasskeyEventDetail>('idp-admin-passkey-revoke', { passkeyId: passkeyArg.id })}>Revoke</button></div>`,
      ],
    }));

    return html`
      ${this.renderPageHeader('Security', 'Manage how you authenticate and protect your account.')}
      <div class="body wide-body">
        ${this.credentialMessage ? html`<div class="notice-card"><idp-icon name="shield" size="16"></idp-icon><div><div class="section-title">Credential update</div><div class="muted">${this.credentialMessage}</div></div></div>` : html``}
        ${this.credentialError ? html`<div class="notice-card" style="border-color:var(--idp-error-border);background:var(--idp-error-bg)"><idp-icon name="alert" size="16" style="color:var(--idp-error)"></idp-icon><div><div class="section-title" style="color:var(--idp-error)">Credential error</div><div class="muted">${this.credentialError}</div></div></div>` : html``}
        ${this.renderSectionCard('Session security', 'Live session data is available and sessions can be revoked from the device list.', html`
          <div class="split-row"><div><div class="section-title">Active sessions</div><div class="muted">Review and revoke sessions from the Sessions & Devices page.</div></div><idp-badge variant="accent">${this.sessions.length}</idp-badge></div>
          <div class="divider"></div>
          <button class="plain-button outline" @click=${() => this.setPage('sessions')}>Open sessions</button>
        `)}
        ${this.renderSectionCard('Password', 'Change your password using the existing production password endpoint. All fields are required.', html`
          ${this.renderFormRow('Current password', '', html`<input class="input" type="password" autocomplete="current-password" .value=${this.currentPassword} @input=${(eventArg: Event) => this.setPasswordField('current', eventArg)} />`, true)}
          ${this.renderFormRow('New password', 'Minimum 12 characters.', html`<input class="input" type="password" autocomplete="new-password" .value=${this.newPassword} @input=${(eventArg: Event) => this.setPasswordField('new', eventArg)} />`, true)}
          ${this.renderFormRow('Confirm password', '', html`<input class="input" type="password" autocomplete="new-password" .value=${this.confirmPassword} @input=${(eventArg: Event) => this.setPasswordField('confirm', eventArg)} />`, true)}
          <div class="divider"></div>
          <button class="plain-button primary" @click=${() => this.submitPasswordChange()}>Update password</button>
        `)}
        ${this.renderSectionCard('Authenticator app', 'TOTP protects password and magic-link sign-ins with a six-digit code from an authenticator app.', html`
          <div class="split-row"><div><div class="section-title">Status</div><div class="muted">${this.totpEnabled ? `${this.backupCodesRemaining} backup codes remaining.` : 'No authenticator app is enrolled.'}</div></div><idp-badge variant=${this.totpEnabled ? 'ok' : 'warn'}>${this.totpEnabled ? 'Enabled' : 'Disabled'}</idp-badge></div>
          ${this.totpEnrollment ? html`
            <div class="divider"></div>
            <div class="notice-card"><idp-icon name="qr" size="16"></idp-icon><div><div class="section-title">TOTP setup pending</div><div class="muted">Scan the otpauth URL or enter the manual secret, then verify the current code.</div></div></div>
            ${this.renderFormRow('Manual secret', '', this.renderCodeBlock(this.totpEnrollment.secret))}
            ${this.renderFormRow('otpauth URL', '', this.renderCodeBlock(this.totpEnrollment.otpauthUrl))}
            <button class="plain-button primary" @click=${() => this.verifyTotpEnrollment()}>Verify setup code</button>
          ` : html`
            <div class="divider"></div>
            ${this.totpEnabled ? html`
              <div class="cell-actions" style="justify-content:flex-start">
                <button class="plain-button outline" @click=${() => this.regenerateBackupCodes()}>Regenerate backup codes</button>
                <button class="plain-button ghost" style="color:var(--idp-error)" @click=${() => this.disableTotp()}>Disable TOTP</button>
              </div>
            ` : html`<button class="plain-button primary" @click=${() => this.requestTotpEnrollment()}>Enable authenticator app</button>`}
          `}
        `)}
        <idp-data-table
          title="Passkeys"
          subtitle="WebAuthn credentials for passwordless login and MFA step-up."
          badge=${`${this.passkeys.length} total`}
          empty-title="No passkeys"
          empty-description="Register a passkey to sign in with platform authenticators or security keys."
          .columns=${[
            { label: 'Passkey' },
            { label: 'Status' },
            { label: 'Transports', hideBelow: 'mobile' },
            { label: 'Last used', mono: true, hideBelow: 'mobile' },
            { label: 'Action', align: 'right' },
          ]}
          .rows=${passkeyRows}
        ></idp-data-table>
        <div class="section-card">
          <div class="section-headline">
            <div><div class="section-title">Register passkey</div><div class="section-description">Creates a WebAuthn registration challenge in this browser.</div></div>
            <button class="plain-button outline" @click=${() => this.requestPasskeyRegistration()}>Register passkey</button>
          </div>
        </div>
        <idp-data-table
          title="Passport devices"
          subtitle="Cryptographic IDP Passport devices registered for this account."
          badge=${`${this.passportDevices.length} total`}
          empty-title="No passport devices"
          empty-description="Passport devices will appear here after enrollment from the mobile passport app."
          .columns=${[
            { label: 'Device' },
            { label: 'Status' },
            { label: 'Capabilities', hideBelow: 'mobile' },
            { label: 'Last seen', mono: true, hideBelow: 'mobile' },
            { label: 'Action', align: 'right' },
          ]}
          .rows=${passportRows}
        ></idp-data-table>
        <div class="section-card">
          <div class="section-headline">
            <div><div class="section-title">Enroll passport device</div><div class="section-description">Creates a signed enrollment challenge for the IDP Passport device flow.</div></div>
            <button class="plain-button outline" @click=${() => this.requestPassportEnrollment()}>Create challenge</button>
          </div>
          ${this.passportEnrollment ? html`
            <div class="notice-card" style="margin-bottom:14px"><idp-icon name="key" size="16"></idp-icon><div><div class="section-title">Enrollment challenge ready</div><div class="muted">Expires ${new Date(this.passportEnrollment.expiresAt).toLocaleString()}. Use the pairing token or payload in a passport client to complete enrollment.</div></div></div>
            ${this.renderFormRow('Pairing token', '', this.renderCodeBlock(this.passportEnrollment.pairingToken))}
            ${this.renderFormRow('Pairing payload', '', this.renderCodeBlock(this.passportEnrollment.pairingPayload))}
            ${this.renderFormRow('Signing payload', '', this.renderCodeBlock(this.passportEnrollment.signingPayload))}
          ` : html`<div class="muted">No active enrollment challenge.</div>`}
        </div>
      </div>
    `;
  }

  private renderSessions(): TemplateResult {
    return html`
      ${this.renderPageHeader('Sessions & Devices', 'Active login sessions across all your devices.')}
      <div class="body narrow-body"><div class="list-stack">
        ${this.sessions.length ? this.sessions.map((sessionArg) => html`
          <div class="row-card">
            <span class="icon-tile"><idp-icon name="monitor" size="17"></idp-icon></span>
            <div style="flex:1">
              <div style="display:flex;gap:8px;align-items:center"><span class="section-title">${sessionArg.deviceName || 'Unknown device'}</span>${sessionArg.isCurrent ? html`<idp-badge variant="ok">Current session</idp-badge>` : html``}</div>
              <div class="muted">${sessionArg.browser || 'Unknown browser'} - ${sessionArg.os || 'Unknown OS'} - IP: <span class="mono">${sessionArg.ip || 'unknown'}</span></div>
              <div class="muted">Started ${this.formatTimeAgo(sessionArg.createdAt)} - Active ${this.formatTimeAgo(sessionArg.lastActive)}</div>
            </div>
            ${sessionArg.isCurrent ? html`` : html`<button class="plain-button ghost" style="color:var(--idp-error)" @click=${() => this.dispatchShellEvent<IIdpAdminSessionEventDetail>('idp-admin-session-revoke', { sessionId: sessionArg.id })}>Revoke</button>`}
          </div>
        `) : this.renderDataState('No sessions', 'There are no active sessions for this account or session telemetry is unavailable.', 'monitor')}
      </div></div>
    `;
  }

  private renderAccountApps(): TemplateResult {
    const apps = this.accountApps;
    return html`
      ${this.renderPageHeader('Connected Apps', 'Third-party apps and services that have OAuth access to your account.')}
      <div class="body narrow-body"><div class="list-stack">
        ${apps.length ? apps.map((appArg) => html`<div class="row-card"><span class="app-avatar">${appArg.name.slice(0, 2).toUpperCase()}</span><div style="flex:1"><div class="section-title" style="margin-bottom:4px">${appArg.name}</div><div class="chip-row">${(appArg.scopes || []).map((scopeArg) => html`<idp-badge variant="outline">${scopeArg}</idp-badge>`)}</div><div class="muted" style="margin-top:6px">${appArg.description || appArg.appUrl || 'Connected application'}</div></div><button class="plain-button ghost" style="color:var(--idp-error)" @click=${() => this.dispatchShellEvent<IIdpAdminAppToggleEventDetail>('idp-admin-app-toggle', { appId: appArg.id, connected: false })}>Revoke</button></div>`) : this.renderDataState('Account app grants not connected', 'No account-level OAuth grant endpoint is wired into this app yet. Organisation app connections are managed under Organisation > OAuth Apps.', 'grid')}
      </div></div>
    `;
  }

  private renderOrgGeneral(): TemplateResult {
    const org = this.currentOrg;
    const connectedAppCount = this.orgApps.filter((appArg) => appArg.isConnected).length;
    const orgActivities = this.activities
      .filter((activityArg) => {
        const searchableText = `${activityArg.targetType || ''} ${activityArg.description || ''}`.toLowerCase();
        return Boolean(org.slug && searchableText.includes(org.slug.toLowerCase()))
          || Boolean(org.name && searchableText.includes(org.name.toLowerCase()))
          || searchableText.includes('org')
          || searchableText.includes('organization')
          || searchableText.includes('organisation');
      })
      .slice(0, 5);
    const kpis: TKpi[] = [
      {
        label: 'Members',
        value: String(this.orgMembers.length),
        delta: `${this.orgInvitations.length} pending`,
        deltaKind: 'up',
        spark: [1, 1, 2, Math.max(this.orgMembers.length, 1)],
        sparkColor: 'var(--idp-spark-up)',
        accent: 'var(--idp-chart-5)',
        sub: org.name,
      },
      {
        label: 'Connected apps',
        value: String(connectedAppCount),
        delta: `${this.orgApps.length} available`,
        deltaKind: 'live',
        spark: [1, 2, 2, Math.max(connectedAppCount, 1)],
        sparkColor: 'var(--idp-spark-info)',
        accent: 'var(--idp-info)',
        sub: 'Organisation OAuth catalogue',
      },
      {
        label: 'Role',
        value: org.myRole || 'member',
        delta: 'live',
        deltaKind: 'live',
        spark: [1, 1, 1, 1],
        sparkColor: 'var(--idp-spark-info)',
        accent: 'var(--idp-chart-1)',
        sub: 'Your access level',
      },
      {
        label: 'Activity',
        value: String(orgActivities.length),
        delta: `${this.activities.length} account events`,
        deltaKind: 'up',
        spark: [1, 1, Math.max(orgActivities.length, 1)],
        sparkColor: 'var(--idp-spark-up)',
        accent: 'var(--idp-chart-2)',
        sub: 'Recent org-related events',
      },
    ];

    return html`
      <header class="page-head">
        <div>
          <div class="eyebrow-row"><span class="eyebrow">Organisation - General</span><span class="live-pill"><span class="live-dot"></span>live</span></div>
          <h1>${org.name}</h1>
          <div class="lead">Selected organisation dashboard for <code>${org.slug ? `@${org.slug}` : org.id || 'no organisation selected'}</code>.</div>
        </div>
        <div class="page-actions"><button class="plain-button outline" @click=${() => this.setPage('org-members')}>Manage members</button><button class="plain-button primary" @click=${() => this.setPage('org-apps')}>Manage apps</button></div>
      </header>
      <div class="body">
        <div class="kpis">${kpis.map((kpiArg) => this.renderKpi(kpiArg))}</div>
        ${this.dataLoading || this.dataError ? this.renderDataState('No organisation data', 'The console has not received live organisation data yet.', 'building') : html``}
        <div class="primary-grid">
          <div class="card">
            <div class="card-head"><span class="card-title">Organisation profile</span><idp-badge>${org.myRole || 'member'}</idp-badge></div>
            <div style="padding:16px">
              <div style="display:flex;align-items:center;gap:14px;margin-bottom:16px"><span class="org-avatar" style="width:48px;height:48px;font-size:17px">${org.name.slice(0, 2).toUpperCase()}</span><div><div style="font-size:16px;font-weight:600;color:var(--idp-fg)">${org.name}</div><div class="mono">idp.global/org/${org.slug || 'unassigned'}</div></div></div>
              ${this.renderCodeBlock(org.id || 'No organisation selected')}
              <div class="muted" style="margin-top:10px">Rename, slug updates, ownership transfer, and deletion are available from Settings with server-side audited confirmation.</div>
            </div>
          </div>
          <div class="card">
            <div class="card-head"><span class="card-title">Recent organisation activity</span><idp-badge>${orgActivities.length} events</idp-badge></div>
            ${orgActivities.length ? orgActivities.map((activityArg) => html`
              <div class="feed-item"><span class="feed-dot" style="--dot-color:var(--idp-info)"></span><div class="feed-text"><strong>${activityArg.action.replace(/_/g, ' ')}</strong> - ${activityArg.description}</div><span class="feed-meta">${this.formatTimeAgo(activityArg.timestamp)}</span></div>
            `) : this.renderStateCard('No org activity yet', 'Organisation-related activity will appear here when the backend reports matching activity events.', 'activity')}
          </div>
        </div>
      </div>
    `;
  }

  private renderOrgSettings(): TemplateResult {
    const org = this.currentOrg;
    this.syncOrgSettingsState();
    const transferCandidates = this.orgMembers.filter((memberArg) => !memberArg.isCurrentUser);
    const transferConfirmText = `transfer ${org.slug}`;
    const deleteConfirmText = `delete ${org.slug}`;
    const identityChanged = this.orgNameDraft.trim() !== org.name || this.orgSlugDraft.trim().toLowerCase() !== org.slug;
    return html`
      ${this.renderPageHeader('Organisation Settings', 'Audited configuration and owner-controlled destructive operations.')}
      <div class="body narrow-body">
        ${this.orgSettingsError ? html`<div class="notice-card" style="border-color:var(--idp-error-border);background:var(--idp-error-bg)"><idp-icon name="alert" size="16" style="color:var(--idp-error)"></idp-icon><div><div class="section-title" style="color:var(--idp-error)">Settings action blocked</div><div class="muted">${this.orgSettingsError}</div></div></div>` : html``}
        ${this.renderSectionCard('Organisation identity', `Type ${org.slug} to confirm name or slug changes. The backend verifies this confirmation before saving.`, html`
          <div style="display:flex;align-items:center;gap:14px;margin-bottom:20px"><span class="org-avatar" style="width:48px;height:48px;font-size:17px">${org.name.slice(0, 2).toUpperCase()}</span><div><div style="font-size:16px;font-weight:600;color:var(--idp-fg)">${org.name}</div><div class="mono">idp.global/org/${org.slug}</div></div></div>
          ${this.renderFormRow('Organisation name', '', html`<input class="input" .value=${this.orgNameDraft} @input=${(eventArg: Event) => this.setOrgSettingsField('name', eventArg)} />`, true)}
          ${this.renderFormRow('URL slug', "Used in your org's public URLs.", html`<div class="input-group"><span class="input-prefix">idp.global/org/</span><input class="input" .value=${this.orgSlugDraft} @input=${(eventArg: Event) => this.setOrgSettingsField('slug', eventArg)} /></div>`)}
          ${this.renderFormRow('Confirmation', `Type ${org.slug}`, html`<input class="input" .value=${this.orgSettingsConfirmation} @input=${(eventArg: Event) => this.setOrgSettingsField('settingsConfirmation', eventArg)} />`)}
          <div class="divider"></div>
          <button class="plain-button primary" ?disabled=${!identityChanged} @click=${() => this.submitOrgSettingsUpdate()}>Apply identity changes</button>
        `)}
        ${this.renderSectionCard('Organisation ID', 'Use this identifier when making API calls.', this.renderCodeBlock(org.id))}
        ${this.renderSectionCard('Transfer ownership', `Owner-only operation. Type ${transferConfirmText} to confirm.`, html`
          ${transferCandidates.length ? html`
            ${this.renderFormRow('New owner', 'The target user must already be an organisation member.', html`
              <select class="select" .value=${this.transferOwnerId} @change=${(eventArg: Event) => this.setOrgSettingsField('transferOwnerId', eventArg)}>
                <option value="">Select a member</option>
                ${transferCandidates.map((memberArg) => html`<option value=${memberArg.userId}>${memberArg.name || memberArg.email} (${memberArg.email})</option>`)}
              </select>
            `, true)}
            ${this.renderFormRow('Confirmation', `Type ${transferConfirmText}`, html`<input class="input" .value=${this.transferConfirmation} @input=${(eventArg: Event) => this.setOrgSettingsField('transferConfirmation', eventArg)} />`)}
            <div class="divider"></div>
            <button class="plain-button outline" @click=${() => this.submitOrgTransfer()}>Transfer ownership</button>
          ` : this.renderStateCard('No transfer candidates loaded', 'Load organisation members before transferring ownership.', 'users')}
        `)}
        ${this.renderSectionCard('Delete organisation', `Owner-only destructive operation. Type ${deleteConfirmText} to permanently remove this organisation, its memberships, pending invitations, billing records, and app connections.`, html`
          ${this.renderFormRow('Confirmation', `Type ${deleteConfirmText}`, html`<input class="input" .value=${this.deleteConfirmation} @input=${(eventArg: Event) => this.setOrgSettingsField('deleteConfirmation', eventArg)} />`)}
          <div class="divider"></div>
          <button class="plain-button destructive" @click=${() => this.submitOrgDelete()}>Delete organisation</button>
        `)}
      </div>
    `;
  }

  private renderOrgMembers(): TemplateResult {
    const customRoleRows = this.orgRoleDefinitions.map((roleArg) => ({
      cells: [
        html`
          <div class="identity-cell">
            <span class="identity-avatar">${this.getInitials(roleArg.name)}</span>
            <div>
              <div class="identity-primary">${roleArg.name}</div>
              <div class="identity-secondary">${roleArg.description || 'Custom organisation role'}</div>
            </div>
          </div>
        `,
        roleArg.key,
        html`
          <div class="cell-actions">
            <button class="table-action" @click=${() => this.openRoleUpsertDialog(roleArg)}>Edit</button>
            <button class="table-action destructive" @click=${() => this.openRoleDeleteDialog(roleArg)}>Delete</button>
          </div>
        `,
      ],
    }));
    const memberRows = this.orgMembers.map((memberArg) => ({
      cells: [
        html`
          <div class="identity-cell">
            <span class="identity-avatar">${this.getInitials(memberArg.name || memberArg.email)}</span>
            <div>
              <div class="identity-primary">${memberArg.name || memberArg.email}</div>
              <div class="identity-secondary">${memberArg.email}</div>
            </div>
          </div>
        `,
        html`<div class="chip-row">${memberArg.roles.map((roleArg) => html`<idp-badge variant=${this.roleVariant(roleArg)}>${roleArg}</idp-badge>`)}</div>`,
        memberArg.isCurrentUser ? html`<idp-badge variant="accent">You</idp-badge>` : html`<idp-badge variant="outline">Member</idp-badge>`,
        memberArg.roles.includes('owner') || memberArg.isCurrentUser
          ? html`<div class="cell-actions"><button class="table-action" @click=${() => this.openMemberRolesDialog(memberArg)}>Edit roles</button></div>`
          : html`<div class="cell-actions"><button class="table-action" @click=${() => this.openMemberRolesDialog(memberArg)}>Edit roles</button><button class="table-action destructive" @click=${() => this.dispatchShellEvent<IIdpAdminMemberEventDetail>('idp-admin-member-remove', { userId: memberArg.userId })}>Remove</button></div>`,
      ],
    }));
    const invitationRows = this.orgInvitations.map((inviteArg) => ({
      cells: [
        html`
          <div class="identity-cell">
            <span class="identity-avatar">${this.getInitials(inviteArg.email)}</span>
            <div>
              <div class="identity-primary">${inviteArg.email}</div>
              <div class="identity-secondary">Invited ${this.formatTimeAgo(inviteArg.invitedAt)}</div>
            </div>
          </div>
        `,
        html`<div class="chip-row">${inviteArg.roles.map((roleArg) => html`<idp-badge variant=${this.roleVariant(roleArg)}>${roleArg}</idp-badge>`)}</div>`,
        new Date(inviteArg.expiresAt).toLocaleDateString(),
        html`
          <div class="cell-actions">
            <button class="table-action" @click=${() => this.dispatchShellEvent<IIdpAdminInvitationEventDetail>('idp-admin-invitation-resend', { invitationId: inviteArg.id })}>Resend</button>
            <button class="table-action destructive" @click=${() => this.dispatchShellEvent<IIdpAdminInvitationEventDetail>('idp-admin-invitation-cancel', { invitationId: inviteArg.id })}>Cancel</button>
          </div>
        `,
      ],
    }));

    return html`
      ${this.renderPageHeader('Members', `${this.orgMembers.length} members - ${this.orgInvitations.length} pending invitations`, html`<button class="plain-button primary" @click=${() => this.dispatchShellEvent('idp-admin-member-invite', { orgId: this.currentOrg.id })}><idp-icon name="plus" size="12"></idp-icon>Invite member</button>`)}
      <div class="body wide-body">
        ${this.orgSettingsError ? html`<div class="notice-card" style="border-color:var(--idp-error-border);background:var(--idp-error-bg)"><idp-icon name="alert" size="16" style="color:var(--idp-error)"></idp-icon><div><div class="section-title" style="color:var(--idp-error)">Role action blocked</div><div class="muted">${this.orgSettingsError}</div></div></div>` : html``}
        <idp-data-table
          title="Custom organisation roles"
          subtitle="Business roles defined by this organisation. Platform roles like owner/admin remain protected."
          badge=${`${this.orgRoleDefinitions.length} custom`}
          empty-title="No custom roles"
          empty-description="Create custom roles such as Finance, Support Lead, or Contractor, then assign them to members."
          .columns=${[
            { label: 'Role' },
            { label: 'Key', mono: true },
            { label: 'Action', align: 'right' },
          ]}
          .rows=${customRoleRows}
        ></idp-data-table>
        <div><button class="plain-button outline" @click=${() => this.openRoleUpsertDialog()}><idp-icon name="plus" size="12"></idp-icon>Add custom role</button></div>
        <idp-data-table
          title="Members"
          badge=${`${this.orgMembers.length} total`}
          empty-title="No members loaded"
          empty-description="Members will appear here after organisation membership data is loaded."
          .columns=${[
            { label: 'User' },
            { label: 'Roles', hideBelow: 'mobile' },
            { label: 'Status' },
            { label: 'Action', align: 'right' },
          ]}
          .rows=${memberRows}
        ></idp-data-table>
        ${this.orgInvitations.length ? html`
          <idp-data-table
            title="Pending invitations"
            badge=${`${this.orgInvitations.length} total`}
            .columns=${[
              { label: 'Invitee' },
              { label: 'Roles', hideBelow: 'mobile' },
              { label: 'Expires', mono: true },
              { label: 'Action', align: 'right' },
            ]}
            .rows=${invitationRows}
          ></idp-data-table>
        ` : html``}
      </div>
    `;
  }

  private renderOrgApps(): TemplateResult {
    const appRows = this.orgApps.map((appArg) => ({
      cells: [
        html`
          <div class="identity-cell">
            <span class="identity-avatar">${this.getInitials(appArg.name)}</span>
            <div>
              <div class="identity-primary">${appArg.name}</div>
              <div class="identity-secondary">${appArg.description || appArg.appUrl || 'Global app'}</div>
            </div>
          </div>
        `,
        appArg.clientId || '-',
        html`<div class="chip-row">${(appArg.scopes || []).slice(0, 4).map((scopeArg) => html`<idp-badge variant="outline">${scopeArg}</idp-badge>`)}</div>`,
        html`<idp-badge variant=${appArg.isConnected ? 'ok' : 'outline'}>${appArg.isConnected ? 'connected' : 'available'}</idp-badge>`,
        html`<idp-badge variant=${appArg.roleMappings?.length ? 'accent' : 'outline'}>${appArg.roleMappings?.length || 0} mappings</idp-badge>`,
        html`<div class="cell-actions">${appArg.isConnected ? html`<button class="table-action" @click=${() => this.openAppRoleMappingsDialog(appArg)}>Map roles</button>` : html``}<button class="table-action ${appArg.isConnected ? '' : 'primary'}" @click=${() => this.dispatchShellEvent<IIdpAdminAppToggleEventDetail>('idp-admin-app-toggle', { appId: appArg.id, connected: !appArg.isConnected })}>${appArg.isConnected ? 'Disconnect' : 'Connect'}</button></div>`,
      ],
    }));

    return html`
      ${this.renderPageHeader('Apps', "Global apps connected to this organisation.")}
      <div class="body wide-body">
        ${this.orgSettingsError ? html`<div class="notice-card" style="border-color:var(--idp-error-border);background:var(--idp-error-bg)"><idp-icon name="alert" size="16" style="color:var(--idp-error)"></idp-icon><div><div class="section-title" style="color:var(--idp-error)">App mapping blocked</div><div class="muted">${this.orgSettingsError}</div></div></div>` : html``}
        <idp-data-table
          title="OAuth apps"
          badge=${`${this.orgApps.length} total`}
          empty-title="No apps available"
          empty-description="Global apps will appear here after app catalogue data is loaded."
          .columns=${[
            { label: 'App' },
            { label: 'Client ID', mono: true, hideBelow: 'tablet' },
            { label: 'Scopes', hideBelow: 'mobile' },
            { label: 'Status' },
            { label: 'Role mappings' },
            { label: 'Action', align: 'right' },
          ]}
          .rows=${appRows}
        ></idp-data-table>
      </div>
    `;
  }

  private renderSupport(): TemplateResult {
    const services = [
      ['Account Recovery', 'Lost access to your account or locked out of your organisation? Our team will verify your identity and restore access securely.', 'EUR149', 'per incident', 'key', '1-2 business days'],
      ['Organisation Recovery', 'All owners have lost access to your organisation. We can verify ownership and restore admin access.', 'EUR249', 'per incident', 'building', '2-3 business days'],
      ['Data Export & Migration', 'Full export of your org data - users, sessions, app connections - for migration or compliance.', 'EUR199', 'per request', 'box', '3-5 business days'],
      ['Identity & SSO Consulting', 'Architecture review, OIDC guidance, and custom SSO setup for your organisation stack.', 'EUR190', 'per hour', 'globe', 'Scheduled session'],
      ['Security Review', 'Audit of connected apps, active sessions, passkey policies, and role assignments.', 'EUR390', 'per review', 'shield', '5-7 business days'],
    ];
    return html`
      ${this.renderPageHeader('Support', 'idp.global is free for everyone. Paid options cover hands-on recovery and consulting work.')}
      <div class="body narrow-body"><div class="row-card" style="background:var(--idp-accent-soft);border-color:var(--idp-info-border)"><idp-icon name="shield" size="16" style="color:var(--idp-accent)"></idp-icon><div><div class="section-title" style="color:var(--idp-accent)">idp.global is free, forever</div><div class="muted">All platform features - authentication, passkeys, OIDC apps, team management - are included at no cost.</div></div></div><div class="list-stack">${services.map((serviceArg) => html`<div class="row-card"><span class="icon-tile"><idp-icon name=${serviceArg[4] as any} size="16"></idp-icon></span><div style="flex:1"><div style="display:flex;gap:8px;align-items:baseline"><span class="section-title">${serviceArg[0]}</span><span class="muted">- ${serviceArg[5]}</span></div><div class="muted">${serviceArg[1]}</div><div style="display:flex;align-items:center;justify-content:space-between;margin-top:10px"><div><span style="font-size:16px;font-weight:700;color:var(--idp-fg)">${serviceArg[2]}</span> <span class="muted">${serviceArg[3]}</span></div><button class="plain-button outline" disabled>Request flow pending</button></div></div></div>`)}</div></div>
    `;
  }

  private renderGAUsers(): TemplateResult {
    return html`${this.renderPageHeader('All Users', 'Platform-wide user administration.')}<div class="body wide-body">${this.renderDataState('User directory not connected', 'The shell is ready for global user data, but no platform user-list endpoint is wired into this app yet. No demo users are shown in production mode.', 'users')}</div>`;
  }

  private renderGAOrgs(): TemplateResult {
    const orgRows = this.orgs.map((orgArg) => ({
      cells: [
        html`
          <div class="identity-cell">
            <span class="identity-avatar">${this.getInitials(orgArg.name)}</span>
            <div>
              <div class="identity-primary">${orgArg.name}</div>
              <div class="identity-secondary">${orgArg.slug ? `idp.global/org/${orgArg.slug}` : 'No slug'}</div>
            </div>
          </div>
        `,
        orgArg.slug || '-',
        html`<idp-badge variant=${this.roleVariant(orgArg.myRole || 'member')}>${orgArg.myRole || 'member'}</idp-badge>`,
        orgArg.id,
      ],
    }));

    return html`
      ${this.renderPageHeader('All Organisations', `${this.orgs.length} organisations visible to this admin session`)}
      <div class="body wide-body">
        <idp-data-table
          title="Organisations"
          badge=${`${this.orgs.length} total`}
          empty-title="No organisations"
          empty-description="No organisations are visible to this admin session."
          .columns=${[
            { label: 'Organisation' },
            { label: 'Slug', mono: true, hideBelow: 'mobile' },
            { label: 'Role' },
            { label: 'Identifier', mono: true, hideBelow: 'tablet' },
          ]}
          .rows=${orgRows}
        ></idp-data-table>
      </div>
    `;
  }

  private renderGAApps(): TemplateResult {
    const apps = this.adminApps.length ? this.adminApps : this.orgApps;
    const appRows = apps.map((appArg) => ({
      cells: [
        html`
          <div class="identity-cell">
            <span class="identity-avatar">${this.getInitials(appArg.name)}</span>
            <div>
              <div class="identity-primary">${appArg.name}</div>
              <div class="identity-secondary">${appArg.description || appArg.appUrl || 'Platform app'}</div>
            </div>
          </div>
        `,
        html`<idp-badge variant=${appArg.type === 'global' ? 'accent' : 'outline'}>${appArg.type || 'global'}</idp-badge>`,
        appArg.category || '-',
        appArg.connectionCount ?? '-',
        html`<idp-badge variant=${appArg.status === 'suspended' ? 'error' : appArg.status === 'pending_review' ? 'warn' : 'ok'}>${appArg.status || 'active'}</idp-badge>`,
      ],
    }));

    return html`
      ${this.renderPageHeader('Platform Apps', 'Global and partner apps across the platform.')}
      <div class="body wide-body">
        <idp-data-table
          title="Platform apps"
          badge=${`${apps.length} total`}
          empty-title="No platform apps"
          empty-description="Global app administration data is not available for this session."
          .columns=${[
            { label: 'App' },
            { label: 'Type' },
            { label: 'Category', hideBelow: 'mobile' },
            { label: 'Connections', align: 'right', mono: true, hideBelow: 'mobile' },
            { label: 'Status' },
          ]}
          .rows=${appRows}
        ></idp-data-table>
      </div>
    `;
  }

  private renderMainContent(): TemplateResult {
    const renderers: Record<TIdpAdminPage, () => TemplateResult> = {
      overview: () => this.renderOverview(),
      profile: () => this.renderProfile(),
      security: () => this.renderSecurity(),
      sessions: () => this.renderSessions(),
      apps: () => this.renderAccountApps(),
      'org-general': () => this.renderOrgGeneral(),
      'org-settings': () => this.renderOrgSettings(),
      'org-members': () => this.renderOrgMembers(),
      'org-apps': () => this.renderOrgApps(),
      support: () => this.renderSupport(),
      'ga-users': () => this.renderGAUsers(),
      'ga-orgs': () => this.renderGAOrgs(),
      'ga-apps': () => this.renderGAApps(),
    };
    return (renderers[this.page] || renderers.overview)();
  }

  public render(): TemplateResult {
    return html`
      <div class="shell">
        ${this.renderSidebar()}
        <main>${this.renderMainContent()}</main>
      </div>
      ${this.renderDialog()}
    `;
  }
}
