/* Sayso vibe.css. The brand layer on top of Meridian: a flat, cool page, soft white cards, slate ink, glass, and one kind of
   colour: light caught in glass. The bead (the microphone) is made of it, and so is every surface that stands for a tool.
   Load it after app.css. It reads tokens.css and meridian.css and never edits those files. It does set a few Meridian
   values again for the app: the page colour, ink, muted ink and the primary (slate, not black), and in dark the surfaces
   (deep page, glass cards).

   INDEX

   Custom properties (":root". The ones marked * are what a tool look sets on its own container)
     --pool-a --pool-b --pool-c --pool-wash   the colour pooled in the bead: pink, coral, violet at its edge (hotter in dark, to read as light). Decoration only.
     --life-a to --life-d   the hues that move in the bead when it is awake. A state swaps them.  --life-dim --life-sat   dark only: how they are dimmed and saturated
     --bead-base --bead-rim --bead-glow --bead-ink --bead-dim   the bead's glass, its rim of light, its glow, the glyph in it, and how far dark dims its pool
     --bead-glow-in         dark only: the glow behind the bead's middle, kept faint so the glass there stays clear
     --eye                  Sayso's eyes on the bead (plum ink in light, white in dark), solid and sharp
     --glass --glass-rim   a card of glass (the Make card, the tab bar, a sheet).  --glass-btn --glass-btn-edge   a small round glass control
     --spill-a --spill-b    the bead's light on what is behind it
     --panel               the pane that rises over a material (the plan in the summary): frosted in light, opaque in dark
     --spectrum-text   gradient for .grad-text, flips with the theme. Large text only (24px and up), one phrase per screen.
   * --m0 --m1 --m2 --m3   a material: a base colour and three soft lights in it. The brand's own is pink, peach and lilac.
     --grain           fine noise, laid over every material, at half strength in dark
     --haze            dark only: a deep glow at the top of the screen, behind the glass
   * --hair            hairline: row dividers, glass edges
   * --fill --fill-press   translucent neutral fill: secondary button, chip, field in a card, icon badge
     --card            shadow of a card. Light: feather-soft, no line. Dark: a glass edge.
     --glass-on --glass-line   glass control on a material (tile, badge, pencil)
     --ink-soft        secondary text on a material, a step stronger than --ink-muted
     --ring            the soft outline of a field that has focus
     --cta-bg --cta-ink --cta-glow   .md-btn-primary: a slate pill (white with dark text in dark), the same on a material
     --fill-bg         small filled states: chip on, tick, progress, selected ring. Follows --primary.
     --accent          the one violet, for the sparkle of "Suggested" and nothing else
     --focus-ring      ink
     --r-btn --r-card --r-field   radii (pill, 28, 16)
     --spring --ease-out --jelly   easings. --spring barely overshoots. --jelly is the one wobble of a pressed bead settling.
     --app-ink --app-bg    the app's own ink and page colour, readable inside a look
     A look also sets Meridian's --surface-subtle --surface --ink --ink-muted --primary --on-primary --border --link.

   Restyled in place, no markup change
     .md-btn (pill. -primary slate, -secondary neutral fill, -tertiary bare, -square round. Disabled is a neutral fill with a readable label)
     .md-progress .md-card .md-material-base .md-input (and every placeholder) .md-note .md-badge-gray .opt .chip .linkbox .linkish
     .list .lens-label .t-h24 .t-h32 (regular weight) .wordmark .composer .stat .lookcard .swatch (a small flat preview) .lookhead
     .topbar .md-btn-square and .stepper .md-btn-square (glass circles) .page #app #view #toasts .md-toast

   Colour carriers. They only set --hue (icons, dots), --hue-b (its partner, for a material), --hue-ink (text), --hue-soft (tint).
     .kind-rsvp .kind-signup .kind-calendar .kind-tracker .kind-list
     .lens-pm .lens-design .lens-build

   Materials: a blurred gradient with grain, the app's imagery. Dark text on the light ones, white on the deep ones.
     .lookcard .lookhead (so also .summary-head.lookhead)   the look's own colours
     .toolpage .page > header                               the title of a tool on its guest page becomes a card of its look
     .market-card                                           the kind's hue and its partner
     .plan-featured, .celebrate                             the brand's own

   Pieces
     .tile [.tile-sm|.tile-lg] [.tile-round] [.tile-brand]   icon tile, holds one svg: the hue as a line icon on a faint tint.
                                                             .tile-brand is a neutral round badge. In a sheet fx.js swaps it for
                                                             the bead itself, small, with its face (orb 56).
     .hero [.center] > .hero-title + .hero-sub, .grad-text   screen title, regular weight. Its first line is ink, the rest muted.
     .tabbar > .tab[aria-current="page"]                     floating glass tabs, a direct child of #app after #view. Each tab: svg + span.
     .meter > .meter-top (span + b) + .md-progress           usage meter
     .lens-chip                                              "who is asking": svg in the role's hue + muted label
     .lens-card > .lens-head (.tile.tile-round + div > .lens-role + .lens-sub) + p or ul.lens-points   plain card. In .msg-cards the
                                                             three arrive as a stack and deal out, each with its role's light in a corner.
     .thinking                                               add to .page: centred column for the orb screens
     .think-steps > .think-step[data-state="wait|active|done"]   one card, hairline rows. Children: .tile.tile-round.tile-sm + span.grow (strong + .sub)
     .market-grid > .market-card                             children: .tile + .market-name + .market-blurb + .market-meta
     .plan [.plan-featured] > .plan-head (.plan-name + .badge-spectrum) + .plan-price (b + span) + ul.plan-list (li: svg + text) + button
     .badge-spectrum                                         "Suggested" and "Your plan": a quiet neutral pill, its svg in --accent
     .stagger                                                children rise in one after another. Add it only when a screen first appears.
     .celebrate                                              same, a touch slower, under a wide field of the brand's material
     .press                                                  press scale for anything else that is tappable
     .sheet-root .scrim .sheet .sheet-label .sheet-close     made by fx.js sheet(), do not write by hand. While one is open
                                                             html has .sheet-open and #view steps back, inert (.sheet-leaving as it returns).
     .orb                                                    the bead, made by fx.js orb(), data-state="idle|listening|speaking|thinking|done"
                                                             Each state has its own hues: idle pink and coral, listening pink into violet,
                                                             speaking pink into amber, thinking blue, violet and amber.
     .orb > span.orb-eyes > i + i                            the face, from orb({ face }): the bead is then Sayso, and each state an expression.
                                                             For beads that are not buttons (thinking line, building, ready, sheets).
                                                             The eyes or the microphone show by whose turn it is, see the voice section.
     .wordmark                                               its ::before is the mark: the character at 18px, two dots on a solid pink bead
                                                             (icons/logo-mark-solid.svg is the same mark as a file)
     .confetti                                               made by fx.js confetti()

   Voice (Make)
     section.voice-hero > button.mic-hero[data-state="idle|listening"] (orb + span.mic-glyph > svg mic) + p.mic-hint + button.linkish.type-toggle
     The section is one card of glass, nothing stacked under it (the owner's rule). The bead (168px, half the card's width) is the
     button, and the glyph is in the glass (no disc under it). The type-toggle keeps its underline: it is a link.
     A bead with a face shows no glyph: the eyes carry the state, the text beside it says what a tap does.
     button.talk-voice (orb 44 + span.mic-glyph)   the way back to voice, in the Make composer and in form.talk-type
   Conversation (Talk)
     div.page.talk [.is-fresh] > header.topbar (back + title + button.talk-mute) + ol.thread + div.talkbar
                                                             .is-fresh: the thread holds only the greeting. By voice the bead is then large (132px)
                                                             on a glass card, the greeting and the live line under it. No markup moves.
     li.msg.msg-sayso [.lens-x] [.is-open] > span.lens-chip? + p.msg-text + div.replies? + div.msg-cards?   .is-open is the question that is
                                                             open: it reads as the title. With none marked, the last one from Sayso does.
     li.msg.msg-sayso.msg-miss > p.msg-text + div.replies?   Sayso did not understand: a 16px line in ink, never the title. The question keeps it.
     li.msg.msg-you > p.msg-text                                                                    her words, a neutral bubble on the right
     li.msg.msg-sayso.is-thinking > orb (28, drawn at 56 while she types) + p.msg-text [+ ul.think-steps]
     .replies > button.reply[aria-pressed] [> span.badge-spectrum]    white pills that wrap. Pressed: ink ring and a corner check.
     .msg-cards > .lookcard (a row that scrolls sideways, a pane of glass on each) | .lens-card | .prompt-card | .summary
     div.prompt-card > button.prompt-pick (strong.prompt-title? + p) + button.prompt-edit (svg + "Edit")   a row: title, two lines, Edit, and a
                                                             chevron drawn by the card. With no title the text shows whole.
     section.summary > header.summary-head.lookhead.look-x (.tile + div > .summary-title + .summary-sub, label.summary-edit) + ul.summary-list > li[.lens-x] (svg + span)
     div.talkbar > p.talk-live + div.talkbar-row (button.talk-keys + button.talk-mic + .md-btn? + .md-btn.talk-second?) + form.talk-type (button.talk-voice | .talk-keys + .md-input + button)
                                                             .talk-second is the quiet second action ("No"): left of the microphone, the main one right.
     button.talk-mic[data-state="idle|listening|speaking|thinking|off"] (orb 72 + span.mic-glyph > svg mic)   the button's state drives the bead inside
                                                             ("off" is styled and nothing sets it at present)

   Tool looks, on .toolpage, .lookcard and .lookhead together with data-theme
     .look-clean .look-violet .look-sunset .look-mint (light)   .look-night .look-party (dark)
     Each is a pale or deep page, plain or glass cards, and a material (--m0 to --m3) for its header and its card in the picker.
     Any class name that contains "look-" is treated as a look container, so keep that prefix for looks only.
*/

/* ---------- brand tokens */

:root {
  --pool-a: #ff7fa6; --pool-b: #ffa196; --pool-c: #ff9ce6; --life-a: #ff5c9d; --life-b: #ff9d7a; --life-c: #f77fdc; --life-d: #ffb08a;
  --r-btn: 9999px; --r-card: 28px; --r-field: 16px;
  --spring: cubic-bezier(.3, 1.3, .5, 1);
  --jelly: cubic-bezier(.2, 2.1, .4, .9);
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --vb-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  --vb-chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 5.5 6.5 6.5L9 18.5'/%3E%3C/svg%3E");
  --vb-arc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='4' stroke-linecap='round'%3E%3Cpath d='M4.5 14.5A9 9 0 0 1 19.5 14.5'/%3E%3C/svg%3E");
  /* Resolved here on the root, so a look card can draw its selected ring in the app's ink, not the look's. */
  --app-ink: var(--ink); --app-bg: var(--surface-subtle); --app-check: var(--check-on);
}

/* What differs between light and dark. Same three selectors as tokens.css. --check-on is the tick drawn on --fill-bg.
   Light: a cool gray page, slate ink. Dark: a deep page with a glow at the top, and cards of glass.
   --grain is the same noise in both, at half the strength in dark, where it showed as speckle on the deep colours. */
