/* danielsolves.ai — UI shared between index.html and portfolio.html.
   Navigation, project cards and their generated visuals, the voice-widget
   theming, accessibility helpers and the footer. Loads after styles/site.css
   and before each page's own <style>. */

/* ====================================================================
     danielsolves.ai — homepage
     Tokens and shared components come from styles/site.css. Everything
     below is homepage-specific and loads after it.
     ==================================================================== */

  /* Animatable angle for the rotating conic border on the featured plan. */
  @property --angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
  }

  :root {
    --ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
  }

  /* --- Scroll progress ------------------------------------------------ */
  .scroll-progress {
    position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 60;
    background: linear-gradient(90deg, var(--accent), var(--accent-3));
    transform: scaleX(0); transform-origin: 0 50%;
    pointer-events: none;
  }
  /* Native scroll-driven animation where supported: the compositor drives it,
     no scroll listener on the main thread. JS fallback handles the rest. */
  @supports (animation-timeline: scroll()) {
    .scroll-progress {
      animation: scrollProgress linear both;
      animation-timeline: scroll(root block);
    }
  }
  @keyframes scrollProgress { from { transform: scaleX(0); } to { transform: scaleX(1); } }

  /* --- Navigation ----------------------------------------------------- */
  .nav {
    position: sticky; top: 0; z-index: 50;
    backdrop-filter: blur(14px);
    background: oklch(from var(--bg) l c h / 0.82);
    border-bottom: 1px solid var(--line);
  }
  .nav-inner { display: flex; align-items: center; justify-content: space-between; height: 70px; gap: 20px; }
  .brand { display: flex; align-items: center; gap: 11px; font-weight: 600; letter-spacing: -0.01em; color: var(--text); text-decoration: none; flex: none; }
  .brand .dot { width: 11px; height: 11px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 14px var(--glow); }
  .nav-links { display: flex; gap: 28px; align-items: center; }
  .nav-links > a { font-family: var(--mono); font-size: 13px; color: var(--muted); text-decoration: none; transition: color 0.2s; position: relative; }
  .nav-links > a::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -6px; height: 1px;
    background: var(--accent); transform: scaleX(0); transform-origin: 0 50%;
    transition: transform 0.32s var(--ease-out-soft);
  }
  .nav-links > a:hover { color: var(--text); }
  .nav-links > a:hover::after, .nav-links > a.active::after { transform: scaleX(1); }
  .nav-links > a.active { color: var(--accent); }
  .nav-right { display: flex; align-items: center; gap: 16px; flex: none; }
  .nav-cta {
    font-family: var(--mono); font-size: 13px; font-weight: 600; padding: 10px 20px; border-radius: var(--r-pill);
    background: var(--accent); color: var(--on-accent); text-decoration: none;
    transition: box-shadow 0.25s, transform 0.25s var(--ease-spring);
    white-space: nowrap;
  }
  .nav-cta:hover { box-shadow: 0 0 26px var(--glow); }
  /* Hidden while an equivalent CTA is on screen (hero at the top, the plans
     section with its own buttons, the contact form at the bottom), so only one
     filled accent button competes at a time.
     Attribute is set by initNavCta() in interactions.js. */
  .nav[data-cta-redundant="1"] .nav-cta {
    opacity: 0; transform: translateY(-6px); pointer-events: none;
    transition: opacity 0.3s var(--ease-out-soft), transform 0.3s var(--ease-out-soft);
  }
  .nav-cta { transition: opacity 0.3s var(--ease-out-soft), transform 0.3s var(--ease-spring), box-shadow 0.25s; }
  .theme-btn {
    background: none; border: 1px solid var(--line-strong); border-radius: 10px;
    width: 34px; height: 34px; padding: 0; cursor: pointer; color: var(--muted);
    display: inline-flex; align-items: center; justify-content: center;
    transition: color 0.2s, border-color 0.2s;
  }
  .theme-btn:hover { color: var(--text); border-color: var(--text); }
  /* Show the icon for the mode the click will switch TO. */
  .theme-btn .ic-sun { display: none; }
  .theme-btn .ic-moon { display: block; }
  :root[data-theme="light"] .theme-btn .ic-sun { display: block; }
  :root[data-theme="light"] .theme-btn .ic-moon { display: none; }
  @media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .theme-btn .ic-sun { display: block; }
    :root:not([data-theme="dark"]) .theme-btn .ic-moon { display: none; }
  }

  .lang-switch { display: inline-flex; align-items: center; gap: 6px; padding-left: 16px; border-left: 1px solid var(--line); }
  .lang-btn { background: none; border: none; padding: 0; font-family: var(--mono); font-size: 12px; color: var(--muted); cursor: pointer; letter-spacing: 0.04em; transition: color 0.18s; }
  .lang-btn:hover { color: var(--text); }
  .lang-btn.active { color: var(--accent); }
  .lang-sep { color: var(--faint); font-family: var(--mono); font-size: 12px; }

  .nav-burger {
    display: none; background: none; border: 1px solid var(--line-strong); border-radius: 10px;
    width: 40px; height: 40px; cursor: pointer; padding: 0;
    align-items: center; justify-content: center; flex-direction: column; gap: 4px;
  }
  .nav-burger span { display: block; width: 16px; height: 1.5px; background: var(--text); border-radius: 2px; transition: transform 0.3s var(--ease-out-soft), opacity 0.2s; }
  .nav-burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
  .nav-burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
  .nav-burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

  .nav-panel {
    position: fixed; inset: 70px 0 0; z-index: 49;
    background: oklch(from var(--bg) l c h / 0.98); backdrop-filter: blur(20px);
    padding: 28px 32px 48px; overflow-y: auto;
  }
  .nav-panel[hidden] { display: none; }
  .nav-panel a {
    display: block; font-size: 26px; font-weight: 600; letter-spacing: -0.02em;
    color: var(--text); text-decoration: none; padding: 16px 0;
    border-bottom: 1px solid var(--line);
    animation: panelIn 0.45s var(--ease-out-soft) backwards;
  }
  .nav-panel a:nth-child(1) { animation-delay: 0.02s; }
  .nav-panel a:nth-child(2) { animation-delay: 0.06s; }
  .nav-panel a:nth-child(3) { animation-delay: 0.10s; }
  .nav-panel a:nth-child(4) { animation-delay: 0.14s; }
  .nav-panel a:nth-child(5) { animation-delay: 0.18s; }
  .nav-panel a:nth-child(6) { animation-delay: 0.22s; }
  .nav-panel a .k { font-family: var(--mono); font-size: 12px; color: var(--accent); display: block; margin-bottom: 4px; letter-spacing: 0.1em; }
  @keyframes panelIn { from { opacity: 0; transform: translateY(14px); } }

  @media (max-width: 900px) {
    .nav-links { display: none; }
    .nav-burger { display: inline-flex; }
    /* The bar CTA does not fit next to burger, theme and language switch, and it
       only duplicates item 06 of the panel. Hidden with display so it stops
       reserving width — opacity alone kept the row 112px too wide at 390px and
       made the whole page scroll sideways. */
    .nav-cta { display: none; }
  }
  @media (max-width: 460px) {
    .nav-cta { padding: 9px 15px; font-size: 12px; }
    .lang-switch { padding-left: 12px; }
  }

  /* --- Projects -------------------------------------------------------- */
  .p-body { gap: 0; }
  .p-links { margin-top: auto; padding-top: 28px; }
  .p-links .p-cta {
    background: none; padding: 0; cursor: pointer; font: inherit;
    display: inline-flex; align-items: center; gap: 9px;
    font-family: var(--mono); font-size: 13px; color: var(--text); text-decoration: none;
    border: none; border-bottom: 1px solid oklch(from var(--accent) l c h / 0.55); padding-bottom: 4px;
    transition: color 0.22s, gap 0.28s var(--ease-out-soft), border-color 0.22s;
  }
  .p-links .p-cta:hover { color: var(--accent); gap: 14px; border-bottom-color: var(--accent); }
  .p-links .p-soon {
    font-family: var(--mono); font-size: 12.5px; color: var(--faint);
    border: 1px solid var(--line); border-radius: var(--r-pill); padding: 6px 13px;
  }

  /* Designed default visual. An <image-slot> sits on top with its empty state
     hidden, so dropping a real screenshot simply covers the artwork. */
  .p-media { min-height: 300px; background: none; overflow: hidden; }
  .p-media image-slot::part(empty) { display: none !important; }
  .viz {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    background:
      radial-gradient(120% 100% at 50% 0%, oklch(from var(--accent) l c h / 0.10), transparent 62%),
      var(--bg-2);
    overflow: hidden;
  }
  .viz::before {
    content: ""; position: absolute; inset: 0; opacity: 0.5;
    background-image:
      linear-gradient(var(--grid-line) 1px, transparent 1px),
      linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
    background-size: 34px 34px;
    mask-image: radial-gradient(100% 80% at 50% 40%, #000 30%, transparent 78%);
  }
  .viz > * { position: relative; }
  .viz-cap {
    position: absolute; left: 22px; bottom: 18px; z-index: 3;
    font-family: var(--mono); font-size: 11px; color: var(--faint);
    letter-spacing: 0.1em; text-transform: uppercase;
  }

  /* MCP topology */
  .viz-mcp { width: 100%; max-width: 360px; padding: 0 24px; }
  .viz-mcp svg { width: 100%; height: auto; overflow: visible; }
  .viz-mcp .ln { stroke: var(--line-strong); stroke-width: 1; fill: none; }
  /* Packets: hidden until mcp-flow.js activates their hop, then exactly one pass.
     dashoffset runs 18 → -100, so the dash enters before the path start and exits
     past its end. The old version looped 14 → -100 against a pattern that repeats
     every 100 units, which is what made it flicker at every restart. */
  .viz-mcp .pulse {
    stroke: var(--accent); stroke-width: 1.7; fill: none;
    stroke-dasharray: 18 82; stroke-dashoffset: 18; stroke-linecap: round;
    opacity: 0;
  }
  .viz-mcp .pulse.is-active { opacity: 1; animation: mcpfFlow 0.72s cubic-bezier(0.4, 0, 0.6, 1) forwards; }
  /* The return trip uses the second accent so request and response read apart. */
  /* Request travels in accent, the response comes back in the success hue. */
  .viz-mcp .pulse.is-back { opacity: 1; stroke: var(--mcpf-ok); animation: mcpfFlowBack 0.72s cubic-bezier(0.4, 0, 0.6, 1) forwards; }
  @keyframes mcpfFlow { from { stroke-dashoffset: 18; } to { stroke-dashoffset: -100; } }
  @keyframes mcpfFlowBack { from { stroke-dashoffset: -100; } to { stroke-dashoffset: 18; } }

  .viz-mcp .node { fill: oklch(from var(--bg) calc(l + 0.05) c h); stroke: var(--line-strong); transition: stroke 0.3s, fill 0.3s; }
  .viz-mcp .node.hot { stroke: oklch(from var(--accent) l c h / 0.7); fill: oklch(from var(--accent) l c h / 0.10); }
  .viz-mcp text { font-family: var(--mono); font-size: 9px; fill: var(--muted); transition: fill 0.3s; }
  .viz-mcp text.hot { fill: var(--accent); }
  /* The node currently taking part in the request. */
  .viz-mcp .node-g.is-lit .node { stroke: var(--accent); fill: oklch(from var(--accent) l c h / 0.2); }
  .viz-mcp .node-g.is-lit text { fill: var(--accent); }

  /* The question, typed into what reads as an input field. */
  .viz-mcp .mcpf-input rect:first-of-type {
    fill: var(--surface-sunken); stroke: var(--line-input); stroke-width: 0.8;
  }
  .viz-mcp .mcpf-input-text { font-size: 10px; fill: var(--text); }
  .viz-mcp .mcpf-caret { fill: var(--accent); opacity: 0; }
  .viz-mcp .mcpf-input.is-typing .mcpf-caret { opacity: 1; animation: mcpfCaret 1s steps(1) infinite; }
  @keyframes mcpfCaret { 50% { opacity: 0; } }
  /* Return key hint, lights up at the moment the request is sent. */
  .viz-mcp .mcpf-enter { font-size: 11px; fill: var(--faint); opacity: 0; transition: opacity 0.25s, fill 0.25s; }
  .viz-mcp .mcpf-input.is-ready .mcpf-enter { opacity: 1; }
  .viz-mcp .mcpf-input.is-sent .mcpf-enter { opacity: 1; fill: var(--accent); }

  /* Request view and result view share the frame. The graph carries the journey,
     the report carries what came back — a number alone undersold it. */
  .viz-mcp .mcpf-graph { transition: opacity 0.45s var(--ease-out-soft); }
  .viz-mcp[data-view="report"] .mcpf-graph { opacity: 0; pointer-events: none; }

  .viz-mcp .mcpf-report {
    opacity: 0; transform: translateY(10px);
    transition: opacity 0.45s var(--ease-out-soft), transform 0.5s var(--ease-out-soft);
    pointer-events: none;
  }
  .viz-mcp[data-view="report"] .mcpf-report { opacity: 1; transform: translateY(0); }
  /* The result reads as a success, not just as more accent. Derived from --accent
     with the hue rotated toward green, so it stays in the palette and follows the
     light/dark switch on its own. */
  .viz-mcp { --mcpf-ok: oklch(from var(--accent) l c 158); }
  .viz-mcp .mcpf-report-card {
    /* srgb, not oklch: --surface-2 is achromatic in light mode, and mixing a hueless
       colour in oklch let the hue drift (measured: 158 turned into 11, a red tint). */
    fill: color-mix(in srgb, var(--mcpf-ok) 7%, var(--surface-2));
    stroke: oklch(from var(--mcpf-ok) l c h / 0.5); stroke-width: 0.8;
  }
  .viz-mcp .mcpf-report-kind { font-size: 6px; fill: var(--faint); letter-spacing: 0.16em; }
  .viz-mcp .mcpf-report-head { font-size: 13px; font-weight: 600; fill: var(--mcpf-ok); }
  .viz-mcp .mcpf-report-rule { stroke: var(--line); stroke-width: 0.8; fill: none; }
  .viz-mcp .mcpf-row-label { font-size: 8px; fill: var(--text); }
  .viz-mcp .mcpf-row-value { font-size: 8px; fill: var(--muted); font-variant-numeric: tabular-nums; }
  .viz-mcp .mcpf-row-track { fill: var(--wash); }
  .viz-mcp .mcpf-row-bar { fill: var(--mcpf-ok); transition: width 0.7s var(--ease-out-soft); }
  .viz-mcp .mcpf-report-foot { font-size: 6.5px; fill: var(--faint); letter-spacing: 0.06em; }

  /* Check badge: pops slightly after the panel, so the eye reads panel, then verdict. */
  .viz-mcp .mcpf-badge circle { fill: var(--mcpf-ok); }
  .viz-mcp .mcpf-badge path { fill: none; stroke: var(--on-accent); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
  .viz-mcp .mcpf-badge {
    opacity: 0; transform: scale(0.5); transform-box: fill-box; transform-origin: center;
    transition: opacity 0.3s var(--ease-out-soft), transform 0.4s var(--ease-spring);
  }
  .viz-mcp[data-view="report"] .mcpf-badge { opacity: 1; transform: scale(1); transition-delay: 0.3s; }

  /* The input rises slightly as the report takes over, so the question reads as
     history and the result as the current thing. */
  .viz-mcp .mcpf-input { transition: transform 0.45s var(--ease-out-soft); }
  .viz-mcp[data-view="report"] .mcpf-input { transform: translateY(-3px); }

  /* On a phone the card is ~320px wide, which scaled the report rows down to 7px
     and the kind label to 5.5px. Reclaim the side padding and size the type up in
     viewBox units so it lands around 9px on screen. */
  @media (max-width: 820px) {
    .viz-mcp { padding: 0 10px; }
    .viz-mcp .mcpf-report-kind { font-size: 7px; }
    .viz-mcp .mcpf-report-head { font-size: 14px; }
    .viz-mcp .mcpf-row-label, .viz-mcp .mcpf-row-value { font-size: 9px; }
    .viz-mcp .mcpf-report-foot { font-size: 7px; }
    .viz-mcp .mcpf-input-text { font-size: 11px; }
    .viz-mcp text { font-size: 10px; }
    .viz-mcp .mcpf-tool-name { font-size: 8px; }
    .viz-mcp .mcpf-tool-label { font-size: 6px; }
  }

  /* Tool call, tied to MCP by its own short connector and a label. */
  .viz-mcp .mcpf-tool { opacity: 0; transition: opacity 0.3s var(--ease-out-soft); }
  .viz-mcp .mcpf-tool.is-shown { opacity: 1; }
  .viz-mcp .mcpf-tool rect { fill: var(--surface-sunken); stroke: oklch(from var(--accent) l c h / 0.5); stroke-width: 0.8; }
  .viz-mcp .mcpf-tool-label { font-size: 5.5px; fill: var(--faint); letter-spacing: 0.16em; }
  .viz-mcp .mcpf-tool-name { font-size: 7.5px; fill: var(--accent); letter-spacing: 0.02em; }
  .viz-mcp .mcpf-tool-link { opacity: 0; transition: opacity 0.3s; }
  .viz-mcp .mcpf-tool-link.is-shown { opacity: 1; }

  /* Pipeline funnel */
  .viz-funnel { width: 100%; max-width: 330px; padding: 0 26px; display: flex; flex-direction: column; gap: 15px; }
  .viz-funnel .row { display: flex; flex-direction: column; gap: 7px; }
  .viz-funnel .rl { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 10.5px; color: var(--faint); letter-spacing: 0.06em; }
  .viz-funnel .rl b { color: var(--muted); font-weight: 500; font-variant-numeric: tabular-nums; }
  .viz-funnel .track { height: 7px; border-radius: 4px; background: var(--wash); overflow: hidden; }
  /* The bars stay at zero until the funnel itself is 60% on screen. Keying this
     to the card's .is-revealed was not enough: that fires at 12% visibility, so
     the sequence finished before the bars scrolled into view. .is-playing comes
     from [data-play-in-view] in reveal.js. */
  .viz-funnel .fill {
    display: block; height: 100%; border-radius: 4px; width: 0;
    background: linear-gradient(90deg, var(--accent-2), var(--accent));
  }
  .viz-funnel.is-playing .fill { animation: funnelFill 1.5s var(--ease-out-soft) forwards; }
  .viz-funnel .row:nth-child(1) .fill { --w: 100%; animation-delay: 0.1s; }
  .viz-funnel .row:nth-child(2) .fill { --w: 62%; animation-delay: 0.3s; }
  .viz-funnel .row:nth-child(3) .fill { --w: 26%; animation-delay: 0.5s; }
  .viz-funnel .row:nth-child(4) .fill { --w: 9%; animation-delay: 0.7s; background: var(--accent); box-shadow: 0 0 14px var(--glow); }
  @keyframes funnelFill { to { width: var(--w); } }

  /* Voice waveform */
  .viz-wave { display: flex; align-items: center; gap: 5px; height: 96px; }
  .viz-wave i {
    display: block; width: 4px; border-radius: 3px;
    background: linear-gradient(180deg, var(--accent), var(--accent-2));
    animation: waveBeat 1.25s ease-in-out infinite alternate;
    height: 16px;
  }
  @keyframes waveBeat { to { height: var(--h, 40px); } }

  /* App surface stack */
  .viz-stack { width: 100%; max-width: 300px; padding: 0 24px; display: flex; flex-direction: column; gap: 10px; }
  .viz-stack .card {
    border: 1px solid var(--line); border-radius: 11px; padding: 13px 15px;
    background: var(--surface-2);
    display: flex; align-items: center; gap: 11px;
  }
  .viz-stack .card .ring {
    width: 26px; height: 26px; border-radius: 50%; flex: none;
    background: conic-gradient(var(--accent) var(--p, 60%), var(--line-strong) 0);
    -webkit-mask: radial-gradient(circle, transparent 8px, #000 9px);
    mask: radial-gradient(circle, transparent 8px, #000 9px);
  }
  .viz-stack .card .bars { flex: 1; display: flex; flex-direction: column; gap: 6px; }
  .viz-stack .card .bars i { display: block; height: 5px; border-radius: 3px; background: var(--line-strong); }
  .viz-stack .card .bars i:last-child { width: 55%; }

  @media (prefers-reduced-motion: reduce) {
    .viz-mcp .pulse, .viz-mcp .pulse.is-active, .viz-mcp .pulse.is-back, .viz-wave i { animation: none; opacity: 0; }
    .viz-funnel .fill, .viz-funnel.is-playing .fill { animation: none; width: var(--w); }
  }

  /* Fiona is the third project. Two image+text splits in a row is the cap, so
     this one runs full width instead of continuing the zigzag. */
  .project--band { display: block; padding: 0; }
  .band-viz { position: relative; height: 190px; display: flex; align-items: center; justify-content: center;
    background: radial-gradient(120% 140% at 50% 0%, oklch(from var(--accent) l c h / 0.12), transparent 62%),
      var(--bg-2);
    border-bottom: 1px solid var(--line); overflow: hidden; }
  .band-viz .viz-wave { height: 110px; gap: 7px; }
  .band-viz .viz-wave i { width: 5px; }
  .band-body { display: grid; grid-template-columns: 1fr auto; gap: 40px; align-items: center; padding: 34px 40px 38px; }
  .band-copy h3 { font-size: clamp(24px, 3vw, 32px); font-weight: 600; letter-spacing: -0.02em; line-height: 1.08; }
  .band-copy p { color: var(--muted); font-size: 15px; line-height: 1.6; margin-top: 14px; max-width: 62ch; text-wrap: pretty; }
  .band-copy .p-stack { margin-top: 20px; }
  .band-action { flex: none; }
  @media (max-width: 760px) {
    .band-body { grid-template-columns: 1fr; gap: 26px; padding: 28px 24px 32px; align-items: start; }
    .band-viz { height: 150px; }
  }

  /* --- Accessibility ------------------------------------------------------ */
  .skip-link {
    position: absolute; left: 12px; top: -100px; z-index: 100;
    background: var(--accent); color: var(--on-accent, var(--on-accent));
    font-family: var(--mono); font-size: 13px; font-weight: 600;
    padding: 12px 20px; border-radius: var(--r-control); text-decoration: none;
    transition: top 0.2s var(--ease-out-soft);
  }
  .skip-link:focus { top: 12px; }

  /* One visible focus ring for everything interactive, instead of an allowlist
     that quietly missed most links and buttons. */
  :where(a, button, input, textarea, select, [tabindex]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
  }

  /* Anchors would otherwise land under the sticky nav. */
  #main [id], .chat-form { scroll-margin-top: 88px; }

  /* Removes the ~300ms tap delay on iOS. */
  a, button, [role="button"], .chat-chip, .slot-card { touch-action: manipulation; }

  /* The DE/EN switch glyphs were a 15x16px target. Grow the hit area only. */
  .lang-btn { padding: 8px 6px; margin: -8px -6px; }

  .nav-panel { overscroll-behavior: contain; padding-bottom: max(48px, env(safe-area-inset-bottom)); }

  @media (prefers-reduced-motion: reduce) {
    html, .chat-messages { scroll-behavior: auto; }
    .nav-panel a { animation: none; }
  }

  :root[data-theme="light"] elevenlabs-convai,
  :root:not([data-theme="dark"]) elevenlabs-convai {
    --el-base: oklch(1 0 0);
    --el-base-hover: oklch(0.96 0.004 260);
    --el-base-active: oklch(0.93 0.006 260);
    --el-base-border: oklch(0.25 0.02 260 / 0.2);
    --el-base-subtle: oklch(0.44 0.015 260);
    --el-base-primary: oklch(0.22 0.015 260);
    --el-accent: oklch(0.58 0.13 195);
    --el-accent-hover: oklch(0.53 0.13 195);
    --el-accent-active: oklch(0.49 0.13 195);
    --el-accent-border: oklch(0.58 0.13 195 / 0.5);
    --el-accent-primary: oklch(0.99 0 0);
    --el-accent-subtle: oklch(0.58 0.13 195 / 0.12);
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) elevenlabs-convai { --el-base: oklch(0.15 0.012 260); }
  }

  /* --- Footer ----------------------------------------------------------- */
  footer { border-top: 1px solid var(--line); padding: 40px 0; margin-top: clamp(28px, 4vw, 45px); }
  .foot-inner { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 18px; }
  .foot-inner .c { font-family: var(--mono); font-size: 12.5px; color: var(--faint); }
  /* Wraps: five links at 22px gaps overflowed a 320px viewport and made the whole
     page scroll sideways. */
  .foot-links { display: flex; flex-wrap: wrap; gap: 12px 22px; }
  .foot-links a { font-family: var(--mono); font-size: 12.5px; color: var(--muted); text-decoration: none; }
  .foot-links a:hover { color: var(--accent); }
