@charset "UTF-8";
/* ═══════════════════════════════════════════════════════════════════════════
   EDGEwise AI — crystal. One visual identity for all three surfaces.

   Previously styled THREE times: inline in landing.html, inline again in
   coming-soon.html, and a third copy in style.css for the terminal. They had
   already drifted. This file is the single source; the three pages link it.

   It styles BOTH naming schemes — the public pages' #pubAi* ids and the
   terminal's .ai-* classes — so no JavaScript had to change.

   ── MATERIAL ───────────────────────────────────────────────────────────────
   The brief is crystal glass, and glass is not "a dark card with blur". Three
   things make a surface read as real glass rather than frosted plastic:

     1. TRANSPARENCY. The first pass sat at 97% opaque, so nothing showed
        through and the blur was doing no work. These surfaces are 46-62%.
     2. A SPECULAR EDGE. Real glass catches light on the rim facing the source.
        Every panel uses a gradient border — bright at the top-left, fading to
        almost nothing at the bottom-right — via the padding-box/border-box
        two-background technique. A flat 1px border is the giveaway of fake glass.
     3. AN INNER HIGHLIGHT. A hairline of light just inside the top edge, where
        the light enters the material.

   The orb is a crystal sphere with gold luminescence INSIDE it, not a gold
   disc: a dark translucent body, a warm core glowing through, a hard specular
   dot top-left and a soft rim light bottom-right. Light through a material
   always looks more expensive than paint on a surface.

   ── COLOUR ─────────────────────────────────────────────────────────────────
   Every AI assistant on the web is a blue-to-purple gradient, and so was this.
   EDGEwise already owns a warm gold (--gold:#e0b46a on the landing page).
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  --ew-gold:#e3b76d;
  --ew-gold-hi:#ffe6b8;
  --ew-gold-dp:#a97c34;
  --ew-glow:rgba(227,183,109,.45);

  /* Live indicator. Deliberately NOT gold: gold is the brand's decorative
     colour and appears all over these panels, so a gold dot reads as ornament.
     Green is the one colour on the surface that means status, which is what
     makes it say "on the air" at a glance. Matches --green on the landing page
     and the terminal's live dots so the whole product agrees. */
  --ew-live:#2fbd8a;
  --ew-live-hi:#8df2cd;

  --ew-ink:#f4f2ee;              /* warm-biased near-white, never pure #fff   */
  --ew-ink-2:#b3ada1;
  --ew-ink-3:#7d776c;

  /* glass bodies — deliberately low opacity so the blur has something to do */
  --ew-glass:rgba(18,17,21,.62);
  --ew-glass-lo:rgba(22,20,25,.46);
  --ew-glass-hi:rgba(255,255,255,.07);

  /* the specular rim: bright where light enters, gone where it leaves */
  --ew-rim:linear-gradient(150deg,
             rgba(255,255,255,.34) 0%,
             rgba(227,183,109,.24) 34%,
             rgba(255,255,255,.05) 62%,
             rgba(255,255,255,.02) 100%);

  --ew-blur:blur(34px) saturate(150%);
  --ew-r:20px;
  --ew-font:'Segoe UI',system-ui,-apple-system,Roboto,Helvetica,Arial,sans-serif;
}

/* ── LAUNCHER — the spark is the subject ───────────────────────────────────
   FIRST ATTEMPT WAS WRONG. A filled sphere with a small glyph inside reads as
   a knob or a dial: the eye lands on the object, and the spark is a detail on
   its surface. Inverted here. The glass is now a thin HALO the spark floats
   inside, the spark itself is large and luminous, and the animation belongs to
   the spark rather than the container. Sized at 45px - present without dominating the corner.                    */
/* ── LAUNCHER ──────────────────────────────────────────────────────────────
   ONE glass pill, permanently in its expanded form.

   It used to be a 45px sphere whose label only unfurled on hover. Two problems
   with that: a phone cannot hover, so on mobile the label never appeared at all
   and the control was an unexplained glowing ball; and the sphere read as
   ornament rather than as a button.

   It now rests DIMMED so it never competes with the page, and comes fully
   forward on intent - cursor over it, keyboard focus, or a touch (which JS
   turns into a temporary .awake class, standing in for the hover a phone
   cannot produce). */
