/* SayYo design system — "Afterglow" (identity round 1, direction 01)
   One bold signature (the living orb) against a quiet, deep world — but the
   world is now warm, not cool. Sunset ramp: amber → coral → pink → violet.
   The name greets you, so the palette should too. Restrained glass + precise
   mono labels stay; the cyan/indigo iridescence is gone.
   Narrative: "You're on. Just talk." */

/* Self-hosted variable fonts (latin) — no third-party request on first paint,
   and the installed PWA keeps its type offline. */
@font-face{font-family:'Inter';font-style:normal;font-weight:300 700;font-display:swap;
  src:url("./fonts/inter.woff2") format("woff2")}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:300 700;font-display:swap;
  src:url("./fonts/space-grotesk.woff2") format("woff2")}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:300 700;font-display:swap;
  src:url("./fonts/jetbrains-mono.woff2") format("woff2")}

:root{
  --bg:#0B0710; --bg2:#070410;   /* warm near-black — a plum-leaning night */
  --text:#F6F2FA; --muted:#B0A5B6; --faint:#95899E; /* kept ≥4.5:1 on --bg for WCAG AA */
  --glass:rgba(255,255,255,.045); --glass2:rgba(255,255,255,.07);
  --line:rgba(255,255,255,.10); --line2:rgba(255,255,255,.16);
  /* The brand yellow, and the single source of truth for the accent. */
  --yel:#FFCE1F;
  /* Warm ramp. iri2 is the workhorse accent — labels, focus rings, carets, the
     status dot — so it IS the brand yellow; that is what makes a button read as
     SayYo rather than as generic warm UI.
     This used to be amber → coral → pink, on the argument that coral was the
     interaction colour because the orb was coral. The orb is the yellow mark
     now, so nothing anchored coral any more and the chrome was simply the old
     brand sitting around the new one. */
  --iri1:#FFE277; --iri2:var(--yel); --iri3:#FF9A3C; /* light yellow → yellow → amber */
  /* Violet stays, but only in the ambient corners. It is what stops black +
     yellow reading as hazard tape, so it earns its place by contrast. */
  --iri4:#8B5CF6;
  --danger:#FF8A9B;                                  /* semantic, deliberately still pink */
  --sans:'Inter',-apple-system,system-ui,sans-serif;
  --disp:'Space Grotesk',var(--sans);
  --mono:'JetBrains Mono',ui-monospace,monospace;
  --safe-t:env(safe-area-inset-top); --safe-b:env(safe-area-inset-bottom);
}
/* ---- CJK ------------------------------------------------------------------
   The self-hosted faces above are latin-only subsets, so a Japanese or Chinese
   interface falls through to whatever the browser picks — which on Android is
   often a Japanese face rendering Chinese text, or the reverse. Name the
   platform faces per script instead.

   This is a plain :lang() rule on a font-family declaration, NOT a custom
   property. A `var()` inside a custom property is substituted where the
   property is DECLARED, so redefining --cjk on a :lang() descendant of :root
   changes nothing about a --sans that was already resolved on :root — the
   obvious-looking token version of this silently does nothing at all.

   Attribute-selector form, because :lang(zh-Hans) does not match a bare "zh"
   and we always stamp the full tag; and because Simplified and Traditional
   want different faces. */
html[lang="ja"] body, html[lang="ja"] input, html[lang="ja"] textarea,
html[lang="ja"] select, html[lang="ja"] button{
  font-family:'Inter',-apple-system,'Hiragino Sans','Hiragino Kaku Gothic ProN',
    'Noto Sans JP','Yu Gothic',Meiryo,system-ui,sans-serif}
html[lang="zh-Hans"] body, html[lang="zh-Hans"] input, html[lang="zh-Hans"] textarea,
html[lang="zh-Hans"] select, html[lang="zh-Hans"] button{
  font-family:'Inter',-apple-system,'PingFang SC','Noto Sans SC',
    'Hiragino Sans GB','Microsoft YaHei',system-ui,sans-serif}
html[lang="zh-Hant"] body, html[lang="zh-Hant"] input, html[lang="zh-Hant"] textarea,
html[lang="zh-Hant"] select, html[lang="zh-Hant"] button{
  font-family:'Inter',-apple-system,'PingFang TC','Noto Sans TC',
    'Microsoft JhengHei',system-ui,sans-serif}
/* The display and mono faces are latin-only too. In CJK the tracked-out mono
   labels are actively hard to read — letter-spacing between ideographs is not
   the same typographic idea — so those two rules are relaxed per script. */
html[lang="ja"] .sec, html[lang^="zh"] .sec,
html[lang="ja"] .chip, html[lang^="zh"] .chip,
html[lang="ja"] .status, html[lang^="zh"] .status{letter-spacing:.06em}
html[lang="ja"] .brand b, html[lang^="zh"] .brand b{letter-spacing:.22em}

*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
/* The app hides things by setting the `hidden` property, which relies on the UA
   rule [hidden]{display:none}. Any class rule that sets `display` outranks it
   (class beats attribute-in-UA-sheet), so a hidden element with a flex/grid
   class silently stays on screen — which is how an empty style-profile card
   ended up showing to users who had never imported one. Make it unconditional. */
[hidden]{display:none !important}
html,body{height:100%}
/* The app should feel bolted down, not like a web page. `overscroll-behavior`
   kills the rubber-band drag past the ends without disabling real scrolling —
   the result card still scrolls when a long email overflows.
   Scoped to the app via :has(.app) so the marketing page keeps normal browser
   bounce; matching on a descendant is deliberate, since ui.js's setBodyState
   assigns body.className wholesale and would wipe a static class off <body>.
   NOTE: this is only half the fix. The other half is `contentInset: "never"`
   in mobile/capacitor.config.json — with "always", WKWebView adds the safe-area
   insets to its scroll view ON TOP of the ones our CSS already consumes via
   env(), so the content is 100dvh tall inside a scroll view ~90pt taller than
   the screen. That phantom range is what you can drag. */
html:has(.app),body:has(.app){overscroll-behavior:none}
body{
  background:var(--bg); color:var(--text); font-family:var(--sans);
  min-height:100dvh; overflow-x:hidden; position:relative;
  -webkit-font-smoothing:antialiased; letter-spacing:-.01em;
}

