/* The inline demo on /, /compare and /demo. Markup: demo-section.mjs. Behaviour:
   demo-experience.js, which writes data-demo-state on [data-demo-panel]:
   resting, loading, idle, call, ended, error, busy, network, fallback.

   Motion moves opacity and transform only. Nothing here animates a height: the height
   reserve snaps in the start click, before the bundle is requested. */

.demo-panel [hidden],.demo-follow[hidden]{display:none!important}

/* ---------- Homepage section heading ---------------------------------------------------- */
#demo-heading{scroll-margin-top:130px}
[data-demo-heading]:focus{outline:none}
.demo-showcase-heading{display:flex;justify-content:space-between;align-items:flex-end;gap:48px;margin-bottom:42px}
.demo-showcase-heading>p{font-size:16px;line-height:1.8;color:var(--medi-muted,#526a5f);margin:0 0 5px;max-width:310px}
@media(max-width:800px){.demo-showcase-heading{gap:30px;align-items:flex-start}.demo-showcase-heading>p{max-width:230px;font-size:14px}}
@media(max-width:600px){.demo-showcase-heading{display:block;margin-bottom:24px}.demo-showcase-heading>p{font-size:15px;line-height:1.6;max-width:none;margin-top:14px}}

/* ---------- The panel ----------------------------------------------------------------------
   A light inset panel in the coverage card's family. Desktop is 1100px and wider, tablet 800
   to 1099px, phone below 800px. overflow:clip, not hidden: hidden would make the panel a
   scroll container and stop the call sheet's booking bar sticking to the viewport. */
.demo-panel{--demo-ease:cubic-bezier(.22,1,.36,1);--demo-pad:48px;
  /* The widget's display face, chosen at the design gate (brief 5.1): Medi Sans, the site's
     own sans. It overrides the widget's Crimson Text default, so nothing on any page renders
     that family and no page asks for its files. Medi Lora stays the site's accent serif and
     the stage sets it on the caller's own quoted line through --stage-quote-font. */
  --demo-display-font:"Medi Sans",Inter,Arial,sans-serif;
  position:relative;overflow:clip;padding:var(--demo-pad);border:1px solid var(--medi-line,#cfdaca);border-radius:28px;background:#f0f3ea;color:var(--medi-forest,#153e35);
  transition:border-color .95s cubic-bezier(.32,.72,.22,1)}
@media(max-width:1099.98px){.demo-panel{--demo-pad:32px}}
@media(max-width:799.98px){.demo-panel{--demo-pad:20px;border-radius:22px}}

/* ---------- The stage light ------------------------------------------------------------------
   While a call is on, the panel dims to a forest ground with beams of light over it. Nothing
   outside the panel changes: the page keeps its paper ground and stays scrollable.

   The widget marks which ground its screen needs on the mount, data-stage-scene="dark" while
   the call is ringing, live or wrapping up, and "light" everywhere else. The ground, the beams
   and the motes follow that one mark, so the light and the screens move together.

   Only opacity and transform animate here. The beams are conic gradients rotating around a
   point above the panel's top edge, which costs one composited layer each and no layout. */
.demo-stage-fx{position:absolute;inset:0;z-index:0;overflow:hidden;border-radius:inherit;pointer-events:none}
.demo-stage-fx:before{content:"";position:absolute;inset:0;
  background:radial-gradient(120% 86% at 50% -14%,#244f43 0%,#153e35 44%,#102d26 100%);
  opacity:0;transition:opacity .95s cubic-bezier(.32,.72,.22,1)}
/* The motes fill the top two thirds of the panel, which is where the beams reach. Their canvas
   is composited on every frame it changes, so it covers no more of the panel than it has to. */
.demo-stage-fx canvas{position:absolute;inset:0 0 32% 0;width:100%;height:68%;opacity:0;transition:opacity .9s var(--demo-ease)}
/* The scrim: the light pools in the middle of the stage, and the top and bottom edges stay
   deep. It paints over the beams and the motes, which is what keeps the text on the caller
   header and on the glass cards above 4.5:1 in the lightest frame the beams produce. */
.demo-stage-fx:after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,#102d26d9 0,#102d2666 12%,#102d2600 30%),radial-gradient(130% 62% at 50% 112%,#102d2699,#102d2600 58%);
  opacity:0;transition:opacity .95s cubic-bezier(.32,.72,.22,1)}
/* Each beam is a 22deg wedge from a point just above the panel, faded out with distance.
   Its box is fixed, not a percentage of the panel: the panel is one height for the whole call and
   a taller one for the call sheet, and a box that tracked it made every beam a layout-shift source
   the moment the sheet arrived. Measured at 1440: walk CLS 0.0917 with the percentage height,
   0.0128 with this one, for the same pixels on screen. 1100px clears the tallest panel any
   breakpoint holds (982px), and the beam fades out over the last quarter of it either way. */
.demo-beam{position:absolute;top:-48px;width:150%;height:1100px;transform-origin:50% 0;
  background:conic-gradient(from 0deg at 50% 0,#c9ea8400 160deg,#c9ea840f 171deg,#c9ea8426 178deg,#c9ea8433 180deg,#c9ea8426 182deg,#c9ea840f 189deg,#c9ea8400 200deg);
  -webkit-mask-image:radial-gradient(64% 78% at 50% 0,#000 8%,#0000 76%);mask-image:radial-gradient(64% 78% at 50% 0,#000 8%,#0000 76%);
  opacity:0;transition:opacity 1.3s var(--demo-ease)}
.demo-beam:nth-child(1){left:-25%;animation:demo-beam-a 23s ease-in-out infinite alternate}
.demo-beam:nth-child(2){left:-11%;width:132%;animation:demo-beam-b 31s ease-in-out infinite alternate}
.demo-beam:nth-child(3){left:-41%;width:168%;animation:demo-beam-c 27s ease-in-out infinite alternate}
@keyframes demo-beam-a{from{transform:rotate(-7deg)}to{transform:rotate(5deg)}}
@keyframes demo-beam-b{from{transform:rotate(9deg)}to{transform:rotate(-4deg)}}
@keyframes demo-beam-c{from{transform:rotate(-13deg) scaleY(.92)}to{transform:rotate(3deg) scaleY(1.04)}}
/* Every loop runs only while the panel is on screen and the tab is on top. The loading
   skeleton's pulse is one of them: it ran on for as long as the bundle took, three viewports
   above the fold, at 917ms of clock per 900ms of wall time. */
.demo-panel:not(.is-fx-live) .demo-beam{animation-play-state:paused}
/* Two animations on .demo-sk, and only the second one is an effect: the list pauses the pulse
   and leaves the fade running. Pausing both held every shape at the opacity:0 its fade starts
   from, so a skeleton that came back on screen came back invisible. */
.demo-panel:not(.is-fx-live) .demo-sk{animation-play-state:running,paused}

/* At rest one beam shows faintly: the hint of the stage, and the only part of it a visitor who
   never presses anything pays for. It brightens while the start button has the pointer or focus. */
.demo-panel[data-demo-state=resting] .demo-beam:nth-child(1){opacity:.5}
.demo-panel[data-demo-state=resting]:has(.demo-start:hover) .demo-beam:nth-child(1),
.demo-panel[data-demo-state=resting]:has(.demo-start:focus-visible) .demo-beam:nth-child(1){opacity:.9}

/* The call: the lights go down, then the beams come up.

   The widget writes data-stage-scene on its own mount and demo-experience.js mirrors it onto the
   panel as is-stage-dark. The class is what paints. :has() alone was the whole switch, and
   Firefox shipped :has() in 121, so before that every call rendered its dark-ground screens on
   the light panel: white captions on #f0f3ea. The two halves are written as separate rules and
   never as one comma list, because a browser that cannot parse :has() drops the whole list. */
.demo-panel.is-stage-dark{border-color:#153e35}
.demo-panel.is-stage-dark .demo-stage-fx:before,
.demo-panel.is-stage-dark .demo-stage-fx:after{opacity:1}
.demo-panel.is-stage-dark .demo-beam{opacity:1;transition-delay:.45s}
.demo-panel.is-stage-dark .demo-stage-fx canvas{opacity:1;transition-delay:.6s}
/* The progressive half, for a page whose script never ran. */
.demo-panel:has([data-demo-mount][data-stage-scene=dark]){border-color:#153e35}
.demo-panel:has([data-demo-mount][data-stage-scene=dark]) .demo-stage-fx:before,
.demo-panel:has([data-demo-mount][data-stage-scene=dark]) .demo-stage-fx:after{opacity:1}
.demo-panel:has([data-demo-mount][data-stage-scene=dark]) .demo-beam{opacity:1;transition-delay:.45s}
.demo-panel:has([data-demo-mount][data-stage-scene=dark]) .demo-stage-fx canvas{opacity:1;transition-delay:.6s}

/* The ground covers the whole panel, and the panel sets no z-index, so it is not a stacking
   context: an unpositioned child of it paints under the gradient. Two things sit there, the
   /compare heading above the stage and the disclosure below it, and both kept their layout
   height while the ground painted over them, leaving a blank dark band for the whole call.
   They are lifted out of it and lit for it. The eyebrow inside the /compare heading is named
   separately: .eyebrow sets its own colour, so the colour the block inherits never reaches it,
   and it measured 1.48:1 as dark forest on the forest ground for the whole call. */
.demo-panel-compare .compare-demo-head,.demo-about{position:relative;z-index:1;transition:color .95s cubic-bezier(.32,.72,.22,1)}
.demo-panel.is-stage-dark :is(.compare-demo-head,.compare-demo-head .eyebrow,.demo-about,.demo-about ul){color:var(--medi-white,#fffefa)}
.demo-panel.is-stage-dark .demo-about summary:after{border-color:#6e8a79}
.demo-panel:has([data-demo-mount][data-stage-scene=dark]) :is(.compare-demo-head,.compare-demo-head .eyebrow,.demo-about,.demo-about ul){color:var(--medi-white,#fffefa)}
.demo-panel:has([data-demo-mount][data-stage-scene=dark]) .demo-about summary:after{border-color:#6e8a79}
/* The film offer and the row under the stage wait for the ground to come back up. */
.demo-panel.is-after-dark :is(.demo-screen,.demo-film-aside,.demo-row){animation-delay:.42s}
.demo-panel.is-after-dark .demo-row{animation:demo-fade-in .5s var(--demo-ease) .42s both}

/* ---------- Resting ------------------------------------------------------------------------ */
.demo-rest,.demo-live{position:relative;z-index:1}
.demo-rest{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:48px;align-items:center}
@media(max-width:1099.98px){.demo-rest{grid-template-columns:minmax(0,1fr);gap:32px}}
/* On start the resting content leaves the flow at once, so the reserve below is the panel's
   height from the first frame, and fades out on top of it. */
.demo-panel:not([data-demo-state=resting]) .demo-rest{position:absolute;inset:var(--demo-pad) var(--demo-pad) auto;pointer-events:none;animation:demo-fade-out .2s var(--demo-ease) both}

.demo-invite{min-width:0;display:flex;flex-direction:column;align-items:flex-start;text-align:left}
.demo-invite-head{display:flex;align-items:center;gap:24px;margin:0 0 20px}
.demo-invite-head:empty{display:none}
.demo-invite-title{margin:0;font-family:"Medi Sans",Inter,Arial,sans-serif;font-size:clamp(26px,2.25vw,32px);font-weight:400;line-height:1.2;letter-spacing:-.025em;color:var(--medi-forest,#153e35);text-wrap:balance}
.demo-hints{list-style:none;margin:0 0 20px;padding:0;display:grid;gap:8px}
.demo-hints li{padding-left:.5em;text-indent:-.5em;font-family:"Medi Lora",Georgia,serif;font-size:18px;line-height:1.4;color:var(--medi-forest,#153e35)}
.demo-hints span{color:var(--medi-positive,#47723f)}
.demo-facts{margin:0 0 24px;max-width:46ch;font-size:15px;line-height:1.6;color:var(--medi-forest,#153e35)}
.demo-privacy{margin:12px 0 0;font-size:13px;line-height:1.55;color:var(--medi-muted,#526a5f)}
/* Below the panel's live area, not inside the resting block, so it is on screen in every state.
   The resting block leaves the flow on the start click and the call sheet has no close control,
   so this is where the page's own facts about the call stay reachable once a visitor has
   started one. */
.demo-about{margin-top:4px;font-size:14px;color:var(--medi-forest,#153e35)}
.demo-panel[data-demo-state=resting] .demo-about{margin-top:0}
.demo-about summary{display:inline-flex;align-items:center;gap:10px;min-height:44px;font-weight:500;cursor:pointer;list-style:none}
.demo-about summary::-webkit-details-marker{display:none}
.demo-about summary:after{content:"+";display:grid;place-items:center;width:22px;height:22px;border:1px solid var(--medi-line,#cfdaca);border-radius:50%;font-size:15px;line-height:1}
.demo-about[open] summary:after{content:"\2212"}
.demo-about ul{margin:2px 0 0;padding:0 0 0 18px;display:grid;gap:6px;max-width:52ch;font-size:14px;line-height:1.55;color:var(--medi-muted,#526a5f)}
@media(max-width:799.98px){.demo-invite-title{font-size:25px}.demo-hints li{font-size:16px}.demo-hints{margin-bottom:16px;gap:6px}.demo-facts{font-size:14px;margin-bottom:18px}}

/* Primary actions: apple fill, forest text, as on every site call to action. */
.demo-panel .demo-start,.demo-primary,.demo-film-play,.demo-follow-return{background:var(--medi-apple,#c9ea84);border:1px solid var(--medi-apple,#c9ea84);color:var(--medi-forest,#153e35)}
.demo-panel .demo-start:hover,.demo-primary:hover,.demo-film-play:hover,.demo-follow-return:hover{background:var(--medi-apple-hover,#d7f0a8);border-color:var(--medi-apple-hover,#d7f0a8)}
.demo-panel .demo-start{min-height:54px;padding:15px 30px;font-size:15px}
.demo-text-button{display:inline-flex;align-items:center;min-height:44px;padding:0;border:0;background:none;color:var(--medi-forest,#153e35);font:inherit;font-weight:500;text-decoration:underline;text-underline-offset:3px;cursor:pointer}

/* The site orb, beside the heading on tablet and desktop only. It flows while in view. It is
   the stage's own sphere at rest: the same forest ball under the same apple light the call
   screens use, so the visual the visitor meets first is the one that grows on the stage. */
.demo-voice-orb{position:relative;width:120px;height:120px;flex:none;isolation:isolate}
@media(max-width:799.98px){.demo-voice-orb{display:none}}
.voice-orb-body{position:absolute;inset:6px;border-radius:50%;overflow:hidden;background:radial-gradient(circle at 30% 24%,#ecf4dd,#c9ea84 26%,#a8d164 52%,#47723f 80%,#153e35);transform:translateZ(0)}
.voice-orb-body i{position:absolute;inset:-30%;display:block;border-radius:44% 56% 60% 40%/55% 38% 62% 45%;filter:blur(11px);mix-blend-mode:screen;background:conic-gradient(from 60deg,transparent 12%,#d7f0a8 25%,#47723f 42%,transparent 54%,#ecf4dd 73%,transparent 91%);animation:voice-orb-flow 9s cubic-bezier(.45,0,.55,1) infinite alternate;animation-play-state:paused;opacity:.95;will-change:transform}
.voice-orb-body i:nth-child(2){inset:-10%;background:radial-gradient(ellipse at 25% 35%,#ecf4ddb0,transparent 42%),conic-gradient(from 140deg,transparent,#244f437a,#a8d164b0,transparent 70%);animation-duration:7s;animation-delay:-3s;mix-blend-mode:soft-light}
.voice-orb-body i:nth-child(3){inset:10% -15%;background:linear-gradient(140deg,transparent 30%,#ecf4ddbf 49%,#c9ea8466 58%,transparent 70%);animation-duration:5.5s;animation-delay:-2s;opacity:.7}
.voice-orb-body:after{content:"";position:absolute;inset:0;border-radius:50%;background:radial-gradient(circle at 37% 22%,#ffffff47,transparent 46%)}
.demo-voice-orb.is-running i{animation-play-state:running}
@keyframes voice-orb-flow{0%{transform:rotate(-30deg) translate(-7%,5%) scale(.95)}45%{transform:rotate(100deg) translate(7%,-4%) scale(1.15)}100%{transform:rotate(240deg) translate(-2%,8%) scale(1.03)}}

/* ---------- The film ------------------------------------------------------------------------
   One player that moves between slots: the resting column, the row under the stage, and the
   failure screens, where it is the main offer. */
.demo-film{margin:0;min-width:0}
.demo-film figcaption{margin:0 0 12px;font-size:14px;line-height:1.5;color:var(--medi-forest,#153e35)}
.demo-film-frame{position:relative;isolation:isolate;aspect-ratio:16/9;border-radius:20px;overflow:hidden;background:#173a31}
.demo-film video{display:block;width:100%;height:100%;object-fit:contain;background:#173a31}
.demo-film-play{position:absolute;left:50%;top:50%;z-index:1;display:none;min-height:54px;padding:14px 26px;white-space:nowrap;transform:translate(-50%,-50%)}
/* The whole silent preview is a keyboard-accessible play-from-start target. */
.demo-film.has-preview:not(.is-offer):not(.is-playing) .demo-film-play{display:flex;inset:0;width:100%;height:100%;transform:none;border:0;border-radius:0;background:transparent;align-items:center;justify-content:center;white-space:normal;padding:20px}
.demo-film.has-preview:not(.is-offer):not(.is-playing) .demo-film-play:hover{background:#153e351a}
.demo-film.has-preview:not(.is-offer) .demo-film-play>span{padding:13px 20px;border-radius:999px;background:var(--medi-apple,#c9ea84);box-shadow:0 4px 24px #0003;font-size:clamp(12px,1.4vw,15px)}
.demo-film-pause{position:absolute;right:12px;bottom:12px;z-index:2;padding:8px 12px;min-height:36px;border:1px solid #fff8;border-radius:999px;background:#153e35e6;color:white;font-size:11px}
.demo-film-pause[hidden],.demo-film.is-offer .demo-film-pause{display:none}
.demo-film-play:focus-visible{outline-offset:-5px}
.demo-film.is-offer:not(.is-playing) .demo-film-play{display:inline-flex}
/* In the row slot the line directly above the player is the same question, and on a failure
   screen the screen's own copy carries it. */
:is([data-demo-film-slot=busy],[data-demo-film-slot=network],[data-demo-film-slot=row]) .demo-film figcaption{display:none}
/* On a failure the film is the offer, and it grows into the height the screen holds. The frame
   keeps 16:9 as its smallest size and letterboxes the video when it is taller. */
:is(.demo-screen .demo-film-slot,.demo-film-aside){display:flex;flex-direction:column}
:is(.demo-screen,.demo-film-aside) .demo-film{flex:1 1 auto;display:flex;flex-direction:column}
:is(.demo-screen,.demo-film-aside) .demo-film-frame{flex:1 1 auto}

/* ---------- Live area: stage, row --------------------------------------------------------- */
.demo-row{max-width:48rem;margin:16px auto 0;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 24px}
.demo-row-film{margin:0;font-size:14px;line-height:1.5;color:var(--medi-muted,#526a5f)}
.demo-row-button{min-height:44px;padding:10px 20px;font-size:14px}
.demo-panel:is([data-demo-state=call],[data-demo-state=error],[data-demo-state=busy],[data-demo-state=network]) .demo-row{display:none}
.demo-film-row{max-width:48rem;margin:16px auto 0}
/* Over the fallback frame the row carries what the page can still offer: the film, a booking
   link, the way back to a demo that runs in this page, and the one exit. The way back is a text
   button beside the two offers, because a bundle that failed once is not the first thing to try
   again. */
.demo-row-fallback{display:flex;flex-wrap:wrap;align-items:center;gap:8px 18px}
/* On a phone the panel's own padding is 20px, and no gap inside it may be wider than that. */
@media(max-width:799.98px){.demo-row{margin-top:14px}}

/* The stage is the width container. Every rule keyed to the widget's width queries it, and it
   is never wider than the widget's own 48rem cap, so its width is the widget's width. */
.demo-stage{container:demo-stage/inline-size;width:100%;max-width:48rem;margin-inline:auto;outline:none}

/* The reserve lays the skeleton out on the idle screen's own grid: heading, caller, three
   phrases, the Start pill and the call record, in one column below 640px and two above it,
   which is where the widget switches (40rem on its own container). The mount, the page
   screens and the fallback frame all span the whole grid on top of each other. */
.demo-reserve{position:relative;display:grid;grid-template-columns:minmax(0,1fr);grid-template-areas:"head" "sub" "caller" "hints" "start" "status" "newtab" "record";align-content:start}
@container demo-stage (min-width:640px){.demo-reserve{grid-template-columns:minmax(0,1fr) 318px;column-gap:22px;grid-template-areas:"head head" "sub sub" "caller record" "hints record" "start record" "status record" "newtab record"}}
.demo-mount,.demo-screen,.demo-frame{grid-area:1/1/-1/-1;min-width:0}

/* ---------- Height reserve table -----------------------------------------------------------
   Keyed to the widget's width through the stage container, never to the viewport.
   --demo-reserve-idle holds loading, idle and the fallback frame. --demo-reserve-call holds the
   whole call window: creating_session, connecting, the live call and generating_summary, because
   the page cannot see those steps apart and a reserve that changed between them would move the
   content under the panel with nobody touching the page. Failures keep the height they inherit
   (demo-experience.js, hold()), and the call sheet takes its natural height.

   The mount hands --demo-reserve-call to the widget as --call-min-height, so the call screens
   fill the same height rather than leaving a band under themselves. The stage screens read the
   same variable: the page holds one height for the whole call, and the stage is that call.

   Measured 2026-09-17 from the built homepage with the mock-mode embed (widget/dist-embed-mock),
   chrome 'flat', the stage on, the data rule handed to the widget, Medi Sans as the display face,
   fonts loaded: the natural height of every screen, with this reserve and --call-min-height
   switched off, at every viewport width from 320 to 910px and at 1024, 1200 and 1440px, which is
   every widget width from 238 to 768px. Each value is the tallest height in its range, rounded up,
   and no range spans more than 5px, so the reserve is never more than 1.5px taller than the screen
   it holds.
   idle: the idle screen. The loading skeleton stands on the same rows and is measured beside it,
   but it sets none of these numbers: its call-record shape has no height of its own and takes
   whatever the reserve leaves. A 220px floor on that shape made the skeleton the taller of the
   two from a 421px widget, and the reserve followed the skeleton to 622px where the idle screen
   is 575.2px, which left a 60.8px band under the idle call record against a 20px panel padding.
   call: the tallest of creating_session, connecting (with the clinic-lines line up), the live call
   with the hints up, the live call with the record full, and wrapping up. All four fill this
   height, so each of them is exactly as tall as the call. The live call is the tallest of the four
   at every width: the copy deck took the greeting quote and the privacy line off the ringing
   screen, so ringing no longer sets it.
   Re-measure if the display face changes to Medi Lora, if the sentence the page hands the widget
   changes, or if the widget's screens change.
   The call column was re-measured 2026-09-18 after the widget's caption box grew to fit the three
   example lines, which were being cut off top and bottom wherever they wrapped. The box holds
   those lines, so it is taller at widget widths under 371px and from 640 to 740px, where they
   wrap; the call is taller in the same places and its breakpoints are the box's. The idle column
   is untouched.
     widget width    idle    call
     0 to 239px      687px   639px
     240 to 250px    687px   624px
     251 to 315px    672px   624px
     316 to 328px    644px   624px
     329 to 368px    644px   603px
     369 to 370px    621px   603px
     371 to 418px    621px   593px
     419 to 639px    598px   593px
     640 to 692px    432px   479px
     693 to 731px    432px   456px
     732 to 740px    409px   456px
     741 to 767px    409px   448px
     768px and up    408px   448px
   The idle row at 768px is the one number below that is not the one measured here. The rule
   that sets it holds 409px, for the loading skeleton rather than the idle screen. Read it for
   why. */
.demo-reserve{--demo-reserve-idle:687px;--demo-reserve-call:639px}
@container demo-stage (min-width:240px){.demo-reserve{--demo-reserve-call:624px}}
@container demo-stage (min-width:251px){.demo-reserve{--demo-reserve-idle:672px}}
@container demo-stage (min-width:316px){.demo-reserve{--demo-reserve-idle:644px}}
@container demo-stage (min-width:329px){.demo-reserve{--demo-reserve-call:603px}}
@container demo-stage (min-width:369px){.demo-reserve{--demo-reserve-idle:621px}}
@container demo-stage (min-width:371px){.demo-reserve{--demo-reserve-call:593px}}
@container demo-stage (min-width:419px){.demo-reserve{--demo-reserve-idle:598px}}
@container demo-stage (min-width:640px){.demo-reserve{--demo-reserve-idle:432px;--demo-reserve-call:479px}}
@container demo-stage (min-width:693px){.demo-reserve{--demo-reserve-call:456px}}
@container demo-stage (min-width:732px){.demo-reserve{--demo-reserve-idle:409px}}
@container demo-stage (min-width:741px){.demo-reserve{--demo-reserve-call:448px}}
/* The one row where the loading skeleton, not the idle screen, sets the height. The skeleton's
   only real text is the status sentence under the Start pill, "Still loading. You can open the
   sample call in a new tab instead.", at 14px in the 428px left column of the two-column grid.
   Any face that needs more than those 428px for it takes a second line, and the skeleton is then
   409px, which is what it already is at every widget width from 640 to 767. Medi Sans needs
   418.3px, so the single line it gets here it gets by 9.7px, and DejaVu Sans, the last-resort
   sans-serif on a browser with no Arial and no Liberation Sans, needs 446.9px and wraps. At the
   408px the idle screen measures, that skeleton stood one pixel over the reserve and moved the
   section under the panel when the widget arrived. What the pixel costs is an empty band under
   the idle screen, 1.5px measured on the built pages at a 1440px viewport, where the panel's own
   padding is 48px. */
@container demo-stage (min-width:768px){.demo-reserve{--demo-reserve-idle:409px}}
/* /demo opens loaded, so its visitor never reads the resting block and the widget prints the
   whole facts line instead of the short rule. That is one paragraph taller than the idle screen
   the table above was measured from, by 14.2px to 33px depending on the width, and the shared
   reserve under-held it: the panel grew when the widget replaced the skeleton. Its own column,
   measured the same way on the built /demo at every widget width from 246 to 768px. The call
   column is shared, because the call screens print no such line.
     widget width    idle
     0 to 315px      702px
     316 to 347px    674px
     348 to 369px    659px
     370 to 419px    636px
     420 to 639px    613px
     640 to 687px    465px
     688 to 731px    449px
     732px and up    424px */
.demo-panel-page .demo-reserve{--demo-reserve-idle:702px}
@container demo-stage (min-width:316px){.demo-panel-page .demo-reserve{--demo-reserve-idle:674px}}
@container demo-stage (min-width:348px){.demo-panel-page .demo-reserve{--demo-reserve-idle:659px}}
@container demo-stage (min-width:370px){.demo-panel-page .demo-reserve{--demo-reserve-idle:636px}}
@container demo-stage (min-width:420px){.demo-panel-page .demo-reserve{--demo-reserve-idle:613px}}
@container demo-stage (min-width:640px){.demo-panel-page .demo-reserve{--demo-reserve-idle:465px}}
@container demo-stage (min-width:688px){.demo-panel-page .demo-reserve{--demo-reserve-idle:449px}}
@container demo-stage (min-width:732px){.demo-panel-page .demo-reserve{--demo-reserve-idle:424px}}
.demo-panel:is([data-demo-state=loading],[data-demo-state=idle],[data-demo-state=fallback]) .demo-reserve{min-height:var(--demo-reserve-idle)}
.demo-panel[data-demo-state=call] .demo-reserve{min-height:var(--demo-reserve-call)}
/* The call sheet and the failure screens arrive a fifth of a second after the stage screen
   leaves. The reserve holds the call's height across that gap, so the panel never collapses
   between the two.

   Not on idle, which is where Cancel and Call again land. That state has a reserve of its own,
   and the two rules tied on specificity, so the later one won: cancelling a ringing call left
   the panel 51px taller than the idle screen it was showing and shrank it 1.1s later with
   nobody touching the page. */
.demo-panel.is-after-dark:not([data-demo-state=idle]) .demo-reserve{min-height:var(--demo-reserve-call)}
/* The skeleton fills the idle reserve: the rows stretch and the call record shape takes the
   extra height, beside the phrases on two columns and under the Start pill on one. Idle keeps
   the same rows, so the shapes stay where they are for the fade after the widget arrives. The
   status row keeps the height of its one line (14px margin, 21px line), because the status
   leaves the grid when loading ends. The mount spans every row either way. */
.demo-panel:is([data-demo-state=loading],[data-demo-state=idle]) .demo-reserve{align-content:stretch;grid-template-rows:auto auto auto auto auto minmax(35px,auto) auto 1fr}
@container demo-stage (min-width:640px){.demo-panel:is([data-demo-state=loading],[data-demo-state=idle]) .demo-reserve{grid-template-rows:auto auto auto 1fr auto minmax(35px,auto) auto}}
/* A failure fills the height it inherits: the rows stretch, the screen spans them, and the film
   offer grows into whatever the copy leaves, so no empty band opens above or below. */
.demo-panel:is([data-demo-state=error],[data-demo-state=busy],[data-demo-state=network]) .demo-reserve{align-content:stretch}
/* The fallback frame is the only thing in the grid in that state, and the grid packs its rows to
   the top, so height:100% on the frame resolved against an auto-height parent and left it at the
   iframe's own 150px inside a 418px reserve. One row, stretched. */
.demo-panel[data-demo-state=fallback] .demo-reserve{align-content:stretch;grid-template-rows:1fr}

/* ---------- Loading --------------------------------------------------------------------------
   A skeleton in the idle screen's shape. It pulses in opacity. The wrapper is display:contents
   so each shape is a cell of the reserve grid. */
/* The shapes stay in the grid for their 300ms fade after the widget arrives. They never take
   a click meant for the widget underneath. */
.demo-skeleton{display:contents}
.demo-skeleton *{pointer-events:none}
.demo-sk{display:block;min-width:0;border-radius:999px;background:#dde6d4;animation:demo-fade-in .2s var(--demo-ease) .2s both,demo-pulse 1.1s ease-in-out .4s infinite alternate}
/* Two bars, because the idle heading is two lines. */
.demo-sk-head{grid-area:head;justify-self:center;display:grid;gap:8px;width:min(540px,88%);margin-top:4px}
.demo-sk-head .demo-sk{height:30px}
.demo-sk-head .demo-sk:nth-child(2){width:76%;justify-self:center}
.demo-sk-sub{grid-area:sub;justify-self:center;width:min(420px,70%);height:14px;margin:14px 0 17px}
.demo-sk-caller{grid-area:caller;width:min(250px,78%);height:46px;margin-bottom:20px;border-radius:14px}
.demo-sk-hints{grid-area:hints;display:grid;gap:12px;align-content:start;margin-bottom:20px}
.demo-sk-hints .demo-sk{height:16px}
.demo-sk-hints .demo-sk:nth-child(1){width:86%}.demo-sk-hints .demo-sk:nth-child(2){width:94%}.demo-sk-hints .demo-sk:nth-child(3){width:58%}
.demo-sk-start{grid-area:start;height:48px;align-self:end}
/* No height of its own. It is the 1fr row below 640px and the stretched column above it, so it
   takes whatever the idle reserve leaves after the other shapes, which is about 195px at a
   424px widget. A 220px floor here made the skeleton taller than the idle screen it stands in
   for, and one reserve column covers both, so the reserve grew to the skeleton and left a 60.8px
   band under the idle call record at every widget width from 321 to 639. The slow-load new-tab
   link takes its 25px from this shape too, rather than from the panel's height. */
.demo-sk-record{grid-area:record;margin-top:18px;border:1px dashed #b3c4ac;border-radius:18px;background:transparent}
@container demo-stage (min-width:640px){.demo-sk-record{margin-top:0}.demo-sk-sub{margin:14px 0 22px}}
/* The 22px above lines the caller shape up with the idle screen's, whose own heading margin is
   17px at every width in flat chrome. The panel's own padding is 20px below 800px of viewport,
   where a 22px gap inside it is a hole. The container query cannot see the padding, so this one
   is keyed to the viewport and comes after it. */
@media(max-width:799.98px){.demo-sk-sub{margin-bottom:17px}}
.demo-panel:not([data-demo-state=loading]) .demo-sk{animation:demo-fade-out .3s var(--demo-ease) both}

/* The status region is visible text only while loading, under the skeleton's Start pill. In
   every other state it stays a live region nobody sees. */
.demo-status{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.demo-panel[data-demo-state=loading] .demo-status{position:static;grid-area:status;width:auto;height:auto;margin:14px 0 0;overflow:visible;clip-path:none;white-space:normal;text-align:center;font-size:14px;line-height:1.5;color:var(--medi-muted,#526a5f)}
.demo-newtab{grid-area:newtab;justify-self:center;margin-top:4px;font-size:14px;line-height:1.5;color:var(--medi-forest,#153e35);text-decoration:underline;text-underline-offset:3px}

/* ---------- The widget ---------------------------------------------------------------------
   Every token is set on the mount itself. A declaration here beats the widget's :host
   defaults. The same declaration on an ancestor does not reach the widget (brief 5.1). */
.demo-mount{position:relative;z-index:1;display:block;width:100%;padding:0;
  --color-ink:var(--medi-apple,#c9ea84);--color-ink-hover:var(--medi-apple-hover,#d7f0a8);
  --color-on-ink:var(--medi-forest,#153e35);--color-ink-active:var(--medi-apple-hover,#d7f0a8);
  --color-neutral-900:var(--medi-forest,#153e35);--color-neutral-800:var(--medi-forest,#153e35);
  --color-neutral-700:var(--medi-forest-hover,#244f43);
  --color-neutral-600:var(--medi-muted,#526a5f);--color-neutral-500:var(--medi-muted,#526a5f);--color-neutral-400:var(--medi-muted,#526a5f);
  --color-neutral-300:#739065;--color-neutral-200:var(--medi-line,#cfdaca);
  --color-neutral-100:var(--medi-soft,#e6eddf);--color-neutral-50:#f0f3ea;
  --color-white:var(--medi-white,#fffefa);
  --color-primary-50:var(--medi-apple-soft,#ecf4dd);--color-primary-100:var(--medi-apple-soft,#ecf4dd);
  --color-primary-500:var(--medi-positive,#47723f);--color-primary-600:var(--medi-forest,#153e35);--color-primary-700:var(--medi-positive,#47723f);
  --color-error-600:#9b4137;--color-red-700:#ac493e;--color-red-800:#9b4137;
  --color-info-100:var(--medi-soft,#e6eddf);--color-info-600:var(--medi-forest,#153e35);
  --color-warning-100:var(--medi-soft,#e6eddf);--color-warning-700:var(--medi-forest,#153e35);
  --font-display:var(--demo-display-font);--font-heading:"Medi Sans",Inter,Arial,sans-serif;--font-body:"Medi Sans",Inter,Arial,sans-serif;
  --radius-xl:18px;--radius-lg:14px;--radius-pill:999px;
  --orb-core:var(--medi-apple,#c9ea84);--orb-mid:var(--medi-leaf,#a8d164);--orb-edge:var(--medi-positive,#47723f);
  /* The dark stage the widget draws its call screens for (contract section 1, `stage`). */
  --stage-ink:var(--medi-white,#fffefa);--stage-muted:var(--medi-soft,#e6eddf);--stage-accent:var(--medi-apple,#c9ea84);
  --stage-ground:var(--medi-forest,#153e35);--stage-soft:var(--medi-apple-soft,#ecf4dd);
  --stage-quote-font:"Medi Lora",Georgia,serif;
  /* The height the page holds for the call, handed to the widget: its four call screens are at
     least this tall and their panels grow into it, so a short screen opens no empty band. */
  --call-min-height:var(--demo-reserve-call);
  font-family:"Medi Sans",Inter,Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.5;letter-spacing:normal;text-transform:none;text-align:left;color:var(--medi-forest,#153e35)}
/* The widget's status colours, in site colours: the ringing circles, the confirmed band and its
   text, the check marks and the "Completed" label, and the circle behind the crossed-out
   microphone. The site has no red of its own, so that circle takes the soft fill every other
   error circle has and the red stays on the icon. */
.demo-mount{--color-primary-300:var(--medi-positive,#47723f);
  --color-success-100:var(--medi-apple-soft,#ecf4dd);--color-green-800:var(--medi-forest,#153e35);--color-success-700:var(--medi-positive,#47723f);
  --color-error-100:var(--medi-soft,#e6eddf)}
.demo-panel[data-demo-state=loading] .demo-mount{opacity:0}
.demo-panel[data-demo-state=idle] .demo-mount{animation:demo-rise .3s var(--demo-ease) both}
.demo-panel:is([data-demo-state=busy],[data-demo-state=network]) .demo-mount{visibility:hidden}

/* ---------- Failures -------------------------------------------------------------------------
   A failure replaces the call inside the same stage, at the same width, and the film offer grows
   into the height the stage inherits. The stage keeps its 48rem cap: widening it on the failure
   moved the reserve and the mount sideways, a layout shift nobody asked for. Side by side from
   640px of stage width, the widget's own two-column width, which covers tablets (brief 4.8 and
   4.9). Stacked on a tablet, the film is as wide as the stage, so copy, film and actions come out
   taller than the call they replace and the panel would grow. */

/* The widget's own microphone and timeout screens, with the film offer beside or below them.
   Stacked, the row gap is the page's: flat, the widget's error screen has no padding of its own. */
.demo-panel[data-demo-state=error] .demo-reserve{grid-template-columns:minmax(0,1fr);grid-template-rows:auto 1fr;grid-template-areas:"mount" "aside";row-gap:12px}
@container demo-stage (min-width:640px){.demo-panel[data-demo-state=error] .demo-reserve{grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:1fr;grid-template-areas:"mount aside";column-gap:32px}}
/* Top-aligned, so the mount's top-left corner stays where the call had it. Centred, the mount
   moved down when the error card replaced the call. */
.demo-panel[data-demo-state=error] .demo-mount{grid-area:mount;align-self:start}
/* The dark call screen fades for 220 ms after the page has already moved to the failure layout,
   and for those frames it is still what the mount holds. In the mount column it would lay out at
   half the width, where it is 520px tall against the 448px the page is holding, and the section
   below moved 71px down and back. It keeps the whole grid until the fade is over, which is what
   is-after-dark marks. */
.demo-panel[data-demo-state=error].is-after-dark .demo-mount{grid-area:1/1/-1/-1}
/* The film offer waits out that fade too. Its own fade-in is delayed past it (.demo-panel
   .is-after-dark rule above), so it is invisible either way, but stacked under the mount it took
   its place twice: once beside the leaving call screen and again under the failure card. */
.demo-panel[data-demo-state=error].is-after-dark .demo-film-aside{display:none}
.demo-film-aside{grid-area:aside;min-width:0;animation:demo-fade-in .15s var(--demo-ease) both}

/* The page's own busy and network screens. Busy: film left, copy and actions right. Stacked:
   copy, film, actions. Network: copy and "Try again" beside the film, or above it. */
.demo-screen{display:grid;gap:12px 40px;align-content:stretch;padding:8px 0;animation:demo-fade-in .15s var(--demo-ease) both}
/* No inset at the foot. The disclosure under the panel's live area is on screen in every state,
   and it centres its own label inside a 44px touch target, so 12.8px of that target already
   sits between the screen's last control and the words. The screen's own 8px on top of it left
   24.8px of empty against a 20px padding on a phone, which is the band claim D4 counts. */
.demo-panel:is([data-demo-state=busy],[data-demo-state=network]) .demo-screen{padding-bottom:0}
.demo-screen h3{margin:0 0 8px;font-family:"Medi Sans",Inter,Arial,sans-serif;font-size:clamp(26px,3vw,34px);font-weight:400;line-height:1.2;letter-spacing:-.025em;color:var(--medi-forest,#153e35);text-wrap:balance}
.demo-screen h3:focus{outline:none}
.demo-screen p{margin:0;max-width:40ch;font-size:16px;line-height:1.6;color:var(--medi-muted,#526a5f)}
.demo-screen-actions{display:flex;flex-wrap:wrap;align-items:center;gap:12px 22px}
.demo-screen-actions .button{min-height:48px;padding:12px 22px;font-size:14px}
.demo-screen-busy{grid-template-columns:minmax(0,1fr);grid-template-rows:auto 1fr auto;grid-template-areas:"copy" "film" "actions"}
.demo-screen-busy>.demo-screen-copy{grid-area:copy}
.demo-screen-busy>.demo-film-slot{grid-area:film;min-width:0}
.demo-screen-busy>.demo-screen-actions{grid-area:actions}
.demo-screen-network{grid-template-columns:minmax(0,1fr);grid-template-rows:auto 1fr;grid-template-areas:"copy" "film"}
.demo-screen-network>.demo-screen-copy{grid-area:copy}
.demo-screen-network>.demo-film-slot{grid-area:film;min-width:0}
.demo-screen-network .demo-screen-actions{margin-top:16px}
@container demo-stage (min-width:640px){
  .demo-screen{row-gap:20px}
  .demo-screen-busy{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);grid-template-rows:auto auto;grid-template-areas:"film copy" "film actions"}
  .demo-screen-busy>.demo-screen-copy{align-self:end}
  .demo-screen-busy>.demo-screen-actions{align-self:start}
  .demo-screen-network{grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);grid-template-rows:auto;grid-template-areas:"copy film"}
  .demo-screen-network>.demo-screen-copy{align-self:center}
}

/* ---------- Fallback frame ------------------------------------------------------------------
   The frame fills the height the page is already holding and never sets one of its own. A 560px
   floor here grew the panel 142px at 834 and 163px at 1440 about a second and a half after the
   click, with nobody touching the page: 0.0537 of layout shift for a box that had been the same
   height while it loaded. The reserve holds --demo-reserve-idle in this state, as it does while
   loading, so the swap moves nothing. */
.demo-frame{display:block;width:100%;height:100%;border:0;border-radius:18px;background:var(--medi-white,#fffefa);animation:demo-fade-in .2s var(--demo-ease) both}

/* ---------- The follow pill ----------------------------------------------------------------
   Fixed, top centre, 8px under the header. demo-experience.js measures the header and writes
   --demo-follow-top. */
.demo-follow{position:fixed;top:var(--demo-follow-top,86px);left:50%;z-index:99;display:flex;align-items:center;gap:10px;max-width:calc(100vw - 24px);padding:6px 6px 6px 16px;border:1px solid var(--medi-line,#cfdaca);border-radius:999px;background:#fffefaf5;box-shadow:0 12px 32px #15352824;color:var(--medi-forest,#153e35);font-size:14px;line-height:1.3;white-space:nowrap;opacity:0;transform:translate(-50%,-8px);transition:opacity .2s cubic-bezier(.22,1,.36,1),transform .2s cubic-bezier(.22,1,.36,1)}
.demo-follow.is-shown{opacity:1;transform:translate(-50%,0)}
.demo-follow-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--medi-positive,#47723f)}
.demo-follow-timer{color:var(--medi-muted,#526a5f);font-variant-numeric:tabular-nums}
.demo-follow-return{min-height:44px;padding:0 18px;border-radius:999px;font:inherit;font-weight:500;cursor:pointer}
@media(max-width:420px){.demo-follow{gap:8px;padding-left:12px;font-size:13px}.demo-follow-return{padding:0 14px}}

/* ---------- The leave question -------------------------------------------------------------- */
.demo-leave{width:min(460px,calc(100vw - 32px));padding:28px;border:1px solid var(--medi-line,#cfdaca);border-radius:24px;background:var(--medi-paper,#fafaf4);color:var(--medi-forest,#153e35);box-shadow:0 24px 80px #0c211933}
.demo-leave::backdrop{background:#102d2659}
.demo-leave-title{margin:0 0 10px;font-family:"Medi Sans",Inter,Arial,sans-serif;font-size:24px;font-weight:400;line-height:1.25;letter-spacing:-.02em}
.demo-leave p{margin:0 0 22px;font-size:15px;line-height:1.6;color:var(--medi-muted,#526a5f)}
.demo-leave-actions{display:flex;flex-wrap:wrap;gap:10px}
.demo-leave-actions .button{min-height:48px;padding:12px 22px;font-size:14px}

/* ---------- /compare -------------------------------------------------------------------------
   Every /compare section runs edge to edge below 1200px, which put this box's corners on the
   viewport edges at tablet widths. The box keeps a gutter instead: 36px, then 20px on a phone. */
.comparison-experience>#compare-demo.section-shell{width:min(1200px,calc(100% - 72px))}
@media(max-width:600px){.comparison-experience>#compare-demo.section-shell{width:calc(100% - 40px)}}
/* The eyebrow and the heading sit above the resting block so they survive the start click.
   The gap under them is the panel's own padding less the 8px .demo-screen insets its content by,
   so a failure screen, which adds that inset on top of this margin, still opens no band wider
   than the padding. 28px is the design's figure and the cap at desktop widths. */
.demo-panel-compare .compare-demo-head{margin:0 0 clamp(12px,calc(var(--demo-pad) - 8px),28px)}
/* No margin of its own on either side. The block's own margin below is the measured gap, and the
   heading's 20px bottom margin collapsed through it and won below 800px, which left 22.6px
   between the heading and the demo under it against a 20px padding. */
.demo-panel-compare .compare-demo-head h2{margin-top:0;margin-bottom:0}
/* The heading above already spaces the screen off the top of the panel, and the screen's own
   8px inset stacked on that margin opened a band wider than the padding on a phone. */
.demo-panel-compare .demo-screen{padding-top:0}
.demo-panel-compare .demo-rest{grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);gap:56px}
@media(max-width:1099.98px){.demo-panel-compare .demo-rest{grid-template-columns:minmax(0,1fr);gap:32px}}
/* /compare's own body colour is --compare-muted #62766c, which on this panel's #f0f3ea ground
   measures 4.32:1. Inside the demo panel the site's --medi-muted #526a5f applies, as it does for
   the same line on / and /demo, and measures 5.22:1. Specificity, not order: compare.css is one
   class short of this and either stylesheet may load first. */
.demo-panel .compare-demo-copy>p:not(.eyebrow){color:var(--medi-muted,#526a5f)}
/* Below 800px the panel's own padding is 20px, and the site's label gap leaves 23.2px of empty
   between the eyebrow and the heading it labels, which inside this box reads as a band. 18px
   puts it at 19.2px, which is what the same block already measures on a phone. Wider viewports
   are padded 32px or 48px and keep the site's spacing. */
@media(max-width:799.98px){.demo-panel-compare .compare-demo-head .eyebrow{margin-bottom:18px}}

/* ---------- /demo ----------------------------------------------------------------------------- */
.demo-standalone{max-width:1040px;margin:0 auto;padding-block:32px 48px}
.demo-standalone h1{margin:0 0 20px;font-family:"Medi Sans",Inter,Arial,sans-serif;font-size:clamp(30px,4vw,40px);font-weight:400;line-height:1.15;letter-spacing:-.03em;color:var(--medi-forest,#153e35)}
body.demo-page{margin:0;padding-inline:16px;background:var(--medi-paper,#fafaf4)}

@keyframes demo-fade-in{from{opacity:0}to{opacity:1}}
@keyframes demo-fade-out{from{opacity:1}to{opacity:0;visibility:hidden}}
@keyframes demo-rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes demo-pulse{from{opacity:1}to{opacity:.45}}

/* Every motion becomes an opacity change of 150ms or less, and the skeleton holds still. */
@media(prefers-reduced-motion:reduce){
  .demo-panel *,.demo-follow{transition-duration:.15s!important}
  .demo-panel:not([data-demo-state=resting]) .demo-rest,.demo-panel:not([data-demo-state=loading]) .demo-sk,.demo-screen,.demo-film-aside,.demo-frame{animation-duration:.15s!important}
  .demo-sk{animation:none!important}
  .demo-panel[data-demo-state=idle] .demo-mount{animation:demo-fade-in .15s both!important}
  .demo-follow,.demo-follow.is-shown{transform:translate(-50%,0)}
  .voice-orb-body i{animation:none}
  .demo-stage-fx:before,.demo-stage-fx:after,.demo-beam,.demo-stage-fx canvas{transition-duration:.15s!important;transition-delay:0s!important}
  .demo-beam{animation:none}
  .demo-beam:nth-child(1){transform:rotate(-2deg)}
  .demo-beam:nth-child(2){transform:rotate(6deg)}
  .demo-beam:nth-child(3){transform:rotate(-8deg)}
  .demo-panel.is-after-dark :is(.demo-screen,.demo-film-aside,.demo-row){animation-delay:.15s}
}