#pubAi,
.ai-launcher{
  position:fixed; right:22px; bottom:22px; z-index:500;
  display:inline-flex; align-items:center; gap:9px;
  padding:11px 19px 11px 16px; cursor:pointer;
  border:1px solid transparent; border-radius:999px;
  background:var(--ew-glass) padding-box, var(--ew-rim) border-box;
  -webkit-backdrop-filter:var(--ew-blur); backdrop-filter:var(--ew-blur);
  box-shadow:0 10px 30px rgba(0,0,0,.42);
  font-family:var(--ew-font); color:var(--ew-ink);
  opacity:.56; filter:saturate(.78);
  transition:opacity .26s ease, filter .26s ease, box-shadow .26s ease,
             transform .26s cubic-bezier(.2,.8,.2,1);
  -webkit-tap-highlight-color:transparent;
}
#pubAi:hover, #pubAi:focus-visible, #pubAi.awake,
.ai-launcher:hover, .ai-launcher:focus-visible, .ai-launcher.awake{
  opacity:1; filter:none; transform:translateY(-2px);
  box-shadow:0 16px 42px rgba(0,0,0,.5), 0 0 30px rgba(227,183,109,.2);
}
/* Never on screen at the same time as the panel. The author `display:inline-flex`
   above would beat the UA stylesheet's [hidden]{display:none}, so this has to
   be stated explicitly or hiding the launcher would silently do nothing. */
#pubAi[hidden], .ai-launcher[hidden]{ display:none!important }

/* The spark, now a flat glyph rather than a sphere: no circle, no rim, no
   backdrop of its own - it sits inside the pill's glass with the label. */
#pubAi .po, .ai-launcher .ai-orb{
  position:static; display:block; width:auto; height:auto; flex:0 0 auto;
  border:0; border-radius:0; background:none; box-shadow:none;
  -webkit-backdrop-filter:none; backdrop-filter:none;
  font-size:15px; line-height:1; font-weight:400; color:#ffe7bb;
  text-shadow:0 0 9px rgba(255,205,120,.8), 0 0 22px rgba(227,183,109,.45);
  animation:ewSparkLife 3.4s ease-in-out infinite;
}
/* the ghosted counter-rotating second spark belonged to the sphere */
#pubAi .po::before, .ai-launcher .ai-orb::before{ content:none }

/* Label is always present now - it is what makes this read as a button. */
#pubAi .pl, .ai-launcher .ai-lbl{
  max-width:none; overflow:visible; white-space:nowrap; opacity:1; margin:0;
  padding:0; background:none; border:0; box-shadow:none;
  -webkit-backdrop-filter:none; backdrop-filter:none;
  font-size:13.5px; font-weight:650; letter-spacing:.01em; color:var(--ew-ink);
}
.ai-launcher.hidden{ opacity:0; pointer-events:none; transform:translateY(24px) scale(.9) }

@keyframes ewBreath{ 0%,100%{opacity:.32; transform:scale(.92)} 50%{opacity:.62; transform:scale(1.08)} }
/* the spark itself breathes and brightens - it is alive, not spinning */
@keyframes ewSparkLife{
  0%,100%{ transform:scale(1) rotate(0deg);
    text-shadow:0 0 8px rgba(255,226,170,1), 0 0 20px rgba(255,205,120,.95), 0 0 44px rgba(227,183,109,.75) }
  50%{ transform:scale(1.12) rotate(8deg);
    text-shadow:0 0 12px rgba(255,236,196,1), 0 0 30px rgba(255,214,140,1), 0 0 62px rgba(227,183,109,.95) }
}
@keyframes ewSparkSpin{ to{ transform:rotate(360deg) } }

/* ── PANELS ────────────────────────────────────────────────────────────────
   Two backgrounds: the glass body clipped to the padding box, the specular rim
   clipped to the border box. That is what produces a lit edge instead of a
   flat outline.                                                              */