/* Ambient spatial field */
#space{position:fixed;inset:0;z-index:0;pointer-events:none}
.ambient{position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(62% 46% at 78% 4%, rgba(255,198,80,.15), transparent 60%),  /* warm light from above */
    radial-gradient(55% 40% at 84% 84%, rgba(255,111,165,.11), transparent 60%),  /* AMBIENT — pink stays here */
    radial-gradient(62% 46% at 10% 88%, rgba(139,92,246,.13), transparent 60%),  /* violet dusk in the low corner */
    radial-gradient(120% 120% at 50% 50%, transparent 55%, rgba(0,0,0,.55) 100%),
    linear-gradient(180deg,var(--bg),var(--bg2));
}
.grain{position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.05;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
#ptr{position:fixed;z-index:1;width:520px;height:520px;left:0;top:0;margin:-260px 0 0 -260px;pointer-events:none;
  background:radial-gradient(circle, rgba(255,198,80,.10), transparent 60%);mix-blend-mode:screen;
  transition:opacity .5s;opacity:0}

.app{position:relative;z-index:2;max-width:460px;margin:0 auto;min-height:100dvh;
  display:flex;flex-direction:column;padding:calc(var(--safe-t) + 18px) 22px calc(var(--safe-b) + 22px)}

/* Header */
header{display:flex;align-items:center;justify-content:space-between}
.brand{display:flex;align-items:center;gap:9px}
/* The mark: a speech bubble that is also a winking face.
   Drawn from icons/mark.svg rather than inlined here, so the app header, the
   sign-in screen and the landing nav all share ONE file and can't drift apart.
   drop-shadow (not box-shadow) because the glow has to follow the bubble's
   alpha, not the square the element occupies. */
.dot{width:23px;height:23px;flex:none;
  background:url(./icons/mark.svg) center/contain no-repeat;
  filter:drop-shadow(0 0 9px rgba(255,206,31,.3));
  animation:bob 4.6s ease-in-out infinite}
@keyframes bob{0%,100%{transform:translateY(0) rotate(0deg)}50%{transform:translateY(-2px) rotate(-4deg)}}
.brand b{font-family:var(--disp);font-weight:600;font-size:20px;letter-spacing:.22em;padding-left:.06em}
/* The bang is decorative, so it lives in CSS: screen readers still say "SAYYO",
   and the negative margin cancels the .22em tracking that would otherwise leave
   it floating a quarter-em off the O. */
.brand b::after{content:"!";color:var(--yel);margin-left:-.15em}
.menu{position:relative;overflow:hidden;width:40px;height:40px;border-radius:11px;border:1px solid var(--line);
  background:var(--glass);color:var(--muted);display:grid;place-items:center;cursor:pointer;
  backdrop-filter:blur(10px);transition:.2s}
.menu:hover{border-color:var(--line2);color:var(--text)}
.menu:active{transform:scale(.93)}
.menu span{display:block;width:15px;height:1.5px;background:currentColor;border-radius:2px;
  box-shadow:0 5px 0 currentColor,0 -5px 0 currentColor}
.head-right{display:flex;align-items:center;gap:8px}
.hbtn{position:relative;overflow:hidden;width:40px;height:40px;border-radius:11px;border:1px solid var(--line);
  background:var(--glass);color:var(--muted);display:grid;place-items:center;cursor:pointer;
  backdrop-filter:blur(10px);transition:.2s}
.hbtn:hover{border-color:var(--line2);color:var(--text)}
.hbtn:active{transform:scale(.93)}
.hbtn svg{width:17px;height:17px}
.lang-pill{display:flex;align-items:center;gap:4px;height:40px;padding:0 2px 0 9px;border:1px solid var(--line);
  border-radius:11px;background:var(--glass);backdrop-filter:blur(10px);transition:.2s}
.lang-pill:hover{border-color:var(--line2)}
.lang-pill .globe{width:14px;height:14px;color:var(--muted);flex:none}
.lang{appearance:none;-webkit-appearance:none;background:none;border:none;color:var(--text);
  font-family:var(--mono);font-size:11px;letter-spacing:.06em;padding:8px 4px;outline:none;cursor:pointer;max-width:92px}