:root, [data-theme="light"] {
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.15' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='5 0 0 0 -2 5 0 0 0 -2 5 0 0 0 -2 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='.42'/%3E%3C/svg%3E");
  --surface-subtle: #f3f5f8; --ink: #252b36; --ink-muted: #5d6273; --primary: #252b36; --primary-hover: #3a404e;
  --hair: rgb(32 36 60 / .08); --fill: rgb(40 46 80 / .055); --fill-press: rgb(40 46 80 / .11); --bubble: rgb(40 46 80 / .085);
  --card: 0 1px 1px rgb(24 28 48 / .02), 0 18px 40px -22px rgb(24 28 48 / .18);
  --cta-glow: inset 0 1px 0 rgb(255 255 255 / .14), 0 1px 2px rgb(24 28 48 / .2), 0 10px 20px -10px rgb(24 28 48 / .45);
  --bar: rgb(255 255 255 / .66); --sheet: rgb(255 255 255 / .88); --toast-bg: #252b36; --haze: none;
  --glass: linear-gradient(165deg, #ffffff 34%, rgb(255 255 255 / .8)); --glass-rim: inset 1px 1px 0 #ffffff, inset 0 0 0 1px rgb(255 255 255 / .6);
  --glass-btn: linear-gradient(150deg, #ffffff, rgb(255 255 255 / .6)); --glass-btn-edge: inset 0 0 0 1px rgb(255 255 255 / .9), 0 1px 2px rgb(24 28 48 / .05), 0 8px 16px -10px rgb(24 28 48 / .28);
  --panel: rgb(255 255 255 / .84);
  --bead-base: radial-gradient(56% 44% at 45% 47%, #fddcdc, #fde2e1 30%, #fee9e8 66%, #fff0ee 92%, #fff2f0); --bead-glow: rgb(255 214 232 / .48); --bead-rim: rgb(255 255 255 / .6); --pool-wash: rgb(254 176 186 / .16);
  --bead-ink: rgb(37 43 54 / .9); --bead-ink-shadow: drop-shadow(0 1px 2px rgb(255 255 255 / .7));
  --eye: #501f47;
  --spill-a: rgb(255 170 205 / .34); --spill-b: rgb(255 200 180 / .24);
  --glass-on: rgb(255 255 255 / .55); --glass-line: rgb(255 255 255 / .7); --mat-base: #ffffff;
  --m0: #f5eefb; --m1: #ffc3d7; --m2: #ffdcc2; --m3: #cfc8ff;
  --accent: #7a5cf0; --spectrum-text: linear-gradient(95deg, #7a5cf0, #dd4a9b 55%, #e2570b);
  --check-on: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18.5 7.5 10 16.5 5.5 12'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) {
  --surface-subtle: #0d0e14; --surface: rgb(255 255 255 / .06); --surface-floating: #1c1e29; --surface-hover: rgb(255 255 255 / .1); --ink: #f3f4f8; --ink-muted: #aaaebd; --primary: #ffffff; --primary-hover: #e8e9ef; --on-primary: #1a1c25;
  --hair: rgb(255 255 255 / .1); --fill: rgb(255 255 255 / .08); --fill-press: rgb(255 255 255 / .15); --bubble: rgb(255 255 255 / .11);
  --card: inset 0 0 0 1px rgb(255 255 255 / .08), inset 0 1px 0 rgb(255 255 255 / .07);
  --cta-glow: 0 1px 2px rgb(0 0 0 / .4), 0 10px 22px -10px rgb(0 0 0 / .6);
  --bar: rgb(44 46 62 / .6); --sheet: rgb(30 32 44 / .84); --toast-bg: #353846;
  --glass: linear-gradient(165deg, rgb(54 56 74 / .9), rgb(34 36 50 / .88)); --glass-rim: inset 1px 1px 0 rgb(255 255 255 / .16), inset 0 0 0 1px rgb(255 255 255 / .07);
  --glass-btn: linear-gradient(150deg, rgb(255 255 255 / .15), rgb(255 255 255 / .05)); --glass-btn-edge: inset 1px 1px 0 rgb(255 255 255 / .16), inset 0 0 0 1px rgb(255 255 255 / .06), 0 8px 16px -10px rgb(0 0 0 / .6);
  --panel: #1f2130;
  --bead-base: radial-gradient(60% 50% at 62% 66%, rgb(255 255 255 / .12), rgb(255 255 255 / .04)); --bead-glow: rgb(255 120 176 / .44); --bead-glow-in: rgb(255 120 176 / .06); --bead-rim: rgb(255 255 255 / .38); --pool-wash: rgb(255 120 150 / .16);
  --pool-a: #ff5f98; --pool-b: #ff8f6e; --pool-c: #e07bff; --bead-ink: #ffffff; --bead-ink-shadow: drop-shadow(0 1px 3px rgb(50 8 34 / .6)); --bead-dim: .9; --life-dim: .8; --life-sat: 1.4;
  --eye: #fff;
  --spill-a: rgb(255 110 170 / .2); --spill-b: rgb(255 150 110 / .12);
  --haze: radial-gradient(560px 420px at 8% -40px, rgb(96 78 214 / .36), transparent 70%), radial-gradient(520px 400px at 104% -20px, rgb(196 78 150 / .22), transparent 68%), radial-gradient(640px 360px at 56% -120px, rgb(40 150 170 / .16), transparent 70%);
  --glass-on: rgb(10 12 24 / .3); --glass-line: rgb(255 255 255 / .2); --mat-base: #161722;
  --m0: #1c1830; --m1: #7d2f6e; --m2: #8a4a2f; --m3: #353d99;
  --accent: #b3a2ff; --spectrum-text: linear-gradient(95deg, #b3a2ff, #f77fbe 55%, #ffa057);
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.15' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='5 0 0 0 -2 5 0 0 0 -2 5 0 0 0 -2 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='.21'/%3E%3C/svg%3E");
  --check-on: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18.5 7.5 10 16.5 5.5 12'/%3E%3C/svg%3E");
} }
[data-theme="dark"] {
  --surface-subtle: #0d0e14; --surface: rgb(255 255 255 / .06); --surface-floating: #1c1e29; --surface-hover: rgb(255 255 255 / .1); --ink: #f3f4f8; --ink-muted: #aaaebd; --primary: #ffffff; --primary-hover: #e8e9ef; --on-primary: #1a1c25;
  --hair: rgb(255 255 255 / .1); --fill: rgb(255 255 255 / .08); --fill-press: rgb(255 255 255 / .15); --bubble: rgb(255 255 255 / .11);
  --card: inset 0 0 0 1px rgb(255 255 255 / .08), inset 0 1px 0 rgb(255 255 255 / .07);
  --cta-glow: 0 1px 2px rgb(0 0 0 / .4), 0 10px 22px -10px rgb(0 0 0 / .6);
  --bar: rgb(44 46 62 / .6); --sheet: rgb(30 32 44 / .84); --toast-bg: #353846;
  --glass: linear-gradient(165deg, rgb(54 56 74 / .9), rgb(34 36 50 / .88)); --glass-rim: inset 1px 1px 0 rgb(255 255 255 / .16), inset 0 0 0 1px rgb(255 255 255 / .07);
  --glass-btn: linear-gradient(150deg, rgb(255 255 255 / .15), rgb(255 255 255 / .05)); --glass-btn-edge: inset 1px 1px 0 rgb(255 255 255 / .16), inset 0 0 0 1px rgb(255 255 255 / .06), 0 8px 16px -10px rgb(0 0 0 / .6);
  --panel: #1f2130;
  --bead-base: radial-gradient(60% 50% at 62% 66%, rgb(255 255 255 / .12), rgb(255 255 255 / .04)); --bead-glow: rgb(255 120 176 / .44); --bead-glow-in: rgb(255 120 176 / .06); --bead-rim: rgb(255 255 255 / .38); --pool-wash: rgb(255 120 150 / .16);
  --pool-a: #ff5f98; --pool-b: #ff8f6e; --pool-c: #e07bff; --bead-ink: #ffffff; --bead-ink-shadow: drop-shadow(0 1px 3px rgb(50 8 34 / .6)); --bead-dim: .9; --life-dim: .8; --life-sat: 1.4;
  --eye: #fff;
  --spill-a: rgb(255 110 170 / .2); --spill-b: rgb(255 150 110 / .12);
  --haze: radial-gradient(560px 420px at 8% -40px, rgb(96 78 214 / .36), transparent 70%), radial-gradient(520px 400px at 104% -20px, rgb(196 78 150 / .22), transparent 68%), radial-gradient(640px 360px at 56% -120px, rgb(40 150 170 / .16), transparent 70%);
  --glass-on: rgb(10 12 24 / .3); --glass-line: rgb(255 255 255 / .2); --mat-base: #161722;
  --m0: #1c1830; --m1: #7d2f6e; --m2: #8a4a2f; --m3: #353d99;
  --accent: #b3a2ff; --spectrum-text: linear-gradient(95deg, #b3a2ff, #f77fbe 55%, #ffa057);
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.15' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='5 0 0 0 -2 5 0 0 0 -2 5 0 0 0 -2 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='.21'/%3E%3C/svg%3E");
  --check-on: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18.5 7.5 10 16.5 5.5 12'/%3E%3C/svg%3E");
}

/* Derived values, declared again on every themed container so they resolve against its own tokens. */
:root, [data-theme] {
  --focus-ring: var(--ink);
  --cta-bg: var(--primary); --cta-ink: var(--on-primary); --fill-bg: var(--primary);
  --ink-soft: color-mix(in srgb, var(--ink-muted), var(--ink));
  --ring: color-mix(in srgb, var(--ink) 50%, transparent);
}

/* ---------- surfaces */

/* The page itself paints nothing. In dark a glow sits still behind it while the glass cards scroll over. */
#app { isolation: isolate; }
#app::before { content: ""; position: fixed; top: 0; bottom: 0; left: 50%; z-index: -1; width: min(100%, 430px); transform: translateX(-50%); background: var(--haze); pointer-events: none; }
/* The bead's light is as wide as the screen and drifts, so the page clips it at its sides (clip, not hidden: nothing
   scrolls and sticky bars keep working). Without this the page grew up to 22px wider than the phone and back. */
.page { background: none; overflow-x: clip; }
.toolpage { background: var(--surface-subtle); }
.md-card, .md-material-base, .meter, .plan, .lens-card, .market-card, .think-steps, .prompt-card, .summary { box-shadow: var(--card); }
.md-card, .lens-card, .market-card, .plan, .prompt-card, .summary, .think-steps { border-radius: var(--r-card); }
.md-card.pad, .lens-card { padding: var(--space-5); }
.md-card.list > li:not(.taprow), .taprow > .opt { padding-left: var(--space-5); padding-right: var(--space-5); }
.list > li + li { box-shadow: 0 -1px 0 var(--hair); }
/* A field is a soft white shape on the page and a pale one inside a card. No outline until it has focus. */
.md-input, .md-textarea { border-color: transparent; border-radius: var(--r-field); background: var(--surface); box-shadow: var(--card); }
:is(.md-card, .lens-card, .sheet) .md-input { background: var(--fill); box-shadow: none; }
.md-input:hover { border-color: transparent; }
.md-input:focus-within, :is(.md-card, .lens-card, .sheet) .md-input:focus-within { outline: 0; box-shadow: 0 0 0 1.5px var(--ring); }
.md-input-error, .md-input-error:hover { border-color: var(--red-900); }
.md-note { border-radius: 18px; }
.md-note-fill.md-note-neutral { background: var(--fill); border-color: transparent; }
/* A small pill can sit on a material, so its label is a step stronger than muted. */
.md-badge-gray, .badge-spectrum { background: var(--fill); color: var(--ink-soft); }
.linkbox { border-radius: var(--r-field); background: var(--fill); box-shadow: none; }
.linkish { text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); }
/* A placeholder here is an example to read, so it gets the muted ink, not the disabled one. */
.idea::placeholder, .md-input input::placeholder, .md-textarea::placeholder { color: var(--ink-muted); opacity: 1; }

.wordmark { display: inline-flex; align-items: center; gap: var(--space-2); font-size: 21px; letter-spacing: -0.03em; }
/* The mark is the character, small: two dots of ink on a tinted bead, glancing the same way as the large one. */
.wordmark::before { content: ""; width: 18px; height: 18px; border-radius: 50%; background: radial-gradient(1.4px 1.9px at 36% 47%, #501f47 66%, transparent), radial-gradient(1.3px 1.75px at 68% 47%, #501f47 66%, transparent), radial-gradient(circle at 30% 30%, var(--pool-a), var(--pool-b) 44%, #ffe1dc 82%); box-shadow: 0 0 0 3px color-mix(in srgb, var(--pool-a) 14%, transparent); }

/* Type: regular weight for the big lines. A title that runs to a second line fades there, ink then muted. */
.t-h32, .t-h24 { font-weight: 400; }
.hero { display: flex; flex-direction: column; gap: var(--space-2); }
.hero-title { font: 400 32px/38px var(--font-sans); letter-spacing: -0.03em; color: var(--ink-muted); text-wrap: balance; }
.hero-title::first-line { color: var(--ink); }
.hero-sub { font: 400 16px/24px var(--font-sans); color: var(--ink-muted); text-wrap: pretty; }
.grad-text { background: var(--spectrum-text); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: transparent; -webkit-box-decoration-break: clone; box-decoration-break: clone; }

.composer { padding: var(--space-5); transition: box-shadow .2s; }
.idea { border-radius: 0; background: none; box-shadow: none; }
.composer:focus-within { box-shadow: 0 0 0 1.5px var(--ring), var(--card); }

/* ---------- materials: a blurred gradient with fine grain. Three soft lights (--m1 to --m3) in a base colour (--m0). */

.lookcard, .lookhead, .toolpage .page > header, .market-card, .plan.plan-featured { background: var(--grain), radial-gradient(120% 95% at 0% 0%, var(--m1), transparent 62%), radial-gradient(115% 100% at 100% 8%, var(--m2), transparent 60%), radial-gradient(120% 90% at 58% 118%, var(--m3), transparent 64%), var(--m0); background-blend-mode: soft-light, normal, normal, normal, normal; }
/* On a material: secondary text a step stronger and glass for the small controls. The action keeps the theme's own
   primary there too: ink in light, white in dark. */
:is(.lookcard, .lookhead, .toolpage .page > header, .market-card, .plan.plan-featured) :is(.muted, .market-blurb, .market-meta, .summary-sub, .plan-price, .t-label13) { color: var(--ink-soft); }
:is(.lookcard, .lookhead, .toolpage .page > header, .market-card, .plan.plan-featured) :is(.tile, .md-badge, .badge-spectrum) { background: var(--glass-on); box-shadow: inset 0 0 0 1px var(--glass-line); }
:is(.lookcard, .lookhead, .toolpage .page > header, .market-card, .plan.plan-featured) :is(.md-badge, .badge-spectrum) { color: var(--ink); }
:is(.lookhead, .market-card) .tile { border-radius: 50%; color: var(--ink); }
.celebrate .muted { color: var(--ink-soft); }

/* ---------- buttons and other things you press */

.md-btn { position: relative; border-radius: var(--r-btn); touch-action: manipulation; -webkit-user-select: none; user-select: none; transition: transform .3s var(--ease-out), background .15s, color .15s; }
.md-btn-lg { padding: 0 22px; } .md-btn-md { padding: 0 var(--space-4); } .md-btn-sm { padding: 0 14px; }
.md-btn.md-btn-square { flex: none; padding: 0; border-radius: 50%; }
/* A small or medium button answers a tap on 44px. The layer is placed from inside the button's 1px border, so it reaches one more than the gap. */
.md-btn-sm::before, .md-btn-md::before { content: ""; position: absolute; inset: -7px; } .md-btn-md::before { inset: -3px; }
.md-btn-primary, .md-btn-primary:hover:not(:disabled) { background: var(--cta-bg); color: var(--cta-ink); border-color: transparent; box-shadow: var(--cta-glow); }
/* Not ready yet: the ink drains to a neutral fill and the label stays readable. */
.md-btn-primary:disabled { background: var(--fill); color: var(--ink-muted); border-color: transparent; box-shadow: none; }
.md-btn-secondary, .md-btn-secondary:disabled, .md-btn-tertiary:disabled { border-color: transparent; }
.md-btn-secondary { background: var(--fill); }
/* A disabled neutral button often states a fact ("On the waitlist"), so its label stays readable. */
.md-btn-secondary:disabled { background: var(--fill); color: var(--ink-muted); }
.md-btn-tertiary:disabled { background: none; }
@media (hover: hover) {
  .md-btn-primary:hover:not(:disabled) { filter: brightness(1.12); }
  .md-btn-secondary:hover:not(:disabled), .md-btn-tertiary:hover:not(:disabled) { background: var(--fill-press); }
}

.opt { transition: transform .3s var(--ease-out), box-shadow .15s; }
.opt.md-material-base { border-radius: 20px; }
/* A choice inside a card is a pale pill, like a field there. Three still fit side by side in the wider card padding. */
:is(.md-card, .lens-card, .sheet) .opt.md-material-base { background: var(--fill); box-shadow: none; }
.choices { grid-template-columns: repeat(auto-fit, minmax(88px, 1fr)); }
.choices .opt { position: relative; border-radius: 24px; padding-left: var(--space-2); padding-right: var(--space-2); }
.opt[aria-checked="true"], .opt[aria-pressed="true"], :is(.md-card, .lens-card, .sheet) .opt.md-material-base[aria-checked="true"] { box-shadow: 0 0 0 1.5px var(--fill-bg); }
.opt:disabled { opacity: .45; cursor: not-allowed; }
.opt > .tile + span { flex: 1; min-width: 0; }

.chip { position: relative; height: 36px; padding: 0 var(--space-4); background: var(--fill); box-shadow: none; transition: transform .3s var(--ease-out), background .15s, color .15s; }
.chip::before { content: ""; position: absolute; inset: -4px 0; }
.chip[aria-pressed="true"] { background: var(--fill); color: var(--ink); box-shadow: 0 0 0 1.5px var(--fill-bg); }

/* Selected is an ink ring, with a small check on the corner where more than one thing can be picked. A chip is the ring
   alone: its row scrolls sideways and cut the check off. Ink fill is kept for the action. */
.choices .opt[aria-checked="true"]::after, .reply[aria-pressed="true"]::after, .lookcard[aria-checked="true"]::after { content: ""; position: absolute; top: -5px; right: -5px; width: 18px; height: 18px; border-radius: 50%; background: var(--fill-bg) var(--check-on) center / 12px no-repeat; box-shadow: 0 0 0 2px var(--app-bg); animation: vb-pop .3s var(--spring) backwards; }

.press, .lookcard, .market-card, .reply, .prompt-card, .mic-hero, .talk-mic, .talk-voice, .talk-keys, .talk-mute, .prompt-edit, .talk-type > .md-input ~ button, a.md-badge { transition: transform .3s var(--ease-out); }
.md-btn:active:not(:disabled), .chip:active, .tab:active, .reply:active, .talk-keys:active, .talk-mute:active, .prompt-edit:active, .talk-type > .md-input ~ button:active:not(:disabled), a.md-badge:active { transform: scale(.94); transition-duration: .08s; }
.linkish:active { opacity: .55; }
.opt:active:not(:disabled), .press:active, .lookcard:active, .market-card:active, .prompt-card:active { transform: scale(.975); transition-duration: .08s; }

.md-progress { height: 4px; background: var(--fill); }
.md-progress span { background: var(--fill-bg); transition: width .6s var(--ease-out); }

.badge-spectrum { display: inline-flex; align-items: center; gap: var(--space-1); height: 24px; padding: 0 10px 0 8px; border-radius: 9999px; font: 500 12px/16px var(--font-sans); white-space: nowrap; }
.badge-spectrum svg { color: var(--accent); fill: currentColor; }

/* ---------- colour carriers: tool kinds and the three roles */

.kind-rsvp, .lens-design { --hue: var(--pink-800); --hue-b: var(--amber-700); --hue-ink: var(--pink-900); }
.kind-signup { --hue: var(--purple-800); --hue-b: var(--pink-700); --hue-ink: var(--purple-900); }
.kind-calendar, .lens-pm { --hue: var(--blue-800); --hue-b: var(--purple-700); --hue-ink: var(--blue-900); }
.kind-tracker { --hue: var(--green-800); --hue-b: var(--blue-700); --hue-ink: var(--green-900); }
.kind-list, .lens-build { --hue: color-mix(in srgb, var(--amber-800), var(--amber-900)); --hue-b: var(--red-700); --hue-ink: var(--amber-900); }
.kind-rsvp, .kind-signup, .kind-calendar, .kind-tracker, .kind-list, .lens-pm, .lens-design, .lens-build { --hue-soft: color-mix(in srgb, var(--hue) 10%, transparent); }

.tile { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 40px; height: 40px; border-radius: 14px; background: var(--hue-soft, var(--fill)); color: var(--hue, var(--ink-muted)); }
.tile svg { width: 22px; height: 22px; }
.tile-sm { width: 32px; height: 32px; border-radius: 11px; } .tile-sm svg { width: 18px; height: 18px; }
.tile-lg { width: 56px; height: 56px; border-radius: 20px; } .tile-lg svg { width: 28px; height: 28px; }
.tile-round, .tile-brand { border-radius: 50%; }
.tile-brand { background: var(--fill); color: var(--ink-muted); }
/* In a sheet that badge is the bead itself with its face: fx.js sheet() puts a real orb in its place. It has room for its glow. */
.sheet .orb { margin: 6px 0 2px 6px; }
/* Sideways rows fade out at both edges instead of being cut by the screen. The fade is as wide as the row's side
   padding, so the first and last items rest clear of it. A mask, so it works over any ground in both themes. */
.shelf, .chips.scroll, .msg-cards:has(> .lookcard) { -webkit-mask: linear-gradient(90deg, transparent, #000 var(--space-4), #000 calc(100% - var(--space-4)), transparent); mask: linear-gradient(90deg, transparent, #000 var(--space-4), #000 calc(100% - var(--space-4)), transparent); }
.shelf-item .tile { width: 56px; height: 56px; border-radius: 20px; background: var(--surface); box-shadow: var(--card); } .shelf-item .tile svg { width: 24px; height: 24px; }

.lens-chip { display: inline-flex; align-items: center; align-self: flex-start; gap: 6px; color: var(--ink-muted); font: 500 13px/16px var(--font-sans); white-space: nowrap; }
.lens-chip svg { flex: none; width: 16px; height: 16px; color: var(--hue); }
.lens-card { display: flex; flex-direction: column; gap: var(--space-3); width: 100%; background: var(--surface); color: var(--ink); }
.lens-head { display: flex; align-items: center; gap: var(--space-3); }
/* In the thread the three cards arrive as a stack and deal out. Each keeps its role's light in one corner, under a glass tile. */
.msg-cards > .lens-card { position: relative; background: radial-gradient(210px 130px at 0% 0%, color-mix(in srgb, var(--hue) 15%, transparent), transparent), var(--surface); animation: vb-deal .75s var(--ease-out) backwards; }
.msg-cards > .lens-card:nth-child(1) { z-index: 3; }
.msg-cards > .lens-card:nth-child(2) { z-index: 2; --deal: 1; animation-delay: 70ms; }
.msg-cards > .lens-card:nth-child(3) { z-index: 1; --deal: 2; animation-delay: 140ms; }
.msg-cards > .lens-card .lens-head .tile { background: var(--glass-btn); box-shadow: var(--glass-btn-edge); }
.lens-role { font: 500 16px/22px var(--font-sans); letter-spacing: -0.01em; }
.lens-sub { font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
.lens-label { color: var(--ink-muted); }
.lens-points { display: flex; flex-direction: column; gap: 6px; font: 400 14px/20px var(--font-sans); }
.lens-points li { position: relative; padding-left: var(--space-4); }
.lens-points li::before { content: ""; position: absolute; left: 2px; top: 8px; width: 5px; height: 5px; border-radius: 50%; background: var(--hue, var(--ink-muted)); }

/* ---------- thinking */

.thinking { align-items: center; justify-content: center; text-align: center; }
.think-steps { display: flex; flex-direction: column; width: 100%; background: var(--surface); text-align: left; overflow: hidden; }
.think-step { display: flex; align-items: center; gap: var(--space-3); min-height: 60px; padding: var(--space-2) var(--space-5) var(--space-2) var(--space-4); color: var(--ink); transition: color .3s; }
.think-step + .think-step { border-top: 1px solid var(--hair); }
.think-step .sub { display: block; font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
.think-step::after { content: ""; flex: none; width: 18px; height: 18px; box-sizing: border-box; }
.think-step[data-state="wait"] { color: var(--ink-muted); }
.think-step[data-state="wait"] .tile { filter: grayscale(1); opacity: .6; }
.think-step[data-state="active"]::after { border-radius: 50%; border: 2px solid var(--hue, var(--ink)); border-right-color: transparent; animation: vb-spin .8s linear infinite; }
.think-step[data-state="done"]::after { background: var(--ink); -webkit-mask: var(--vb-check) center / contain no-repeat; mask: var(--vb-check) center / contain no-repeat; animation: vb-pop .4s var(--spring) backwards; }

/* ---------- the bead (markup from fx.js orb()). A sphere of frosted glass with colour caught in one side of it.
   Back to front: a soft glow that fades to nothing, the body (a circle that clips what is in it), the pool of colour in
   its upper left, the moving hues that only show when it is awake, and a rim of light. It is the microphone button too.
   Only transform and opacity animate. The pool's blur sits on a layer whose content never changes (the layer itself
   travels), and the hues' blur on one that is hidden at rest.
   The bead is jelly: let go of a press and it springs back through three small overshoots (the linear() easing, a
   damped spring). Where linear() is not known the first declaration stays: one wobble. */

.orb { --size: 120px; position: relative; flex: none; width: var(--size); height: var(--size); isolation: isolate; transition: transform .7s var(--jelly); transition: transform .6s linear(0, .24 4%, .87 10%, 1.2 14%, 1.4 17.5%, 1.456 21%, 1.39 25%, .96 35%, .83 40%, .8 43%, .81 46%, 1.05 60%, 1.087 65%, 1.04 74%, .96 89%, 1); }
.orb-glow, .orb-body, .orb-pool, .orb-life, .orb-life div, .orb-pool i, .orb-life i, .orb-rim { position: absolute; inset: 0; display: block; border-radius: 50%; }
.orb-glow { inset: -15%; z-index: -1; background: radial-gradient(closest-side, var(--bead-glow-in, var(--bead-glow)) 48%, var(--bead-glow) 76%, color-mix(in srgb, var(--bead-glow) 60%, transparent) 82%, color-mix(in srgb, var(--bead-glow) 24%, transparent) 87%, color-mix(in srgb, var(--bead-glow) 6%, transparent) 92%, transparent 97%); -webkit-mask: conic-gradient(from 20deg, rgb(0 0 0 / .62), rgb(0 0 0 / .8) 30%, #000 52%, #000 86%, rgb(0 0 0 / .62)); mask: conic-gradient(from 20deg, rgb(0 0 0 / .62), rgb(0 0 0 / .8) 30%, #000 52%, #000 86%, rgb(0 0 0 / .62)); transition: opacity .9s; animation: vb-turn 15s linear infinite; }
.orb-body { overflow: hidden; background: var(--bead-base); -webkit-mask: radial-gradient(closest-side, #000 calc(100% - 2.5px), transparent); mask: radial-gradient(closest-side, #000 calc(100% - 2.5px), transparent); transition: transform .9s var(--ease-out); }
/* The pool: pink, with coral to its right and violet at its far edge. Where it is not, the glass is almost clear.
   It thins toward the rim (the mask), so the colour has no edge there: it dissolves into the glow, violet last.
   It is alive at rest: the colour travels slowly round inside the glass, one turn in 15 seconds, like liquid settling
   in a ball, while the glass and its rim stay still. A second, slower sway changes its pace and its distance from the
   middle, so it never moves like a clock hand. It starts from the pose drawn here (colour upper left), which is also
   where it rests when motion is reduced. The glow's brighter side and the light on the card turn with it. */
.orb-pool { filter: blur(calc(var(--size) * .11)) saturate(var(--pool-sat, 1)) brightness(var(--bead-dim, 1)); -webkit-mask: radial-gradient(closest-side, #000 62%, rgb(0 0 0 / .82) 78%, rgb(0 0 0 / .55) 92%, rgb(0 0 0 / .4)); mask: radial-gradient(closest-side, #000 62%, rgb(0 0 0 / .82) 78%, rgb(0 0 0 / .55) 92%, rgb(0 0 0 / .4)); opacity: var(--pool-o, 1); scale: var(--pool-grow, 1); transition: opacity .9s, scale 1.2s var(--ease-out); animation: vb-turn 15s linear infinite, vb-sway 6.8s ease-in-out -3.4s infinite alternate; }
.orb-pool i:nth-child(1) { inset: 2% 38% 40% 2%; background: radial-gradient(closest-side, var(--pool-wash) 10%, transparent); }
.orb-pool i:nth-child(2) { inset: 8% 45% 36% -1%; background: radial-gradient(closest-side, var(--pool-a) 48%, transparent); }
.orb-pool i:nth-child(3) { inset: 14% 24% 50% 24%; background: radial-gradient(closest-side, var(--pool-b) 38%, transparent); }
.orb-pool i:nth-child(4) { inset: 10% 68% 50% -6%; background: radial-gradient(closest-side, var(--pool-c) 30%, transparent); opacity: .95; }
/* The moving hues. Hidden and paused at rest, so idle is always the calm bead and waking never makes it jump. */
.orb-life { filter: blur(calc(var(--size) * .1)) saturate(var(--life-sat, 1)) brightness(var(--life-dim, 1)); opacity: var(--life-o, 0); transition: opacity .9s; }
.orb-life div { animation: vb-spin 12s linear infinite paused; }
.orb-life i { transition: background-color .9s; }
.orb-life i:nth-child(1) { inset: 2% 38% 40% 4%; border-radius: 60% 40% 54% 46% / 48% 58% 42% 52%; background: var(--life-a); animation: vb-drift-a 5s ease-in-out infinite alternate paused; }
.orb-life i:nth-child(2) { inset: 40% 2% 4% 40%; border-radius: 42% 58% 38% 62% / 58% 44% 56% 42%; background: var(--life-b); animation: vb-drift-b 6s ease-in-out infinite alternate paused; }
.orb-life i:nth-child(3) { inset: 44% 46% 0% 2%; border-radius: 55% 45% 60% 40% / 45% 55% 45% 55%; background: var(--life-c); animation: vb-drift-c 4.5s ease-in-out infinite alternate paused; }
.orb-life i:nth-child(4) { inset: 0% 4% 52% 50%; border-radius: 48% 52% 44% 56% / 56% 46% 54% 44%; background: var(--life-d); animation: vb-drift-a 7s ease-in-out infinite alternate-reverse paused; }
/* The rim: a thin line of light, a little clearer on the lower right, where the glass is clear, than on the upper left. */
.orb-rim { inset: 1px; box-shadow: inset 0 0 1.5px .5px var(--bead-rim), inset 0 0 calc(var(--size) * .055) var(--bead-rim); -webkit-mask: linear-gradient(135deg, rgb(0 0 0 / .3) 22%, #000 74%); mask: linear-gradient(135deg, rgb(0 0 0 / .3) 22%, #000 74%); }
.orb-check { position: absolute; inset: 0; margin: auto; width: 34%; height: 34%; color: var(--bead-ink); filter: var(--bead-ink-shadow); stroke-width: 2; opacity: 0; transform: scale(.6); transition: opacity .25s, transform .5s var(--spring); }

/* States grow out of the same bead. A mic button passes its own state to the bead inside it, so the builders set one attribute.
   Each has its own second hue, so a still frame tells them apart. Idle: the calm pool, the rest clear. Listening: the
   colour wakes, spreads through the glass and moves, pink into violet, and the glow breathes. Speaking: pink into warm
   amber, and the colour swells and fades with a gentle pulse. Thinking: blue, violet and amber circulate inside the rim.
   Done: it settles and a check lands in the glass. */
[data-state="listening"] { --pool-grow: 1.34; --pool-sat: 1.25; --life-o: .68; --life-a: #ff5c9d; --life-b: #a58bff; --life-c: #ee6fe6; --life-d: #ff9d7a; }
[data-state="thinking"] { --pool-o: .45; --life-o: 1; --life-a: #ff7ab6; --life-b: #9585ff; --life-c: #6fb4ff; --life-d: #ffbc6f; }
[data-state="speaking"] { --pool-grow: 1.16; --pool-sat: 1.15; --life-o: .5; --life-a: #ff6fa8; --life-b: #ff9650; --life-c: #ff7f6e; --life-d: #ffb870; }
:is([data-state="listening"], [data-state="thinking"], [data-state="speaking"]) :is(.orb-life div, .orb-life i) { animation-play-state: running; }
[data-state="thinking"] .orb-life div { animation-duration: 3.6s; }
/* Listening has to look like listening (the owner could not tell the microphone was open), and the owner's rules for
   how: the bead itself bounces like jelly, very small, like breathing, and an outline just outside it breathes with
   it. Nothing leaves the bead. One soft wobble every two seconds, the same whether she is silent or speaking.
   When her voice is heard (data-voice="on" on the button, from voice.js) the colour deepens, and the bead gives one
   light bounce as her voice starts and one as it stops, like a switch (fx.js beat()). No bouncing in between: the
   owner found that ugly. The outline is the bead's ::after. */
[data-state="listening"] :is(.orb-body, .orb-rim) { animation: vb-inhale 2s ease-in-out infinite; }
[data-state="listening"] .orb-glow { animation: vb-turn 15s linear infinite, vb-breathe 2s ease-in-out infinite; }
[data-state="listening"] .orb::after { content: ""; position: absolute; inset: calc(var(--size) * -.045); z-index: -1; border-radius: 50%; box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--life-a) 55%, transparent), 0 0 calc(var(--size) * .06) color-mix(in srgb, var(--life-a) 35%, transparent); pointer-events: none; animation: vb-outline 2s ease-in-out infinite; }
[data-state="listening"][data-voice="on"] { --pool-sat: 1.4; --life-o: .92; }
[data-state="thinking"] .orb-glow { opacity: .8; }
/* Speaking is calm too: one light bounce as Sayso starts a line (fx.js beat()), then only the colour and the glow breathe. */
[data-state="speaking"] .orb-glow { animation: vb-turn 15s linear infinite, vb-breathe 2.4s ease-in-out infinite; }
[data-state="speaking"] .orb-life { animation: vb-swell 2.4s ease-in-out infinite; }
.orb[data-state="done"] .orb-check { opacity: 1; transform: none; transition-delay: .15s; }

/* ---------- the face. With eyes the bead is Sayso, the character in the icon and the logo (markup from fx.js orb({ face })).
   Two seeds of ink on the glass, set well apart and above the middle so they read as eyes and not as a snout, leaning
   toward each other. They sit on top of the glass, solid and sharp (the owner's rule): no blur, no see-through, no halo.
   The icon and the logo files draw the same numbers. The bead is a ball, so the face turns on it, and the far eye is
   the smaller one. At rest it is alive: it glances toward the clear side of the glass, looks left, looks at you,
   looks right, tilting its head a little each way, and blinks. It turns to you and opens wide to listen, looks up to
   think, nods as it speaks, smiles when the tool is ready and sleeps when the microphone is off. No mouth. Idle sets
   nothing, so a button's state reaches the bead inside it. */
.orb-eyes { position: absolute; inset: 0; translate: var(--look, 3.5% 3.5%); transition: translate .45s var(--ease-out), opacity .4s; animation: vb-look 11s ease-in-out infinite; }
.orb-eyes i { position: absolute; top: 44%; left: 35%; width: 11%; height: 15.5%; border-radius: 50%; background: var(--eye); translate: -50% -50%; rotate: var(--lean, 6deg); scale: var(--eye-l, 1); transition: scale .3s var(--ease-out); animation: vb-blink 7s infinite, vb-far-l 11s ease-in-out infinite; }
.orb-eyes i + i { left: 65%; --lean: -6deg; scale: var(--eye-r, .92); animation-name: vb-blink, vb-far-r; }
/* Looking round is for the bead at rest. In a state the face holds that state's pose, and still blinks. */
:is([data-state="listening"], [data-state="thinking"], [data-state="done"], [data-state="off"]) .orb-eyes { animation: none; }
:is([data-state="listening"], [data-state="thinking"], [data-state="speaking"]) .orb-eyes i { animation: vb-blink 7s infinite; }
[data-state="listening"] { --look: 0% -1%; --eye-l: 1.15; --eye-r: 1.15; }
[data-state="thinking"] { --look: 5% -6.5%; --eye-r: .86; }
[data-state="speaking"] { --look: 0% 1%; --eye-l: 1.06; --eye-r: 1.06; }
[data-state="speaking"] .orb-eyes { animation: vb-nod 1.1s ease-in-out infinite alternate; }
/* Happy and asleep are the same closed arc, one the right way up and one turned over. */
:is([data-state="done"], [data-state="off"]) .orb-eyes { --look: 0% 0%; }
:is([data-state="done"], [data-state="off"]) .orb-eyes i { width: 17%; height: 17%; border-radius: 0; -webkit-mask: var(--vb-arc) center / contain no-repeat; mask: var(--vb-arc) center / contain no-repeat; rotate: none; scale: 1; animation: none; }
[data-state="off"] .orb-eyes { opacity: .5; } [data-state="off"] .orb-eyes i { scale: 1 -1; }
.orb:has(.orb-eyes) .orb-check { display: none; }

/* ---------- voice: the bead is the microphone button, on Make and in the talk bar */

/* Make, back to front: the bead's light spilling past the card's edge onto the page, the card of glass, the bead,
   the glyph in the glass, the label. The card is drawn by the section's ::after, behind everything in it, so the
   light can sit under it. One card: nothing stacked under its foot (the owner found the stack odd). */
.voice-hero { position: relative; display: flex; flex-direction: column; align-items: center; gap: var(--space-2); padding: 40px var(--space-4) var(--space-6); border-radius: 32px; text-align: center; }
.voice-hero::after, .talk.is-fresh:has(.talk-mic:not([hidden]))::after { content: ""; z-index: -1; border-radius: 32px; background: var(--glass); box-shadow: var(--glass-rim), var(--card); }
.voice-hero::after { position: absolute; inset: 0; }
.mic-hero::before, .talk-mic::before { content: ""; position: absolute; top: 50%; left: 50%; z-index: -1; width: min(100vw, 430px); height: 460px; translate: -50% -50%; background: radial-gradient(closest-side at 34% 44%, var(--spill-a), transparent), radial-gradient(closest-side at 70% 60%, var(--spill-b), transparent); pointer-events: none; animation: vb-spill 11s ease-in-out infinite alternate, vb-turn 15s linear infinite; }
.talk-mic::before { width: 250px; height: 170px; }
.talk-mic[data-state="off"]::before { display: none; }
.mic-hero, .talk-mic, .talk-voice { --m: 168px; --g: 36px; position: relative; display: grid; place-items: center; flex: none; width: var(--m); height: var(--m); padding: 0; border: 0; border-radius: 50%; background: none; cursor: pointer; touch-action: manipulation; -webkit-user-select: none; user-select: none; }
.talk-mic { --m: 76px; --g: 26px; }
.talk-voice { --m: 44px; --g: 18px; }
.mic-hero { margin-bottom: 24px; }
.mic-hero > .orb, .talk-mic > .orb, .talk-voice > .orb { --size: var(--m) !important; }
.mic-hero:focus-visible, .talk-mic:focus-visible, .talk-voice:focus-visible, .reply:focus-visible, .talk-keys:focus-visible, .talk-mute:focus-visible, .prompt-pick:focus-visible::after, .prompt-edit:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }
.prompt-pick:focus-visible { outline: 0; }   /* its ring is drawn around the whole card, by the layer that takes the tap */
/* The glyph is in the glass: a fine line with a very soft shadow, and no disc under it. */
.mic-glyph { position: absolute; inset: 0; z-index: 1; display: grid; place-items: center; margin: auto; width: var(--g); height: var(--g); color: var(--bead-ink); filter: var(--bead-ink-shadow); pointer-events: none; transition: opacity .3s; }
.mic-glyph svg { width: 100%; height: 100%; stroke-width: 1.5; transition: opacity .2s; }
.talk-voice .mic-glyph svg { stroke-width: 1.8; }
/* A press is liquid: the bead flattens at once and settles with one small wobble. */
:is(.mic-hero, .talk-mic, .talk-voice):active:not(:disabled) > .orb { transform: scale(1.07, .92); transition: transform .09s ease-out; }
/* Sayso is thinking: the button rests, the glyph leaves, and the colours turn inside the glass. */
.talk-mic[data-state="thinking"] { cursor: default; }
.talk-mic[data-state="thinking"] > .mic-glyph { opacity: 0; }
.mic-hint { font: 500 17px/24px var(--font-sans); letter-spacing: -0.01em; color: var(--ink); }
/* The way to the keyboard is a link, so it keeps the underline of .linkish (40% of its colour). */
.type-toggle { font: 400 14px/20px var(--font-sans); }
/* Sayso is speaking: the mic glyph gives way to four bars. Off: clear glass, no colour, a line through the mic. */
.talk-mic[data-state="speaking"] .mic-glyph svg { opacity: 0; }
.talk-mic[data-state="speaking"] .mic-glyph::before, .talk-mic[data-state="speaking"] .mic-glyph::after { content: ""; position: absolute; top: 50%; left: 50%; width: 2.5px; height: 18px; margin: -9px 0 0 -1.25px; border-radius: 2px; background: currentColor; box-shadow: 12px 0 0 currentColor; translate: -9px 0; animation: vb-voice 1s ease-in-out infinite; }
.talk-mic[data-state="speaking"] .mic-glyph::after { translate: -3px 0; animation-delay: -.5s; }
.talk-mic[data-state="off"] { cursor: default; }
.talk-mic[data-state="off"] :is(.orb-pool, .orb-life, .orb-glow) { opacity: 0; }
.talk-mic[data-state="off"] .mic-glyph { color: var(--ink-muted); filter: none; }
.talk-mic[data-state="off"] .mic-glyph::after { content: ""; position: absolute; top: 50%; left: 50%; width: 1.5px; height: 30px; margin: -15px 0 0 -.75px; border-radius: 2px; background: currentColor; rotate: -45deg; }
/* Eyes or microphone, by whose turn it is (the owner's rule). When it is Sayso's turn, or Sayso is at rest on Make,
   the bead shows its eyes and the glyph steps aside. When it is her turn (the talk bar waiting for a tap, or listening)
   the microphone shows and the eyes fade out, so she can always see that it is recording. A glyph over the eyes or
   under them (a mouth) fights the face, so the two never show together. */
.mic-hero:has(.orb-eyes):not([data-state="listening"]) > .mic-glyph,
.talk-mic:has(.orb-eyes):is([data-state="speaking"], [data-state="thinking"], [data-state="done"]) > .mic-glyph { opacity: 0; }
.mic-hero[data-state="listening"] .orb-eyes,
.talk-mic:not([data-state="speaking"], [data-state="thinking"], [data-state="done"]) .orb-eyes { opacity: 0; }

/* ---------- conversation */

.talk { gap: var(--space-4); padding-bottom: 0; }
/* The top bar paints what is behind the page, so at rest it cannot be seen. The thread fades out under its lower edge. */
.talk > .topbar { position: sticky; top: 0; z-index: var(--z-sticky); margin: calc(-1 * (var(--space-4) + env(safe-area-inset-top, 0px))) calc(-1 * var(--space-4)) 0; padding: calc(var(--space-2) + env(safe-area-inset-top, 0px)) var(--space-3) var(--space-4); background: var(--haze), var(--surface-subtle); -webkit-mask: linear-gradient(#000 calc(100% - 16px), transparent); mask: linear-gradient(#000 calc(100% - 16px), transparent); }
.talk-keys, .talk-mute, .talk-type > .md-input ~ button { position: relative; display: grid; place-items: center; flex: none; width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%; background: var(--fill); color: var(--ink); cursor: pointer; touch-action: manipulation; }
/* The small round controls are the same glass as the bead, without the colour: a pale lens with a rim of light. */
.topbar .md-btn-square, .talk-keys, .talk-mute, .sheet-close, .stepper .md-btn-square { background: var(--glass-btn); box-shadow: var(--glass-btn-edge); }
.talk-keys svg, .talk-mute svg, .talk-type > .md-input ~ button svg { width: 20px; height: 20px; }
.talk-mute { width: 40px; height: 40px; color: var(--ink-muted); } .talk-mute::before { content: ""; position: absolute; inset: -2px; }

.thread { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; gap: var(--space-5); margin: 0; padding: var(--space-2) 0 var(--space-2); list-style: none; }
.msg { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; min-width: 0; }
.msg-text { margin: 0; max-width: 100%; font: 400 16px/24px var(--font-sans); color: var(--ink-muted); overflow-wrap: anywhere; text-wrap: pretty; }
/* The question that is open is the title of the screen (.is-open). Everything else steps back. A thread that marks
   none falls back to its last message from Sayso. */
.msg-sayso.is-open:not(.is-thinking, .msg-miss) > .msg-text, .thread:not(:has(.is-open)) > .msg-sayso:last-child:not(.is-thinking, .msg-miss) > .msg-text { font: 400 24px/31px var(--font-sans); letter-spacing: -0.025em; color: var(--ink); text-wrap: balance; }
/* Sayso did not understand (.msg-miss): one short line in ink right under the open question, which keeps the title.
   It is never the title itself, and it sits closer to the question than a new message would. */
.msg-miss { margin-top: calc(-1 * var(--space-2)); }
.msg-miss > .msg-text { color: var(--ink); }
.msg-you { align-self: flex-end; align-items: flex-end; max-width: 84%; }
.msg-you > .msg-text { padding: 9px var(--space-4); border-radius: 20px 20px 6px 20px; background: var(--bubble); color: var(--ink); }
.thread > .msg:last-child { animation: vb-rise .5s var(--ease-out) backwards; }
.msg.is-thinking { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 10px; }
.msg.is-thinking > .msg-text { font: 400 15px/24px var(--font-sans); letter-spacing: 0; color: var(--ink-muted); }
.is-thinking .think-steps { flex-basis: 100%; margin-top: var(--space-2); } .is-thinking .think-step { min-height: 52px; }
/* While she types there is no mic in the bar to show the wait, so the orb in the thread carries it and doubles in size. */
.talk:has(.talk-type:not([hidden])) .is-thinking > .orb { --size: 56px !important; margin: var(--space-1) var(--space-1) var(--space-1) 0; }

.replies { display: flex; flex-wrap: wrap; gap: var(--space-2); padding-top: var(--space-1); }
.reply { position: relative; display: inline-flex; align-items: center; gap: 6px; min-height: 42px; padding: 9px var(--space-4); border: 0; border-radius: 21px; background: var(--surface); box-shadow: var(--card); color: var(--ink); font: 400 15px/20px var(--font-sans); text-align: left; cursor: pointer; touch-action: manipulation; -webkit-user-select: none; user-select: none; }
.reply::before { content: ""; position: absolute; inset: -1px 0; }
.reply[aria-pressed="true"] { box-shadow: 0 0 0 1.5px var(--fill-bg); }
.reply:disabled { opacity: .5; cursor: default; }
/* Inside a reply "Suggested" shrinks to its sparkle. The word stays for screen readers. */
.reply .badge-spectrum { order: -1; height: auto; padding: 0; background: none; font-size: 0; gap: 0; } .reply .badge-spectrum svg { width: 14px; height: 14px; }

.msg-cards { display: flex; flex-direction: column; gap: 10px; align-self: stretch; padding-top: var(--space-1); }
.msg-cards:has(> .lookcard) { flex-direction: row; margin: 0 calc(-1 * var(--space-4)); padding: var(--space-2) var(--space-4) var(--space-4); overflow-x: auto; scroll-snap-type: x proximity; scroll-padding: 0 var(--space-4); scrollbar-width: none; }
.msg-cards::-webkit-scrollbar { display: none; }
.msg-cards > .lookcard { --pane: 46px; flex: none; width: 184px; min-height: 160px; padding: var(--space-4); scroll-snap-align: start; }
.msg-cards > .lookcard > :last-child { margin-top: auto; }
/* A way to start: a row, so three fit above the talk bar. Its title and two lines of what it will make, Edit beside
   them, and a chevron at the right edge that says the card itself can be tapped. */
.prompt-card { position: relative; display: flex; align-items: center; gap: 10px; width: 100%; padding: 14px 14px 14px var(--space-5); border: 0; background: var(--surface); color: var(--ink); font: 400 15px/22px var(--font-sans); text-align: left; }
.prompt-card::after { content: ""; flex: none; width: 16px; height: 16px; background: var(--ink-muted); -webkit-mask: var(--vb-chev) center / contain no-repeat; mask: var(--vb-chev) center / contain no-repeat; }
.prompt-card p { margin: 0; }
.prompt-pick, .prompt-edit { cursor: pointer; touch-action: manipulation; }
/* The whole card picks the prompt. Edit sits above that layer. */
.prompt-pick { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: inherit; }
.prompt-pick::after { content: ""; position: absolute; inset: 0; border-radius: var(--r-card); }
.prompt-title { font: 500 16px/22px var(--font-sans); letter-spacing: -0.01em; }
.prompt-title + p { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; font: 400 14px/20px var(--font-sans); color: var(--ink-muted); }
.prompt-edit { position: relative; z-index: 1; flex: none; display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px 0 10px; border: 0; border-radius: 16px; background: var(--fill); color: var(--ink); font: 500 13px/16px var(--font-sans); }
.prompt-edit::before { content: ""; position: absolute; inset: -6px; } .prompt-edit svg { width: 14px; height: 14px; }
.summary { display: flex; flex-direction: column; width: 100%; background: var(--surface); color: var(--ink); overflow: hidden; }
.summary-head { display: flex; align-items: center; gap: var(--space-3); min-height: 122px; padding: var(--space-5) var(--space-5) calc(var(--space-5) + 20px); }
/* The plan is a pane of glass that rises over the look's colour field. */
.summary-list { position: relative; margin-top: -20px; border-radius: 24px 24px 0 0; background: linear-gradient(var(--panel), var(--mat-base) 44px); box-shadow: var(--glass-rim); }
.summary-title { overflow: hidden; font: 500 18px/24px var(--font-sans); letter-spacing: -0.015em; white-space: nowrap; text-overflow: ellipsis; }
.summary-sub { font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
/* The name is an input one line tall. Its box reaches over the line under it, so a tap anywhere on the two lines edits it. */
input.summary-title { box-sizing: content-box; height: 24px; margin: -10px 0 -18px; padding: 10px 0 18px; }
.summary-sub { position: relative; pointer-events: none; }
/* The pencil beside the name is a small glass circle. Its label keeps the 44px it had. */
.summary-edit { position: relative; z-index: 0; color: var(--ink); }
.summary-edit::before { content: ""; position: absolute; inset: 6px; z-index: -1; border-radius: 50%; background: var(--glass-on); box-shadow: inset 0 0 0 1px var(--glass-line); }
.summary-list li { display: flex; align-items: flex-start; gap: var(--space-3); padding: 14px var(--space-5); font: 400 14px/20px var(--font-sans); }
.summary-list li + li { border-top: 1px solid var(--hair); }
.summary-list svg { flex: none; width: 18px; height: 18px; margin-top: 1px; color: var(--hue, var(--ink-muted)); }

/* The talk bar has no edge. The thread fades out under it. */
.talkbar { position: sticky; bottom: 0; z-index: var(--z-sticky); display: flex; flex-direction: column; gap: var(--space-3); margin: 0 calc(-1 * var(--space-4)); padding: var(--space-8) var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom, 0px)); background: linear-gradient(to bottom, transparent, var(--surface-subtle) 32px); }
/* Her words as she speaks, or a one-line hint. Three lines at most, and the newest words stay in view. */
.talk-live { display: flex; flex-direction: column; justify-content: flex-end; min-height: 24px; max-height: 72px; margin: 0; overflow: hidden; text-align: center; text-wrap: balance; font: 400 15px/24px var(--font-sans); color: var(--ink-muted); }
.talkbar:has(.talk-mic[data-state="listening"]) .talk-live { font-size: 17px; color: var(--ink); }
.talkbar-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--space-2); }
.talkbar-row > .talk-keys { grid-column: 1; justify-self: start; }
.talkbar-row > .talk-mic { grid-column: 2; }
.talkbar-row > .md-btn { grid-column: 3; justify-self: end; padding: 0 var(--space-4); }
/* Two actions (the brief: "No" and "Looks right"): the second, quiet one sits at the microphone's left (app.css puts
   .talk-second in the first column) and the main one stays on its right. The pair flanks the bead, which stays in
   the middle, and all of it fits a 375px screen. Everything is on one row, so the keyboard keeps its corner. */
.talkbar-row > * { grid-row: 1; }
/* While she types the row holds only the actions: side by side at the right, the main one last. */
.talkbar-row:not([hidden]):has(> .talk-mic[hidden]) { display: flex; justify-content: flex-end; }
.talkbar-row:has(> .talk-mic[hidden]) > .talk-second { order: -1; }
.talk-type { display: flex; align-items: center; gap: var(--space-2); }
.talk-type > .md-input { flex: 1; min-width: 0; height: 48px; padding: 0 var(--space-1); border-radius: 24px; }
.talk-type > .md-input ~ button { background: var(--fill-bg); color: var(--cta-ink); box-shadow: var(--cta-glow); }
.talk-type > .md-input ~ button:disabled { background: var(--fill); color: var(--ink-muted); box-shadow: none; cursor: default; }

/* The first turn (.is-fresh, by voice): the thread holds only Sayso's greeting. The bead leaves the bar and sits large on
   the same card of glass as on Make, with the greeting under it and the live line under that. Nothing
   moves in the markup: the page becomes a grid, the bar and its row dissolve into it (display: contents), and the
   page's ::after draws the card behind rows 3 to 5 (its rule is with Make's, above).
   The keyboard stays in its corner. When the class goes, everything is back in the bar and the bead settles into it. */
.talk.is-fresh:has(.talk-mic:not([hidden])) { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(60px, 3fr) auto auto auto minmax(76px, 4fr) auto; gap: 0; }
.talk.is-fresh:has(.talk-mic:not([hidden])) :is(.talkbar, .talkbar-row:not([hidden])) { display: contents; }
.talk.is-fresh:has(.talk-mic:not([hidden])) :is(.topbar, .thread, .talk-live, .talk-mic, .talk-keys, .talkbar-row > .md-btn) { grid-column: 1; }
.talk.is-fresh:has(.talk-mic:not([hidden])) .talk-mic { --m: 168px; --g: 36px; grid-row: 3; justify-self: center; }
/* The tap on Make lands here. Same bead, same size, and it springs like jelly as it wakes: squashed as the finger left it, then three overshoots. */
.talk.is-fresh .talk-mic > .orb { animation: vb-jelly .75s linear both; }
.talk.is-fresh:has(.talk-mic:not([hidden])) .talk-mic::before { width: min(100vw, 430px); height: 400px; }
.talk.is-fresh:has(.talk-mic:not([hidden])) .thread { grid-row: 4; padding: var(--space-6) var(--space-5) 0; text-align: center; }
.talk.is-fresh:has(.talk-mic:not([hidden])) .thread > .msg { align-items: center; }
/* Room for two lines of her words from the start, so the bead does not move when she begins to speak. */
.talk.is-fresh:has(.talk-mic:not([hidden])) .talk-live { grid-row: 5; box-sizing: content-box; min-height: 48px; padding: var(--space-2) var(--space-5) 0; }
.talk.is-fresh:has(.talk-mic:not([hidden])) :is(.talk-keys, .talkbar-row > .md-btn) { grid-row: 7; align-self: end; margin-bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px)); }
.talk.is-fresh:has(.talk-mic:not([hidden]))::after { grid-area: 3 / 1 / 6 / 2; }
.talk.is-fresh:has(.talk-mic:not([hidden]))::after { margin: -40px 0 -24px; }
.talk:not(.is-fresh) .talk-mic > .orb { animation: vb-settle .5s var(--ease-out) backwards; }

/* ---------- tab bar */

/* Each tab is as wide as its own label plus an equal share of the room left over, so a long label (Community) gets
   the same space inside its pill as a short one. Three equal columns left it none. */
.tabbar { position: sticky; bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px)); z-index: var(--z-sticky); display: flex; gap: 2px; margin: 0 var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom, 0px)); padding: 5px; border-radius: 9999px; background: var(--bar); -webkit-backdrop-filter: blur(22px) saturate(1.8); backdrop-filter: blur(22px) saturate(1.8); box-shadow: var(--glass-rim), 0 18px 36px -18px rgb(24 28 48 / .32); transition: opacity .3s, transform .5s var(--ease-out); }
.tab { position: relative; z-index: 0; flex: 1 1 auto; min-width: 0; display: flex; align-items: center; justify-content: center; gap: 6px; height: 46px; padding: 0 var(--space-2); border: 0; border-radius: inherit; background: none; color: var(--ink-muted); font: 500 14px/20px var(--font-sans); text-decoration: none; cursor: pointer; transition: transform .3s var(--ease-out), color .2s; }
.tab svg { flex: none; width: 20px; height: 20px; }
.tab::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background: var(--glass-btn); box-shadow: var(--glass-btn-edge); opacity: 0; transform: scale(.85); transition: transform .4s var(--spring), opacity .2s; }
.tab[aria-current="page"] { color: var(--ink); }
.tab[aria-current="page"]::before { opacity: 1; transform: none; }
.tab:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.tabbar[hidden] { display: none; }
#app:has(.tabbar:not([hidden])) .page { padding-bottom: var(--space-6); }
/* The page fades out behind the floating bar, as the thread does behind the talk bar. */
#app:has(.tabbar:not([hidden]))::after { content: ""; position: fixed; bottom: 0; left: 50%; z-index: calc(var(--z-sticky) - 1); width: min(100%, 430px); height: calc(88px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%); background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--surface-subtle) 88%, transparent) 70%); pointer-events: none; transition: opacity .3s; }
html.sheet-open #app::after { opacity: 0; }
html.sheet-leaving #app::after { opacity: 1; }

/* ---------- meter, stats, market, plans */

.meter { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-4) var(--space-5) var(--space-5); border: 0; border-radius: 24px; background: var(--surface); color: var(--ink); text-align: left; }
.meter-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); font: 400 13px/20px var(--font-sans); color: var(--ink-muted); }
.meter-top b { font: 500 13px/20px var(--font-sans); color: var(--ink); }

.stat { border-radius: 20px; padding: var(--space-3) var(--space-4); } .stat b { font-weight: 400; }

.market-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
.market-card { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); min-height: 176px; padding: var(--space-4); border: 0; color: var(--ink); text-align: left; font: inherit; cursor: pointer; }
/* A working template wears its kind: the hue and its partner, very pale in light, deep in dark. */
.market-card { --m0: color-mix(in srgb, var(--hue) 7%, var(--mat-base)); --m1: color-mix(in srgb, var(--hue) 26%, var(--mat-base)); --m2: color-mix(in srgb, var(--hue-b) 26%, var(--mat-base)); --m3: color-mix(in srgb, var(--hue) 14%, var(--mat-base)); }
.market-card .tile { color: var(--hue-ink); }
.market-card:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.market-name { margin-top: var(--space-1); font: 500 16px/22px var(--font-sans); letter-spacing: -0.01em; }
.market-blurb { font: 400 13px/18px var(--font-sans); color: var(--ink-muted); }
.market-meta { display: inline-flex; align-items: center; gap: var(--space-1); margin-top: auto; padding-top: var(--space-1); font: 500 12px/16px var(--font-sans); color: var(--ink-muted); }
.market-meta svg { width: 14px; height: 14px; }

.plan { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-6) var(--space-5); background: var(--surface); color: var(--ink); }
.plan-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.plan-name { font: 500 17px/24px var(--font-sans); letter-spacing: -0.01em; }
.plan-price { display: flex; align-items: baseline; gap: 6px; font: 400 14px/20px var(--font-sans); color: var(--ink-muted); }
.plan-price b { font: 400 44px/48px var(--font-sans); letter-spacing: -0.04em; color: var(--ink); }
.plan-list { display: flex; flex-direction: column; gap: 10px; padding-top: var(--space-4); border-top: 1px solid var(--hair); font: 400 14px/20px var(--font-sans); }
.plan-list li { display: flex; align-items: flex-start; gap: 10px; }
.plan-list svg { flex: none; box-sizing: border-box; width: 18px; height: 18px; margin-top: 1px; padding: 4px; border-radius: 50%; background: var(--ink); color: var(--app-bg); stroke-width: 3.4; }

/* ---------- look picker, and the header a tool wears */

.lookcard { --pane: 52px; position: relative; isolation: isolate; padding: var(--space-5); border-radius: var(--r-card); box-shadow: var(--card); }
/* A pane of glass over the colour field holds the title and the sample button. The look's name sits on the field above it. */
.lookcard::before { content: ""; position: absolute; inset: var(--pane) 8px 8px; z-index: -1; border-radius: 20px; background: var(--look-pane, rgb(255 255 255 / .46)); box-shadow: inset 1px 1px 0 var(--glass-line), inset 0 0 0 1px color-mix(in srgb, var(--glass-line) 40%, transparent); }
.lookcard[data-theme="dark"] { --look-pane: rgb(10 12 24 / .3); }
/* The first row holds the look's name, and sometimes a badge. One height, so the titles line up from card to card. */
.lookcard > :first-child { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); min-height: 24px; }
.lookcard[aria-checked="true"] { box-shadow: 0 0 0 2px var(--app-bg), 0 0 0 3.5px var(--app-ink); }
.lookcard[aria-checked="true"]::after { top: -8px; right: -8px; width: 22px; height: 22px; background: var(--app-ink) var(--app-check) center / 14px no-repeat; }
/* The sample button on a look card is a preview, so it is small and flat and cannot be taken for Done. */
.swatch { height: 26px; padding: 0 12px; border-radius: var(--r-btn); background: var(--primary); color: var(--on-primary); font: 500 12px/16px var(--font-sans); white-space: nowrap; }
/* On her own page and on the guest page the tool's title sits low in a tall card of its look. */
.md-card.lookhead, .toolpage .page > header { display: flex; flex-direction: column; justify-content: flex-end; min-height: 168px; padding: var(--space-5); border-radius: var(--r-card); }
.toolpage .page > header { box-shadow: var(--card); }

/* ---------- sheets (markup from fx.js). Glass that floats above the bottom edge, while the page behind steps back. */

html.sheet-open { overflow: hidden; }
.sheet-root { position: fixed; top: 0; bottom: 0; left: 50%; width: min(100%, 430px); transform: translateX(-50%); z-index: var(--z-modal); display: flex; align-items: flex-end; overflow: hidden; }
.scrim { position: absolute; inset: 0; background: rgb(12 14 24 / .34); touch-action: none; animation: vb-fade .35s ease backwards; }
.sheet { position: relative; width: 100%; max-height: calc(92% - env(safe-area-inset-bottom, 0px)); margin: 0 var(--space-2) calc(var(--space-2) + env(safe-area-inset-bottom, 0px)); padding: 60px var(--space-5) var(--space-5); overflow-y: auto; overscroll-behavior: contain; border-radius: 32px; background: var(--sheet); -webkit-backdrop-filter: blur(28px) saturate(1.5); backdrop-filter: blur(28px) saturate(1.5); color: var(--ink); box-shadow: var(--glass-rim), 0 24px 48px -12px rgb(12 14 24 / .35); animation: vb-sheet .5s var(--ease-out) backwards; }
.sheet:focus { outline: 0; }
.sheet-label { position: absolute; top: 22px; left: var(--space-5); right: 64px; overflow: hidden; font: 500 13px/20px var(--font-sans); color: var(--ink-muted); white-space: nowrap; text-overflow: ellipsis; }
.sheet-close { position: absolute; top: 14px; right: 14px; display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; padding: 0; border: 0; border-radius: 50%; color: var(--ink-muted); cursor: pointer; transition: transform .3s var(--ease-out); }
.sheet-close::before { content: ""; position: absolute; inset: -4px; }
.sheet-close:active { transform: scale(.92); transition-duration: .08s; }
.sheet-close:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.sheet-root.closing .scrim { animation: vb-fade .26s ease reverse forwards; }
.sheet-root.closing .sheet { animation: vb-sheet-out .26s cubic-bezier(.4, 0, 1, 1) forwards; }
/* The page behind becomes a card: clipped to the screen, round-cornered, a little smaller. fx.js gives #view the
   three lengths. Only the transform and the clip animate. Dark shows around it, and the tab bar leaves. */
html.sheet-open #app { background: #090a0f; }
html.sheet-open #view { transform-origin: 50% var(--recede-y, 50%); background-color: var(--surface-subtle); background-image: var(--haze); background-position: 0 var(--recede-t, 0); background-repeat: no-repeat; animation: vb-recede .55s var(--ease-out) both; }
html.sheet-open.sheet-leaving #view { animation: vb-advance .26s cubic-bezier(.4, 0, .6, 1) both; }
html.sheet-open .tabbar { opacity: 0; transform: translateY(8px) scale(.96); }
html.sheet-leaving .tabbar { opacity: 1; transform: none; }

/* ---------- toasts. A slate pill that never takes a tap and never sits on a bottom action. */

#toasts { align-items: center; }
#toasts .md-toast { width: auto; max-width: 100%; padding: 10px 20px; border-radius: 24px; background: var(--toast-bg); color: #ffffff; box-shadow: inset 0 0 0 1px rgb(255 255 255 / .08), 0 14px 30px -14px rgb(12 14 24 / .6); pointer-events: none; animation: vb-toast .4s var(--ease-out) backwards; }
#toasts .md-toast-text span { color: inherit; opacity: .74; }
#app:has(.tabbar:not([hidden])) #toasts { bottom: calc(88px + env(safe-area-inset-bottom, 0px)); }
/* Over a conversation or a sheet the bottom is where the answer happens, so the toast goes to the top instead. */
#app:has(.talkbar) #toasts { top: calc(60px + env(safe-area-inset-top, 0px)); bottom: auto; }
html.sheet-open #toasts { top: calc(var(--space-3) + env(safe-area-inset-top, 0px)); bottom: auto; }

/* ---------- entrances, and the celebration */

.stagger > *:not(.talkbar) { animation: vb-rise .5s var(--ease-out) backwards; }
.celebrate > * { animation: vb-rise .7s var(--ease-out) backwards; }
.stagger > :nth-child(2), .celebrate > :nth-child(2) { animation-delay: 50ms; }
.stagger > :nth-child(3), .celebrate > :nth-child(3) { animation-delay: 100ms; }
.stagger > :nth-child(4), .celebrate > :nth-child(4) { animation-delay: 150ms; }
.stagger > :nth-child(5), .celebrate > :nth-child(5) { animation-delay: 200ms; }
.stagger > :nth-child(n+6), .celebrate > :nth-child(n+6) { animation-delay: 250ms; }
/* The moment a tool is ready, the orb's light fills the top of the screen and fades into the page. */
.celebrate { position: relative; isolation: isolate; }
.celebrate::before { content: ""; position: absolute; inset: 0 0 auto; z-index: -1; height: 78%; background: var(--grain), radial-gradient(120% 80% at 0% 0%, var(--m1), transparent 62%), radial-gradient(110% 80% at 100% 6%, var(--m2), transparent 60%), radial-gradient(90% 70% at 50% 60%, var(--m3), transparent 66%); background-blend-mode: soft-light, normal, normal, normal; -webkit-mask: linear-gradient(#000 30%, transparent); mask: linear-gradient(#000 30%, transparent); animation: vb-fade 1.2s ease backwards; }

.confetti { position: fixed; top: 0; bottom: 0; left: 50%; width: min(100%, 430px); transform: translateX(-50%); z-index: var(--z-toast); overflow: hidden; pointer-events: none; }
.confetti i { position: absolute; }

@keyframes vb-spin { to { transform: rotate(360deg); } }
@keyframes vb-rise { from { opacity: 0; transform: translateY(10px); } }
@keyframes vb-pop { from { opacity: 0; transform: scale(.7); } }
@keyframes vb-fade { from { opacity: 0; } }
@keyframes vb-sheet { from { transform: translateY(calc(100% + 24px)); } }
@keyframes vb-sheet-out { to { transform: translateY(calc(100% + 24px)); } }
@keyframes vb-recede { from { clip-path: inset(var(--recede-t, 0) 0 var(--recede-b, 0) round 0px); } to { transform: scale(.94); clip-path: inset(var(--recede-t, 0) 0 var(--recede-b, 0) round 30px); } }
@keyframes vb-advance { from { transform: scale(.94); clip-path: inset(var(--recede-t, 0) 0 var(--recede-b, 0) round 30px); } to { clip-path: inset(var(--recede-t, 0) 0 var(--recede-b, 0) round 0px); } }
@keyframes vb-toast { from { opacity: 0; transform: translateY(10px); } }
@keyframes vb-glow { from { opacity: .55; } to { opacity: 1; } }
@keyframes vb-breathe { 50% { scale: 1.06; } }
/* Jelly at rest: squashed, stretched, a smaller echo of each, then still. Three percent at most. The outline swells
   and brightens with it. */
@keyframes vb-inhale { 0%, 78%, 100% { scale: 1 1; } 16% { scale: 1.03 .972; } 34% { scale: .982 1.026; } 50% { scale: 1.012 .99; } 64% { scale: .996 1.006; } }
@keyframes vb-outline { 0%, 100% { scale: 1; opacity: .35; } 45% { scale: 1.035; opacity: .9; } }
@keyframes vb-swell { 50% { opacity: .7; } }
@keyframes vb-jelly { 0% { transform: scale(1.09, .9); } 16% { transform: scale(.88, 1.14); } 34% { transform: scale(1.07, .94); } 52% { transform: scale(.96, 1.05); } 70% { transform: scale(1.025, .98); } 86% { transform: scale(.992, 1.008); } 100% { transform: none; } }
@keyframes vb-settle { from { transform: scale(1.5); opacity: .5; } }
@keyframes vb-turn { to { rotate: 360deg; } }
@keyframes vb-sway { from { transform: rotate(-10deg) translate(2.5%, 2%); } to { transform: rotate(10deg) translate(-2.5%, -2%); } }
@keyframes vb-spill { from { transform: translate(-2%, -1%) scale(1); opacity: .8; } to { transform: translate(2%, 2%) scale(1.08); opacity: 1; } }
@keyframes vb-deal { from { opacity: 0; transform: translateY(calc(12px - var(--deal, 0) * 72px)) scale(calc(1 - var(--deal, 0) * .04)); } }
/* Two blinks, unevenly spaced. The lid closes level, so the lean is taken back out as it shuts. */
@keyframes vb-blink { 0%, 38%, 42%, 94%, 98%, 100% { transform: none; } 40%, 96% { transform: rotate(calc(var(--lean, 6deg) * -1)) scaleY(.08); } }
@keyframes vb-nod { to { translate: 0% -2%; } }
/* The bead at rest looks round: rest, left, at you, right, rest. The head tilts with the look, and the eye on the far
   side of the ball is the smaller one (vb-far-l is the left eye, vb-far-r the right). */
@keyframes vb-look { 0%, 12%, 90%, 100% { translate: 3.5% 3.5%; rotate: 0deg; } 17%, 36% { translate: -8% 1%; rotate: -4deg; } 42%, 54% { translate: 0% -1%; rotate: 0deg; } 60%, 80% { translate: 8.5% 1%; rotate: 4deg; } }
@keyframes vb-far-l { 0%, 12%, 90%, 100% { scale: 1; } 17%, 36% { scale: .84; } 42%, 54% { scale: 1; } 60%, 80% { scale: 1.06; } }
@keyframes vb-far-r { 0%, 12%, 90%, 100% { scale: .92; } 17%, 36% { scale: 1.06; } 42%, 54% { scale: 1; } 60%, 80% { scale: .84; } }
@keyframes vb-voice { 0%, 100% { transform: scaleY(.35); } 50% { transform: scaleY(1); } }
@keyframes vb-drift-a { from { transform: translate(0, 0) rotate(0deg) scale(1); } to { transform: translate(14%, 10%) rotate(50deg) scale(1.12); } }
@keyframes vb-drift-b { from { transform: translate(0, 0) rotate(0deg) scale(1.05); } to { transform: translate(-12%, -10%) rotate(-60deg) scale(.9); } }
@keyframes vb-drift-c { from { transform: translate(0, 0) rotate(0deg) scale(1); } to { transform: translate(12%, -14%) rotate(70deg) scale(1.15); } }

/* Reduced motion: nothing travels or spins. Pressing dims instead of shrinking, the page does not step back, and
   the orb shows its state by colour and size and a slow glow.
   !important because the rules above are more specific than a plain list of classes. */
@media (prefers-reduced-motion: reduce) {
  .stagger > *, .celebrate > *, .celebrate::before, .thread > .msg, .scrim, .sheet, #toasts .md-toast, .think-step::after, .orb *, .orb::after, .mic-hero::before, .talk-mic::before, .msg-cards > .lens-card, .mic-glyph::before, .mic-glyph::after, .choices .opt::after, .chip::after, .reply::after, .lookcard::after, .talk-mic > .orb, html.sheet-open #view { animation: none !important; }
  .md-btn, .opt, .chip, .tab, .tab::before, .tabbar, .press, .lookcard, .market-card, .reply, .prompt-card, .prompt-edit, .mic-hero, .talk-mic, .talk-voice, .talk-keys, .talk-mute, .talk-type > .md-input ~ button, a.md-badge, .mic-glyph, .think-step, .sheet-close, .orb, .orb-check, .orb-body, .orb-pool, .orb-life, .orb-glow, .orb-eyes, .orb-eyes i, .md-progress span { transition: none !important; }
  .md-btn:active:not(:disabled), .opt:active:not(:disabled), .chip:active, .tab:active, .press:active, .lookcard:active, .market-card:active, .reply:active, .prompt-card:active, .prompt-edit:active, .mic-hero:active, .talk-mic:active, .talk-voice:active, .talk-keys:active, .talk-mute:active, .talk-type > .md-input ~ button:active:not(:disabled), a.md-badge:active, .sheet-close:active { transform: none; opacity: .7; }
  .talk.is-fresh .talk-mic > .orb { animation: none !important; }
  /* Same choice as Meridian's own spinner: keep it, slow it down. */
  .think-step[data-state="active"]::after { animation: vb-spin 2s linear infinite !important; }
  :is([data-state="thinking"], [data-state="listening"], [data-state="speaking"]) .orb-glow { animation: vb-glow 1.8s ease-in-out infinite alternate !important; }
  :is(.mic-hero, .talk-mic, .talk-voice):active:not(:disabled) > .orb { transform: none; }
  .talk-mic[data-state="speaking"] .mic-glyph::before { transform: scaleY(.55); } .talk-mic[data-state="speaking"] .mic-glyph::after { transform: scaleY(.9); }
}

/* ---------- tool looks. Variables only, on the tool's own container, so the app around it keeps its brand.
   Each one: a page colour, ink, and a material (--m0 to --m3) for its header and its card in the picker. */

[class*="look-"] { --accent: var(--ink); --haze: none; }
.look-clean { --surface-subtle: #f4f5f8; --m0: #eff0f5; --m1: #ffffff; --m2: #e1e4ef; --m3: #ebe7f3; }
.look-night { --surface-subtle: #0a0c18; --surface: rgb(255 255 255 / .07); --ink: #f3f5ff; --ink-muted: #aab2d0; --primary: #ffffff; --primary-hover: #e6e9f5; --on-primary: #0a0c18; --border: rgb(255 255 255 / .1); --hair: rgb(255 255 255 / .11); --link: #a9bcff;
  --m0: #0f1534; --m1: #3148c4; --m2: #136a78; --m3: #5a2676; }
.look-violet { --surface-subtle: #f4f1fb; --ink: #20123a; --ink-muted: #4f4269; --primary: #20123a; --primary-hover: #33205a; --border: rgb(64 32 128 / .12); --hair: rgb(64 32 128 / .1); --fill: rgb(64 32 128 / .06); --link: #5b34c8;
  --m0: #e7dffc; --m1: #c6b4ff; --m2: #ffc4e3; --m3: #bccfff; }
.look-sunset { --surface-subtle: #fcf4ee; --ink: #33121c; --ink-muted: #66444e; --primary: #33121c; --primary-hover: #4d1d2b; --border: rgb(120 50 30 / .13); --hair: rgb(120 50 30 / .1); --fill: rgb(120 50 30 / .06); --link: #b4234f;
  --m0: #ffe4d4; --m1: #ffb08a; --m2: #ff9fba; --m3: #ffd89c; }
.look-mint { --surface-subtle: #eef8f2; --ink: #0c2b22; --ink-muted: #38554b; --primary: #0c2b22; --primary-hover: #154236; --border: rgb(16 96 70 / .14); --hair: rgb(16 96 70 / .11); --fill: rgb(16 96 70 / .06); --link: #0f766e;
  --m0: #dcf3e8; --m1: #a2e6ca; --m2: #b8ddff; --m3: #dff2a2; }
.look-party { --surface-subtle: #130a22; --surface: rgb(255 255 255 / .07); --ink: #fbf5ff; --ink-muted: #c6b6de; --primary: #ffffff; --primary-hover: #f1e6ff; --on-primary: #1c0d33; --border: rgb(255 255 255 / .11); --hair: rgb(255 255 255 / .12); --link: #ff9bd6;
  --m0: #1d0f3a; --m1: #c0267f; --m2: #5a3cf0; --m3: #dc5f2e; }