#pubAiPanel,
.ai-chat{
  font-family:var(--ew-font); color:var(--ew-ink);
  background:
    linear-gradient(168deg, rgba(30,27,34,.66) 0%, rgba(12,11,15,.72) 100%) padding-box,
    var(--ew-rim) border-box;
  border:1px solid transparent;
  -webkit-backdrop-filter:var(--ew-blur); backdrop-filter:var(--ew-blur);
  box-shadow:
    0 34px 90px rgba(0,0,0,.66),
    0 1px 0 rgba(255,255,255,.14) inset;        /* light entering the top edge */
  display:flex; flex-direction:column; overflow:hidden;
}
#pubAiPanel{
  position:fixed; right:18px; bottom:98px; z-index:501;
  width:392px; max-width:calc(100vw - 32px);
  max-height:min(600px, calc(100vh - 142px));
  border-radius:var(--ew-r);
  transform-origin:bottom right;
  animation:ewIn .36s cubic-bezier(.2,.9,.25,1);
}
#pubAiPanel[hidden]{ display:none!important }
.ai-chat{
  position:fixed; top:0; right:0; z-index:200;
  width:412px; max-width:94vw; height:100%;
  border-radius:0; border-right:0; border-top:0; border-bottom:0;
  transform:translateX(102%); transition:transform .42s cubic-bezier(.2,.9,.25,1);
}
.ai-chat.open{ transform:translateX(0) }
@keyframes ewIn{ from{ opacity:0; transform:translateY(16px) scale(.96) } to{ opacity:1; transform:none } }

/* ── HEADER ───────────────────────────────────────────────────────────────*/
.pub-ai-h, .ai-head{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:16px 18px; position:relative;
  background:linear-gradient(180deg, rgba(227,183,109,.09), transparent);
}
/* hairline that fades at both ends, so the divider does not box the glass in */
.pub-ai-h::after, .ai-head::after{
  content:""; position:absolute; left:0; right:0; bottom:0; height:1px;
  background:linear-gradient(90deg, transparent, rgba(227,183,109,.3) 22%, rgba(227,183,109,.3) 78%, transparent);
}
.pub-ai-h > span:first-child, .ai-head > div:first-child{
  display:flex; align-items:center; gap:10px; min-width:0;
}
/* ── LIVE DOT ──────────────────────────────────────────────────────────────
   The dot used to fade its own opacity in and out, which reads as "blinking
   out" - the opposite of the intent. A broadcast indicator holds steady and
   emits: the core stays fully lit at all times, and a ring expands out of it
   and dissolves. Nothing ever dims, so the panel always looks powered on. */
.pub-ai-h > span:first-child::before, .ai-head > div:first-child::before{
  content:""; width:8px; height:8px; border-radius:50%; flex:0 0 auto;
  background:radial-gradient(circle at 34% 30%, var(--ew-live-hi), var(--ew-live));
  animation:ewLive 2.4s ease-out infinite;
}
@keyframes ewLive{
  0%   { box-shadow:0 0 0 0    rgba(47,189,138,.5), 0 0 9px rgba(47,189,138,.7) }
  70%  { box-shadow:0 0 0 7px  rgba(47,189,138,0),  0 0 9px rgba(47,189,138,.7) }
  100% { box-shadow:0 0 0 0    rgba(47,189,138,0),  0 0 9px rgba(47,189,138,.7) }
}
/* Motion off still needs the glow, or the dot loses its lit core entirely. */
@media (prefers-reduced-motion:reduce){
  .pub-ai-h > span:first-child::before, .ai-head > div:first-child::before{
    box-shadow:0 0 0 3px rgba(47,189,138,.16), 0 0 9px rgba(47,189,138,.7);
  }
}
.pub-ai-h{ font-size:13.5px; font-weight:700; letter-spacing:.01em }
.ai-head b{ font-size:14.5px; font-weight:750; letter-spacing:.01em }
.ai-sub{ color:var(--ew-ink-3); font-size:10.5px; letter-spacing:.1em; text-transform:uppercase; font-weight:600 }
.pub-ai-h button, .ai-head .ai-x{
  background:transparent; border:0; color:var(--ew-ink-3); font-size:15px; cursor:pointer;
  width:30px; height:30px; border-radius:9px; flex:0 0 auto; transition:.16s;
}
.pub-ai-h button:hover, .ai-head .ai-x:hover{ color:var(--ew-ink); background:rgba(255,255,255,.07) }