.lang option{background:#14131c;color:var(--text)}

/* Stage */
.stage{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;min-height:0;position:relative}
.tagline{font-family:var(--disp);font-weight:500;font-size:26px;line-height:1.2;text-align:center;
  letter-spacing:-.02em;margin-bottom:30px;transition:opacity .5s,transform .5s;max-width:300px}
.tagline .g{background:linear-gradient(100deg,var(--iri1),var(--iri2) 55%,var(--iri3));
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
body.active .tagline{opacity:0;transform:translateY(-8px);pointer-events:none;position:absolute}

.core-wrap{position:relative;width:300px;height:300px;display:grid;place-items:center;cursor:pointer;flex:none;
  transition:transform .55s cubic-bezier(.3,.9,.3,1),margin .55s cubic-bezier(.3,.9,.3,1)}
/* When the words arrive, they become the hero: the orb docks small so the
   result card gets the room. */
body.result .core-wrap{transform:scale(.6);margin:-52px 0 -104px}
/* ---- The mark ------------------------------------------------------------
   The thing you tap IS the logo — the same lopsided bubble as icons/mark.svg,
   with a face. It replaced a rendered gradient sphere, which could never also
   be the app icon and the header mark; it could only reference them. Flat means
   one drawing, everywhere.

   Geometry (the outline, the eyes, the mouth) is recomputed per frame in
   src/orb.js. Everything here is just paint. */
/* Fills the 300px stage. Sized here and framed by the viewBox in app.html —
   both have been pushed around; this is the pair that looked right. */
.mark{width:100%;height:100%;overflow:visible;will-change:transform;
  filter:drop-shadow(0 0 14px rgba(255,206,31,.10))}
.mk-fill{fill:var(--yel)}
.mk-y{stroke:var(--yel);fill:none}
/* Features are punched out in the background colour, so they read as holes in
   the mark rather than as ink laid on top of it. */
.mk-ink{fill:var(--bg)}
.mk-line{stroke:var(--bg);fill:none;stroke-linecap:round}
.mk-stroke{stroke:var(--bg);stroke-linejoin:round;stroke-linecap:round}
/* The ball is the one thing that is NOT a hole in the mark — it passes in
   front of the character, so it needs its own fill plus an outline in the
   background colour to separate it from the face it crosses. */
.mk-ball{fill:var(--yel);stroke:var(--bg);stroke-width:5}
.core-wrap:hover .mark{filter:drop-shadow(0 0 20px rgba(255,206,31,.20))}

.readout{margin-top:26px;height:44px;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center}
.status{font-family:var(--mono);font-size:12px;letter-spacing:.28em;color:var(--muted);text-transform:uppercase;
  display:flex;align-items:center;gap:9px;transition:color .3s}
.status .ld{width:6px;height:6px;border-radius:50%;background:var(--iri2);box-shadow:0 0 10px var(--iri2);opacity:0;transition:.3s}
body.listening .status .ld{opacity:1;animation:blink 1.1s ease-in-out infinite}
@keyframes blink{50%{opacity:.25}}
.timer{font-family:var(--mono);font-size:13px;letter-spacing:.18em;color:var(--faint);opacity:0;transition:opacity .3s}
body.listening .timer{opacity:1}

/* Pills (retry / format suggestion) */
.pill{margin-top:12px;border:1px solid var(--line2);background:var(--glass2);color:var(--text);
  border-radius:30px;padding:9px 16px;font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  cursor:pointer;transition:.2s;backdrop-filter:blur(10px)}
.pill:hover{border-color:var(--iri2)}
.pill:active{transform:scale(.96)}
.pill.suggest{color:var(--iri1);border-color:rgba(255,226,119,.35)}
.pill[hidden]{display:none}

/* "Replying to" — the invitation, and the chip that shows it's active. */
.reply-add{display:inline-flex;align-items:center;gap:7px;color:var(--muted);border-color:var(--line)}
.reply-add svg{width:14px;height:14px;flex:none}
.reply-add:hover{color:var(--text)}
/* The invitation is chrome; it shouldn't compete while they're actually
   talking. The CHIP stays up in every state — it changes what the output is,
   so hiding it would make this a hidden mode. */
body.listening .reply-add,body.processing .reply-add{display:none}

/* ---- Silent mode ---------------------------------------------------------
   A library, a meeting, a sleeping house — or simply not wanting to talk to a
   phone. Same pipeline, keyboard instead of microphone.

   The pill row holds both idle affordances side by side and wraps on a narrow
   screen. Silent mode is NOT in the header: at 393px that row is already
   brand + language pill + history + menu, and the language pill grows to 92px
   once a target language is set. */
.stage-pills{display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap;
  max-width:min(560px,92vw)}
body.listening .stage-pills,body.processing .stage-pills{display:none}
.silent-toggle{display:inline-flex;align-items:center;gap:7px;color:var(--muted);border-color:var(--line)}
.silent-toggle svg{width:14px;height:14px;flex:none}
.silent-toggle:hover{color:var(--text)}
.silent-toggle[aria-pressed="true"]{color:var(--iri1);border-color:rgba(255,226,119,.38);
  background:rgba(255,226,119,.07)}

/* In silent mode the orb steps aside for the composer. It is NOT just hidden:
   #core is role="button" wired to startListening(), so leaving it on screen —
   or leaving it focusable — puts a microphone trigger one stray tap away in
   the exact room this mode exists for. */
body.silent .core-wrap{display:none}
body.silent .readout{display:none}
.composer{width:min(560px,92vw);display:flex;flex-direction:column;gap:10px;margin-top:4px}
.composer[hidden]{display:none}
.composer textarea{width:100%;background:var(--bg2);border:1px solid var(--line);border-radius:14px;
  color:var(--text);padding:14px;font:inherit;font-size:16px;line-height:1.5;outline:none;resize:vertical;
  min-height:120px}
.composer textarea:focus{border-color:var(--iri2)}
.composer textarea::placeholder{color:var(--faint)}
.composer-foot{display:flex;align-items:center;justify-content:space-between;gap:12px}
.composer-count{font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;color:var(--faint)}
.composer-count.near{color:var(--iri1)}
.composer-foot .btn{flex:none}
/* The composer STAYS up once there's a result. It is where the orb was, so
   hiding it just leaves a hole — and in this mode the next message is typed,
   so the box you type it in is the thing that should be waiting for you. It
   does shrink, because the result is the hero now. */
body.silent.result .composer textarea{min-height:64px;font-size:15px}
/* Refine records a second take. It is the one action silent mode exists to
   prevent, so the button goes — and onRefine() refuses it too, because a
   hidden button is still clickable from a script. */
body.silent #refine{display:none}

/* ---- Notes ---------------------------------------------------------------
   A note is the one output you keep rather than send, so it gets an action the
   other formats don't, and the library that holds it. */
.note-save{display:flex;align-items:center;gap:8px;margin-bottom:10px;flex-wrap:wrap}
.note-save[hidden]{display:none}
.note-save-btn{display:inline-flex;align-items:center;gap:7px;border-color:rgba(255,226,119,.35);
  color:var(--iri1)}
.note-save-btn svg{width:15px;height:15px;flex:none}
.note-save-btn.saved{color:var(--text);border-color:var(--line)}
.note-due input{background:var(--bg2);border:1px solid var(--line);border-radius:11px;color:var(--muted);
  padding:9px 11px;font-family:var(--mono);font-size:11px;outline:none;color-scheme:dark}
.note-due input:focus{border-color:var(--iri2)}

/* Library tabs — captures and notes in one sheet, because the header has no
   room for a third button (measured: needs 388px, has 349px at 393px wide). */
.lib-tabs{display:flex;gap:6px;margin-bottom:14px}
.lib-tab{flex:1;border:1px solid var(--line);background:none;color:var(--faint);cursor:pointer;
  border-radius:11px;padding:9px 10px;font-family:var(--mono);font-size:11px;letter-spacing:.14em;
  text-transform:uppercase;transition:.2s}
.lib-tab:hover{color:var(--muted)}
.lib-tab[aria-selected="true"]{color:var(--text);border-color:rgba(255,226,119,.38);
  background:rgba(255,226,119,.07)}
.lib-count{font-size:10px;opacity:.75}

.note-row{border:1px solid var(--line);border-radius:12px;padding:11px 12px;margin-bottom:9px;
  background:var(--glass);display:flex;gap:10px;align-items:flex-start}
.note-row.done{opacity:.5}
.note-row.done .note-text{text-decoration:line-through}
/* `.sheet input` sets width:100% and outranks a single class (0,1,1 beats
   0,1,0), so a bare `.note-check` rule lost and the checkbox stretched to the
   full row — which pushed the note text to zero width and rendered it one
   character per line. Qualify it so it wins. */
.note-row input.note-check{flex:none;width:20px;height:20px;margin:1px 0 0;
  accent-color:var(--iri2);cursor:pointer}
.note-body{flex:1;min-width:0}
.note-text{font-size:13.5px;line-height:1.5;white-space:pre-wrap;word-break:break-word;text-align:left}
.note-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:7px;
  font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:var(--faint)}
.note-meta .due{color:var(--muted)}
.note-meta .due.over{color:var(--danger)}
/* flex:none, or it grows to fill the row and pushes the actions onto a third
   line — `.sheet input` sets width:100% and the flex container would honour it. */
.note-meta input[type="date"]{flex:none;width:auto;background:none;border:1px solid var(--line);
  border-radius:8px;color:var(--muted);padding:3px 6px;font:inherit;font-size:10px;
  color-scheme:dark;outline:none}
.note-act{border:none;background:none;color:var(--iri2);cursor:pointer;font-family:var(--mono);
  font-size:10px;letter-spacing:.06em;padding:2px 0}
.note-act:hover{text-decoration:underline}
.note-act.danger{color:var(--danger)}

/* Visually hidden but still announced. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}
.reply-chip{margin-top:12px;display:flex;align-items:center;gap:9px;max-width:min(560px,92vw);
  border:1px solid rgba(255,226,119,.3);background:rgba(255,226,119,.06);border-radius:14px;
  padding:8px 10px 8px 12px;backdrop-filter:blur(10px)}
.reply-chip .k{font-family:var(--mono);font-size:9px;letter-spacing:.18em;color:var(--iri1);flex:none}
.reply-chip .v{font-size:12.5px;color:var(--muted);text-align:left;flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.reply-chip button{background:none;border:none;color:var(--faint);font-size:18px;line-height:1;
  cursor:pointer;padding:0 2px;flex:none}
.reply-chip button:hover{color:var(--text)}

/* Format chips */
.chips{position:relative;display:flex;gap:4px;margin-top:26px;padding:5px;border-radius:16px;
  background:var(--glass);border:1px solid var(--line);backdrop-filter:blur(14px);
  width:100%;max-width:400px;align-self:center;
  overflow-x:auto;scrollbar-width:none} /* six defaults + custom formats: scroll, never squeeze */
.chips::-webkit-scrollbar{display:none}
.chip{position:relative;flex:1 0 auto;z-index:2;border:none;background:none;cursor:pointer;padding:10px 8px;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);
  transition:color .25s;border-radius:11px;white-space:nowrap}
