/* Neva dock styles — extracted from the bundle. */

  .neva-root, .neva-root * { box-sizing: border-box; }
  #nevaCanvasHost{position:fixed;inset:0;z-index:52;pointer-events:none;opacity:0;transition:opacity .7s ease}
  #nevaCanvasHost.neva-core-ready{opacity:1}
  #nevaCanvasHost canvas{width:100%!important;height:100%!important}

  /* ===================== FIXED BOTTOM DOCK: NEVA (design component, verbatim) ===================== */
  :root {
    --neva-radius-sm: 14px;
    --neva-text-primary: rgba(255, 255, 255, 0.92);
    --neva-text-muted: rgba(255, 255, 255, 0.45);
    --neva-col:#eef2f8;
  }
  /* ---------- THE DOCK IS NOT ON UNTIL IT IS REACHED FOR ----------
     Nine icons with a word under each, standing over the world at all times, is
     the grammar of an application and this is meant to be a place. So the resting
     state is the orb and one hairline, and the keys arrive when a hand comes down
     to the bottom of the frame — blur-to-sharp, on the file's own curve.

     AND THE KEYS ARE NOT EIGHT SURFACES, THEY ARE TWO. Eight bordered discs in a
     row is eight objects to look at, which is what made this read as a strip of
     buttons rather than as one piece of furniture. It is now exactly the cluster
     the top right of the frame already uses: one borderless panel with bare glyphs
     inside it, and one on the other side of the orb. Two islands at the top of the
     frame, two at the bottom, the orb between them — one component, used four
     times, instead of four things that resemble each other.

     No labels on the keys. The word lives above the key under the pointer only, so
     eight names never have to be read at once to find one. */
  .neva-dock {
    position: fixed; left: 50%; bottom: 32px; transform: translateX(-50%);
    transform-origin: bottom center;
    z-index: 54; display: flex; align-items: center;
    padding: 6px 10px;
    /* THE SPHERE IS THREADED THROUGH THE BAR, NOT PERCHED ON IT. The notch was
       aimed above the top edge, which left the orb floating a full bar-height clear
       of the menu and reading as a separate object that happened to be nearby.
       Centred, the bar closes in around it and the clearance is an even 6px arc the
       whole way round. cy is a percentage on purpose: it stays the middle if the
       padding or the key size ever change. */
    --neva-notch-r: 44px;
    --neva-notch-cy: 50%;
    --neva-notch: radial-gradient(circle var(--neva-notch-r) at 50% var(--neva-notch-cy),
      transparent calc(var(--neva-notch-r) - 1px), #000 var(--neva-notch-r));
    font-family: "SF Pro Rounded","SF Pro Display",-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
  }
  /* ONE BAR, NOT THREE OBJECTS. Two boxes flanking a sphere is three things of very
     unequal weight with two dead gaps between them, which is what made this read as
     assembled rather than composed. The surface runs under all four keys now and the
     sphere is a hole cut in it.

     No drop shadow on this one, and that is forced rather than chosen: an outer
     shadow is painted outside the border box, the mask that cuts the notch is
     clipped to the border box, so the shadow would be masked away to nothing. The
     depth is carried by the top highlight and a dark lip along the bottom instead. */
  .neva-dock::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    border-radius: var(--chrome-radius, 10px);
    background: var(--chrome-glass, rgba(14, 17, 19, 0.55));
    backdrop-filter: var(--chrome-blur, blur(22px));
    -webkit-backdrop-filter: var(--chrome-blur, blur(22px));
    box-shadow: var(--chrome-highlight), inset 0 -1px 0 rgba(0, 0, 0, 0.35);
    -webkit-mask: var(--neva-notch); mask: var(--neva-notch);
    opacity: 0; transform: translateY(10px);
    transition:
      opacity .9s var(--chrome-ease, cubic-bezier(.2,.6,.2,1)),
      transform .9s var(--chrome-ease, cubic-bezier(.2,.6,.2,1));
  }
  .neva-dock.is-near::before { opacity: 1; transform: translateY(0); }
  .neva-dock-group {
    display: flex; align-items: center; gap: 2px;
    opacity: 0; filter: blur(9px); transform: translateY(10px);
    pointer-events: none;
    transition:
      opacity .9s var(--chrome-ease, cubic-bezier(.2,.6,.2,1)),
      filter .9s var(--chrome-ease, cubic-bezier(.2,.6,.2,1)),
      transform .9s var(--chrome-ease, cubic-bezier(.2,.6,.2,1));
  }
  .neva-dock.is-near .neva-dock-group {
    opacity: 1; filter: blur(0); transform: translateY(0); pointer-events: auto;
  }
  /* the far group leaves a beat after the near one, so the row reads as arriving
     rather than as being switched on */
  .neva-dock.is-near .neva-dock-group:last-of-type { transition-delay: .09s; }

  /* the key itself owns no surface: the panel behind it is the surface, and a
     glass disc sitting on glass is the same material twice */
  .neva-dock-item {
    display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
    /* the stack is top-aligned, not centred: the bottom strip of the key is not free
       space, the active dot lives there.
       the gap is 4 and not 7 so the glyph and its name read as ONE thing rather than
       as two stacked things. min-width is set by the longest names (ACTIONS,
       CONTEXT) plus the padding, so all four keys come out the same width without
       being padded out to a round number with dead space in them. Rename a key and
       this comes with it, or the long ones grow past the rest and the row goes
       lopsided. */
    gap: 4px; position: relative;
    min-width: 70px; height: 40px; padding: 3px 6px 0;
    border-radius: 8px;
    color: rgba(240, 242, 236, 0.72); cursor: pointer;
    background: transparent; border: none; font: inherit;
    transition:
      color .3s var(--chrome-ease, cubic-bezier(.2,.6,.2,1)),
      background .3s var(--chrome-ease, cubic-bezier(.2,.6,.2,1));
  }
  .neva-dock-item:hover, .neva-dock-item:focus-visible {
    color: #eef1f6; outline: none;
    background: var(--chrome-fill-hover, rgba(255, 255, 255, 0.09));
  }
  /* the panel that is open is lit, not lifted: a key that moves under the hand
     and a key that stays put mean two different things, and only one of them is
     "this is the one you are in" */
  .neva-dock-item.on { color: #ffffff; }
  .neva-dock-item.on::after {
    content: ""; position: absolute; left: 50%; bottom: 2px; transform: translateX(-50%);
    width: 2.5px; height: 2.5px; border-radius: 50%;
    background: #fff; box-shadow: 0 0 6px 1px rgba(255,255,255,.55);
  }
  /* An open panel is lit; an open MICROPHONE has to read as louder than that, because
     it is the one dock state that keeps doing something after you look away. Same dot as
     .on, breathing and in warning red. */
  .neva-dock-item.is-listening { color: #fff; }
  .neva-dock-item.is-listening::after {
    background: #ff5f57; box-shadow: 0 0 8px 2px rgba(255, 95, 87, .7);
    animation: neva-listening 1.4s ease-in-out infinite;
  }
  @keyframes neva-listening { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
  @media (prefers-reduced-motion: reduce) {
    .neva-dock-item.is-listening::after { animation: none; }
  }
  .neva-dock-icon { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

  /* THE NAME IS UNDER THE ICON AND IT IS ALWAYS THERE. It used to appear on hover
     only, which is the right trade when there are eight keys and the row would
     otherwise be a wall of words. There are four. Four names read as a menu; hiding
     them made you point at a glyph to find out what it was.
     It is set as a kicker rather than as a caption, which is the only label voice
     this product has: uppercase, tracked out, and dim enough that the icon is still
     what you see first. */
  .neva-dock-label {
    font-size: 8.5px; font-weight: 300; line-height: 1;
    letter-spacing: var(--chrome-kicker-track, 0.28em);
    text-indent: var(--chrome-kicker-track, 0.28em);
    text-transform: uppercase; white-space: nowrap;
    color: rgba(214, 221, 232, 0.62);
    transition: color .3s var(--chrome-ease, cubic-bezier(.2,.6,.2,1));
  }
  .neva-dock-item:hover .neva-dock-label,
  .neva-dock-item:focus-visible .neva-dock-label { color: #eef1f6; }
  .neva-dock-item.on .neva-dock-label { color: #eef1f6; }
  @media (prefers-reduced-motion: reduce) {
    .neva-dock-group { transition-duration: .01ms; filter: none; transform: none; }
  }

  /* the orb never leaves. It is the one thing on the frame that says the product is
     awake, so it stands whether the keys are out or not.

     Its box is a key's height and the sphere is drawn absolutely inside it, so the
     bar's geometry never depends on the sphere's size. bottom is negative because
     the sphere is bigger than its box: -18px puts its centre exactly on the bar's
     centreline, which is where the notch is aimed. Those two numbers are a pair. If
     one moves the other has to.

     The sphere is deliberately NOT scaled with the bar. It is the one thing down
     here that is not chrome, so every time the furniture around it gets quieter it
     should read as more present, not less. */
  .neva-orb {
    position: relative; display: block;
    width: 76px; height: 40px;
    margin: 0 14px; padding: 0; background: none; border: none; cursor: pointer;
  }
  .neva-orb-stage {
    position: absolute; left: 0; right: 0; bottom: -18px; margin-inline: auto;
    width: 76px; height: 76px; border-radius: 50%; overflow: hidden; display: block;
    background: var(--chrome-glass, rgba(14, 17, 19, 0.55));
    background-attachment: fixed;
    backdrop-filter: var(--chrome-blur, blur(22px));
    -webkit-backdrop-filter: var(--chrome-blur, blur(22px));
    box-shadow: var(--chrome-shadow), var(--chrome-highlight);
    will-change: transform; animation: neva-orb-breathe 6s ease-in-out infinite;
  }
  .neva-orb-stage::after {
    content: ""; position: absolute; inset: 0; border-radius: 50%; z-index: 3; pointer-events: none;
    background: var(--neva-tint, transparent); mix-blend-mode: multiply;
    opacity: var(--neva-tint-opacity, 0); transition: background .45s ease, opacity .45s ease;
  }
  .neva-orb-visual { position: absolute; inset: 0; opacity: 0; transition: opacity 1s ease; }
  .neva-orb-visual.is-active { opacity: 1; }
  .neva-orb-outer { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; will-change: transform; animation: neva-orb-spin 40s linear infinite; }
  .neva-orb-light { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; will-change: transform; animation: neva-orb-light-spin 30s linear infinite reverse; transition: opacity 1.3s ease; }
  body:not(.neva-docked) .neva-orb-light { opacity: .18; }
  body.neva-docked .neva-orb-stage { backdrop-filter: var(--chrome-blur, blur(22px)); -webkit-backdrop-filter: var(--chrome-blur, blur(22px)); }
  .neva-orb-light-img { width: 80%; height: auto; mix-blend-mode: screen; will-change: transform, opacity, filter; animation: neva-orb-light-pulse 4.6s ease-in-out infinite; }
  .neva-orb-light-img-overlay { position: absolute; left: 50%; top: 50%; opacity: .5; mix-blend-mode: screen; filter: url(#nevaFlow); animation: neva-orb-light-flow 5.2s ease-in-out infinite; }
  @keyframes neva-orb-breathe { 0%{transform:scale(1)} 50%{transform:scale(1.05)} 100%{transform:scale(1)} }
  @keyframes neva-orb-spin { 0%{transform:rotate(0)} 100%{transform:rotate(360deg)} }
  @keyframes neva-orb-light-spin { 0%{transform:rotate(0)} 100%{transform:rotate(360deg)} }
  @keyframes neva-orb-light-pulse {
    0%{transform:scale(.97);opacity:.85;filter:brightness(1) drop-shadow(0 0 5px rgba(255,255,255,.1))}
    25%{transform:scale(1.05);opacity:.94;filter:brightness(1.12) drop-shadow(0 0 12px rgba(255,255,255,.2))}
    50%{transform:scale(1.12);opacity:1;filter:brightness(1.24) drop-shadow(0 0 20px rgba(255,255,255,.32))}
    75%{transform:scale(1.05);opacity:.94;filter:brightness(1.1) drop-shadow(0 0 12px rgba(255,255,255,.18))}
    100%{transform:scale(.97);opacity:.85;filter:brightness(1) drop-shadow(0 0 5px rgba(255,255,255,.1))}
  }
  @keyframes neva-orb-light-flow {
    0%{transform:translate(-50%,-50%) scale(.97);opacity:.4;filter:url(#nevaFlow) brightness(1.05)}
    33%{transform:translate(-50%,calc(-50% + 1.5px)) scale(1.08);opacity:.6;filter:url(#nevaFlow) brightness(1.2)}
    66%{transform:translate(-50%,calc(-50% + 1px)) scale(1.04);opacity:.5;filter:url(#nevaFlow) brightness(1.14)}
    100%{transform:translate(-50%,-50%) scale(.97);opacity:.4;filter:url(#nevaFlow) brightness(1.05)}
  }
  @media (prefers-reduced-motion: reduce) {
    .neva-orb-stage,.neva-orb-outer,.neva-orb-light,.neva-orb-light-img,.neva-orb-light-img-overlay{animation:none}
  }
  @media (max-width: 768px) {
    .neva-dock{bottom:20px;padding:6px 8px;max-width:calc(100vw - 20px);
      --neva-notch-r:34px; --neva-notch-cy:50%}
    .neva-dock-item{min-width:58px;height:36px;gap:4px;padding:2px 4px 0}
    .neva-dock-icon{width:16px;height:16px}
    .neva-dock-label{font-size:7.5px;letter-spacing:.16em;text-indent:.16em}
    .neva-orb{width:56px;height:36px;margin:0 10px}
    .neva-orb-stage{width:56px;height:56px;bottom:-10px}
  }

  /* ===================== flyout panels — same frosted face ===================== */
  .neva-panel{
    position:fixed; left:50%; bottom:calc(28px + 90px);
    transform:translateX(-50%) translateY(10px) scale(.98);
    z-index:55; min-width:300px; max-width:min(760px,94vw);
    background:linear-gradient(145deg, rgba(30,33,32,.38) 0%, rgba(16,19,18,.34) 100%);
    backdrop-filter:blur(25px) saturate(120%); -webkit-backdrop-filter:blur(25px) saturate(120%);
    border-radius:22px; box-shadow:0 32px 80px rgba(0,0,0,.6), inset 0 0 0 1px rgba(255,255,255,.08);
    padding:15px 18px; opacity:0; pointer-events:none;
    transition:opacity .2s ease, transform .2s ease;
    font-family:"SF Pro Rounded","SF Pro Display",-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
    color:var(--neva-text-primary);
  }
  .neva-panel.open{opacity:1; pointer-events:auto; transform:translateX(-50%) translateY(0) scale(1)}
  .neva-panel h4{font-size:10.5px; font-weight:500; letter-spacing:.14em; text-transform:uppercase; color:var(--neva-text-muted); margin:0 0 11px}
  .neva-row{display:flex; flex-wrap:wrap; gap:7px; justify-content:center; align-items:center}
  .neva-row + .neva-row{margin-top:9px}
  .neva-panel button{
    font:inherit; font-size:12px; letter-spacing:.01em; color:var(--neva-text-primary);
    background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.09);
    border-radius:10px; padding:7px 13px; cursor:pointer;
    transition:background .18s ease, transform .18s ease, border-color .18s ease;
  }
  .neva-panel button:hover{background:rgba(255,255,255,.10); transform:translateY(-1px)}
  .neva-panel button.on{background:rgba(255,255,255,.13); border-color:color-mix(in srgb, var(--neva-col) 45%, transparent)}
  .neva-orb-colors{gap:12px}
  .neva-panel .neva-orb-swatch{
    width:32px; height:32px; padding:0; border-radius:50%;
    border:1px solid rgba(255,255,255,.22); cursor:pointer;
    background:var(--sw, #fefefe);
    transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease;
  }
  .neva-panel .neva-orb-swatch:hover{background:var(--sw, #fefefe); transform:translateY(-1px) scale(1.08)}
  .neva-panel .neva-orb-swatch.is-active{background:var(--sw, #fefefe); border-color:#fff; box-shadow:0 0 11px 1px var(--sw, #fefefe)}
  .orb-swatch-white{--sw:#fefefe}
  .orb-swatch-gold{--sw:#d9a36f}
  .orb-swatch-green{--sw:#72854f}
  .orb-swatch-blue{--sw:#668ebd}
  .orb-swatch-purple{--sw:#c59ad8}
  .orb-swatch-red{--sw:#c70c36}
  .neva-slider{display:flex; align-items:center; gap:8px; color:var(--neva-text-muted); font-size:11px}
  .neva-panel input[type="range"]{accent-color:#cfd8e2; width:108px; cursor:pointer}
  #nevaDen{font-style:normal; font-variant-numeric:tabular-nums; min-width:52px; color:var(--neva-text-muted); font-size:11px}
  #nevaText{
    font:inherit; font-size:13px; color:var(--neva-text-primary); background:rgba(255,255,255,.05);
    border:1px solid rgba(255,255,255,.09); border-radius:999px; padding:10px 16px; width:min(400px,68vw); outline:none;
  }
  #nevaText:focus{border-color:color-mix(in srgb, var(--neva-col) 40%, transparent)}
  #nevaCols{display:flex; align-items:center; gap:8px; color:var(--neva-text-muted); font-size:11px}
  #nevaCols input[type="color"]{width:25px;height:25px;border:1px solid rgba(255,255,255,.14);border-radius:50%;
    background:none;padding:0;cursor:pointer;-webkit-appearance:none;appearance:none}
  #nevaCols input[type="color"]::-webkit-color-swatch-wrapper{padding:2px}
  #nevaCols input[type="color"]::-webkit-color-swatch{border:none;border-radius:50%}
  .neva-note{color:var(--neva-text-muted); font-size:10.5px; letter-spacing:.02em; text-align:center; margin-top:9px}
  /* What she heard, quoted back. Brighter than the status line above it: the status is
     chrome, this is the content, and reading it back is the only way to tell a bad
     transcription from a bad answer. */
  .neva-voice-heard{color:#dfe5ee; font-style:italic; margin-top:5px; max-width:34ch; margin-inline:auto}
  #nevaFps{color:var(--neva-text-muted); font-size:11px; font-variant-numeric:tabular-nums}
  body.neva-drag::after{
    content:"drop photo · GLB · SVG"; position:fixed; inset:12px; z-index:70;
    border:2px dashed rgba(255,255,255,.35); border-radius:24px; display:grid; place-items:center;
    font-size:20px; letter-spacing:1px; color:#fff; background:rgba(8,9,10,.55); backdrop-filter:blur(4px);
  }

  /* ---------- THE OPTIONS ARE PARTICLES; THESE ARE ONLY THE HANDLES ----------
     YES and NO live in the point cloud with the rest of the question — nothing
     here draws them. What these buttons carry is everything a point cloud cannot:
     a hit target, somewhere for focus to land, a name to read aloud. They are positioned every
     frame from the same coordinates the particles were built from (see
     neva.acts.js), which is why they have no size of their own here.

     The layer ignores the pointer so the world underneath stays live; only the
     handles take it back. */
  .neva-choices{
    position:fixed; inset:0; z-index:53; pointer-events:none;
    opacity:0; transition:opacity .55s ease;
  }
  .neva-choices.is-shown{opacity:1}
  /* Nothing is drawn here, at any state. Hover is answered in the cloud instead
     — the hovered word's particles scale about its own centre (neva.acts.js,
     setHover) — because a rectangle of glass around a word made of light is a
     second object appearing next to the first, and the word is the only object
     there is. Same for focus: the zoom is the focus ring. */
  .neva-choice{
    position:absolute; transform:translate(-50%,-50%);
    appearance:none; -webkit-appearance:none; background:none; border:0; outline:none;
    padding:0; margin:0; font:inherit; color:transparent; cursor:pointer;
    pointer-events:auto;
  }
  .neva-choices.is-answered .neva-choice{cursor:default}

  /* ---------- THE PROMPT IS A LINE, NOT A PANEL ----------
     It sits above the orb and it is not on screen at rest. The dock already
     behaves that way, and a permanent input at the bottom of the frame would be
     a fifth piece of furniture in a place meant to hold one.

     No box around it either: a hairline under the text is enough to say "type
     here" when the thing you are typing to is a sphere of light directly below
     it. The blur-to-sharp arrival is the dock's own curve, so the two read as
     one surface waking up. */
  .neva-prompt{
    position:fixed; left:50%; bottom:132px; transform:translateX(-50%) translateY(8px);
    z-index:56; width:min(520px, 78vw);
    opacity:0; filter:blur(7px); pointer-events:none;
    transition:
      opacity .5s var(--chrome-ease, cubic-bezier(.2,.6,.2,1)),
      filter .5s var(--chrome-ease, cubic-bezier(.2,.6,.2,1)),
      transform .5s var(--chrome-ease, cubic-bezier(.2,.6,.2,1));
    font-family:"SF Pro Rounded","SF Pro Display",-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
  }
  .neva-prompt.is-open{
    opacity:1; filter:blur(0); transform:translateX(-50%) translateY(0); pointer-events:auto;
  }
  .neva-prompt-field{
    width:100%; padding:10px 4px; text-align:center;
    font:inherit; font-size:15px; letter-spacing:.01em; color:var(--neva-text-primary, rgba(255,255,255,.92));
    background:none; border:0; border-bottom:1px solid rgba(255,255,255,.14); border-radius:0;
    outline:none; caret-color:var(--neva-col, #eef2f8);
    transition:border-color .35s ease;
  }
  .neva-prompt-field::placeholder{
    color:rgba(255,255,255,.32); letter-spacing:.22em; text-transform:uppercase; font-size:10.5px;
  }
  .neva-prompt-field:focus{border-bottom-color:color-mix(in srgb, var(--neva-col, #eef2f8) 45%, transparent)}
  /* Thinking is shown by the hairline breathing, because she is the one who
     should look busy and she is right there under it. */
  .neva-prompt.is-working .neva-prompt-field{animation:neva-prompt-wait 1.6s ease-in-out infinite}
  @keyframes neva-prompt-wait{
    0%,100%{border-bottom-color:rgba(255,255,255,.10)}
    50%{border-bottom-color:rgba(255,255,255,.34)}
  }
  @media (max-width: 768px){
    .neva-prompt{bottom:110px; width:min(420px, 88vw)}
  }