/* ── THREAD ───────────────────────────────────────────────────────────────*/
.pub-ai-t, .ai-thread{
  flex:1; overflow-y:auto; padding:18px 16px;
  display:flex; flex-direction:column; gap:13px;
  font-size:13.5px; line-height:1.62;
  scrollbar-width:thin; scrollbar-color:rgba(227,183,109,.22) transparent;
}
.pub-ai-t::-webkit-scrollbar, .ai-thread::-webkit-scrollbar{ width:7px }
.pub-ai-t::-webkit-scrollbar-thumb, .ai-thread::-webkit-scrollbar-thumb{
  background:rgba(227,183,109,.2); border-radius:99px }

.pub-m, .ai-msg{
  white-space:pre-wrap;
  animation:ewMsg .32s cubic-bezier(.2,.9,.25,1) both;
}
@keyframes ewMsg{ from{ opacity:0; transform:translateY(8px) } to{ opacity:1; transform:none } }

/* ── MEMBER — a compact gold bubble ────────────────────────────────────────
   Questions are short, so a bubble suits them and the accent lets the eye find
   where each turn begins.                                                    */
.pub-m.u, .ai-msg.ai-user{
  align-self:flex-end; max-width:82%;
  padding:10px 15px; border-radius:16px; border-bottom-right-radius:5px;
  background:
    linear-gradient(150deg, rgba(255,214,150,.92) 0%, rgba(199,150,72,.85) 100%) padding-box,
    linear-gradient(150deg, rgba(255,255,255,.5), rgba(227,183,109,.15)) border-box;
  border:1px solid transparent;
  color:#241a07; font-weight:560;
  box-shadow:0 4px 18px rgba(169,124,52,.26), 0 1px 0 rgba(255,255,255,.4) inset;
  -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
}

/* ── ASSISTANT — NO BUBBLE ─────────────────────────────────────────────────
   The first pass boxed every message. Answers here run several sentences, so
   the panel filled with stacked slabs and the glass disappeared behind them —
   exactly the problem raised. Longer text does not want a container.
   The answer is set as plain type on the glass, marked by a small gold spark
   in the gutter and closed by a hairline that fades out. It reads as a desk
   note rather than a chat bubble, it scales to any length, and the panel stays
   visibly glass.                                                             */
.pub-m.a, .ai-msg.ai-bot{
  align-self:stretch; max-width:100%;
  position:relative; padding:2px 4px 15px 26px;
  background:none; border:0; box-shadow:none;
  color:var(--ew-ink); font-size:13.5px; line-height:1.68;
}
/* the spark in the gutter — who is speaking, without an avatar chip */
.pub-m.a::before, .ai-msg.ai-bot::before{
  content:"\2726"; position:absolute; left:2px; top:2px;
  font-size:11px; color:var(--ew-gold); opacity:.85;
  text-shadow:0 0 9px var(--ew-glow);
}
/* turn separator: fades at both ends so it divides without drawing a box */
.pub-m.a::after, .ai-msg.ai-bot::after{
  content:""; position:absolute; left:26px; right:8px; bottom:0; height:1px;
  background:linear-gradient(90deg, rgba(227,183,109,.22), rgba(255,255,255,.05) 55%, transparent);
}
/* the last answer in the thread needs no closing rule */
.pub-ai-t > .pub-m.a:last-child::after,
.ai-thread > .ai-msg.ai-bot:last-child::after{ display:none }