.chip[aria-selected="true"]{color:var(--text)}
.chip:hover{color:var(--muted)}
/* Note is not a sibling of Email. Every other chip decides how the capture is
   WRITTEN; this one makes a thing that outlives it, so it reads differently. */
.chip-note{display:inline-flex;align-items:center;gap:5px;color:var(--iri1);opacity:.72}
.chip-note svg{width:12px;height:12px;flex:none}
.chip-note[aria-selected="true"]{color:var(--iri1);opacity:1}
.chip-note:hover{color:var(--iri1);opacity:.9}
.chips .glow{position:absolute;top:5px;bottom:5px;z-index:1;border-radius:11px;
  background:linear-gradient(120deg,rgba(255,226,119,.16),rgba(255,206,31,.18),rgba(255,154,60,.15));
  border:1px solid rgba(255,255,255,.14);
  box-shadow:0 4px 18px rgba(255,206,31,.25),inset 0 1px 0 rgba(255,255,255,.12);
  transition:left .4s cubic-bezier(.3,.9,.3,1),width .4s cubic-bezier(.3,.9,.3,1)}

/* Result card */
/* Until there's a result the card is invisible — and it must also take up no
   room. Left in flow it reserved ~260px of the stage on every screen, which
   overflowed the stage box, degenerated `justify-content:center` into
   top-alignment, and jammed the tagline against the header. `position:absolute`
   (no offsets) parks it at its static position, out of flow, so the idle stage
   budgets only what's actually on screen; `body.result` puts it back and the
   fade/slide still animates. */
.card{margin-top:18px;width:100%;background:var(--glass);border:1px solid var(--line);border-radius:22px;
  backdrop-filter:blur(20px);overflow:hidden;opacity:0;transform:translateY(22px) scale(.98);
  transition:opacity .5s cubic-bezier(.2,.8,.3,1),transform .5s cubic-bezier(.2,.8,.3,1);
  pointer-events:none;position:absolute}
body.result .card{opacity:1;transform:none;pointer-events:auto;position:relative}
.card .scan{position:absolute;left:0;right:0;height:120px;top:-120px;z-index:5;pointer-events:none;
  background:linear-gradient(180deg,transparent,rgba(255,206,31,.14),rgba(255,226,119,.10),transparent)}
body.reveal .card .scan{animation:scan 1s ease-out}
@keyframes scan{0%{top:-120px}100%{top:100%}}
.card-in{padding:18px 18px 16px}
.heard{display:flex;gap:8px;align-items:baseline;margin-bottom:14px;padding-bottom:14px;border-bottom:1px solid var(--line)}
.heard .k{font-family:var(--mono);font-size:10px;letter-spacing:.2em;color:var(--faint);flex:none;padding-top:2px}
.heard .v{font-size:13.5px;color:var(--muted);font-style:italic;line-height:1.4}
.out-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:9px}
.out-label{font-family:var(--mono);font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;
  color:var(--iri2);display:flex;align-items:center;gap:7px}
.out-label::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--iri2);box-shadow:0 0 8px var(--iri2)}
.out-tools{display:flex;align-items:center;gap:9px}
.out-tools .cost{font-family:var(--mono);font-size:9.5px;letter-spacing:.08em;color:var(--faint)}
.tool{border:none;background:none;color:var(--faint);cursor:pointer;padding:4px;border-radius:8px;transition:.18s;display:grid;place-items:center;min-width:40px;min-height:40px}
.tool:hover{color:var(--text)}
.tool:active{transform:rotate(-40deg) scale(.9)}
.tool svg{width:14px;height:14px}
.out{font-size:16px;line-height:1.6;white-space:pre-wrap;color:var(--text);min-height:26px;letter-spacing:-.005em;
  outline:none;border-radius:6px}
.out[contenteditable="true"]:focus{box-shadow:0 0 0 1px var(--line2);padding:2px 4px;margin:-2px -4px}
.out .caret{color:var(--iri2);font-weight:600;animation:cb .8s steps(1) infinite}
@keyframes cb{50%{opacity:0}}

