<!DOCTYPE html>
<html lang="{{ ui_language }}">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{ title or app_name ~ " Configuration" }}</title>
    <link rel="icon" type="image/png" href="/static/favicon.png" />
    <link rel="apple-touch-icon" href="/static/favicon.png" />
    <style>
      :root {
        --bg: #000000;
        --ink: #f2f2f2;
        --ink-dim: rgba(242, 242, 242, 0.68);
        --line: rgba(255, 255, 255, 0.12);
        --panel: rgba(11, 11, 11, 0.9);
        --section: rgba(16, 16, 16, 0.84);
        --red-glow: rgba(214, 42, 42, 0.28);
        --red-soft: rgba(193, 33, 33, 0.14);
        --accent: #dd5a5a;
        --accent-strong: #c73838;
        --success: #86d39c;
        --warn: #ffb4b4;
        --ease: cubic-bezier(0.42, 0, 0.58, 1);
        --enter-ms: 820ms;
        --viewport-pad-y: clamp(20px, 5vh, 58px);
      }

      * {
        box-sizing: border-box;
      }

      html,
      body {
        margin: 0;
        width: 100%;
        min-height: 100%;
        height: 100%;
        background: var(--bg);
      }

      body {
        color: var(--ink);
        font-family: "SF Pro Text", "Segoe UI", -apple-system, BlinkMacSystemFont, sans-serif;
        line-height: 1.5;
        overflow: hidden;
      }

      .config-page {
        background: var(--bg);
      }

      .config-shell,
      .viewport {
        position: relative;
        height: 100dvh;
        width: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: var(--viewport-pad-y) 16px;
        overflow: hidden;
      }

      .config-shell::before,
      .viewport::before {
        content: "";
        position: fixed;
        inset: -16vmax;
        background:
          radial-gradient(circle at 50% 18%, var(--red-glow), rgba(0, 0, 0, 0) 54%),
          radial-gradient(circle at 20% 84%, var(--red-soft), rgba(0, 0, 0, 0) 48%);
        filter: blur(28px);
        pointer-events: none;
      }

      .screen-fade {
        position: fixed;
        inset: 0;
        background: #000;
        opacity: 1;
        z-index: 60;
        pointer-events: auto;
        transition: opacity 620ms var(--ease), visibility 0ms linear 620ms;
      }

      .screen-fade.hidden {
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
      }

      .config-card {
        position: relative;
        width: min(980px, 100%);
        max-height: calc(100dvh - (var(--viewport-pad-y) * 2));
        border: 1px solid var(--line);
        border-radius: 24px;
        background: linear-gradient(180deg, rgba(18, 18, 18, 0.86), rgba(8, 8, 8, 0.94));
        box-shadow: 0 34px 62px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(210, 56, 56, 0.12);
        backdrop-filter: blur(8px);
        padding: 24px;
        overflow-y: auto;
        overflow-x: hidden;
        opacity: 0;
        transform: translateY(12px);
        transition: opacity var(--enter-ms) var(--ease), transform var(--enter-ms) var(--ease);
        scrollbar-width: thin;
        scrollbar-color: rgba(221, 90, 90, 0.32) transparent;
      }

      .config-card::-webkit-scrollbar {
        width: 6px;
        height: 6px;
      }

      .config-card::-webkit-scrollbar-track {
        background: transparent;
      }

      .config-card::-webkit-scrollbar-thumb {
        background: rgba(221, 90, 90, 0.28);
        border-radius: 999px;
      }

      .config-card::-webkit-scrollbar-thumb:hover {
        background: rgba(221, 90, 90, 0.44);
      }

      body.ready .config-card {
        opacity: 1;
        transform: translateY(0);
      }

      .toolbar {
        display: flex;
        justify-content: flex-end;
        position: sticky;
        top: 0;
        z-index: 3;
        padding-bottom: 8px;
        background: linear-gradient(180deg, rgba(12, 12, 12, 0.94), rgba(12, 12, 12, 0));
        margin-bottom: 10px;
      }

      .ghost-button {
        border: 1px solid rgba(255, 255, 255, 0.2);
        background: rgba(0, 0, 0, 0.5);
        color: var(--ink);
        font-size: 0.8rem;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        padding: 8px 12px;
        border-radius: 999px;
        cursor: pointer;
        transition: border-color 180ms ease, transform 180ms ease;
      }

      .ghost-button:hover {
        border-color: rgba(221, 90, 90, 0.72);
        transform: translateY(-1px);
      }

      h1 {
        margin: 0;
        font-size: clamp(1.45rem, 3vw, 1.85rem);
        font-weight: 620;
      }

      .subtitle {
        margin: 8px 0 18px;
        color: var(--ink-dim);
        font-size: 0.94rem;
      }

      .section-list {
        display: grid;
        gap: 14px;
        min-width: 0;
      }

      .section {
        border: 1px solid var(--line);
        border-radius: 16px;
        background: var(--section);
        padding: 16px;
        display: grid;
        gap: 14px;
        min-width: 0;
      }

      .config-row {
        display: grid;
        gap: 10px;
        min-width: 0;
      }

      .section-head {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: start;
        gap: 10px;
        min-width: 0;
      }

      .section-title {
        margin: 0;
        font-size: 0.94rem;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        line-height: 1.35;
        min-width: 0;
        overflow-wrap: anywhere;
      }

      .badge {
        border: 1px solid rgba(255, 255, 255, 0.22);
        border-radius: 999px;
        padding: 5px 10px;
        font-size: 0.73rem;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        color: rgba(255, 255, 255, 0.84);
        line-height: 1.2;
        text-align: center;
        white-space: normal;
        overflow-wrap: anywhere;
      }

      .badge.ok {
        border-color: rgba(134, 211, 156, 0.45);
        color: rgba(174, 233, 189, 0.96);
      }

      .badge.warn {
        border-color: rgba(221, 90, 90, 0.52);
        color: rgba(255, 191, 191, 0.96);
      }

      .badge.dim {
        border-color: rgba(255, 255, 255, 0.16);
        color: rgba(255, 255, 255, 0.65);
      }

      .config-field,
      .field {
        display: grid;
        gap: 8px;
        min-width: 0;
      }

      .config-label,
      .field-label {
        display: block;
        font-size: 0.8rem;
        color: rgba(255, 255, 255, 0.75);
        line-height: 1.35;
        min-width: 0;
        overflow-wrap: anywhere;
      }

      .config-input,
      .field input,
      .field select,
      .path-row input,
      .provider-grid input,
      .provider-grid select {
        width: 100%;
        border: 1px solid rgba(255, 255, 255, 0.2);
        background: rgba(0, 0, 0, 0.5);
        color: var(--ink);
        border-radius: 12px;
        padding: 11px 13px;
        font-size: 0.92rem;
        outline: none;
        transition: border-color 180ms ease, box-shadow 180ms ease;
        min-width: 0;
      }

      .config-input:focus,
      .field input:focus,
      .field select:focus,
      .path-row input:focus,
      .provider-grid input:focus,
      .provider-grid select:focus {
        border-color: rgba(221, 90, 90, 0.72);
        box-shadow: 0 0 0 2px rgba(221, 90, 90, 0.2);
      }

      .config-help,
      .helper {
        margin: 0;
        color: rgba(255, 255, 255, 0.56);
        font-size: 0.82rem;
        line-height: 1.5;
        min-width: 0;
        overflow-wrap: anywhere;
      }

      .language-options {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
      }

      .lang-option {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        border: 1px solid rgba(255, 255, 255, 0.18);
        border-radius: 999px;
        background: rgba(0, 0, 0, 0.34);
        padding: 8px 12px;
        font-size: 0.88rem;
      }

      .lang-option input {
        accent-color: #d84f4f;
      }

      .ai-options {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
      }

      .ai-option {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        border: 1px solid rgba(255, 255, 255, 0.18);
        border-radius: 999px;
        background: rgba(0, 0, 0, 0.34);
        padding: 8px 12px;
        font-size: 0.84rem;
      }

      .ai-option input {
        accent-color: #d84f4f;
      }

      .ai-panel {
        display: grid;
        gap: 10px;
      }

      .ai-panel.hidden {
        display: none;
      }

      .hidden {
        display: none !important;
      }

      .path-row {
        display: grid;
        gap: 10px;
        grid-template-columns: 1fr auto;
        align-items: start;
        min-width: 0;
      }

      .pill-list {
        list-style: none;
        margin: 0;
        padding: 0;
        display: grid;
        gap: 8px;
      }

      .pill-item {
        border: 1px solid rgba(255, 255, 255, 0.14);
        background: rgba(0, 0, 0, 0.42);
        border-radius: 12px;
        padding: 10px 10px 10px 12px;
        display: grid;
        align-items: center;
        grid-template-columns: 1fr auto;
        gap: 8px;
      }

      .pill-path {
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
        font-size: 0.8rem;
        color: rgba(255, 255, 255, 0.86);
        word-break: break-all;
      }

      .remove-path {
        border: 1px solid rgba(240, 124, 124, 0.4);
        background: rgba(98, 18, 18, 0.28);
        color: #ffc7c7;
        border-radius: 999px;
        padding: 5px 10px;
        font-size: 0.74rem;
        cursor: pointer;
      }

      .section-actions,
      .config-actions,
      .global-actions {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        align-items: stretch;
        min-width: 0;
      }

      .section-actions > .btn-primary,
      .section-actions > .btn-secondary,
      .section-actions > .btn-danger,
      .config-actions > .btn-primary,
      .config-actions > .btn-secondary,
      .config-actions > .btn-danger {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-height: 38px;
      }

      .btn-primary,
      .btn-secondary,
      .btn-danger {
        border-radius: 11px;
        border: 1px solid transparent;
        padding: 10px 14px;
        font-size: 0.78rem;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        cursor: pointer;
        transition: transform 170ms ease, filter 170ms ease;
      }

      .btn-primary {
        background: linear-gradient(135deg, var(--accent), var(--accent-strong));
        color: #fff;
      }

      .btn-secondary {
        background: rgba(20, 20, 20, 0.74);
        border-color: rgba(255, 255, 255, 0.2);
        color: var(--ink);
      }

      .btn-danger {
        background: rgba(85, 8, 8, 0.45);
        border-color: rgba(255, 136, 136, 0.35);
        color: #ffd2d2;
      }

      .btn-primary:hover,
      .btn-secondary:hover,
      .btn-danger:hover {
        transform: translateY(-1px);
        filter: brightness(1.05);
      }

      .provider-grid {
        display: grid;
        gap: 12px;
        grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
        min-width: 0;
      }

      .provider-card {
        border: 1px solid rgba(255, 255, 255, 0.14);
        border-radius: 14px;
        padding: 14px;
        display: grid;
        gap: 12px;
        background: rgba(0, 0, 0, 0.28);
        min-width: 0;
        align-content: start;
      }

      .provider-head {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: start;
        gap: 8px;
        min-width: 0;
      }

      .provider-name {
        margin: 0;
        font-size: 0.92rem;
        line-height: 1.35;
        min-width: 0;
        overflow-wrap: anywhere;
      }

      .provider-meta {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        align-items: center;
        color: rgba(255, 255, 255, 0.62);
        font-size: 0.75rem;
        line-height: 1.4;
        min-width: 0;
        overflow-wrap: anywhere;
      }

      .toggle-row,
      .config-toggle,
      .provider-toggle {
        display: flex;
        align-items: center;
        gap: 8px;
        min-height: 38px;
        padding: 8px 10px;
        border: 1px solid rgba(255, 255, 255, 0.12);
        border-radius: 10px;
        background: rgba(0, 0, 0, 0.26);
        font-size: 0.82rem;
        line-height: 1.35;
        margin: 0;
        min-width: 0;
      }

      .toggle-row input[type="checkbox"],
      .toggle-control,
      .config-toggle input[type="checkbox"],
      .provider-toggle input[type="checkbox"] {
        -webkit-appearance: none;
        appearance: none;
        width: 18px;
        height: 18px;
        margin: 0;
        border: 1px solid rgba(255, 255, 255, 0.35);
        border-radius: 5px;
        background: rgba(0, 0, 0, 0.58);
        display: grid;
        place-content: center;
        cursor: pointer;
        flex: 0 0 auto;
        transition: border-color 180ms ease, background 180ms ease, box-shadow 180ms ease;
      }

      .toggle-row input[type="checkbox"]::before,
      .toggle-control::before,
      .config-toggle input[type="checkbox"]::before,
      .provider-toggle input[type="checkbox"]::before {
        content: "";
        width: 9px;
        height: 9px;
        border-radius: 2px;
        background: #d84f4f;
        transform: scale(0);
        transition: transform 160ms ease;
      }

      .toggle-row input[type="checkbox"]:checked,
      .toggle-control:checked,
      .config-toggle input[type="checkbox"]:checked,
      .provider-toggle input[type="checkbox"]:checked {
        border-color: rgba(221, 90, 90, 0.85);
        background: rgba(120, 14, 24, 0.48);
      }

      .toggle-row input[type="checkbox"]:checked::before,
      .toggle-control:checked::before,
      .config-toggle input[type="checkbox"]:checked::before,
      .provider-toggle input[type="checkbox"]:checked::before {
        transform: scale(1);
      }

      .toggle-row input[type="checkbox"]:focus-visible,
      .toggle-control:focus-visible,
      .config-toggle input[type="checkbox"]:focus-visible,
      .provider-toggle input[type="checkbox"]:focus-visible {
        outline: none;
        box-shadow: 0 0 0 2px rgba(221, 90, 90, 0.22);
      }

      .toggle-label,
      .config-toggle label,
      .provider-toggle label {
        margin: 0;
        color: rgba(255, 255, 255, 0.82);
        font-size: 0.82rem;
        line-height: 1.35;
        cursor: pointer;
        min-width: 0;
        overflow-wrap: anywhere;
      }

      .card-result {
        min-height: 1.45em;
        margin: 2px 0 0;
        color: rgba(255, 255, 255, 0.72);
        font-size: 0.82rem;
        line-height: 1.45;
      }

      .card-result.ok {
        color: var(--success);
      }

      .card-result.warn {
        color: var(--warn);
      }

      .card-result.pending {
        color: rgba(255, 255, 255, 0.72);
      }

      .card-result[data-empty="true"] {
        visibility: hidden;
      }

      .meta {
        color: rgba(255, 255, 255, 0.54);
        font-size: 0.77rem;
      }

      .risk-notice {
        color: rgba(255, 200, 200, 0.82);
        font-size: 0.78rem;
      }

      @media (max-width: 760px) {
        .viewport {
          align-items: flex-start;
        }

        .config-card {
          padding: 16px;
          max-height: calc(100dvh - 24px);
        }

        .section-head,
        .provider-head {
          grid-template-columns: minmax(0, 1fr);
        }

        .path-row {
          grid-template-columns: 1fr;
        }

        .provider-grid {
          grid-template-columns: 1fr;
        }
      }
    </style>
  </head>
  <body class="config-page">
    <div id="screen-fade" class="screen-fade"></div>
    <div class="config-shell viewport">
      <main class="config-card">
        <div class="toolbar{% if embedded_dashboard %} hidden{% endif %}">
          <button id="back-button" class="ghost-button" type="button">{{ t("config.toolbar.back") }}</button>
        </div>

        <h1>{{ t("config.title") }}</h1>
        <p class="subtitle">{{ t("config.subtitle") }}</p>

        <div class="section-list">
          <section class="section config-row" aria-labelledby="language-title">
            <div class="section-head">
              <h2 id="language-title" class="section-title">{{ t("config.sections.language.title") }}</h2>
              <span id="language-badge" class="badge">{{ t("config.common.default") }}</span>
            </div>
            <div class="language-options">
              <label class="lang-option"><input type="radio" name="assistant-language" value="es" /> {{ t("config.fields.language_es") }}</label>
              <label class="lang-option"><input type="radio" name="assistant-language" value="en" /> {{ t("config.fields.language_en") }}</label>
            </div>
            <p class="helper">{{ t("config.sections.language.helper") }}</p>
            <div class="section-actions">
              <button id="save-language" class="btn-secondary" type="button">{{ t("config.sections.language.save") }}</button>
            </div>
            <p id="result-language" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
          </section>

          <section class="section config-row" aria-labelledby="ai-provider-title">
            <div class="section-head">
              <h2 id="ai-provider-title" class="section-title">{{ t("config.sections.ai.title_remote") }}</h2>
              <span id="ai-provider-badge" class="badge warn">{{ t("config.sections.ai.badge_not_configured") }}</span>
            </div>
            <div class="ai-options">
              <label class="ai-option"><input type="radio" name="ai-provider" value="ollama" /> {{ t("config.sections.ai.radio_ollama") }}</label>
              <label class="ai-option"><input type="radio" name="ai-provider" value="gemini" /> {{ t("config.sections.ai.radio_gemini") }}</label>
            </div>
            <p id="ai-provider-helper" class="helper">
              {{ t("config.sections.ai.helper_remote") }}
            </p>

            <div id="ai-local-panel" class="ai-panel">
              <label class="field">
                <span id="ollama-model-label" class="field-label">{{ t("config.sections.ai.ollama_model") }}</span>
                <input id="ollama-model-input" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.ollama_model_placeholder') }}" />
              </label>
              <div class="section-actions">
                <button id="test-ollama" class="btn-secondary" type="button">{{ t("config.sections.ai.test_local") }}</button>
              </div>
              <p id="result-ollama-test" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
              <p id="ollama-status" class="helper">{{ t("config.sections.ai.status_not_detected") }}</p>
            </div>

            <div id="ai-remote-panel" class="ai-panel">
              <label class="field">
                <span id="gemini-api-key-label" class="field-label">{{ t("config.sections.ai.gemini_key") }}</span>
                <input id="gemini-api-key-input" type="password" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.gemini_api_key_placeholder') }}" />
              </label>
              <label class="field">
                <span id="gemini-model-label" class="field-label">{{ t("config.sections.ai.gemini_model") }}</span>
                <input
                  id="gemini-model-input"
                  type="text"
                  autocomplete="off"
                  spellcheck="false"
                  placeholder="{{ t('config.fields.gemini_model_placeholder') }}"
                />
              </label>
              <div class="section-actions">
                <button id="test-gemini" class="btn-secondary" type="button">{{ t("config.sections.ai.test_api") }}</button>
              </div>
              <p id="result-gemini-test" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
              <p id="gemini-status" class="helper">{{ t("config.sections.ai.status_not_configured") }}</p>
            </div>

            <div class="config-toggle toggle-row">
              <input id="allow-fallback" class="toggle-control" type="checkbox" />
              <label class="toggle-label" for="allow-fallback">{{ t("config.sections.ai.allow_fallback") }}</label>
            </div>
            <div class="section-actions config-actions">
              <button id="save-ai-provider" class="btn-secondary" type="button">{{ t("config.sections.ai.save") }}</button>
            </div>
            <p id="result-ai" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
          </section>

          <section class="section config-row" aria-labelledby="defaults-title">
            <div class="section-head">
              <h2 id="defaults-title" class="section-title">{{ t("config.sections.defaults.title") }}</h2>
              <span class="badge dim">{{ t("config.sections.defaults.badge") }}</span>
            </div>
            <div class="provider-grid">
              <label class="field">
                <span class="field-label">{{ t("config.sections.defaults.task_provider") }}</span>
                <select id="default-task-provider">
                  <option value="todoist">{{ t("config.providers.todoist") }}</option>
                  <option value="google_tasks">{{ t("config.providers.google_tasks") }}</option>
                  <option value="microsoft_todo">{{ t("config.providers.microsoft_todo") }}</option>
                  <option value="trello">{{ t("config.providers.trello") }}</option>
                  <option value="github_issues">{{ t("config.providers.github_issues") }}</option>
                </select>
              </label>
              <label class="field">
                <span class="field-label">{{ t("config.sections.defaults.notes_provider") }}</span>
                <select id="default-notes-provider">
                  <option value="vault_local">{{ t("config.providers.vault_local") }}</option>
                  <option value="notion">{{ t("config.providers.notion") }}</option>
                  <option value="github_issues">{{ t("config.providers.github_issues") }}</option>
                </select>
              </label>
            </div>
            <div class="section-actions config-actions">
              <button id="save-defaults" class="btn-secondary" type="button">{{ t("config.sections.defaults.save") }}</button>
            </div>
            <p id="result-defaults" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
          </section>

          <section class="section config-row" aria-labelledby="todoist-title">
            <div class="section-head">
              <h2 id="todoist-title" class="section-title">{{ t("config.sections.todoist.title") }}</h2>
              <span id="todoist-badge" class="badge warn">{{ t("config.common.not_configured") }}</span>
            </div>
            <div class="config-toggle toggle-row">
              <input id="enabled-todoist" class="toggle-control" type="checkbox" />
              <label class="toggle-label" for="enabled-todoist">{{ t("config.common.enabled") }}</label>
            </div>
            <div class="field">
              <input
                id="todoist-token"
                type="password"
                autocomplete="off"
                spellcheck="false"
                value="{{ todoist_token }}"
                placeholder="{{ t('config.fields.todoist_token_placeholder') }}"
              />
            </div>
            <p class="helper">{{ t("config.sections.todoist.helper") }}</p>
            <div class="section-actions config-actions">
              <button id="save-todoist" class="btn-secondary" type="button">{{ t("config.sections.todoist.save") }}</button>
              <button id="test-todoist" class="btn-secondary" type="button">{{ t("config.sections.integrations.test_connection") }}</button>
              <button id="disconnect-todoist" class="btn-danger" type="button">{{ t("config.sections.integrations.disconnect") }}</button>
            </div>
            <p id="result-todoist" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
          </section>

          <section class="section config-row" aria-labelledby="vault-title">
            <div class="section-head">
              <h2 id="vault-title" class="section-title">{{ t("config.sections.vault.title") }}</h2>
              <span id="vault-badge" class="badge warn">{{ t("config.common.not_configured") }}</span>
            </div>
            <div class="config-toggle toggle-row">
              <input id="enabled-vault_local" class="toggle-control" type="checkbox" />
              <label class="toggle-label" for="enabled-vault_local">{{ t("config.common.enabled") }}</label>
            </div>
            <div class="path-row">
              <input
                id="vault-path-input"
                type="text"
                autocomplete="off"
                spellcheck="false"
                placeholder="{{ t('config.fields.vault_path_placeholder') }}"
              />
              <button id="add-path-button" class="btn-secondary" type="button">{{ t("config.sections.vault.add_path") }}</button>
            </div>
            <ul id="vault-path-list" class="pill-list"></ul>
            <p class="helper">{{ t("config.sections.vault.helper") }}</p>
            <div class="section-actions config-actions">
              <button id="save-vault" class="btn-secondary" type="button">{{ t("config.sections.vault.save") }}</button>
              <button id="test-vault_local" class="btn-secondary" type="button">{{ t("config.sections.integrations.test_connection") }}</button>
              <button id="disconnect-vault_local" class="btn-danger" type="button">{{ t("config.sections.integrations.disconnect") }}</button>
            </div>
            <p id="result-vault_local" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
          </section>

          <section class="section config-row" aria-labelledby="integrations-title">
            <div class="section-head">
              <h2 id="integrations-title" class="section-title">{{ t("config.sections.integrations.title") }}</h2>
              <span class="badge dim">{{ t("config.sections.integrations.badge") }}</span>
            </div>

            <div class="provider-grid">
              <article class="provider-card config-row">
                <div class="provider-head">
                  <h3 class="provider-name">{{ t("config.providers.google_tasks") }}</h3>
                  <span id="badge-google_tasks" class="badge warn">{{ t("config.common.not_configured") }}</span>
                </div>
                <div class="provider-meta"><span>{{ t("config.sections.integrations.oauth") }}</span></div>
                <div class="config-toggle toggle-row">
                  <input id="enabled-google_tasks" class="toggle-control" type="checkbox" />
                  <label class="toggle-label" for="enabled-google_tasks">{{ t("config.common.enabled") }}</label>
                </div>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.google_task_list_id") }}</span>
                  <input id="google-task-list-id" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.google_task_list_placeholder') }}" />
                </label>
                <div class="section-actions config-actions">
                  <button id="oauth-google_tasks" class="btn-secondary" type="button">{{ t("config.sections.integrations.connect_oauth") }}</button>
                  <button id="save-google_tasks" class="btn-secondary" type="button">{{ t("config.sections.integrations.save") }}</button>
                  <button id="test-google_tasks" class="btn-secondary" type="button">{{ t("config.sections.integrations.test") }}</button>
                  <button id="disconnect-google_tasks" class="btn-danger" type="button">{{ t("config.sections.integrations.disconnect") }}</button>
                </div>
                <p id="result-google_tasks" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
              </article>

              <article class="provider-card config-row">
                <div class="provider-head">
                  <h3 class="provider-name">{{ t("config.providers.microsoft_todo") }}</h3>
                  <span id="badge-microsoft_todo" class="badge warn">{{ t("config.common.not_configured") }}</span>
                </div>
                <div class="provider-meta"><span>{{ t("config.sections.integrations.oauth") }}</span></div>
                <div class="config-toggle toggle-row">
                  <input id="enabled-microsoft_todo" class="toggle-control" type="checkbox" />
                  <label class="toggle-label" for="enabled-microsoft_todo">{{ t("config.common.enabled") }}</label>
                </div>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.microsoft_list_id") }}</span>
                  <input id="microsoft-list-id" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.microsoft_list_placeholder') }}" />
                </label>
                <div class="section-actions config-actions">
                  <button id="oauth-microsoft_todo" class="btn-secondary" type="button">{{ t("config.sections.integrations.connect_oauth") }}</button>
                  <button id="save-microsoft_todo" class="btn-secondary" type="button">{{ t("config.sections.integrations.save") }}</button>
                  <button id="test-microsoft_todo" class="btn-secondary" type="button">{{ t("config.sections.integrations.test") }}</button>
                  <button id="disconnect-microsoft_todo" class="btn-danger" type="button">{{ t("config.sections.integrations.disconnect") }}</button>
                </div>
                <p id="result-microsoft_todo" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
              </article>

              <article class="provider-card config-row">
                <div class="provider-head">
                  <h3 class="provider-name">{{ t("config.providers.trello") }}</h3>
                  <span id="badge-trello" class="badge warn">{{ t("config.common.not_configured") }}</span>
                </div>
                <div class="provider-meta"><span>{{ t("config.sections.integrations.token_based") }}</span></div>
                <div class="config-toggle toggle-row">
                  <input id="enabled-trello" class="toggle-control" type="checkbox" />
                  <label class="toggle-label" for="enabled-trello">{{ t("config.common.enabled") }}</label>
                </div>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.trello_api_key") }}</span>
                  <input id="trello-api-key" type="password" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.trello_api_key_placeholder') }}" />
                </label>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.trello_token") }}</span>
                  <input id="trello-token" type="password" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.trello_token_placeholder') }}" />
                </label>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.trello_board_id") }}</span>
                  <input id="trello-board-id" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.trello_board_id_placeholder') }}" />
                </label>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.trello_list_id") }}</span>
                  <input id="trello-list-id" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.trello_list_id_placeholder') }}" />
                </label>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.trello_done_list_id") }}</span>
                  <input id="trello-done-list-id" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.trello_done_list_id_placeholder') }}" />
                </label>
                <div class="section-actions config-actions">
                  <button id="save-trello" class="btn-secondary" type="button">{{ t("config.sections.integrations.save") }}</button>
                  <button id="test-trello" class="btn-secondary" type="button">{{ t("config.sections.integrations.test") }}</button>
                  <button id="disconnect-trello" class="btn-danger" type="button">{{ t("config.sections.integrations.disconnect") }}</button>
                </div>
                <p id="result-trello" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
              </article>

              <article class="provider-card config-row">
                <div class="provider-head">
                  <h3 class="provider-name">{{ t("config.providers.github_issues") }}</h3>
                  <span id="badge-github_issues" class="badge warn">{{ t("config.common.not_configured") }}</span>
                </div>
                <div class="provider-meta"><span>{{ t("config.sections.integrations.token_based_pat") }}</span></div>
                <div class="config-toggle toggle-row">
                  <input id="enabled-github_issues" class="toggle-control" type="checkbox" />
                  <label class="toggle-label" for="enabled-github_issues">{{ t("config.common.enabled") }}</label>
                </div>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.github_token") }}</span>
                  <input id="github-token" type="password" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.github_token_placeholder') }}" />
                </label>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.github_repo") }}</span>
                  <input id="github-repo" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.github_repo_placeholder') }}" />
                </label>
                <div class="section-actions config-actions">
                  <button id="save-github_issues" class="btn-secondary" type="button">{{ t("config.sections.integrations.save") }}</button>
                  <button id="test-github_issues" class="btn-secondary" type="button">{{ t("config.sections.integrations.test") }}</button>
                  <button id="disconnect-github_issues" class="btn-danger" type="button">{{ t("config.sections.integrations.disconnect") }}</button>
                </div>
                <p id="result-github_issues" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
              </article>

              <article class="provider-card config-row">
                <div class="provider-head">
                  <h3 class="provider-name">{{ t("config.providers.notion") }}</h3>
                  <span id="badge-notion" class="badge warn">{{ t("config.common.not_configured") }}</span>
                </div>
                <div class="provider-meta"><span>{{ t("config.sections.integrations.token_based") }}</span></div>
                <div class="config-toggle toggle-row">
                  <input id="enabled-notion" class="toggle-control" type="checkbox" />
                  <label class="toggle-label" for="enabled-notion">{{ t("config.common.enabled") }}</label>
                </div>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.notion_token") }}</span>
                  <input id="notion-token" type="password" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.notion_token_placeholder') }}" />
                </label>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.notion_page_id") }}</span>
                  <input id="notion-page-id" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.notion_page_id_placeholder') }}" />
                </label>
                <label class="field config-field">
                  <span class="field-label">{{ t("config.fields.notion_database_id") }}</span>
                  <input id="notion-database-id" type="text" autocomplete="off" spellcheck="false" placeholder="{{ t('config.fields.notion_database_id_placeholder') }}" />
                </label>
                <div class="section-actions config-actions">
                  <button id="save-notion" class="btn-secondary" type="button">{{ t("config.sections.integrations.save") }}</button>
                  <button id="test-notion" class="btn-secondary" type="button">{{ t("config.sections.integrations.test") }}</button>
                  <button id="disconnect-notion" class="btn-danger" type="button">{{ t("config.sections.integrations.disconnect") }}</button>
                </div>
                <p id="result-notion" class="card-result" data-empty="true" role="status" aria-live="polite"></p>
              </article>
            </div>

            <p class="risk-notice" id="risk-notice"></p>
          </section>
        </div>

        <div class="global-actions{% if embedded_dashboard %} hidden{% endif %}">
          <button id="save-all" class="btn-primary" type="button">{{ t("config.global.save_all_enter") }}</button>
          <button id="skip-button" class="btn-secondary" type="button">{{ t("config.global.enter_without_config") }}</button>
        </div>

        <p class="meta" id="status-line"></p>
      </main>
    </div>

    <script>
      ;(function () {
        var overlay = document.getElementById("screen-fade")
        var body = document.body
        var timerId = null
        var revealed = false

        function reveal(reason) {
          if (revealed) return
          revealed = true
          if (timerId) {
            window.clearTimeout(timerId)
            timerId = null
          }
          if (overlay) {
            overlay.classList.add("hidden")
          }
          if (body) {
            body.classList.add("ready")
          }
          try {
            var payload = {
              phase: "config_ui_revealed_preflight",
              detail: String(reason || ""),
              ts_ms: Date.now(),
            }
            fetch("/assistant/ui/startup-phase", {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify(payload),
              keepalive: true,
            })
          } catch (error) {
            void error
            // ignore telemetry errors in fallback path
          }
        }

        timerId = window.setTimeout(function () {
          reveal("hard-failsafe")
        }, 4200)
        window.__orionStartupReveal = reveal
      })()
    </script>

    <script>
      (() => {
        const i18n = {{ i18n_strings | tojson }}
        const configState = {{ config_state | tojson }}
        const fromAssistant = {{ from_assistant | tojson }}
        const embeddedDashboard = {{ embedded_dashboard | tojson }}
        const uiMode = "config"
        const initialLanguage = {{ language | tojson }}
        const initialPaths = {{ vault_paths | tojson }}
        const initialAIProvider = {{ ai_provider | tojson }}
        const initialGeminiApiKey = {{ gemini_api_key | tojson }}
        const initialGeminiModel = {{ gemini_model | tojson }}
        const initialOllamaModel = {{ ollama_model | tojson }}
        const initialAllowFallback = {{ allow_fallback | tojson }}
        const providersSnapshot = {{ providers_snapshot | tojson }}
        const initialIntegrations = {{ integrations_status | tojson }}
        const STARTUP_FAILSAFE_MS = 2600

        const tt = (key, params = {}) => {
          const parts = String(key || "").split(".")
          let value = i18n
          for (const part of parts) {
            if (!value || typeof value !== "object" || !(part in value)) {
              value = null
              break
            }
            value = value[part]
          }
          let text = typeof value === "string" ? value : key
          for (const [name, raw] of Object.entries(params)) {
            text = text.replaceAll(`{${name}}`, String(raw))
          }
          return text
        }

        const postStartupPhase = (phase, detail = "") => {
          const payload = {
            phase: String(phase || "").trim(),
            detail: String(detail || "").trim(),
            ts_ms: Date.now(),
          }
          if (!payload.phase) return
          try {
            void fetch("/assistant/ui/startup-phase", {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify(payload),
              keepalive: true,
            })
          } catch {
            // Ignore telemetry send errors.
          }
        }

        const syncUiMode = async () => {
          if (embeddedDashboard) return
          try {
            await fetch("/assistant/ui-mode", {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ mode: uiMode }),
            })
          } catch {
            // Best-effort signal for voice pause; route-level mode sync still applies.
          }
        }

        const overlay = document.getElementById("screen-fade")
        let pageRevealDone = false
        let pageFailsafeTimer = null
        const backButton = document.getElementById("back-button")
        const saveLanguageButton = document.getElementById("save-language")
        const saveTodoistButton = document.getElementById("save-todoist")
        const saveVaultButton = document.getElementById("save-vault")
        const saveAIProviderButton = document.getElementById("save-ai-provider")
        const saveDefaultsButton = document.getElementById("save-defaults")
        const saveAllButton = document.getElementById("save-all")
        const skipButton = document.getElementById("skip-button")
        const addPathButton = document.getElementById("add-path-button")

        const tokenInput = document.getElementById("todoist-token")
        const pathInput = document.getElementById("vault-path-input")
        const pathList = document.getElementById("vault-path-list")
        const aiProviderInputs = Array.from(document.querySelectorAll('input[name="ai-provider"]'))
        const aiLocalPanel = document.getElementById("ai-local-panel")
        const aiRemotePanel = document.getElementById("ai-remote-panel")
        const ollamaModelInput = document.getElementById("ollama-model-input")
        const geminiApiKeyInput = document.getElementById("gemini-api-key-input")
        const geminiModelInput = document.getElementById("gemini-model-input")
        const allowFallbackInput = document.getElementById("allow-fallback")
        const aiProviderTitleEl = document.getElementById("ai-provider-title")
        const aiProviderHelperEl = document.getElementById("ai-provider-helper")
        const ollamaModelLabelEl = document.getElementById("ollama-model-label")
        const geminiApiKeyLabelEl = document.getElementById("gemini-api-key-label")
        const geminiModelLabelEl = document.getElementById("gemini-model-label")
        const aiProviderBadge = document.getElementById("ai-provider-badge")
        const ollamaStatusEl = document.getElementById("ollama-status")
        const geminiStatusEl = document.getElementById("gemini-status")
        const testOllamaButton = document.getElementById("test-ollama")
        const testGeminiButton = document.getElementById("test-gemini")
        const geminiTestResultEl = document.getElementById("result-gemini-test")
        const ollamaTestResultEl = document.getElementById("result-ollama-test")

        const defaultTaskProviderInput = document.getElementById("default-task-provider")
        const defaultNotesProviderInput = document.getElementById("default-notes-provider")

        const riskNoticeEl = document.getElementById("risk-notice")
        const statusLineEl = document.getElementById("status-line")

        const languageBadge = document.getElementById("language-badge")
        const todoistBadge = document.getElementById("todoist-badge")
        const vaultBadge = document.getElementById("vault-badge")

        const languageInputs = Array.from(document.querySelectorAll('input[name="assistant-language"]'))
        const paths = Array.isArray(initialPaths) ? [...initialPaths] : []

        const providerIds = [
          "todoist",
          "vault_local",
          "google_tasks",
          "microsoft_todo",
          "trello",
          "github_issues",
          "notion",
        ]

        const integrationCardProviders = [
          "google_tasks",
          "microsoft_todo",
          "trello",
          "github_issues",
          "notion",
        ]

        const providerBadges = {
          todoist: todoistBadge,
          vault_local: vaultBadge,
          google_tasks: document.getElementById("badge-google_tasks"),
          microsoft_todo: document.getElementById("badge-microsoft_todo"),
          trello: document.getElementById("badge-trello"),
          github_issues: document.getElementById("badge-github_issues"),
          notion: document.getElementById("badge-notion"),
        }

        const providerToggles = {
          todoist: document.getElementById("enabled-todoist"),
          vault_local: document.getElementById("enabled-vault_local"),
          google_tasks: document.getElementById("enabled-google_tasks"),
          microsoft_todo: document.getElementById("enabled-microsoft_todo"),
          trello: document.getElementById("enabled-trello"),
          github_issues: document.getElementById("enabled-github_issues"),
          notion: document.getElementById("enabled-notion"),
        }

        const providerFields = {
          google_tasks: {
            settings: {
              task_list_id: document.getElementById("google-task-list-id"),
            },
          },
          microsoft_todo: {
            settings: {
              list_id: document.getElementById("microsoft-list-id"),
            },
          },
          trello: {
            credentials: {
              api_key: document.getElementById("trello-api-key"),
              token: document.getElementById("trello-token"),
            },
            settings: {
              board_id: document.getElementById("trello-board-id"),
              list_id: document.getElementById("trello-list-id"),
              done_list_id: document.getElementById("trello-done-list-id"),
            },
          },
          github_issues: {
            credentials: {
              token: document.getElementById("github-token"),
            },
            settings: {
              repo: document.getElementById("github-repo"),
            },
          },
          notion: {
            credentials: {
              token: document.getElementById("notion-token"),
            },
            settings: {
              page_id: document.getElementById("notion-page-id"),
              database_id: document.getElementById("notion-database-id"),
            },
          },
        }

        const AI_VIEW = {
          gemini: {
            title: tt("sections.ai.title_remote"),
            helper: tt("sections.ai.helper_remote"),
            geminiLabel: tt("sections.ai.gemini_key"),
            geminiModelLabel: tt("sections.ai.gemini_model"),
            geminiPlaceholder: tt("fields.gemini_api_key_placeholder"),
            geminiModelPlaceholder: tt("fields.gemini_model_placeholder"),
            geminiTestLabel: tt("sections.ai.test_api"),
            ollamaLabel: tt("sections.ai.ollama_model"),
            ollamaPlaceholder: tt("fields.ollama_model_placeholder"),
            ollamaTestLabel: tt("sections.ai.test_local"),
          },
          ollama: {
            title: tt("sections.ai.title_local"),
            helper: tt("sections.ai.helper_local"),
            geminiLabel: tt("sections.ai.gemini_key_fallback"),
            geminiModelLabel: tt("sections.ai.gemini_model"),
            geminiPlaceholder: tt("fields.gemini_api_key_fallback_placeholder"),
            geminiModelPlaceholder: tt("fields.gemini_model_placeholder"),
            geminiTestLabel: tt("sections.ai.test_fallback_api"),
            ollamaLabel: tt("sections.ai.ollama_model"),
            ollamaPlaceholder: tt("fields.ollama_model_placeholder"),
            ollamaTestLabel: tt("sections.ai.test_local"),
          },
        }

        const currentState = { ...configState }
        const uiState = {
          aiProvider: initialAIProvider === "ollama" ? "ollama" : "gemini",
        }
        let integrationState = { ...initialIntegrations }
        let isSubmitting = false

        const cardResultEls = {
          language: document.getElementById("result-language"),
          ai: document.getElementById("result-ai"),
          defaults: document.getElementById("result-defaults"),
          todoist: document.getElementById("result-todoist"),
          vault_local: document.getElementById("result-vault_local"),
          google_tasks: document.getElementById("result-google_tasks"),
          microsoft_todo: document.getElementById("result-microsoft_todo"),
          trello: document.getElementById("result-trello"),
          github_issues: document.getElementById("result-github_issues"),
          notion: document.getElementById("result-notion"),
        }

        const aiTestResultEls = {
          gemini: geminiTestResultEl,
          ollama: ollamaTestResultEl,
        }

        const setBusy = (busy) => {
          isSubmitting = busy
          ;[
            backButton,
            saveLanguageButton,
            saveTodoistButton,
            saveVaultButton,
            saveAIProviderButton,
            saveDefaultsButton,
            saveAllButton,
            skipButton,
            addPathButton,
            testOllamaButton,
            testGeminiButton,
          ].forEach((item) => {
            if (item) item.disabled = busy
          })
        }

        const setCardResult = (cardId, text, tone = "") => {
          const resultEl = cardResultEls[cardId]
          if (!resultEl) return
          const content = String(text || "").trim()
          resultEl.classList.remove("warn", "ok", "pending")
          if (tone) resultEl.classList.add(tone)
          resultEl.textContent = content
          resultEl.dataset.empty = content ? "false" : "true"
        }

        const setAiTestResult = (providerId, text, tone = "") => {
          const resultEl = aiTestResultEls[providerId]
          if (!resultEl) return
          const content = String(text || "").trim()
          resultEl.classList.remove("warn", "ok", "pending")
          if (tone) resultEl.classList.add(tone)
          resultEl.textContent = content
          resultEl.dataset.empty = content ? "false" : "true"
        }

        const warningResultCard = (warningText, fallbackCard = "ai") => {
          const text = String(warningText || "").toLowerCase()
          if (text.includes("todoist")) return "todoist"
          if (text.includes("vault")) return "vault_local"
          if (text.includes("gemini")) return "ai"
          return fallbackCard
        }

        const showWarnings = (items, fallbackCard = "ai") => {
          const list = (items || []).filter(Boolean)
          if (!list.length) {
            return
          }
          const grouped = {}
          for (const warning of list) {
            const cardId = warningResultCard(warning, fallbackCard)
            if (!grouped[cardId]) grouped[cardId] = []
            grouped[cardId].push(warning)
          }
          for (const [cardId, warnings] of Object.entries(grouped)) {
            setCardResult(cardId, warnings.join(" | "), "warn")
          }
        }

        const selectedLanguage = () => {
          const selected = languageInputs.find((item) => item.checked)
          return selected ? selected.value : "es"
        }

        const setLanguageSelection = (value) => {
          const target = value === "en" ? "en" : "es"
          languageInputs.forEach((item) => {
            item.checked = item.value === target
          })
        }

        const selectedAIProvider = () => {
          return uiState.aiProvider
        }

        const setAIProviderSelection = (value) => {
          const target = value === "ollama" ? "ollama" : "gemini"
          uiState.aiProvider = target
          aiProviderInputs.forEach((item) => {
            item.checked = item.value === target
          })
        }

        const renderAIProviderPanels = () => {
          const provider = uiState.aiProvider
          const copy = AI_VIEW[provider]
          aiLocalPanel.classList.toggle("hidden", provider !== "ollama")
          aiRemotePanel.classList.toggle("hidden", provider !== "gemini")
          aiProviderTitleEl.textContent = copy.title
          aiProviderHelperEl.textContent = copy.helper
          ollamaModelLabelEl.textContent = copy.ollamaLabel
          geminiApiKeyLabelEl.textContent = copy.geminiLabel
          geminiModelLabelEl.textContent = copy.geminiModelLabel
          ollamaModelInput.placeholder = copy.ollamaPlaceholder
          geminiApiKeyInput.placeholder = copy.geminiPlaceholder
          geminiModelInput.placeholder = copy.geminiModelPlaceholder
          testOllamaButton.textContent = copy.ollamaTestLabel
          testGeminiButton.textContent = copy.geminiTestLabel
          allowFallbackInput.disabled = provider !== "ollama"
        }

        const canonicalPath = (value) => String(value || "").trim()
        const optionalNonEmpty = (value) => {
          const normalized = String(value || "").trim()
          return normalized ? normalized : undefined
        }

        const renderPaths = () => {
          pathList.innerHTML = ""
          if (!paths.length) {
            const empty = document.createElement("li")
            empty.className = "pill-item"
            empty.innerHTML = `<span class="pill-path">${tt("sections.vault.empty_paths")}</span>`
            pathList.appendChild(empty)
            return
          }

          for (const path of paths) {
            const item = document.createElement("li")
            item.className = "pill-item"
            const pathSpan = document.createElement("span")
            pathSpan.className = "pill-path"
            pathSpan.textContent = path

            const removeButton = document.createElement("button")
            removeButton.type = "button"
            removeButton.className = "remove-path"
            removeButton.textContent = tt("common.remove")
            removeButton.addEventListener("click", () => {
              const index = paths.indexOf(path)
              if (index >= 0) {
                paths.splice(index, 1)
                renderPaths()
                refreshBadges()
              }
            })

            item.appendChild(pathSpan)
            item.appendChild(removeButton)
            pathList.appendChild(item)
          }
        }

        const setBadge = (element, text, tone) => {
          if (!element) return
          element.textContent = text
          element.classList.remove("ok", "warn", "dim")
          if (tone) element.classList.add(tone)
        }

        const canonicalProviderState = (state) => {
          const normalized = String(state || "")
            .trim()
            .toLowerCase()
          if (normalized === "enabled" || normalized === "habilitado") return "enabled"
          if (normalized === "connected" || normalized === "conectado") return "connected"
          if (normalized === "disabled" || normalized === "deshabilitado") return "disabled"
          if (normalized === "error") return "error"
          if (normalized === "not configured" || normalized === "no configurado" || normalized === "not_configured") {
            return "not_configured"
          }
          return "not_configured"
        }

        const statusBadgeState = (state) => {
          const canonical = canonicalProviderState(state)
          if (canonical === "enabled" || canonical === "connected") {
            return "ok"
          }
          if (canonical === "error" || canonical === "not_configured") {
            return "warn"
          }
          return "dim"
        }

        const localizedProviderState = (state) => {
          const canonical = canonicalProviderState(state)
          if (canonical === "enabled") return tt("common.status_enabled")
          if (canonical === "connected") return tt("common.status_connected")
          if (canonical === "disabled") return tt("common.status_disabled")
          if (canonical === "error") return tt("common.status_error")
          return tt("common.status_not_configured")
        }

        const providerStatus = (providerId) => {
          if (!integrationState || typeof integrationState !== "object") return {}
          const providers = integrationState.providers
          if (!providers || typeof providers !== "object") return {}
          const status = providers[providerId]
          return status && typeof status === "object" ? status : {}
        }

        const providerLabel = (providerId) => {
          const translated = tt(`providers.${providerId}`)
          if (translated !== `providers.${providerId}`) {
            return translated
          }
          const status = providerStatus(providerId)
          const label = status.label
          return typeof label === "string" && label.trim() ? label.trim() : providerId
        }

        const syncToggleFromStatus = (providerId) => {
          const toggle = providerToggles[providerId]
          if (!toggle) return
          const status = providerStatus(providerId)
          toggle.checked = Boolean(status.enabled)
        }

        const syncDefaultsFromStatus = () => {
          const taskProvider = integrationState.default_task_provider || "todoist"
          const notesProvider = integrationState.default_notes_provider || "vault_local"
          defaultTaskProviderInput.value = taskProvider
          defaultNotesProviderInput.value = notesProvider
        }

        const refreshBadges = () => {
          if (currentState.language_configured) {
            setBadge(languageBadge, tt("common.configured"), "ok")
          } else {
            setBadge(languageBadge, tt("common.default"), "warn")
          }

          const aiProvider = uiState.aiProvider
          const hasGeminiKey = String(geminiApiKeyInput.value || "").trim().length > 0
          const geminiModel = String(geminiModelInput.value || "").trim()
          const ollamaModel = String(ollamaModelInput.value || "").trim()
          if (aiProvider === "ollama") {
            setBadge(aiProviderBadge, tt("sections.ai.badge_local"), "ok")
          } else if (hasGeminiKey) {
            setBadge(aiProviderBadge, tt("sections.ai.badge_remote"), "ok")
          } else {
            setBadge(aiProviderBadge, tt("sections.ai.badge_not_configured"), "warn")
          }
          geminiStatusEl.textContent = hasGeminiKey
            ? tt("sections.ai.status_model_set", { model: geminiModel || tt("messages.model_pending") })
            : tt("sections.ai.status_not_configured")
          ollamaStatusEl.textContent = ollamaModel
            ? tt("sections.ai.status_model_set", { model: ollamaModel })
            : tt("sections.ai.status_not_detected")

          const todoistStatus = providerStatus("todoist")
          if (todoistStatus.configured) {
            setBadge(
              todoistBadge,
              todoistStatus.enabled ? tt("common.connected") : tt("common.configured"),
              todoistStatus.enabled ? "ok" : "dim",
            )
          } else {
            setBadge(todoistBadge, tt("common.not_configured"), "warn")
          }

          const vaultStatus = providerStatus("vault_local")
          if (paths.length) {
            const label = tt("common.paths_configured", { count: paths.length })
            setBadge(vaultBadge, vaultStatus.enabled ? label : tt("common.configured"), vaultStatus.enabled ? "ok" : "dim")
          } else {
            setBadge(vaultBadge, tt("common.not_configured"), "warn")
          }

          for (const providerId of integrationCardProviders) {
            const status = providerStatus(providerId)
            const stateText = status.state || "not_configured"
            setBadge(providerBadges[providerId], localizedProviderState(stateText), statusBadgeState(stateText))
            syncToggleFromStatus(providerId)
          }

          syncToggleFromStatus("todoist")
          syncToggleFromStatus("vault_local")
          syncDefaultsFromStatus()

          const languageLabel = selectedLanguage() === "en" ? tt("fields.language_en") : tt("fields.language_es")
          const aiLabel =
            aiProvider === "ollama"
              ? `${providerLabel("ollama")} (${ollamaModel || tt("messages.model_pending")})`
              : `${providerLabel("gemini")} (${geminiModel || tt("messages.model_pending")})`
          statusLineEl.textContent = tt("messages.status_line", {
            language: languageLabel,
            ai: aiLabel,
            task_provider: providerLabel(integrationState.default_task_provider || "todoist"),
            notes_provider: providerLabel(integrationState.default_notes_provider || "vault_local"),
          })
        }

        const fadeToAssistant = () => {
          if (embeddedDashboard) return
          postStartupPhase("config_ui_hide", "to_assistant")
          overlay.classList.remove("hidden")
          window.setTimeout(() => {
            window.location.assign("/assistant/ui")
          }, 620)
        }

        const forceRevealPage = (reason) => {
          if (pageRevealDone) return
          pageRevealDone = true
          if (pageFailsafeTimer) {
            clearTimeout(pageFailsafeTimer)
            pageFailsafeTimer = null
          }
          if (typeof window.__orionStartupReveal === "function") {
            window.__orionStartupReveal(reason)
          } else {
            overlay.classList.add("hidden")
            document.body.classList.add("ready")
          }
          postStartupPhase("config_ui_revealed", reason)
        }

        const revealPage = () => {
          const delayMs = embeddedDashboard ? 320 : !fromAssistant && currentState.status === "missing" ? 1500 : 760
          window.setTimeout(() => {
            forceRevealPage("normal")
          }, delayMs)
        }

        const postJson = async (url, payload = {}) => {
          const response = await fetch(url, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(payload),
          })
          const body = await response.json().catch(() => ({}))
          if (!response.ok) {
            const detail = body?.detail || tt("messages.request_failed", { status: response.status })
            throw new Error(String(detail))
          }
          return body
        }

        const getJson = async (url) => {
          const response = await fetch(url)
          const body = await response.json().catch(() => ({}))
          if (!response.ok) {
            const detail = body?.detail || tt("messages.request_failed", { status: response.status })
            throw new Error(String(detail))
          }
          return body
        }

        const refreshIntegrationsState = async () => {
          const payload = await getJson("/integrations/status")
          integrationState = payload
          refreshBadges()
          riskNoticeEl.textContent = tt("messages.risk_notice")
        }

        const savePartial = async ({ payload, successMessage, exitAfterSave, resultCard = "ai" }) => {
          if (isSubmitting) return
          setBusy(true)
          setCardResult(resultCard, tt("messages.saving"), "pending")
          const shouldExitAfterSave = Boolean(exitAfterSave) && !embeddedDashboard
          try {
            const response = await postJson("/config/save", payload)
            const warnings = Array.isArray(response.warnings) ? response.warnings : []
            if (Array.isArray(response.vault_paths)) {
              paths.splice(0, paths.length, ...response.vault_paths.map((item) => String(item)))
              renderPaths()
            }
            if (response && typeof response.state === "object") {
              Object.assign(currentState, response.state)
              if (response.state.ai_provider) {
                setAIProviderSelection(response.state.ai_provider)
              }
              if (Object.prototype.hasOwnProperty.call(response.state, "allow_fallback")) {
                allowFallbackInput.checked = Boolean(response.state.allow_fallback)
              }
              if (response.state.ollama_model) {
                ollamaModelInput.value = String(response.state.ollama_model)
              }
              if (response.state.gemini_model) {
                geminiModelInput.value = String(response.state.gemini_model)
              }
              if (response.state.language) {
                setLanguageSelection(response.state.language)
              }
            }
            if (warnings.length) {
              showWarnings(warnings, resultCard)
            } else {
              setCardResult(resultCard, successMessage, "ok")
            }
            if (Object.prototype.hasOwnProperty.call(payload, "language")) {
              setLanguageSelection(payload.language)
              currentState.language_configured = true
            }
            renderAIProviderPanels()
            await refreshIntegrationsState()
            if (shouldExitAfterSave) {
              window.setTimeout(() => {
                fadeToAssistant()
              }, warnings.length ? 900 : 260)
              return
            }
            if (Object.prototype.hasOwnProperty.call(payload, "language")) {
              window.setTimeout(() => {
                window.location.reload()
              }, 180)
            }
          } catch (error) {
            setCardResult(resultCard, String(error), "warn")
          } finally {
            if (!shouldExitAfterSave) {
              setBusy(false)
            }
          }
        }

        const saveLanguage = () =>
          savePartial({
            payload: { language: selectedLanguage() },
            successMessage: tt("messages.language_saved"),
            exitAfterSave: false,
            resultCard: "language",
          })

        const saveAIProvider = () =>
          savePartial({
            payload: {
              ai_provider: selectedAIProvider(),
              gemini_api_key: optionalNonEmpty(geminiApiKeyInput.value),
              gemini_model: String(geminiModelInput.value || ""),
              ollama_model: String(ollamaModelInput.value || ""),
              allow_fallback: Boolean(allowFallbackInput.checked),
            },
            successMessage: tt("messages.ai_saved"),
            exitAfterSave: false,
            resultCard: "ai",
          })

        const saveTodoist = async () => {
          await saveProvider("todoist")
        }

        const saveVault = async () => {
          await savePartial({
            payload: { vault_paths: paths },
            successMessage: tt("messages.provider_saved", { provider: providerLabel("vault_local") }),
            exitAfterSave: false,
            resultCard: "vault_local",
          })
        }

        const saveAllAndEnter = () =>
          savePartial({
            payload: {
              language: selectedLanguage(),
              ai_provider: selectedAIProvider(),
              gemini_api_key: optionalNonEmpty(geminiApiKeyInput.value),
              gemini_model: String(geminiModelInput.value || ""),
              ollama_model: String(ollamaModelInput.value || ""),
              allow_fallback: Boolean(allowFallbackInput.checked),
              todoist_api_token: optionalNonEmpty(tokenInput.value),
              vault_paths: paths,
              default_task_provider: defaultTaskProviderInput.value,
              default_notes_provider: defaultNotesProviderInput.value,
            },
            successMessage: tt("messages.config_saved"),
            exitAfterSave: true,
            resultCard: "ai",
          })

        const saveDefaults = async () => {
          if (isSubmitting) return
          setBusy(true)
          setCardResult("defaults", tt("messages.saving_defaults"), "pending")
          try {
            integrationState = await postJson("/integrations/defaults", {
              default_task_provider: defaultTaskProviderInput.value,
              default_notes_provider: defaultNotesProviderInput.value,
            })
            refreshBadges()
            setCardResult("defaults", tt("messages.defaults_saved"), "ok")
          } catch (error) {
            setCardResult("defaults", String(error), "warn")
          } finally {
            setBusy(false)
          }
        }

        const providerPayload = (providerId) => {
          const current = providersSnapshot[providerId]
          return current && typeof current === "object" ? current : {}
        }

        const collectCredentials = (providerId) => {
          if (providerId === "todoist") {
            const token = tokenInput.value.trim()
            return token ? { token } : {}
          }
          const fields = providerFields[providerId]?.credentials || {}
          const payload = {}
          for (const [key, input] of Object.entries(fields)) {
            const value = String(input?.value || "").trim()
            if (value) payload[key] = value
          }
          return payload
        }

        const collectSettings = (providerId) => {
          if (providerId === "vault_local") {
            return { paths }
          }
          const fields = providerFields[providerId]?.settings || {}
          const payload = {}
          for (const [key, input] of Object.entries(fields)) {
            const value = String(input?.value || "").trim()
            if (value) payload[key] = value
          }
          return payload
        }

        const saveProvider = async (providerId) => {
          if (isSubmitting) return
          setBusy(true)
          setCardResult(providerId, tt("messages.saving_provider", { provider: providerLabel(providerId) }), "pending")
          try {
            integrationState = await postJson(`/integrations/${providerId}/save`, {
              enabled: Boolean(providerToggles[providerId]?.checked),
              credentials: collectCredentials(providerId),
              settings: collectSettings(providerId),
            })
            refreshBadges()
            setCardResult(providerId, tt("messages.provider_saved", { provider: providerLabel(providerId) }), "ok")
          } catch (error) {
            setCardResult(providerId, String(error), "warn")
          } finally {
            setBusy(false)
          }
        }

        const setProviderEnabled = async (providerId, enabled) => {
          if (isSubmitting) return
          setBusy(true)
          setCardResult(providerId, tt("messages.saving_provider", { provider: providerLabel(providerId) }), "pending")
          try {
            integrationState = await postJson(`/integrations/${providerId}/${enabled ? "enable" : "disable"}`, {})
            refreshBadges()
            setCardResult(
              providerId,
              enabled
                ? tt("messages.provider_enabled", { provider: providerLabel(providerId) })
                : tt("messages.provider_disabled", { provider: providerLabel(providerId) }),
              "ok",
            )
          } catch (error) {
            setCardResult(providerId, String(error), "warn")
            syncToggleFromStatus(providerId)
          } finally {
            setBusy(false)
          }
        }

        const disconnectProvider = async (providerId) => {
          if (isSubmitting) return
          setBusy(true)
          setCardResult(providerId, tt("messages.disconnecting_provider", { provider: providerLabel(providerId) }), "pending")
          try {
            integrationState = await postJson(`/integrations/${providerId}/disconnect`, {})
            if (providerId === "todoist") {
              tokenInput.value = ""
            }
            if (providerId === "vault_local") {
              paths.splice(0, paths.length)
              renderPaths()
            }
            const fieldMap = providerFields[providerId]
            if (fieldMap) {
              const resetGroup = (group) => {
                for (const input of Object.values(group || {})) {
                  input.value = ""
                }
              }
              resetGroup(fieldMap.credentials)
              resetGroup(fieldMap.settings)
            }
            refreshBadges()
            setCardResult(providerId, tt("messages.provider_disconnected", { provider: providerLabel(providerId) }), "ok")
          } catch (error) {
            setCardResult(providerId, String(error), "warn")
          } finally {
            setBusy(false)
          }
        }

        const testProvider = async (providerId) => {
          if (isSubmitting) return
          setBusy(true)
          setCardResult(providerId, tt("messages.testing_provider", { provider: providerLabel(providerId) }), "pending")
          try {
            const result = await postJson(`/integrations/${providerId}/test`, {})
            await refreshIntegrationsState()
            setCardResult(
              providerId,
              result.ok ? result.detail : tt("messages.test_failed", { detail: result.detail }),
              result.ok ? "ok" : "warn",
            )
          } catch (error) {
            setCardResult(providerId, String(error), "warn")
          } finally {
            setBusy(false)
          }
        }

        const testAIProvider = async (providerId) => {
          if (isSubmitting) return
          setBusy(true)
          setAiTestResult(providerId, tt("messages.testing_ai", { provider: providerLabel(providerId) }), "pending")
          setCardResult("ai", tt("messages.testing_ai", { provider: providerLabel(providerId) }), "pending")
          try {
            const payload = {
              provider: providerId,
              gemini_api_key: optionalNonEmpty(geminiApiKeyInput.value),
              gemini_model: optionalNonEmpty(geminiModelInput.value) || "gemini-2.5-flash",
              ollama_model: optionalNonEmpty(ollamaModelInput.value),
            }
            const result = await postJson("/config/ai/test", {
              ...payload,
            })
            if (providerId === "ollama") {
              ollamaStatusEl.textContent = tt("sections.ai.status_detail", { detail: result.detail })
            } else {
              geminiStatusEl.textContent = tt("sections.ai.status_detail", { detail: result.detail })
            }
            setAiTestResult(providerId, result.detail, result.ok ? "ok" : "warn")
            setCardResult("ai", result.detail, result.ok ? "ok" : "warn")
          } catch (error) {
            setAiTestResult(providerId, String(error), "warn")
            setCardResult("ai", String(error), "warn")
          } finally {
            setBusy(false)
          }
        }

        const startOAuth = async (providerId) => {
          if (isSubmitting) return
          setBusy(true)
          setCardResult(providerId, tt("messages.oauth_opening", { provider: providerLabel(providerId) }), "pending")
          try {
            const result = await postJson(`/integrations/oauth/start/${providerId}`, { open_browser: true })
            if (!result.browser_opened && result.auth_url) {
              window.open(result.auth_url, "_blank", "noopener")
            }
            setCardResult(
              providerId,
              result.browser_opened
                ? tt("messages.oauth_started", { provider: providerLabel(providerId) })
                : tt("messages.oauth_url_fallback", { url: result.auth_url }),
              "ok",
            )
          } catch (error) {
            setCardResult(providerId, String(error), "warn")
          } finally {
            setBusy(false)
          }
        }

        const enterWithoutConfig = async () => {
          if (embeddedDashboard) return
          if (isSubmitting) return
          setBusy(true)
          setCardResult("language", tt("messages.opening_assistant"), "pending")
          try {
            if (!currentState.configured) {
              await postJson("/config/skip", {})
            }
            fadeToAssistant()
          } catch (error) {
            setCardResult("language", String(error), "warn")
            setBusy(false)
          }
        }

        const goBack = async () => {
          if (embeddedDashboard) return
          if (isSubmitting) return
          if (!currentState.configured && !fromAssistant) {
            await enterWithoutConfig()
            return
          }
          setBusy(true)
          fadeToAssistant()
        }

        const addPath = () => {
          if (isSubmitting) return
          const path = canonicalPath(pathInput.value)
          if (!path) return
          if (paths.includes(path)) {
            setCardResult("vault_local", tt("messages.path_already_added"), "warn")
            return
          }
          paths.push(path)
          pathInput.value = ""
          renderPaths()
          refreshBadges()
          setCardResult("vault_local", "", "")
        }

        const hydrateFieldsFromSnapshot = () => {
          setAIProviderSelection(initialAIProvider)
          geminiApiKeyInput.value = String(initialGeminiApiKey || "")
          geminiModelInput.value = String(initialGeminiModel || "gemini-2.5-flash")
          ollamaModelInput.value = String(initialOllamaModel || "llama3.2:3b")
          allowFallbackInput.checked = Boolean(initialAllowFallback)
          renderAIProviderPanels()

          const vaultPayload = providerPayload("vault_local")
          const vaultPaths = vaultPayload?.settings?.paths
          if (Array.isArray(vaultPaths) && vaultPaths.length) {
            paths.splice(0, paths.length, ...vaultPaths.map((item) => String(item)))
          }

          for (const providerId of integrationCardProviders) {
            const payload = providerPayload(providerId)
            const credentials = payload?.credentials || {}
            const settings = payload?.settings || {}
            const fieldMap = providerFields[providerId]
            if (!fieldMap) continue
            for (const [key, input] of Object.entries(fieldMap.credentials || {})) {
              if (credentials[key]) input.value = String(credentials[key])
            }
            for (const [key, input] of Object.entries(fieldMap.settings || {})) {
              if (settings[key]) input.value = String(settings[key])
            }
          }
        }

        addPathButton.addEventListener("click", addPath)
        pathInput.addEventListener("keydown", (event) => {
          if (event.key === "Enter") {
            event.preventDefault()
            addPath()
          }
        })

        tokenInput.addEventListener("input", refreshBadges)
        geminiApiKeyInput.addEventListener("input", refreshBadges)
        geminiModelInput.addEventListener("input", refreshBadges)
        ollamaModelInput.addEventListener("input", refreshBadges)
        allowFallbackInput.addEventListener("change", refreshBadges)
        languageInputs.forEach((item) => {
          item.addEventListener("change", refreshBadges)
        })
        aiProviderInputs.forEach((item) => {
          item.addEventListener("change", () => {
            setAIProviderSelection(item.value)
            renderAIProviderPanels()
            refreshBadges()
          })
        })

        saveLanguageButton.addEventListener("click", saveLanguage)
        saveAIProviderButton.addEventListener("click", saveAIProvider)
        saveTodoistButton.addEventListener("click", saveTodoist)
        saveVaultButton.addEventListener("click", saveVault)
        saveDefaultsButton.addEventListener("click", saveDefaults)
        saveAllButton.addEventListener("click", saveAllAndEnter)
        skipButton.addEventListener("click", enterWithoutConfig)
        backButton.addEventListener("click", goBack)
        testOllamaButton.addEventListener("click", () => testAIProvider("ollama"))
        testGeminiButton.addEventListener("click", () => testAIProvider("gemini"))

        document.getElementById("test-todoist").addEventListener("click", () => testProvider("todoist"))
        document.getElementById("disconnect-todoist").addEventListener("click", () => disconnectProvider("todoist"))
        document.getElementById("test-vault_local").addEventListener("click", () => testProvider("vault_local"))
        document.getElementById("disconnect-vault_local").addEventListener("click", () => disconnectProvider("vault_local"))

        for (const providerId of integrationCardProviders) {
          const saveButton = document.getElementById(`save-${providerId}`)
          const testButton = document.getElementById(`test-${providerId}`)
          const disconnectButton = document.getElementById(`disconnect-${providerId}`)
          const oauthButton = document.getElementById(`oauth-${providerId}`)

          if (saveButton) saveButton.addEventListener("click", () => saveProvider(providerId))
          if (testButton) testButton.addEventListener("click", () => testProvider(providerId))
          if (disconnectButton) disconnectButton.addEventListener("click", () => disconnectProvider(providerId))
          if (oauthButton) oauthButton.addEventListener("click", () => startOAuth(providerId))
        }

        for (const providerId of providerIds) {
          const toggle = providerToggles[providerId]
          if (!toggle) continue
          toggle.addEventListener("change", () => {
            setProviderEnabled(providerId, Boolean(toggle.checked))
          })
        }

        setLanguageSelection(initialLanguage)
        pageFailsafeTimer = window.setTimeout(() => {
          forceRevealPage("failsafe")
        }, STARTUP_FAILSAFE_MS)
        window.addEventListener("error", () => {
          forceRevealPage("error")
        })
        window.addEventListener("unhandledrejection", () => {
          forceRevealPage("unhandledrejection")
        })
        postStartupPhase("config_ui_dom_ready", embeddedDashboard ? "embedded" : "app_mode")
        if (!embeddedDashboard) {
          void syncUiMode()
        }
        hydrateFieldsFromSnapshot()
        renderPaths()
        refreshBadges()
        riskNoticeEl.textContent = tt("messages.risk_notice")
        revealPage()
        postStartupPhase("config_ui_ready", currentState.status || "unknown")
      })()
    </script>
  </body>
</html>