/* ── SUGGESTED PROMPTS ─────────────────────────────────────────────────────*/
.pub-ai-chips, .ai-chips{
  display:flex; flex-direction:column; align-items:flex-start; gap:7px;
  padding:2px 16px 12px; flex-wrap:wrap;
}
.pub-ai-chips button, .ai-chip{
  background:
    linear-gradient(150deg, rgba(227,183,109,.09), rgba(227,183,109,.03)) padding-box,
    linear-gradient(150deg, rgba(227,183,109,.34), rgba(227,183,109,.08)) border-box;
  border:1px solid transparent; border-radius:12px; padding:9px 14px;
  color:#dcccae; font-size:12.5px; font-family:inherit; cursor:pointer; text-align:left;
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
  transition:.2s cubic-bezier(.2,.8,.2,1);
}
.pub-ai-chips button:hover, .ai-chip:hover{
  color:var(--ew-gold-hi); transform:translateX(3px);
  background:
    linear-gradient(150deg, rgba(227,183,109,.18), rgba(227,183,109,.07)) padding-box,
    linear-gradient(150deg, rgba(255,220,160,.6), rgba(227,183,109,.18)) border-box;
  box-shadow:0 4px 16px rgba(169,124,52,.2);
}

/* ── INPUT ────────────────────────────────────────────────────────────────*/
.pub-ai-in, .ai-input-row{
  display:flex; gap:9px; align-items:center; padding:13px 14px; position:relative;
  background:linear-gradient(0deg, rgba(227,183,109,.06), transparent);
}
.pub-ai-in::before, .ai-input-row::before{
  content:""; position:absolute; left:0; right:0; top:0; height:1px;
  background:linear-gradient(90deg, transparent, rgba(227,183,109,.26) 22%, rgba(227,183,109,.26) 78%, transparent);
}
/* an inset well — light falls INTO the field rather than sitting on it */
.pub-ai-in input, .ai-input2{
  flex:1; min-width:0; border-radius:999px; outline:none;
  padding:12px 17px; font-size:13.5px; font-family:inherit; color:var(--ew-ink);
  background:rgba(0,0,0,.26); border:1px solid rgba(255,255,255,.09);
  box-shadow:0 2px 8px rgba(0,0,0,.3) inset, 0 1px 0 rgba(255,255,255,.06);
  transition:.22s;
}
.pub-ai-in input::placeholder, .ai-input2::placeholder{ color:var(--ew-ink-3) }
.pub-ai-in input:focus, .ai-input2:focus{
  border-color:rgba(227,183,109,.5); background:rgba(0,0,0,.18);
  box-shadow:0 2px 8px rgba(0,0,0,.26) inset, 0 0 0 3px rgba(227,183,109,.13);
}
/* send — the same crystal sphere as the launcher, in miniature */
.pub-ai-in button, .ai-send{
  flex:0 0 auto; width:43px; height:43px; border:0; border-radius:50%; padding:0;
  display:grid; place-items:center; cursor:pointer; font-size:15px; font-family:inherit;
  color:var(--ew-gold-hi); font-weight:700; position:relative; overflow:hidden;
  text-shadow:0 0 10px rgba(255,214,150,.85);
  background:
    radial-gradient(circle at 50% 116%, rgba(227,183,109,.9) 0%, rgba(169,124,52,.36) 36%, transparent 64%),
    radial-gradient(circle at 30% 24%, rgba(255,255,255,.46) 0%, transparent 32%),
    linear-gradient(158deg, rgba(58,52,44,.9) 0%, rgba(16,14,18,.95) 100%);
  box-shadow:
    0 6px 20px rgba(0,0,0,.42),
    0 0 0 1px rgba(255,255,255,.13) inset,
    0 -8px 16px rgba(227,183,109,.22) inset;
  transition:.2s cubic-bezier(.2,.8,.2,1);
}
.pub-ai-in button:hover, .ai-send:hover{
  transform:scale(1.07); box-shadow:0 8px 26px var(--ew-glow), 0 0 0 1px rgba(255,255,255,.2) inset }
.pub-ai-in button:active, .ai-send:active{ transform:scale(.95) }

/* ── TYPING ───────────────────────────────────────────────────────────────*/
/* Two distinct states, which the previous build collapsed into one blinking
   block character.

   THINKING (nothing has arrived yet) — the gutter spark spins up, so the wait
   reads as work rather than a hang.

   STREAMING (words arriving) — a warm caret trails the text and a light sweep
   travels across the line currently being written. This is the moment the
   assistant feels like it is composing rather than pasting a stored answer.  */