.actions{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;padding:0 18px 18px}
/* Refine while listening for the instruction */
#refine.rec{border-color:rgba(255,138,155,.5);color:var(--danger)}
#refine.rec svg{animation:refpulse 1.1s ease-in-out infinite}
@keyframes refpulse{50%{opacity:.35}}
.btn{position:relative;overflow:hidden;border:1px solid var(--line);background:var(--glass2);color:var(--text);
  border-radius:13px;padding:13px;font-family:var(--sans);font-size:14px;font-weight:500;cursor:pointer;
  display:flex;align-items:center;justify-content:center;gap:8px;transition:.18s}
.btn:hover{border-color:var(--line2);transform:translateY(-1px)}
.btn:active{transform:scale(.97)}
.btn svg{width:16px;height:16px;opacity:.85}
.btn.pri{border-color:transparent;color:#08070e;
  background:linear-gradient(120deg,var(--iri1),var(--iri2) 60%,var(--iri3));font-weight:600}
.btn.pri svg{opacity:1}

/* Quick share — one-tap destinations under the main actions */
.qshare{display:flex;gap:7px;padding:0 18px 18px;overflow-x:auto;scrollbar-width:none}
.qshare::-webkit-scrollbar{display:none}
.qbtn{flex:1 1 0;min-width:54px;border:1px solid var(--line);background:var(--glass2);color:var(--muted);
  border-radius:12px;padding:9px 4px 8px;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;gap:6px;transition:.18s}
.qbtn:hover{border-color:var(--line2);color:var(--text);transform:translateY(-1px)}
.qbtn:active{transform:scale(.95)}
.qbtn svg{width:17px;height:17px;opacity:.9}
.qbtn span{font-family:var(--mono);font-size:8.5px;letter-spacing:.05em;color:var(--faint);text-transform:uppercase;white-space:nowrap}
.qbtn:hover span{color:var(--muted)}
.rip{position:absolute;border-radius:50%;background:rgba(255,255,255,.35);transform:translate(-50%,-50%);
  pointer-events:none;animation:rip .6s ease-out forwards}
@keyframes rip{from{width:0;height:0;opacity:.5}to{width:320px;height:320px;opacity:0}}

.foot{text-align:center;margin-top:16px;font-family:var(--mono);font-size:10px;letter-spacing:.2em;
  color:var(--faint);text-transform:uppercase;opacity:.7;height:14px;transition:opacity .4s}
body.result .foot{opacity:.9}

/* Toast */
.toast{position:fixed;left:50%;bottom:calc(var(--safe-b) + 26px);transform:translate(-50%,16px);z-index:60;
  background:rgba(20,19,28,.9);border:1px solid var(--line2);backdrop-filter:blur(14px);
  color:var(--text);padding:11px 20px;border-radius:30px;font-size:13.5px;font-weight:500;
  font-family:var(--mono);letter-spacing:.05em;opacity:0;pointer-events:none;transition:.3s;
  box-shadow:0 10px 40px rgba(0,0,0,.5)}
.toast.show{opacity:1;transform:translate(-50%,0)}

/* Settings sheet */
.scrim{position:fixed;inset:0;background:rgba(4,3,8,.62);backdrop-filter:blur(6px);
  opacity:0;pointer-events:none;transition:.28s;z-index:40}
.scrim.open{opacity:1;pointer-events:auto}
.sheet{position:fixed;left:0;right:0;bottom:0;z-index:50;background:rgba(14,13,20,.96);
  border-radius:26px 26px 0 0;border-top:1px solid var(--line2);backdrop-filter:blur(24px);
  padding:10px 22px calc(var(--safe-b) + 24px);transform:translateY(103%);
  transition:transform .34s cubic-bezier(.2,.8,.25,1), visibility 0s .34s;
  max-width:460px;margin:0 auto;max-height:90dvh;overflow-y:auto}
/* A closed sheet is only moved off-screen by the transform, so its inputs stay
   in the tab order — including Settings' access-token field. Four closed sheets
   plus the sign-in form sit between the orb and the end of the document, which
   is what made Tab from the result card walk into invisible controls.
   `visibility:hidden` removes them; the delayed transition means it applies
   only AFTER the slide-out has finished, so the animation is unchanged. */
.sheet:not(.open){visibility:hidden}
.sheet.open{transform:none;visibility:visible;transition:transform .34s cubic-bezier(.2,.8,.25,1)}
.sheet:focus{outline:none}
.grip{width:38px;height:4px;border-radius:3px;background:var(--line2);margin:6px auto 14px}
.sheet-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}
.sheet-head h3{font-family:var(--disp);font-weight:600;font-size:20px;letter-spacing:-.01em}
.sheet-head button{border:none;background:var(--glass);color:var(--muted);width:40px;height:40px;
  border-radius:10px;cursor:pointer;font-size:18px;line-height:1}
.sheet .sub{color:var(--muted);font-size:13px;margin:0 0 18px;line-height:1.5}
.sec{font-family:var(--mono);font-size:10px;letter-spacing:.24em;text-transform:uppercase;color:var(--iri2);
  margin:20px 0 12px;display:flex;align-items:center;gap:8px}
.sec::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--line2),transparent)}
label.f{display:block;font-size:12.5px;color:var(--muted);margin:0 0 6px;letter-spacing:.02em;font-weight:500}
/* 16px inputs: stops iOS Safari's auto-zoom on focus, so pinch-zoom can stay
   enabled for accessibility (no user-scalable=no). */
.sheet input,.sheet textarea,.sheet select{width:100%;background:var(--bg2);border:1px solid var(--line);
  border-radius:12px;color:var(--text);padding:12px 13px;font:inherit;font-size:16px;outline:none;margin-bottom:12px}