.pub-typing::after{
  content:""; display:inline-block; vertical-align:-2px;
  width:7px; height:15px; margin-left:4px; border-radius:2px;
  background:linear-gradient(180deg, var(--ew-gold-hi), var(--ew-gold));
  box-shadow:0 0 10px var(--ew-glow);
  animation:ewCaret 1s steps(1) infinite;
}
@keyframes ewCaret{ 0%,55%{opacity:1} 56%,100%{opacity:.12} }

/* the gutter spark spins while the answer is being composed */
.pub-m.a.pub-typing::before, .ai-msg.ai-bot.pub-typing::before{
  animation:ewSparkSpin 1.8s linear infinite;
  opacity:1; text-shadow:0 0 14px var(--ew-gold-hi);
}
/* light sweeping across the line currently arriving */
.pub-m.a.pub-typing, .ai-msg.ai-bot.pub-typing{
  background:linear-gradient(100deg,
    transparent 0%, rgba(227,183,109,.09) 42%, rgba(255,232,190,.17) 50%,
    rgba(227,183,109,.09) 58%, transparent 100%);
  background-size:220% 100%;
  animation:ewStream 2.4s linear infinite;
}
@keyframes ewStream{ from{ background-position:120% 0 } to{ background-position:-120% 0 } }

@keyframes ewDots{ 0%,100%{opacity:.25} 50%{opacity:1} }
.ai-think{ color:var(--ew-ink-3); animation:ewDots 1.4s ease-in-out infinite }
.ai-think::before{ animation:ewSparkSpin 1.8s linear infinite }

/* ── DISCLAIMER ───────────────────────────────────────────────────────────*/
.pub-ai-d{ font-size:10px; line-height:1.5; color:var(--ew-ink-3); text-align:center; padding:0 16px 12px }

/* ── RESPONSIVE ───────────────────────────────────────────────────────────*/
@media (max-width:520px){
  /* The pill keeps its label on small screens - that label is the whole point
     of the redesign, and a phone is exactly where the bare orb was useless. */
  #pubAi, .ai-launcher{ right:14px; bottom:14px; padding:10px 16px 10px 14px }
  #pubAi .po, .ai-launcher .ai-orb{ font-size:14px }
  #pubAi .pl, .ai-launcher .ai-lbl{ font-size:12.5px }
  #pubAiPanel{ right:10px; left:10px; bottom:84px; width:auto; max-height:calc(100vh - 122px) }
  .ai-chat{ width:100%; max-width:100% }
}

/* Where backdrop-filter is unsupported, transparency alone would leave the text
   sitting on raw page content. Fall back to an opaque body. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  #pubAiPanel, .ai-chat{ background:linear-gradient(168deg,#1b181f,#0c0b0f) padding-box, var(--ew-rim) border-box }
  .pub-m.a, .ai-msg.ai-bot{ background:rgba(38,35,42,.96) padding-box, linear-gradient(158deg, rgba(255,255,255,.2), rgba(255,255,255,.04)) border-box }
}

/* Motion is decoration here — never a requirement to use the assistant. */
@media (prefers-reduced-motion:reduce){
  /* Selectors updated when the orb was rebuilt and the streaming state added —
     the old list still named .ai-orb::after, which no longer animates, and
     missed the spark, the sweep and the caret entirely. A reduced-motion block
     that has drifted out of sync with the animations is worse than none,
     because it looks like the case is handled. */
  #pubAi::before, .ai-launcher::before,
  #pubAi .po, .ai-launcher .ai-orb,
  #pubAi .po::before, .ai-launcher .ai-orb::before,
  .pub-ai-h > span:first-child::before, .ai-head > div:first-child::before,
  .pub-typing::after, .ai-think, .ai-think::before,
  .pub-m.a.pub-typing, .ai-msg.ai-bot.pub-typing,
  .pub-m.a.pub-typing::before, .ai-msg.ai-bot.pub-typing::before{ animation:none }
  /* the streaming sweep is a background, so it must also be cleared */
  .pub-m.a.pub-typing, .ai-msg.ai-bot.pub-typing{ background:none }
  #pubAiPanel, .pub-m, .ai-msg{ animation:none }
  .ai-chat{ transition:none }
}