.sheet input:focus,.sheet textarea:focus,.sheet select:focus{border-color:var(--iri2)}
.sheet textarea{resize:vertical;min-height:52px;line-height:1.5}
.hint{font-size:12px;color:var(--faint);margin:-6px 0 14px;line-height:1.5}
.two{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.lock{display:flex;gap:9px;align-items:flex-start;background:var(--glass);border:1px solid var(--line);
  border-radius:12px;padding:11px 13px;margin:6px 0 4px}
.lock svg{width:16px;height:16px;color:var(--iri1);flex:none;margin-top:1px}
.lock p{margin:0;font-size:12px;color:var(--muted);line-height:1.5}
.conn-row{display:flex;align-items:center;gap:12px;margin:-4px 0 10px}
.conn-test{border:1px solid var(--line2);background:var(--glass2);color:var(--text);border-radius:11px;
  padding:9px 14px;font-family:var(--sans);font-size:13px;font-weight:500;cursor:pointer;flex:none;transition:.18s}
.conn-test:hover{border-color:var(--iri2)}
.conn-test:active{transform:scale(.97)}
.conn-status{font-family:var(--mono);font-size:11px;letter-spacing:.02em;color:var(--muted);line-height:1.4}
.conn-status.ok{color:var(--iri1)}
.conn-status.bad{color:var(--danger)}
.conn-status.wait{color:var(--muted)}
.save{width:100%;border:none;border-radius:14px;padding:15px;font-size:15.5px;font-weight:650;
  font-family:var(--sans);color:#08070e;cursor:pointer;margin-top:16px;
  background:linear-gradient(120deg,var(--iri1),var(--iri2) 60%,var(--iri3))}
.save:active{transform:scale(.98)}

/* Behavior controls */
.seg{display:flex;gap:6px;margin-bottom:12px}
.seg-opt{flex:1;position:relative}
.seg-opt input{position:absolute;opacity:0;pointer-events:none}
.seg-opt span{display:block;text-align:center;padding:10px 4px;border:1px solid var(--line);border-radius:11px;
  background:var(--bg2);color:var(--muted);font-size:12.5px;font-weight:500;cursor:pointer;transition:.18s;
  letter-spacing:.01em;white-space:nowrap}
.seg-opt input:checked + span{color:var(--text);border-color:rgba(255,206,31,.55);
  background:linear-gradient(120deg,rgba(255,226,119,.10),rgba(255,206,31,.14),rgba(255,154,60,.10));
  box-shadow:0 2px 12px rgba(255,206,31,.18)}
.check{display:flex;align-items:center;gap:10px;margin:0 0 12px;cursor:pointer;font-size:13.5px;color:var(--text)}
.check input{width:auto;margin:0;accent-color:var(--iri2)}
.check span{line-height:1.4}

/* Format editor */
.fmt{border:1px solid var(--line);border-radius:12px;background:var(--glass);margin-bottom:8px;overflow:hidden}
.fmt summary{list-style:none;display:flex;align-items:center;justify-content:space-between;gap:8px;
  cursor:pointer;padding:12px 14px;font-size:13.5px;font-weight:500}
.fmt summary::-webkit-details-marker{display:none}
.fmt summary::after{content:"▾";color:var(--faint);font-size:11px;transition:.2s}
.fmt[open] summary::after{transform:rotate(180deg)}
.fmt-tag{font-family:var(--mono);font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--iri1);
  border:1px solid rgba(255,226,119,.3);border-radius:6px;padding:2px 7px;margin-left:auto;margin-right:6px}
.fmt-body{padding:2px 14px 12px}
.fmt-del{border:none;background:none;color:var(--danger);font-size:12px;cursor:pointer;padding:4px 0}
/* "Improve" sits ON the Instructions label row rather than under the field, so
   it reads as an action on that one input and cannot be mistaken for a form
   button that saves anything. */
.fmt-inst-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.fmt-improve{border:1px solid var(--line);background:var(--glass);color:var(--iri2);
  font-family:var(--mono);font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;
  padding:5px 10px;border-radius:8px;cursor:pointer;transition:.18s;flex:none}
.fmt-improve:hover:not(:disabled){border-color:var(--iri2);background:var(--glass2)}
.fmt-improve:disabled{opacity:.55;cursor:default}
/* Undo is the same button, so getting your own words back is one tap in the
   place you already looked — not a separate control to hunt for. */
.fmt-improve.undo{color:var(--muted)}
.fmt-msg{font-size:11.5px;line-height:1.4;margin-top:6px}
.fmt-msg.ok{color:var(--iri2)} .fmt-msg.bad{color:var(--danger)}

/* History sheet */
.hist-tools{display:flex;gap:8px;margin:6px 0 10px}
.hist-tools input{margin-bottom:0;flex:1}
.hist-tools .conn-test{flex:none}

/* Recent/history list */
.recent-item{background:var(--glass);border:1px solid var(--line);border-radius:12px;padding:11px 13px;margin-bottom:8px}
.recent-item p{font-size:13.5px;line-height:1.45;color:var(--text);margin:0 0 6px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.recent-meta{font-family:var(--mono);font-size:10px;letter-spacing:.12em;color:var(--faint);
  display:flex;align-items:center;justify-content:space-between}
.recent-btns{display:flex;gap:12px}
.recent-copy{border:none;background:none;color:var(--iri2);font-family:var(--mono);font-size:10px;
  letter-spacing:.14em;text-transform:uppercase;cursor:pointer}
.recent-copy.danger{color:var(--danger)}
.recent-empty{color:var(--faint);font-size:13px;padding:4px 0}

/* Update-ready bar */
.upd-bar{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(var(--safe-b) + 70px);z-index:65;
  border:1px solid rgba(255,206,31,.5);background:rgba(20,19,28,.94);color:var(--text);
  border-radius:30px;padding:10px 18px;font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  cursor:pointer;backdrop-filter:blur(14px);box-shadow:0 10px 40px rgba(0,0,0,.5);animation:updIn .4s ease-out}
@keyframes updIn{from{opacity:0;transform:translateX(-50%) translateY(10px)}}

/* iOS install hint */
.a2hs{position:fixed;left:16px;right:16px;bottom:calc(var(--safe-b) + 14px);z-index:30;
  background:rgba(20,19,28,.94);border:1px solid var(--line2);border-radius:16px;padding:13px 15px;
  display:none;align-items:center;gap:12px;max-width:440px;margin:0 auto;backdrop-filter:blur(14px);
  box-shadow:0 12px 40px rgba(0,0,0,.5)}
.a2hs.show{display:flex}
.a2hs p{margin:0;font-size:13px;color:var(--text);line-height:1.45;flex:1}
.a2hs .x{border:none;background:none;color:var(--faint);font-size:20px;cursor:pointer;padding:0 4px;flex:none}
.a2hs svg{width:18px;height:18px;color:var(--iri2);flex:none}

/* Sign-in screen (multi-user) */
.auth-screen{position:fixed;inset:0;z-index:70;display:none;place-items:center;padding:24px}
body.auth .auth-screen{display:grid}
body.auth .app{display:none}
.auth-card{width:100%;max-width:360px;background:var(--glass);border:1px solid var(--line);border-radius:22px;
  padding:28px 24px;backdrop-filter:blur(20px);text-align:center;position:relative}
/* The mark, carried into sign-in. This used to be a hand-rolled radial-gradient
   sphere in the retired Afterglow sunset palette (#FFF6EC → #FFC46B → #FF8A78 →
   #FF6FA5 → #8B5CF6) — a second, older logo living on the one screen every new
   user sees first. It is icons/mark.svg now, like .dot and every PNG icon, so
   there is exactly one logo in the product and it cannot drift again.
   drop-shadow, not box-shadow: the glow has to follow the bubble's alpha. */
.auth-orb{width:62px;height:62px;margin:0 auto 16px;
  background:url(./icons/mark.svg) center/contain no-repeat;
  filter:drop-shadow(0 0 18px rgba(255,206,31,.34));
  animation:authbob 3.4s ease-in-out infinite}
@keyframes authbob{0%,100%{transform:translateY(0) rotate(0deg)}50%{transform:translateY(-5px) rotate(-4deg)}}
.auth-toggle.faint{color:var(--faint);margin-top:8px}
.auth-card h2{font-family:var(--disp);font-weight:600;font-size:24px;margin-bottom:4px}
.auth-sub{color:var(--muted);font-size:13.5px;margin-bottom:22px}
.auth-card input{width:100%;background:var(--bg2);border:1px solid var(--line);border-radius:12px;color:var(--text);
  padding:13px;font:inherit;font-size:16px;outline:none;margin-bottom:10px}
.auth-card input:focus{border-color:var(--iri2)}
.auth-card .btn{width:100%;justify-content:center}
.auth-card .btn.lg{padding:14px;font-size:15px;border-radius:13px;margin-top:4px}
.auth-msg{font-family:var(--mono);font-size:11.5px;margin-top:12px;min-height:16px;line-height:1.4}
.auth-msg.bad{color:var(--danger)} .auth-msg.ok{color:var(--iri1)} .auth-msg.wait{color:var(--muted)}
/* display:block, or these two separate actions sit side by side and read as one
   sentence. They only stacked in English because "New here? Create an account"
   happens to fill the line; CJK says the same thing in six characters and both
   fitted, which is exactly the class of bug translation introduces and no
   English screenshot can show. */
.auth-toggle{display:block;width:100%;border:none;background:none;color:var(--iri2);
  font-family:var(--sans);font-size:13px;cursor:pointer;margin-top:16px}
.auth-or{display:flex;align-items:center;gap:10px;margin:14px 0;color:var(--faint);
  font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase}
.auth-or::before,.auth-or::after{content:"";flex:1;height:1px;background:var(--line)}
.auth-fine{font-size:11.5px;color:var(--faint);margin-top:18px;letter-spacing:.01em;line-height:1.5}

/* Social sign-in (Apple / Google) */
.auth-social{display:flex;flex-direction:column;gap:9px}
.btn.social{width:100%;justify-content:center;gap:10px;background:var(--bg2);border:1px solid var(--line);
  color:var(--text);font-size:14.5px;font-weight:500}
.btn.social:hover{background:var(--glass);border-color:var(--line2,var(--line))}
.btn.social svg{width:18px;height:18px;flex:0 0 auto}
/* Apple's mark is monochrome and must sit on a plain background. */
#authApple svg{color:var(--text)}

/* "Your voice" panel in Settings */
.voice-box{border:1px solid var(--line);border-radius:12px;padding:14px;background:var(--glass);
  display:flex;flex-direction:column;gap:8px;margin-bottom:6px}
.voice-box .check{margin:0}
.voice-box .hint{margin:0}
.voice-box #voiceStat,.voice-box #lexStat{color:var(--iri2)}
.voice-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:2px}
/* The learned words, shown back so a wrong one can be spotted and cleared. */
.terms{margin:0;font-family:var(--mono);font-size:11px;line-height:1.9;color:var(--text);
  word-break:break-word;border:1px solid var(--line);border-radius:10px;padding:9px 10px;background:var(--bg2)}
.voice-profile{border:1px solid var(--line);border-radius:10px;padding:10px;background:var(--bg2);
  display:flex;flex-direction:column;gap:7px}
.vp-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.vp-head .k{font-family:var(--mono);font-size:9.5px;letter-spacing:.2em;color:var(--iri1)}
.vp-x{background:none;border:none;color:var(--faint);font-size:19px;line-height:1;cursor:pointer;padding:0 2px}
.vp-x:hover{color:var(--text)}
.voice-profile textarea{width:100%;background:var(--bg);border:1px solid var(--line);border-radius:8px;
  color:var(--text);font-family:var(--sans);font-size:13px;line-height:1.5;padding:9px 10px;resize:vertical}
.voice-profile textarea:focus{outline:none;border-color:var(--iri2)}
/* Snippet rows: trigger phrase -> exact text. */
.snip{display:flex;gap:7px;align-items:flex-start;margin-bottom:7px}
.snip input,.snip textarea{background:var(--bg);border:1px solid var(--line);border-radius:9px;color:var(--text);
  font-family:var(--sans);font-size:13px;padding:9px 10px;width:100%;resize:vertical}
.snip input:focus,.snip textarea:focus{outline:none;border-color:var(--iri2)}
.snip .k{flex:0 0 36%;min-width:0}
.snip .v{flex:1 1 auto;min-width:0}
.snip .x{flex:none;background:none;border:none;color:var(--faint);font-size:18px;line-height:1;
  cursor:pointer;padding:8px 2px}
.snip .x:hover{color:var(--text)}
.snip-empty{margin:0 0 7px;color:var(--faint);font-size:12.5px}

/* File input: the native control can't be styled, so a label drives it. */
.style-files{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:6px}
.file-btn{cursor:pointer;display:inline-block}
.style-files .hint{margin:0;flex:1;min-width:160px}

/* Account row in Settings */
.account-row{display:flex;align-items:center;justify-content:space-between;gap:12px;background:var(--glass);
  border:1px solid var(--line);border-radius:12px;padding:12px 14px;margin-bottom:6px}
/* The text side of the row. `.account-row .btn{flex:none}` says the button
   keeps its width, so this half is what absorbs the rest — but it had no rule
   at all, so it was a plain block that would not shrink, and a long email
   (word-break:break-all below) pushed Sign out off the edge instead of
   wrapping. min-width:0 is the part that actually lets a flex child shrink
   below its content. */
.account-info{flex:1;min-width:0}
.account-email{font-size:14px;font-weight:500;word-break:break-all}
.account-usage{font-family:var(--mono);font-size:10.5px;color:var(--muted);letter-spacing:.04em;margin-top:3px}
.account-row .btn{flex:none}
.up-settings-btn{width:100%;margin-top:8px;border:none;border-radius:12px;padding:12px;font-family:var(--sans);
  font-size:14px;font-weight:600;color:#08070e;cursor:pointer;
  background:linear-gradient(120deg,var(--iri1),var(--iri2) 60%,var(--iri3))}
.up-settings-btn:active{transform:scale(.98)}

/* No-signup "Try it" demo overlay */
.demo-overlay{position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:24px;
  background:rgba(4,3,8,.72);backdrop-filter:blur(8px);opacity:0;transition:opacity .3s}
.demo-overlay.show{opacity:1}
.demo-card{width:100%;max-width:400px;background:var(--glass);border:1px solid var(--line2);border-radius:22px;
  padding:18px;backdrop-filter:blur(20px);box-shadow:0 20px 60px rgba(0,0,0,.5)}
.demo-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.demo-badge{font-family:var(--mono);font-size:9px;letter-spacing:.22em;color:var(--iri2);
  border:1px solid rgba(255,206,31,.4);border-radius:6px;padding:3px 8px}
.demo-x{border:none;background:var(--glass);color:var(--muted);width:34px;height:34px;border-radius:10px;cursor:pointer;font-size:18px;line-height:1}
.demo-heard{display:flex;gap:8px;align-items:baseline;margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid var(--line)}
.demo-heard .k{font-family:var(--mono);font-size:10px;letter-spacing:.2em;color:var(--faint);flex:none;padding-top:2px}
.demo-heard .v{font-size:13.5px;color:var(--muted);font-style:italic;line-height:1.4}
.demo-status{font-family:var(--mono);font-size:11px;letter-spacing:.22em;color:var(--muted);text-transform:uppercase;min-height:14px;margin-bottom:8px}
.demo-outlabel{font-family:var(--mono);font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--iri2);margin-bottom:8px}
.demo-out{font-size:16px;line-height:1.6;white-space:pre-wrap;color:var(--text);min-height:118px}
.demo-out .caret{color:var(--iri2);font-weight:600;animation:cb .8s steps(1) infinite}
.demo-cta{margin-top:14px;padding-top:14px;border-top:1px solid var(--line);text-align:center}
.demo-cta p{font-size:14px;color:var(--muted);line-height:1.5;margin-bottom:14px}
.demo-cta .btn.lg{width:100%;justify-content:center;padding:14px;font-size:15px}
.demo-later{border:none;background:none;color:var(--faint);font-family:var(--sans);font-size:13px;cursor:pointer;margin-top:10px}

/* Upgrade / pricing sheet (free -> paid) */
.up-overlay{position:fixed;inset:0;z-index:82;display:grid;place-items:center;padding:24px;
  background:rgba(4,3,8,.72);backdrop-filter:blur(8px);opacity:0;transition:opacity .3s;overflow-y:auto}
.up-overlay.show{opacity:1}
.up-card{width:100%;max-width:420px;background:var(--glass);border:1px solid var(--line2);border-radius:22px;
  padding:18px;backdrop-filter:blur(20px);box-shadow:0 20px 60px rgba(0,0,0,.5);margin:auto}
.up-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.up-badge{font-family:var(--mono);font-size:9px;letter-spacing:.22em;color:var(--iri2);
  border:1px solid rgba(255,206,31,.4);border-radius:6px;padding:3px 8px}
.up-x{border:none;background:var(--glass);color:var(--muted);width:34px;height:34px;border-radius:10px;cursor:pointer;font-size:18px;line-height:1}
.up-h{font-family:var(--disp);font-weight:600;font-size:20px;letter-spacing:-.01em;margin-bottom:6px}
.up-s{color:var(--muted);font-size:13.5px;line-height:1.5;margin-bottom:16px}
.up-tiers{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px}
.up-tier{border:1px solid var(--line);border-radius:14px;padding:14px;background:var(--bg2)}
.up-tier.pro{border-color:rgba(255,206,31,.5);
  background:linear-gradient(160deg,rgba(255,226,119,.06),rgba(255,206,31,.10),rgba(255,154,60,.06))}
.up-tier-name{font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:4px}
.up-tier.pro .up-tier-name{color:var(--iri2)}
.up-tier-price{font-family:var(--disp);font-weight:600;font-size:22px;margin-bottom:10px}
.up-tier ul{list-style:none;margin:0;padding:0}
.up-tier li{font-size:12.5px;color:var(--muted);line-height:1.5;padding-left:16px;position:relative;margin-bottom:4px}
.up-tier li::before{content:"✓";position:absolute;left:0;color:var(--iri1);font-size:11px}
.up-card .btn.lg{width:100%;justify-content:center;padding:14px;font-size:15px}
.up-card .btn.lg:disabled{opacity:.7;cursor:default}
.up-msg{font-family:var(--mono);font-size:11px;color:var(--iri1);text-align:center;min-height:14px;margin-top:10px;line-height:1.4}
.up-later{display:block;width:100%;border:none;background:none;color:var(--faint);font-family:var(--sans);font-size:13px;cursor:pointer;margin-top:8px;text-align:center}

:focus-visible{outline:2px solid var(--iri2);outline-offset:3px;border-radius:8px}
/* ...except the language <select>, which is smaller than the pill it sits in:
   the generic ring drew a stray inner rectangle around the label that traced no
   real control (and iOS keeps focus on a select after the picker closes, so it
   just sat there). Ring the pill, which is the thing the user sees. */
.lang:focus-visible{outline:none}
.lang-pill:has(.lang:focus-visible){outline:2px solid var(--iri2);outline-offset:2px}
@supports not selector(:has(*)){
  .lang:focus-visible{outline:2px solid var(--iri2);outline-offset:3px}
}

@media (max-width:390px){ .core-wrap{width:270px;height:270px} .tagline{font-size:23px} }
/* The stage's real constraint is HEIGHT, not width: a 13 mini is narrow but
   tall (plenty of room) while an SE is short (none). Keying only on width
   shrinks the wrong devices and misses the ones that actually overflow — and
   when the stage overflows, `justify-content:center` degenerates to
   top-alignment and the tagline lands on the header. Tighten the vertical
   rhythm on short viewports instead. */
@media (max-height:720px){
  .core-wrap{width:240px;height:240px}
  .tagline{font-size:22px;margin-bottom:18px}
  .readout{margin-top:16px}
  .chips{margin-top:16px}
  .pill,.reply-chip{margin-top:8px}
}
@media (prefers-reduced-motion:reduce){
  *{animation:none!important;transition-duration:.001s!important}
  #ptr{display:none} .grain{display:none}
  .card{opacity:1;transform:none}
}
