/* ============================================================================
   hq-ui.css — foundation layer, loaded AFTER app.css.

   Why a second file rather than 1,100 more lines in the first: everything here is
   cross-cutting. It applies to all 169 pages without any of them being edited, which
   matters because there are ~2,300 inline style attributes in the markup and no
   component layer to change instead. Keeping it separate also means the whole layer
   can be reverted with one <link> tag if something regresses.

   Order inside the file: reset/type, accessibility, responsive, motion, components.
   ========================================================================== */


/* ============================================================================
   1. TYPE & RHYTHM
   ========================================================================== */

/* Fluid, capped. 15px on a phone and 16px on a desktop, without a breakpoint —
   clamp() interpolates between them. Everything sized in rem follows automatically. */
html{font-size:clamp(15px,.92rem + .18vw,16px);-webkit-text-size-adjust:100%}

body{line-height:1.55;text-rendering:optimizeLegibility}

/* Long clinical notes, addresses and expert bios were breaking layouts because nothing
   constrained them. */
p,li,dd{overflow-wrap:break-word}

/* Numbers that change in place — counters, prices, durations — were shifting their own
   width as digits changed, making dashboards twitch. Opt-in via [data-numeric] for table
   cells, since a blanket rule would also hit text columns where it looks wrong. */
.stat .n,.kv .val,.pkg-price-display b,.hq-count,
[data-numeric]{font-variant-numeric:tabular-nums}


/* ============================================================================
   2. ACCESSIBILITY
   This app handles PHI and is sold to corporate buyers, whose procurement asks
   about WCAG. Before this layer: role= appeared 0 times across 169 files, aria-*
   3 times, there was no skip link, and h1/h2/h3 had outline:none.
   ========================================================================== */

/* ---- Focus. A visible, consistent ring on anything reachable by keyboard.
   :focus-visible (not :focus) so a mouse click doesn't leave a ring behind — that
   is the reason people reach for outline:none in the first place. The double ring
   (card-coloured inner, accent outer) stays visible on every background in the
   app, including the dark sidebar and coloured banners. */
/* [tabindex="-1"] is deliberately excluded.
   
   A heading given tabindex="-1" and focused on navigation is a screen-reader convenience —
   it moves the reading position to the top of the new page. It is not something the person
   clicked or tabbed to, so drawing a focus ring around it puts a box around the page title
   for a sighted user who did nothing. That is what was happening on the login heading.
   
   -1 means "focusable by script, not by tab", which is exactly the set that should not get a
   visible ring. Every genuinely tabbable element (tabindex="0" or positive) still does. */
:where(a,button,input,select,textarea,summary,[tabindex]:not([tabindex="-1"]),[role="button"],[role="tab"]):focus-visible{
  outline:none;
  box-shadow:var(--focus-ring);
  border-radius:var(--r-sm);
  position:relative;
  z-index:1;
}
/* Fields already own their focus treatment; keep it and add the ring on top of it. */
.field:focus-visible{box-shadow:0 0 0 4px var(--rose-soft), var(--focus-ring)}

/* Forced-colours (Windows High Contrast) throws away box-shadow, so the ring needs a
   real outline there or focus becomes invisible for the people most relying on it. */
@media (forced-colors:active){
  /* Same exclusion as above: a script-focused heading is not a tab stop. */
  :where(a,button,input,select,textarea,[tabindex]:not([tabindex="-1"])):focus-visible{outline:3px solid Highlight;outline-offset:2px}
}

/* ---- Skip link. Every page put the same ~40-item sidebar before its content, so a
   keyboard or screen-reader user had to pass all of it on every single navigation.
   fixed, not absolute, so it still works if focus is restored after a scroll. */
.skip-link{
  position:fixed;left:50%;top:calc(var(--s-2) * -12);
  transform:translateX(-50%);
  z-index:calc(var(--z-toast) + 1);
  background:var(--accent);color:#fff;
  padding:var(--s-3) var(--s-5);border-radius:0 0 var(--r-md) var(--r-md);
  font-size:14px;font-weight:600;
  box-shadow:var(--e-3);
  transition:top var(--t-base) var(--ease-out);
}
/* Centred rather than tucked into the corner, because on mobile the corner is where the
   hamburger lives and the two would sit on top of each other. */
.skip-link:focus{top:0}

/* ---- Screen-reader-only text, for labels that would be visual clutter but that a
   non-visual user needs (icon-only buttons, table captions, live regions). */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}
.sr-only-focusable:focus,.sr-only-focusable:focus-within{
  position:static;width:auto;height:auto;overflow:visible;clip:auto;clip-path:none;white-space:normal;
}

/* ---- Icon-only controls. Emoji and glyph buttons read as literal characters
   ("black telephone", "heavy multiplication x") without a label. */
button:not([aria-label]):not([title]):empty::after{content:"button"}

/* ---- Colour is never the only signal. Status pills carried meaning purely in hue,
   which excludes ~8% of men. A leading marker gives the same information without it. */
.pill.ok::before{content:"● ";}
.pill.pending::before{content:"◐ ";}
.pill.err::before,.pill.rejected::before{content:"✕ ";}

/* ---- Motion. Was one rule covering .reveal only; the login page's floating blobs,
   the spinning brand dot and every hover transform ran regardless. Vestibular
   disorders are triggered by exactly that kind of large, looping movement. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms!important;
    animation-iteration-count:1!important;
    transition-duration:.001ms!important;
    scroll-behavior:auto!important;
  }
  .login-page::before,.login-page::after,.brand .dot{animation:none!important}
  .reveal,.hq-stagger>*{opacity:1!important;transform:none!important}
}


/* ============================================================================
   3. RESPONSIVE
   The MAUI app reuses these same Razor components, so anything unfixed here ships
   into the phone app too.
   ========================================================================== */

/* ---- Tables. 18 files render a <table>; only 2 wrapped it in something scrollable,
   so 16 pages tore their layout open on a phone. Fixing that per page would mean
   editing markup in all of them; this does it once, for every table that exists now
   or is added later, without any of them being touched.

   display:block on the table is what makes overflow work at all — a <table> ignores
   overflow otherwise. It costs the column-width algorithm, which is why min-width
   forces the table to keep its natural desktop width and scroll rather than crush
   columns into unreadable slivers. */
@media (max-width:900px){
  table{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
  table > tbody,table > thead,table > tfoot{min-width:max-content}
  /* Scrolling has to be discoverable or it may as well not exist — this is the shadow
     that appears only when there is more table to the right. */
  table{
    background:
      linear-gradient(to right,var(--card) 30%,transparent) left/40px 100% no-repeat,
      linear-gradient(to left,var(--card) 30%,transparent) right/40px 100% no-repeat,
      radial-gradient(farthest-side at 0 50%,rgba(var(--shadow-ink),.14),transparent) left/14px 100% no-repeat,
      radial-gradient(farthest-side at 100% 50%,rgba(var(--shadow-ink),.14),transparent) right/14px 100% no-repeat;
    background-attachment:local,local,scroll,scroll;
  }
}

/* ---- Touch targets. Several .btn variants sat at padding:8px 14px — about 32px tall.
   Apple and Google both specify 44px, and sub-44px controls are most of why a web app
   feels "not quite native" on a phone. Applied only on coarse pointers so desktop
   density (which this admin tool depends on) is untouched. */
@media (pointer:coarse){
  .btn,.cta,button:not(.star):not(.mobile-nav-toggle),
  .role-choice-btn,.slotchip,.chip,select,input[type="checkbox"],input[type="radio"]{
    min-height:44px;
  }
  input[type="checkbox"],input[type="radio"]{min-width:24px;width:24px;height:24px}
  .btn,.cta{padding-inline:var(--s-4)}
  /* A tap target can be 44px without the element looking 44px — this keeps small
     inline actions visually compact while still being comfortably tappable. */
  .exp-actions .btn,.sess-actions .btn{display:inline-flex;align-items:center}
  /* iOS zooms the whole page when a focused input is under 16px. */
  input,select,textarea{font-size:max(16px,1em)}
}

/* ---- One breakpoint scale. The stylesheet used 480/600/640/700/800/900/1000 with no
   system behind which was which. New rules use these three; existing ones are left as
   they are rather than risk regressions across 169 pages for tidiness alone.
      sm  640  — phone
      md  900  — tablet / sidebar collapse (matches the existing nav breakpoint)
      lg 1200  — desktop
   ---------------------------------------------------------------------------- */

/* Content width was fixed at 1080px, which wastes half of a modern monitor on the
   data-dense admin tables this app is mostly made of. */
@media (min-width:1400px){
  .wrap{max-width:1200px}
  .wrap-wide{max-width:1500px}
}

/* Horizontal overflow anywhere shows as a page that rubber-bands sideways on a phone —
   easy to ship, hard to notice on a desktop. */
@media (max-width:640px){
  html,body{overflow-x:hidden}
  .wrap,.wrap-wide{padding-left:var(--s-4);padding-right:var(--s-4)}
  .topbar{padding-inline:var(--s-4)}
  .topbar h1{font-size:18px}
  .card{padding:var(--s-4);border-radius:var(--r-md)}
  .grid,.cols,.card-grid,.pkg-grid{grid-template-columns:1fr!important;gap:var(--s-3)}
  .modal-card{border-radius:var(--r-lg) var(--r-lg) 0 0}
}

/* Full-height layouts were using 100vh, which on iOS Safari means "the viewport with
   the address bar hidden" — so the bottom of the sidebar and the login screen sat
   under the browser chrome and could not be reached. dvh is the fix. */
@supports (height:100dvh){
  .hq-side{height:100dvh}
  .login-page{min-height:100dvh}
  .hq-app{min-height:100dvh}
}

/* Devices with a notch or a home indicator. The padding goes on .nav, not on the scroll
   container — padding on .hq-side sits outside the nav's painted area, which reopened the
   same two-colour band at the very bottom of the menu that .hq-side's background fixes. */
@supports (padding:max(0px)){
  .nav{padding-bottom:max(var(--s-5),env(safe-area-inset-bottom))}
  .fab-wrap,.crisis-fab{bottom:max(var(--s-5),env(safe-area-inset-bottom))}
}


/* ============================================================================
   4. MOTION
   What existed was decorative — floating blobs on the login page. What was missing
   was the kind that carries information: what is loading, what just arrived, what
   responded to a tap. That is the difference people read as "well built".
   ========================================================================== */

/* ---- Skeletons. 33 pages showed the word "Loading…". Text-as-loading-state reads as
   unfinished; a shape the size of the content that is coming reads as fast, because the
   layout stops jumping when data lands. */
.sk{
  background:linear-gradient(90deg,var(--line-2) 25%,var(--field) 37%,var(--line-2) 63%);
  background-size:400% 100%;
  animation:sk-shimmer 1.4s var(--ease-in-out) infinite;
  border-radius:var(--r-sm);
}
@keyframes sk-shimmer{from{background-position:100% 50%}to{background-position:0 50%}}
.sk-text{height:1em;margin:.35em 0}
.sk-text.w-40{width:40%}.sk-text.w-60{width:60%}.sk-text.w-80{width:80%}
.sk-title{height:1.5em;width:45%;margin-bottom:var(--s-4)}
.sk-avatar{width:44px;height:44px;border-radius:var(--r-md);flex:0 0 auto}
.sk-card{height:120px;border-radius:var(--r-lg)}
.sk-row{display:flex;gap:var(--s-3);align-items:center;padding:var(--s-3) 0}
.sk-row .sk-text{flex:1}

/* ---- Stagger. Lists arriving all at once read as a flash; arriving in sequence reads
   as being assembled. Capped at 10 because beyond that the last item feels late rather
   than deliberate. */
.hq-stagger>*{animation:hq-rise .38s var(--ease-out) both}
.hq-stagger>*:nth-child(1){animation-delay:.02s}
.hq-stagger>*:nth-child(2){animation-delay:.05s}
.hq-stagger>*:nth-child(3){animation-delay:.08s}
.hq-stagger>*:nth-child(4){animation-delay:.11s}
.hq-stagger>*:nth-child(5){animation-delay:.14s}
.hq-stagger>*:nth-child(6){animation-delay:.17s}
.hq-stagger>*:nth-child(7){animation-delay:.20s}
.hq-stagger>*:nth-child(8){animation-delay:.23s}
.hq-stagger>*:nth-child(9){animation-delay:.26s}
.hq-stagger>*:nth-child(n+10){animation-delay:.28s}
@keyframes hq-rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* ---- Press feedback. Blazor Server round-trips to the server for every click, so
   without a local response the UI feels dead for the length of the network hop —
   the single biggest reason a Blazor Server app can feel sluggish when it isn't. */
@media (prefers-reduced-motion:no-preference){
  .btn,.cta,.chip,.slotchip,.role-choice-btn,.nav a{
    transition:transform var(--t-fast) var(--ease-spring),
               background-color var(--t-fast) linear,
               box-shadow var(--t-base) var(--ease-out);
  }
  .btn:active:not(:disabled),.cta:active:not(:disabled),
  .chip:active,.slotchip:active,.role-choice-btn:active{transform:scale(.97)}
}

/* ---- Busy state. A disabled button during a save tells you nothing about why. */
[aria-busy="true"],.is-busy{position:relative;pointer-events:none;color:transparent!important}
[aria-busy="true"]::after,.is-busy::after{
  content:"";position:absolute;inset:0;margin:auto;
  width:16px;height:16px;border-radius:50%;
  border:2px solid currentColor;border-top-color:transparent;
  color:#fff;animation:hq-spin .6s linear infinite;
}
.btn.ghost[aria-busy="true"]::after,.btn.ghost.is-busy::after{color:var(--ink)}
@keyframes hq-spin{to{transform:rotate(360deg)}}


/* ============================================================================
   5. COMPONENT REFINEMENT
   Surface-level only. Everything below reuses classes the markup already applies,
   so no page needs editing to pick it up.
   ========================================================================== */

/* Cards: a hairline plus a genuine two-layer shadow instead of the flat 1px one.
   Calm-clinical means restraint — this is deliberately close to flat when at rest. */
.card,.stat,.exp-card,.pkg-card,.phead{
  box-shadow:var(--e-1);
  transition:box-shadow var(--t-base) var(--ease-out),border-color var(--t-base) var(--ease-out);
}
@media (hover:hover){.card:hover,.stat:hover{box-shadow:var(--e-2)}}
@media (hover:hover){.exp-card:hover,.pkg-card:hover{box-shadow:var(--e-3);border-color:var(--line)}}

/* Section headings sat at the same weight as body text in several places. */
.card h3{letter-spacing:-.005em;color:var(--ink)}

/* The sidebar's active item was a solid accent block — heavy, and it fought every
   other accent on the page. A rail plus a tinted ground is quieter and still
   unambiguous, and it survives a theme change without a contrast problem. */
.nav a.active{
  background:var(--accent-soft);
  color:var(--accent);
  font-weight:600;
  position:relative;
}
.nav a.active::before{
  content:"";position:absolute;left:0;top:20%;bottom:20%;
  width:3px;border-radius:0 var(--r-pill) var(--r-pill) 0;background:var(--accent);
}
.nav a{transition:background-color var(--t-fast) linear,color var(--t-fast) linear}

/* The sticky header's border was a hard 1px line that read as a box edge. A hairline plus
   a very short ambient shadow reads as a surface floating above the content instead.
   (A scroll-aware border that only appears once content is behind it would be better
   still, but it needs either a JS listener or scroll-driven animations, which Safari and
   Firefox do not ship yet — so this is the version that actually works everywhere.) */
.topbar{
  border-bottom-color:var(--line-2);
  box-shadow:0 1px 0 rgba(var(--shadow-ink),.03), 0 4px 12px -8px rgba(var(--shadow-ink),.10);
}

/* Table legibility lives in section 11, with the rest of the admin list styling — these
   rules were written here first and then again there, which is exactly the duplication this
   whole exercise is meant to remove. .simple-table picks up the same treatment there. */

/* Empty states: there is already an <EmptyState /> component with its own styles in
   app.css (.empty-state-icon, h3, .empty-state-actions). A parallel set was written here
   before that was noticed, which silently overrode its padding and paragraph width while
   adding .ico/b rules for elements the component never renders. Removed — the existing
   one is correct and this file should not be quietly reinterpreting it. */

/* Anything that scrolls inside the page keeps a slim, theme-aware scrollbar rather
   than the OS default, which on Windows is a wide grey slab. */
.hq-side,.msg-contacts,[class*="-scroll"]{scrollbar-width:thin;scrollbar-color:var(--line) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:var(--line);border-radius:var(--r-pill);border:3px solid transparent;background-clip:content-box}
@media (hover:hover){::-webkit-scrollbar-thumb:hover{background:var(--navy-2);background-clip:content-box}}
::-webkit-scrollbar-track{background:transparent}

/* Selection in the app's own accent rather than the browser's default blue. */
::selection{background:var(--accent-soft);color:var(--accent)}

/* Print — invoices, prescriptions and reports are printed from this app, and they were
   printing the sidebar, the floating assistant and the crisis button with them. */
@media print{
  .hq-side,.mobile-nav-toggle,.nav-backdrop,.topbar,.hq-back-bar,
  .fab-wrap,.crisis-fab,.skip-link,[class*="floating"]{display:none!important}
  .hq-app{display:block}
  .wrap,.wrap-wide{max-width:none;padding:0}
  .card{box-shadow:none;border:1px solid #ddd;break-inside:avoid}
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:11px;color:#555}
}


/* ============================================================================
   6. UTILITY LAYER

   The markup carried ~2,300 inline style attributes and no component layer, which
   made "change the corner radius everywhere" a 2,300-place find-and-replace instead
   of one edit. 603 distinct style strings turned out to be 76% expressible as a
   small set of names — spacing, text, flex — so those became classes and the markup
   was migrated onto them. The remaining 24% are genuinely one-off (width:70px,
   object-fit:cover) and stay inline, which is the correct place for them.

   Spacing snaps to the 4pt scale in app.css. Two values in the old markup were off
   it — 10px and 14px — and map UP to 12px and 16px. That is a deliberate 2px shift
   in ~300 places: invisible on its own, and the entire reason to have a scale.

   Deliberately NOT a full utility framework. Every class here earns its place by
   replacing at least ten occurrences; anything rarer stays inline where it is
   easier to read in context.
   ========================================================================== */

/* ---- Spacing ---- */
.m-0{margin:0}
.p-0{padding:0}
.mt-0{margin-top:0}
.mt-1{margin-top:var(--s-1)}
.mt-2{margin-top:var(--s-2)}
.mt-3{margin-top:var(--s-3)}
.mt-4{margin-top:var(--s-4)}
.mt-5{margin-top:var(--s-5)}
.mt-6{margin-top:var(--s-6)}
.mb-1{margin-bottom:var(--s-1)}
.mb-2{margin-bottom:var(--s-2)}
.mb-3{margin-bottom:var(--s-3)}
.mb-4{margin-bottom:var(--s-4)}
.mb-5{margin-bottom:var(--s-5)}
.mb-6{margin-bottom:var(--s-6)}
.my-2{margin-block:var(--s-2)}
.ml-auto{margin-left:auto}

/* ---- Text ----
   Sizes collapse the old set (11/11.5/12/12.5/13/13.5/14px — seven sizes doing the
   work of three) onto a type scale. Nothing in an interface needs a half-pixel
   distinction nobody can perceive. */
.t-xs{font-size:12px}
.t-sm{font-size:13px}
.t-base{font-size:14px}
.t-muted{color:var(--muted)}
.t-body{color:var(--navy-2)}
.t-err{color:var(--err)}
/* Used on the retried count in the email log and defined nowhere, so a non-zero retry count
   rendered in the default colour and read as normal. */
.t-warn{color:var(--amber)}
.t-ok{color:var(--ok)}
.t-accent{color:var(--gold-text)}
.t-center{text-align:center}
.t-right{text-align:right}
.fw-400{font-weight:400}
.fw-600{font-weight:600}
.fw-700{font-weight:700}
.lh-loose{line-height:1.7}
.mono{font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;font-size:.92em}

/* ---- Layout ---- */
.flex{display:flex}
.flex-wrap{flex-wrap:wrap}     /* NOT .wrap — that name is already the page container in app.css,
                                  which carries max-width and page padding. Anything given .wrap
                                  as a flex utility would have silently inherited both. */
.items-center{align-items:center}
.items-start{align-items:flex-start}
.between{justify-content:space-between}
.grow{flex:1}
.gap-2{gap:var(--s-2)}
.gap-3{gap:var(--s-3)}
.gap-4{gap:var(--s-4)}
.w-full{width:100%}
.pointer{cursor:pointer}
.no-border{border:none}

/* ---- State ----
   background + border-color in the accent tint appeared as a pair 31 times and always
   meant the same thing: this is the option you picked. */
.is-picked{background:var(--gold-soft);border-color:var(--gold-soft)}

/* ---- Composite ----
   These are the shapes that repeated most across pages, named for what they are rather
   than what they are made of. */
.hint-text{color:var(--muted);font-size:13px;margin:0;line-height:1.6}
.row-between{display:flex;justify-content:space-between;align-items:center;gap:var(--s-3)}
.actions{display:flex;gap:var(--s-3);margin-top:var(--s-4);flex-wrap:wrap}


/* ============================================================================
   7. TOUCH & WEBVIEW HARDENING

   The MAUI app renders these same pages in a WebView, so anything left unhandled
   here does not stay a web problem — it ships as an app problem, where users judge
   it against native apps rather than against websites.
   ========================================================================== */

/* ---- Tap highlight. Android and older iOS WebViews paint a translucent grey or blue
   rectangle over anything tapped, sized to the element box rather than its visual
   shape — so a pill-shaped button flashes as a square. The :active states defined
   earlier are the intended feedback; this removes the one the platform adds. */
a,button,[role="button"],.chip,.slotchip,.tag,.pill,label,summary{
  -webkit-tap-highlight-color:transparent;
}

/* ---- Text selection. Long-pressing a button or a nav item pops the selection
   handles and a copy menu, which never makes sense on a control. Content keeps
   selection — people copy phone numbers, invoice IDs and prescription text. */
.nav,.topbar,.btn,.cta,.pill,.badge,.chip,.tag,.mobile-nav-toggle,
.wiz-steps,.stat .l,.skip-link{
  -webkit-user-select:none;user-select:none;
}
p,td,pre,code,.kv .val,.doc,.hint-text,input,textarea,[data-selectable]{
  -webkit-user-select:text;user-select:text;
}

/* ---- Scroll chaining. Scrolling to the end of a modal, the notification dropdown or
   the sidebar handed the gesture to the page behind it — so the page scrolled while a
   modal was open, and in a WebView the whole app view bounced or triggered
   pull-to-refresh mid-interaction. */
.modal-card,.notif-dropdown,.fab-log,.chat-log,.hq-side,.med-results,
.rx-history,.transcript-log,.checkbox-list,.terms-scroll{
  overscroll-behavior:contain;
}
body{overscroll-behavior-y:none}

/* ---- Momentum scrolling for older iOS WebViews. */
.hq-side,.modal-card,.chat-log,.fab-log,table{-webkit-overflow-scrolling:touch}

/* ---- Double-tap zoom. A 300ms delay before click fires on anything the browser thinks
   might be double-tapped — the classic "the app feels laggy" that isn't the network. */
a,button,input,select,textarea,[role="button"],.chip,.slotchip{touch-action:manipulation}

/* ---- backdrop-filter. Cheap on iOS, expensive on Android WebView, where a blurred
   sticky header repaints on every scroll frame and drops the whole list to ~30fps.
   The header stays legible with a solid background instead. */
@media (max-width:900px){
  .topbar{backdrop-filter:none;-webkit-backdrop-filter:none;background:var(--card)}
}

/* ---- The on-screen keyboard. A fixed-position action bar sits under the keyboard on
   Android, which puts the submit button somewhere unreachable. Anchoring to the small
   viewport keeps it above. */
@supports (height:100svh){
  @media (max-width:900px){
    .modal-overlay{height:100svh}
    .fab-panel{max-height:70svh}
  }
}

/* ---- Pointer-coarse hit area for close buttons, which are consistently the smallest
   and most-missed control in any modal. */
@media (pointer:coarse){
  .modal-card > button:first-child{min-width:44px;min-height:44px}
}

/* ---- Landscape phones. The sidebar drawer at 270px eats most of a 640px-tall
   landscape viewport; and stat grids at four columns become unreadable slivers. */
@media (max-height:480px) and (orientation:landscape){
  .hq-side{width:min(270px,60vw)}
  .modal-card{max-height:92vh;padding:var(--s-4)}
  .stats{grid-template-columns:repeat(2,1fr)}
}

/* ---- Fonts. The app requests Inter from Google's CDN, which in a mobile app means a
   network round-trip to a third party before text can paint with the right face — and on
   a slow or absent connection, either a long swap or no swap at all. system-ui is the
   fallback so the first paint is always correct-ish, and for the packaged app the font
   should be bundled locally rather than fetched. Documented here because the change
   belongs in the MAUI project, not in this stylesheet. */


/* ============================================================================
   8. SAFETY-CRITICAL UI
   Crisis support is the one control in this product that must never be the thing
   that failed. It gets rules that no theme, page or animation can override.
   ========================================================================== */

.crisis-fab,.crisis-link{
  /* Never dimmed by a reduced-motion reset, a busy state or a disabled parent. */
  opacity:1!important;
  pointer-events:auto!important;
}
@media (pointer:coarse){
  .crisis-fab{min-width:52px;min-height:52px}   /* above the 44px floor, deliberately */
}


/* ---- Boot screen -----------------------------------------------------------------
   Shown while the stored session is restored, before any page renders. Deliberately
   almost nothing: a page-coloured field and one quiet mark. A spinner here would be a
   promise about duration that this screen cannot make — it is usually gone in under
   200ms, and a spinner that flashes for 200ms reads as a glitch. */
.boot-screen{
  min-height:100vh;display:flex;align-items:center;justify-content:center;
  background:var(--page);
}
@supports (height:100dvh){ .boot-screen{min-height:100dvh} }
.boot-mark{
  width:34px;height:34px;border-radius:var(--r-md);
  background:var(--accent);
  animation:boot-pulse 1.6s var(--ease-in-out) infinite;
}
@keyframes boot-pulse{0%,100%{opacity:.35;transform:scale(.94)}50%{opacity:1;transform:scale(1)}}
@media (prefers-reduced-motion:reduce){ .boot-mark{animation:none;opacity:.8} }

/* Skeletons need a container so the wrapper does not collapse or inherit a flex context
   that squashes the placeholder rows. */
.sk-wrap{display:block;width:100%}


/* ============================================================================
   9. AI STATES

   The AI companion, the health summary and the nudges all produce text after a
   wait that is genuinely variable — a local Ollama model, Claude over the network,
   or an instant rule-based fallback. A generic spinner says none of that. These
   states say "something is being worked out", which is what makes a wait feel
   deliberate instead of broken.

   Restraint on purpose: this is a clinical product, and a reply about someone's
   mental health should not arrive with a flourish. Everything here is quiet, and
   every piece of it stops under prefers-reduced-motion.
   ========================================================================== */

/* ---- Thinking. Three dots that breathe rather than bounce — bounce reads playful,
   which is wrong next to a crisis-support button. */
.ai-thinking{display:inline-flex;align-items:center;gap:5px;padding:12px 16px}
.ai-thinking i{
  width:6px;height:6px;border-radius:50%;background:var(--muted);
  animation:ai-breathe 1.4s var(--ease-in-out) infinite;
}
.ai-thinking i:nth-child(2){animation-delay:.18s}
.ai-thinking i:nth-child(3){animation-delay:.36s}
@keyframes ai-breathe{0%,100%{opacity:.25;transform:scale(.8)}45%{opacity:1;transform:scale(1)}}

/* ---- Streaming text. A cursor that holds while text arrives, so a pause in the
   stream reads as the model still working rather than as the answer being finished. */
.ai-streaming::after{
  content:"";display:inline-block;width:2px;height:1em;margin-left:2px;
  background:var(--accent);vertical-align:-.15em;
  animation:ai-caret 1s steps(1) infinite;
}
@keyframes ai-caret{0%,49%{opacity:1}50%,100%{opacity:0}}

/* ---- Arrival. Each line fades up as it lands instead of the block appearing at once,
   which is what makes a generated answer feel composed rather than pasted. */
.ai-line{animation:ai-arrive .34s var(--ease-out) both}
@keyframes ai-arrive{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}

/* ---- Working surface. While a result is being generated the panel gets a slow sheen
   along its accent edge — visible enough to say "still going", quiet enough to ignore. */
.ai-result.is-generating{position:relative;overflow:hidden}
.ai-result.is-generating::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:4px;
  background:linear-gradient(180deg,transparent,var(--accent),transparent);
  animation:ai-sweep 1.8s var(--ease-in-out) infinite;
}
@keyframes ai-sweep{0%{transform:translateY(-100%)}100%{transform:translateY(100%)}}

/* ---- Confidence. A generated suggestion is not a clinical judgement, and the interface
   should not present the two identically. */
.ai-tag{
  display:inline-flex;align-items:center;gap:5px;
  font-size:11px;font-weight:600;letter-spacing:.02em;
  color:var(--muted);background:var(--field);
  border:1px solid var(--line);border-radius:var(--r-pill);
  padding:3px 9px;
}
.ai-tag::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--accent)}

/* Reduced motion: the states stay, the movement goes. Someone who cannot tolerate
   animation still needs to know the model is working. */
@media (prefers-reduced-motion:reduce){
  .ai-thinking i{animation:none;opacity:.55}
  .ai-streaming::after,.ai-result.is-generating::before{animation:none}
  .ai-line{animation:none}
}


/* ============================================================================
   10. DASHBOARD HIERARCHY

   The patient dashboard was sixteen sections of equal weight, ordered by the sequence
   in which features had shipped. "Upcoming appointments" — the reason someone opens the
   page — sat thirteenth, below a mood tracker and a category browser, while four
   different call-to-action buttons competed above the fold.

   Reordered by how fast a thing decays: the session ahead, then anything that expires,
   then the daily habit, then evergreen browsing. These styles carry that hierarchy. The
   sections themselves stayed; only their weight and order changed.
   ========================================================================== */

/* ---- NEXT: the one card the page exists for. Everything else on the page is a plain
   surface, so this earns its prominence from tint and scale rather than from more
   shadow — a heavier shadow would just start an arms race with the cards below it. */
.dash-next{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);
  flex-wrap:wrap;
  background:var(--accent-soft);
  border:1px solid var(--line);
  border-color:color-mix(in srgb,var(--accent) 18%,transparent);
  border-radius:var(--r-lg);
  padding:var(--s-5) var(--s-6);
  margin-bottom:var(--s-5);
  position:relative;overflow:hidden;
}
/* A rail rather than a full tint edge: reads as "this one" without turning the card into
   a banner, which is what the three stacked banners already were. */
.dash-next::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent);
}
.dash-next-main{min-width:0;flex:1 1 260px}
.dash-next-eyebrow{
  display:block;font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--accent);margin-bottom:var(--s-1);
}
.dash-next-title{font-size:19px;font-weight:700;color:var(--ink);margin:0;letter-spacing:-.01em}
.dash-next-meta{font-size:13.5px;color:var(--navy-2);margin:var(--s-1) 0 0}
.dash-next-actions{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap}
.dash-countdown{
  font-size:13.5px;font-weight:600;color:var(--navy-2);
  padding:var(--s-2) var(--s-3);
  background:var(--card);border-radius:var(--r-pill);
}

/* Live state. The only looping animation on the page, and only when a session is
   genuinely joinable — a pulse that runs all day teaches people to ignore it. */
.dash-next.is-live{
  background:var(--ok-soft);
  background:color-mix(in srgb,var(--ok) 10%,var(--card));
  border-color:var(--ok);
  border-color:color-mix(in srgb,var(--ok) 32%,transparent);
}
.dash-next.is-live::before{background:var(--ok)}
.dash-next.is-live .dash-next-eyebrow{color:var(--ok)}
@media (prefers-reduced-motion:no-preference){
  .dash-join{animation:dash-live 2.4s var(--ease-in-out) infinite}
}
@keyframes dash-live{
  0%,100%{box-shadow:0 0 0 0 var(--accent-soft);box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 40%,transparent)}
  50%{box-shadow:0 0 0 7px transparent}
}

/* Nothing booked. Same footprint, so the page does not reflow depending on whether the
   person has a session — and the space asks for the first booking instead of sitting
   empty, which is the most valuable thing an empty dashboard can do. */
.dash-next-empty{
  background:var(--card);
  border:1px dashed var(--line);
}
.dash-next-empty::before{display:none}
.dash-next-empty .dash-next-eyebrow{color:var(--muted)}

/* ---- ATTENTION: open loops, grouped. Three separate full-width banners each shouted at
   the same volume as the page; one list of rows reads as a short to-do and stops fighting
   the card above it. */
.dash-attention{
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  margin-bottom:var(--s-6);overflow:hidden;
}
.dash-todo{
  display:flex;align-items:center;gap:var(--s-3);
  padding:var(--s-4) var(--s-5);
  border-bottom:1px solid var(--line-2);
}
.dash-todo:last-child{border-bottom:0}
.dash-todo-ico{
  width:30px;height:30px;flex:0 0 auto;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--gold-soft);color:var(--gold-text);font-size:13px;
}
.dash-todo-body{flex:1;min-width:0}
.dash-todo-body b{display:block;font-size:14px;color:var(--ink);font-weight:600}
.dash-todo-body small{display:block;color:var(--muted);font-size:12.5px;margin-top:2px}

/* ---- Section rhythm. Headings were only used in two places, so the page read as one
   undifferentiated scroll. Consistent spacing above each is what turns a stack into
   sections a person can skim past. */
.dash-section{margin:var(--s-10) 0 var(--s-4)}
.dash-section:first-of-type{margin-top:var(--s-8)}

/* ---- Quick actions and activity tiles became real <button>s rather than clickable
   <div>s. A div with onclick is invisible to keyboard and screen-reader users — it cannot
   be tabbed to and is not announced as a control — so these were unreachable for anyone
   not using a mouse. Resetting the button defaults keeps the existing look. */
.qa,.activity-tile{
  font:inherit;text-align:left;width:100%;
  border-width:1px;border-style:solid;
  -webkit-appearance:none;appearance:none;
}
.qa{border-color:var(--line);width:auto}
.activity-tile{border-color:transparent}

@media (max-width:640px){
  .dash-next{padding:var(--s-4);gap:var(--s-4)}
  .dash-next-actions{width:100%}
  .dash-next-actions .btn,.dash-join{flex:1;text-align:center}
  .dash-todo{padding:var(--s-3) var(--s-4);flex-wrap:wrap}
  .dash-todo .btn{margin-left:auto}
  .quick-actions .qa{flex:1 1 calc(50% - var(--s-3))}
}


/* ---- Daily check-in ---------------------------------------------------------------
   The dashboard used to carry two mood widgets; this is the merged one. Styles here cover
   the states the old pair never had between them: a logged state that still offers detail,
   and the optional slider panel that appears only if asked for. */
.qmb-head{margin-bottom:var(--s-4)}
.qmb-done-row{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);flex-wrap:wrap}
.qmb-more{font-size:12.5px;padding:var(--s-2) var(--s-3)}
.qmb-detail{
  margin-top:var(--s-4);padding-top:var(--s-4);
  border-top:1px solid var(--line-2);
  animation:hq-rise .3s var(--ease-out) both;
}
.qmb-trend{margin-top:var(--s-5);padding-top:var(--s-4);border-top:1px solid var(--line-2)}

/* A slider row has to stay usable one-handed on a phone, where the old three-column
   layout squeezed the track to about 90px. */
@media (max-width:560px){
  .mood-row{display:grid;grid-template-columns:1fr auto;gap:var(--s-2) var(--s-3);align-items:center}
  .mood-row input[type="range"]{grid-column:1/-1;width:100%}
}
@media (pointer:coarse){
  .mood-row input[type="range"]{height:32px}
}


/* ---- Session rows: past / now -----------------------------------------------------
   Every row in "Today's schedule" looked identical whether it had finished an hour ago or
   started in five minutes, so the list could only be read with a clock in hand. Finished
   sessions stay visible — a clinician checks who they have already seen — but recede. */
.srow.is-past{opacity:.5}
.srow.is-past .stag{background:var(--field);color:var(--muted)}

/* The one in progress. A left rail rather than a fill, so the row is marked without the
   list turning into a block of colour. */
.srow.is-now{
  position:relative;
  background:var(--ok-soft);
  background:color-mix(in srgb,var(--ok) 8%,transparent);
  margin-inline:calc(var(--s-4) * -1);
  padding-inline:var(--s-4);
  border-radius:var(--r-sm);
}
.srow.is-now::before{
  content:"";position:absolute;left:0;top:6px;bottom:6px;width:3px;
  border-radius:var(--r-pill);background:var(--ok);
}
.srow.is-now .time{color:var(--ok)}

/* .join became an <a> so it can actually navigate — it was a <button> with no handler, so
   clicking Join on your own session did nothing at all. */
a.join{display:inline-flex;align-items:center;justify-content:center;text-decoration:none}
@media (pointer:coarse){ a.join,.srow .join{min-height:44px} }


/* ---- Admin action queue ------------------------------------------------------------
   The admin dashboard led with five totals and four trend charts and never said what was
   waiting — "Pending doctors" was a button with no number on it, so nothing pending and
   forty pending looked the same. These cards are the work; the totals below them are the
   monthly review. */
.admin-queue{margin-bottom:var(--s-6)}
.admin-queue-head{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--s-3);margin-bottom:var(--s-4);flex-wrap:wrap;
}
.admin-queue-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:var(--s-3);
}
.admin-queue-item{
  display:flex;flex-direction:column;gap:2px;
  background:var(--card);
  border:1px solid var(--line);
  border-left:3px solid var(--accent);
  border-radius:var(--r-md);
  padding:var(--s-4) var(--s-5);
  text-decoration:none;
  transition:box-shadow var(--t-base) var(--ease-out),border-color var(--t-base) var(--ease-out);
}
@media (hover:hover){
  .admin-queue-item:hover{box-shadow:var(--e-2);border-color:var(--accent)}
}
.admin-queue-item .n{font-size:26px;font-weight:800;color:var(--ink);line-height:1.1}
.admin-queue-item .l{font-size:13px;font-weight:600;color:var(--ink)}
/* The reason it matters, not just the count. A number with no consequence attached gets
   triaged by whichever is largest rather than by what actually costs something. */
.admin-queue-item .why{font-size:11.5px;color:var(--muted);margin-top:var(--s-1)}

/* Erasures carry a statutory deadline, so they are the one queue allowed to look different
   from the others. Not red-alert styling — nothing here is an emergency — just first and
   unmistakably its own thing. */
.admin-queue-item.is-urgent{border-left-color:var(--maroon)}
.admin-queue-item.is-urgent .n{color:var(--maroon)}
@media (hover:hover){
  .admin-queue-item.is-urgent:hover{border-color:var(--maroon)}
}

/* An explicitly empty queue. If the section simply disappeared, a clear day would be
   indistinguishable from a feature that had not loaded. */
.admin-queue.is-clear{
  background:var(--ok-soft);border-radius:var(--r-md);
  padding:var(--s-4) var(--s-5);
}
.admin-queue-clear{color:var(--ok);font-weight:600;font-size:14px}

.admin-inline-n{font-size:20px}
.admin-role-row{padding-block:var(--s-2)}


/* ---- HR: department wellbeing basis ------------------------------------------------
   These scores are employees' mental-health data shown to their employer, and the number
   was previously presented bare. A confident-looking 68 built from two check-ins reads
   identically to one built from two hundred, which is the failure mode that matters most
   on this page. The basis line sits directly under each bar, and rows drawn from thin data
   are dimmed rather than hidden — withholding them entirely would leave HR wondering
   whether a department was missing or simply quiet. */
.dept-row.is-thin{opacity:.62}
.dept-basis{
  font-size:11.5px;color:var(--muted);
  margin:calc(var(--s-1) * -1) 0 var(--s-4);
  padding-left:2px;
}
.dept-row + .dept-basis{margin-top:2px}

/* ---- Two classes the HR dashboard has always used and that were never defined anywhere.
   .list-row rendered as an unstyled stack of divs and .stat-row left its two stats
   unaligned — pre-existing, and easy to miss because nothing errors when a class simply
   does not exist. */
.list-row{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);
  padding:var(--s-3) 0;border-bottom:1px solid var(--line-2);
}
.list-row:last-of-type{border-bottom:0}
.list-row .nm b{display:block;font-size:14px;color:var(--ink)}
.list-row .nm small{display:block;color:var(--muted);font-size:12.5px;margin-top:2px}
.stat-row{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s-3)}


/* ============================================================================
   11. ADMIN LIST PAGES

   Fifteen tables across the admin and HR sections all use .report-table, so this is
   the one place that lifts every list at once.
   ========================================================================== */

/* Headers stay put on long lists. An admin scrolling 200 users lost the column names
   after the first screen and had to scroll back up to remember which column was which. */
.report-table thead th,.simple-table thead th{
  position:sticky;top:0;z-index:2;
  background:var(--field);
}

/* Numeric columns right-align and use tabular figures, so amounts and counts line up on
   the decimal instead of ragging. Opt-in per column via <th data-numeric>. */
.report-table th[data-numeric],.report-table td[data-numeric]{
  text-align:right;font-variant-numeric:tabular-nums;
}

/* Row separation without hover. The zebra was hover-only, which meant a static screenshot
   of an admin table — or anyone using touch — got an undifferentiated grid. */
.report-table tbody tr:nth-child(even) td,
.simple-table tbody tr:nth-child(even) td{background:var(--line-2)}
@media (hover:hover){
  .report-table tbody tr:hover td{background:var(--accent-soft)}
}

/* Actions column: keep buttons on one line and hard against the right edge, where a
   list's actions belong. */
.report-table td.actions{white-space:nowrap;text-align:right}
.report-table td.actions .btn{padding:5px 10px;font-size:12px}

/* Long free-text cells (emails, notes, addresses) were the reason tables sprawled — one
   long value stretched its column and pushed everything else off screen. */
.report-table td.truncate{max-width:22ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* On narrow screens the header cells are the ones that must not wrap; body cells still may,
   which keeps the scroll distance proportional to the data rather than to the longest
   sentence in it. */
@media (max-width:900px){
  .report-table th{white-space:nowrap}
}

/* ---- "No results" is not "nothing here" ------------------------------------------
   Both rendered as the same empty table. They mean opposite things: one says your filter
   is too narrow, the other says the feature has no data yet — and the fix for each is the
   opposite of the fix for the other. */
.list-status{
  text-align:center;padding:var(--s-10) var(--s-5);color:var(--muted);
}
.list-status b{display:block;color:var(--ink);font-size:15px;margin-bottom:var(--s-1)}
.list-status p{margin:0 auto;max-width:40ch;font-size:13.5px;line-height:1.6}
.list-status .btn{margin-top:var(--s-4)}

/* ---- Result header ---------------------------------------------------------------- */
.list-head{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--s-3);flex-wrap:wrap;margin-bottom:var(--s-3);
}
.list-head .count{font-size:13px;color:var(--muted);font-variant-numeric:tabular-nums}


/* ---- Toast variants ---------------------------------------------------------------
   One surface now carries both server-pushed notifications and the confirmation a page
   raises after an action. Colour distinguishes them, but never alone — the dot changes
   shape too, because "did my save work?" answered only in green is not answered for
   everyone. */
.toast-success{background:var(--ok);color:#fff}
.toast-success .dotlive{background:#fff;border-radius:0;width:9px;height:9px;
  clip-path:polygon(14% 44%,0 58%,40% 100%,100% 16%,86% 2%,38% 70%)}
.toast-error{background:var(--maroon);color:#fff}
.toast-error .dotlive{background:#fff;border-radius:0;width:9px;height:9px;
  clip-path:polygon(20% 0,0 20%,30% 50%,0 80%,20% 100%,50% 70%,80% 100%,100% 80%,70% 50%,100% 20%,80% 0,50% 30%)}
.toast-info{background:var(--navy)}

/* On a phone the toast belongs at the top edge, full width — the desktop corner position
   puts it under the thumb and over the content it is describing. */
@media (max-width:640px){
  .live-toasts{top:0;left:0;right:0;max-width:none;padding:var(--s-2)}
  .toast{border-radius:var(--r-md)}
}
@supports (padding:max(0px)){
  @media (max-width:640px){ .live-toasts{padding-top:max(var(--s-2),env(safe-area-inset-top))} }
}


/* ---- Tooltips on a phone ------------------------------------------------------------
   The bubble is centred on its trigger, which runs off the screen when the trigger sits
   near an edge — and on a 360px viewport most things are near an edge. Constrained to the
   viewport rather than to the trigger. */
@media (max-width:640px){
  .hq-tip::after{max-width:min(230px,calc(100vw - 32px))}
}
/* A tapped tooltip needs a way to close. Focus does that on its own — tapping anywhere else
   blurs the element — but the trigger must be visibly tappable first, or nobody discovers
   that the little ⓘ does anything at all. */
@media (pointer:coarse){
  .hq-tip{min-height:32px;display:inline-flex;align-items:center}
  .hq-tip-icon{width:20px;height:20px;font-size:12px}
}


/* ============================================================================
   12. iOS / WebKit

   Android and iOS fail differently. These are the ones specific to WebKit, and the
   one that matters most is not here at all — it is `viewport-fit=cover` in the
   viewport meta tag, without which every env(safe-area-inset-*) rule above silently
   resolves to zero on every iPhone.
   ========================================================================== */

/* ---- Rubber-band scrolling: deliberately NOT "fixed".
   overscroll-behavior is honoured by Chrome and Firefox but ignored by iOS Safari, so the
   scroll-chaining rule earlier does nothing there. The usual workaround is to stop the
   document scrolling and let an inner container own it — html,body{overflow:hidden} plus
   .hq-main{overflow-y:auto}.

   That was written here and then removed, because the fifteen pages on LoginLayout — login,
   every signup, guest call join, the public blog — have no .hq-main at all. On those, the
   body would stop scrolling and nothing would take over: a signup form that simply cannot be
   scrolled on an iPhone. Trading a cosmetic bounce for an unusable signup page is not a
   trade worth making, and doing it properly needs a JS scroll lock that knows what is open.

   The bounce itself is native iOS behaviour that users expect, so it stays. */

/* ---- Long-press. Holding a link or button on iOS pops the share/preview sheet, which on a
   control is never what was wanted. Content keeps it — people long-press a phone number to
   call it. */
a,button,[role="button"],.chip,.slotchip,.qa,.activity-tile{-webkit-touch-callout:none}
p,td,.kv .val,[data-selectable]{-webkit-touch-callout:default}

/* ---- Form controls. iOS applies its own rounded, shaded chrome to text inputs and ignores
   their background-color until appearance is reset — which is why a themed field can look
   right everywhere and grey on an iPhone.

   Scoped to text-like inputs on purpose. A blanket `input { appearance: none }` would strip
   checkboxes and radios down to invisible empty boxes and delete the track and thumb from
   every range slider — including the mood sliders, which would become an unusable blank
   strip. The type list is explicit for that reason. */
.field,
input[type="text"],input[type="email"],input[type="tel"],input[type="number"],
input[type="password"],input[type="search"],input[type="url"],
textarea{
  -webkit-appearance:none;appearance:none;
}
/* Checkboxes, radios, ranges, dates and file pickers keep their native rendering — on iOS
   the native control is both better and the one people recognise. */
input[type="checkbox"],input[type="radio"],input[type="range"],
input[type="date"],input[type="time"],input[type="datetime-local"],input[type="file"]{
  -webkit-appearance:auto;appearance:auto;
}
/* iOS collapses an empty date field to zero height inside a flex row. */
input[type="date"],input[type="time"],input[type="datetime-local"]{min-height:1.4em}

/* ---- Autofill. Safari paints a hard yellow over autofilled fields, ignoring the theme —
   glaring in dark mode and unreadable when the text colour is light. The inset shadow trick
   is the only way to override it. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
select:-webkit-autofill{
  -webkit-box-shadow:0 0 0 1000px var(--field) inset;
  -webkit-text-fill-color:var(--ink);
  caret-color:var(--ink);
  transition:background-color 9999s ease-in-out 0s;
}

/* ---- position: sticky needs the old prefix on iOS versions before 13. Harmless everywhere
   else, since the unprefixed declaration follows and wins. */
.topbar,.report-table thead th,.simple-table thead th,.xd-bar{
  position:-webkit-sticky;position:sticky;
}

/* ---- The on-screen keyboard. iOS shrinks the visual viewport but leaves fixed elements
   positioned against the layout viewport, so a fixed footer or FAB ends up underneath the
   keyboard. dvh follows the visual viewport and keeps them reachable. */
@supports (height:100dvh){
  .modal-overlay{max-height:100dvh}
  .fab-panel{max-height:min(70dvh,600px)}
}

/* ---- Momentum scrolling for every scrollable region, not just the few listed earlier. */
[class*="-scroll"],.modal-card,.chat-log,.fab-log,.msg-contacts{-webkit-overflow-scrolling:touch}

/* ---- Text size. iOS enlarges text in landscape unless told not to; the fluid scale in
   section 1 already handles sizing, and this stops Safari overriding it. */
body{-webkit-text-size-adjust:100%;text-size-adjust:100%}


/* ---- Chat attachments --------------------------------------------------------------
   A client sending a lab report to their therapist, or an expert sending back a care plan.
   The chip shows what the file is before it is opened, which matters here: the person may
   be on a train, or sharing their screen in a session. */
.chat-attach{
  display:flex;align-items:center;gap:var(--s-2);
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s-2) var(--s-3);
  max-width:100%;
}
.chat-row.me .chat-attach{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.28)}
.chat-attach-ico{font-size:17px;flex:0 0 auto}
.chat-attach-meta{min-width:0;flex:1;display:flex;flex-direction:column;line-height:1.3}
.chat-attach-meta b{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chat-attach-meta small{font-size:11px;opacity:.75;font-variant-numeric:tabular-nums}
.chat-attach .btn{padding:5px 10px;font-size:12px;flex:0 0 auto}
.chat-attach-img{
  display:block;max-width:100%;max-height:340px;
  border-radius:var(--r-md);object-fit:contain;background:var(--field);
}

/* Selected-but-not-yet-sent, above the input. Shown before sending so a wrong file can be
   removed rather than recalled. */
.chat-pending{
  display:flex;align-items:center;gap:var(--s-2);
  background:var(--accent-soft);border-radius:var(--r-md);
  padding:var(--s-2) var(--s-3);margin-bottom:var(--s-2);
}

/* The attach control. The real <input type="file"> stays in the DOM and focusable — hiding
   it with display:none would take it out of the tab order and make attaching impossible
   without a mouse — so it is made invisible but still reachable, with the label as its
   visible surface. */
.chat-attach-btn{
  position:relative;overflow:hidden;
  display:inline-flex;align-items:center;justify-content:center;
  flex:0 0 auto;width:42px;height:42px;
  border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--card);cursor:pointer;font-size:17px;
}
.chat-attach-btn input[type="file"]{
  position:absolute;inset:0;opacity:0;cursor:pointer;
  width:100%;height:100%;
}
.chat-attach-btn:focus-within{box-shadow:var(--focus-ring)}
@media (hover:hover){ .chat-attach-btn:hover{border-color:var(--accent)} }
@media (pointer:coarse){ .chat-attach-btn{width:44px;height:44px} }


/* ---- Call side panel ---------------------------------------------------------------
   Chat, transcript and (for the expert) notes share one column beside the video. Tabs
   rather than three stacked panes, because on the width left over next to a video feed
   three panes each get too little room to be usable. */
.call-side{display:flex;flex-direction:column;min-height:0}
.call-tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin-bottom:var(--s-3)}
.call-tab{
  flex:1;padding:var(--s-2) var(--s-3);
  background:none;border:none;border-bottom:2px solid transparent;
  font:inherit;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
}
.call-tab.active{color:var(--accent);border-bottom-color:var(--accent)}
.call-tab-dot{
  min-width:17px;height:17px;padding:0 4px;border-radius:var(--r-pill);
  background:var(--accent);color:var(--on-accent);
  font-size:10.5px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;
}
.call-panel{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch}
.call-notes{display:flex;flex-direction:column}
.call-notes-box{flex:1;min-height:180px;resize:vertical;line-height:1.6}

/* On a phone the video takes the width and the panel sits under it — side by side would
   leave both too narrow to use. */
@media (max-width:900px){
  .call-side{max-height:52vh}
  .call-tab{font-size:12.5px}
}


/* ============================================================================
   13. BRAND THEME BEHAVIOUR

   The tokens live in app.css; these are the rules that use the ones the old palette
   had no equivalent for — links and the WhatsApp button — plus the type assignment.
   ========================================================================== */

/* ---- Type. Marcellus carries the headings, Montserrat everything else. Applied here
   rather than scattered, so changing the pairing is one edit. */
/* Montserrat Medium is the default body weight, not Regular. At 15px Regular reads thin
   against a serif heading; Medium holds its own beside Merriweather without looking bold. */
body{font-family:var(--sans);font-weight:500}
h1,h2,h3,h4,h5,h6,
.hello,.section-h,.dash-next-title,.qmb-title{
  font-family:var(--display);
  color:var(--heading,var(--ink));
  /* 600, not 400. Merriweather ships four real weights, so a heading can be genuinely
     semibold instead of relying on the browser to fake one — which is what the previous
     single-weight face forced and why headings looked slightly smeared. */
  font-weight:600;
  letter-spacing:-.005em;
}
/* Merriweather is a large-x-height serif and reads bigger than the sans around it at the
   same px. Pulled back slightly rather than leaving every heading a size too big. */
h1,.hello{font-size:clamp(22px,1.4rem + .4vw,26px)}
/* Deep headings sit close to body text, and at 600 in a serif they can crowd the line
   above; the smaller ones step back to 500. */
h4,h5,h6{font-weight:500}

/* ---- Links. The one place the brand orange had to move: #E8673A is 3.03:1 on the cream
   background, and link text needs 4.5. Same hue, darker, and still obviously the brand. */
a:not(.btn):not(.cta):not(.qa):not(.activity-tile):not(.chip):not(.share-btn):not(.nav a){color:var(--link)}
@media (hover:hover){
  a:not(.btn):not(.cta):not(.qa):not(.activity-tile):not(.chip):not(.share-btn):hover{color:var(--link-hover)}
}
/* Colour is never the only signal for a link inside a paragraph — someone who cannot
   distinguish orange from the body text around it has nothing else to go on. */
p a,li a,.hint-text a,td a{text-decoration:underline;text-underline-offset:2px}

/* ---- WhatsApp. Their green is fixed by their brand; white on it is 1.98:1, which is why
   the label here is dark. */
.btn-whatsapp,.whatsapp-btn{
  background:var(--whatsapp);
  color:#262626;
  border:none;
  font-weight:600;
}
@media (hover:hover){ .btn-whatsapp:hover,.whatsapp-btn:hover{filter:brightness(1.06)} }

/* ---- Hover brightens rather than darkens, because the label is dark. Darkening would
   move contrast the wrong way; this lifts 5.33 to 6.58 and still reads as a response. */
/* Hover darkens now, not brightens. The earlier rule lightened because the label was dark
   and a lighter fill improved its contrast; with a white label the relationship inverts. */
@media (hover:hover){
  .btn.primary:hover:not(:disabled),.btn.rose:hover:not(:disabled),.cta:hover:not(:disabled){
    filter:brightness(.92);
  }
}

/* ---- Cards sit on cream, so their own tint needs a boundary or the two warm surfaces
   blur into each other. */
.card,.stat,.exp-card,.pkg-card{border:1px solid var(--line)}


/* ---- Face scan notices -------------------------------------------------------------
   Three states that used to share one style, which meant a camera failure and a reading
   worth mentioning to a doctor looked identical. */
.fs-notice-err{
  border-left:3px solid var(--err);
  background:var(--maroon-soft);
}
.fs-notice-err b{color:var(--err)}

/* A vitals figure outside the usual resting range. Deliberately not red: it is a prompt to
   mention something at a consultation, not an alarm, and styling it as an emergency on a
   mental-health platform would frighten people over a camera estimate. */
.fs-notice-attention{
  border-left:3px solid var(--accent);
  background:var(--accent-soft);
  text-align:left;
}
.fs-notice-attention b{color:var(--ink)}
.fs-notice-attention small{display:block;line-height:1.6;margin-top:4px}
.fs-notice-attention .btn{display:inline-flex}

/* .fs-pop was on every result card and defined nowhere. It was also unnecessary:
   .fs-result-card in app.css already runs fs-pop-in, and the inline animation-delay was
   staggering that. Defining it here would have redeclared the keyframe and quietly changed
   the existing animation, so the class was removed from the markup instead. */
@media (prefers-reduced-motion:reduce){ .fs-result-card{animation:none;opacity:1} }


/* ============================================================================
   14. SECOND-PASS UTILITIES

   Patterns that appeared five or more times as inline styles. Naming them is not
   tidiness for its own sake: an inline style cannot be changed in one place, so
   twenty-five subtitles meant twenty-five separate decisions that had already
   drifted into three different spacings.
   ========================================================================== */

/* The line under a section heading. One spacing, everywhere. */
.section-sub{
  color:var(--muted);
  font-size:13px;
  line-height:1.6;
  margin:var(--s-1) 0 var(--s-4);
}

/* Right-aligned numeric table columns. Tabular figures so digits line up column-wise —
   the reason to right-align in the first place. */
.num-col-sm,.num-col,.num-col-lg{text-align:right;font-variant-numeric:tabular-nums}
.num-col-sm{width:70px}
.num-col{width:80px}
.num-col-lg{width:90px}

/* Filter-row fields. The flex ratio is the layout; min-width stops a select collapsing
   below its own longest option. */
.filter-wide{flex:2;min-width:200px}
.filter-mid{flex:1;min-width:170px}

.sec-underline{border-bottom:2px solid var(--gold-soft);padding-bottom:var(--s-2)}
.head-sp{margin:var(--s-5) 0 var(--s-2)}

/* A section that is a link target. scroll-margin keeps the heading clear of the sticky
   topbar when jumped to — without it the anchor lands underneath it. */
/* --s-9 does not exist — the scale skips from 8 to 10. The original 36px sits
   between them; snapped up, which is what the scale is for. */
.anchor-block{margin-bottom:var(--s-10);scroll-margin-top:var(--s-5)}

.cap-lg{max-width:560px}
.cap-md{max-width:150px}
/* For a year field and similar — four digits needs less room than a date. */
.cap-sm{max-width:100px}
.row-pad{padding:var(--s-2) 0}

/* ---- .fs-result-burst -----------------------------------------------------------------
   Restored. These rules were deleted along with the celebration on the face-scan result,
   where a confetti burst above a resting pulse of 120 was the wrong response — but two other
   pages use the same class for things that ARE achievements: finishing a certification and
   completing a study-success assessment. Only the face-scan usage was wrong; the class was
   not. Checking one caller before deleting a shared rule was the mistake. */
.fs-result-burst{
  font-size:38px;
  position:relative;
  display:inline-block;
  animation:fs-pop-in .5s cubic-bezier(.34,1.56,.64,1);
}
.fs-result-burst::before,.fs-result-burst::after{
  content:"";position:absolute;inset:-14px;border-radius:50%;pointer-events:none;
}
.fs-result-burst::before{
  background:radial-gradient(circle,var(--gold) 0 3px,transparent 4px) 0 0/22px 22px;
  opacity:.5;
}
@media (prefers-reduced-motion:reduce){
  .fs-result-burst{animation:none}
  .fs-result-burst::before,.fs-result-burst::after{display:none}
}


/* ---- Confirmation dialog ------------------------------------------------------------
   Used before anything is deleted. Deliberately small — a full-width sheet reads as a page
   and invites scanning past it; a compact card reads as an interruption, which is the point. */
.confirm-card{max-width:420px;text-align:center}
.confirm-icon{
  width:46px;height:46px;margin:0 auto var(--s-3);
  display:flex;align-items:center;justify-content:center;
  border-radius:50%;font-size:20px;
  background:var(--maroon-soft);color:var(--maroon);
}
.confirm-actions{justify-content:center;margin-top:var(--s-5)}

/* The destructive button is red-filled, which nothing else in the app is — so it cannot be
   mistaken for the primary action it sits next to. */
.btn-danger{
  background:var(--maroon);
  color:#fff;
  border:1px solid var(--maroon);
}
@media (hover:hover){ .btn-danger:hover:not(:disabled){filter:brightness(1.1)} }
.btn-danger:disabled{opacity:.6}

/* ---- Third-pass utilities ------------------------------------------------------------ */
.break-all{word-break:break-all}
.nowrap{white-space:nowrap}
.cap-xl{max-width:640px}
.cap-lg-sm{max-width:200px}
.fixed-200{flex:0 0 200px}
.fixed-110{flex:0 0 110px}

/* ---- Blog sidebar: the four-step journey block ------------------------------------- */
.hq-steps{list-style:none;counter-reset:hqstep;margin:0;padding:0}
.hq-steps li{
  counter-increment:hqstep;position:relative;padding:0 0 var(--s-3) var(--s-8);
  font-size:12.5px;line-height:1.5;
}
.hq-steps li::before{
  content:counter(hqstep);position:absolute;left:0;top:0;
  width:22px;height:22px;border-radius:50%;
  background:var(--accent-soft);color:var(--gold-text);
  font-weight:700;font-size:11.5px;display:flex;align-items:center;justify-content:center;
}
/* The vertical thread between steps — it is what makes four cards read as one sequence. */
.hq-steps li:not(:last-child)::after{
  content:"";position:absolute;left:10px;top:24px;bottom:4px;width:1px;background:var(--line);
}
.hq-steps li b{display:block;color:var(--ink);font-size:13px}
.hq-steps li span{color:var(--muted)}

/* ---- Report filters and the email composer ------------------------------------------ */
.filter-row{display:flex;flex-wrap:wrap;gap:var(--s-3);align-items:flex-end}
/* Capped: a month picker stretched to a third of a wide screen is harder to use, not easier
   — the eye has to travel to find the value it just set. */
.filter-row > *{max-width:260px}
.filter-row .lbl{display:block;margin-bottom:var(--s-1)}

/* Wider than a confirmation, because the preview has to be readable at the size it will
   arrive in — a preview squeezed into a narrow column is not a preview of anything. */
.modal-wide{max-width:720px;max-height:88dvh;overflow-y:auto}

/* The rendered email, shown as it will land. Framed and on a white ground regardless of
   theme: the recipient's inbox is not running your theme, and previewing it in dark mode
   would show something nobody will ever see. */
.mail-preview{
  background:#fff;color:#1a1a1a;
  border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s-4);max-height:340px;overflow-y:auto;
}
.mail-preview a{color:#0b57d0}


/* ---- color-scheme: follow the THEME, not the operating system ----------------------
   Checkboxes, radios, selects, date pickers and scrollbars are drawn by the browser, and it
   decides light or dark from color-scheme. App.razor declares "light dark", which is honest
   — the app supports both — but it leaves the decision to the OS. So a person on a dark
   desktop viewing a LIGHT theme got black checkboxes on white cards: the page light, the
   controls dark, and nothing in the theme able to say otherwise.

   Set per theme instead. hq-theme.js writes --scheme from the page colour's luminance, so a
   light theme gets light controls whatever the OS is set to, and a dark theme gets dark ones. */
:root{color-scheme:light}
html[data-theme],html[style*="--page"]{color-scheme:var(--scheme,light)}

/* The two built-in dark themes, for the CSS-only path where JS has not run yet — otherwise
   the controls flash light before the script lands. */
html[data-theme="midnight"],html[data-theme="slate-pro"]{color-scheme:dark}
@media (prefers-color-scheme:dark){
  html:not([data-theme]):not([style*="--page"]){color-scheme:dark}
}

/* Checkbox and radio tint. accent-color is the one property that recolours a native control
   without replacing it — which matters, because a replaced checkbox loses its keyboard
   behaviour and its announcement to a screen reader. */
input[type="checkbox"],input[type="radio"]{accent-color:var(--accent)}

/* ---- Analytics: clickable page rows and the daily sparkline ------------------------- */
.page-row{
  width:100%;border:0;background:none;text-align:left;font:inherit;cursor:pointer;
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);
  border-radius:var(--r-sm);
  color:inherit;
}
/* A .doc row that is also a button: it was picking up the button reset AND the card padding,
   so it sat on a tinted block of its own instead of reading as a row in the list. */
.doc.page-row{background:none;border-bottom:1px solid var(--line-2)}
.doc.page-row:last-child{border-bottom:0}
.doc.page-row .dinfo{display:flex;flex-direction:column;gap:2px;min-width:0}
.doc.page-row .dinfo small{color:var(--muted);font-size:12px}
@media (hover:hover){ .doc.page-row:hover{background:var(--field)} }
.page-row-name{display:flex;flex-direction:column;min-width:0}
.page-row-name b{font-size:13.5px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.page-row-name small{font-size:11.5px;color:var(--muted);font-family:ui-monospace,monospace}
@media (hover:hover){ .page-row:hover{background:var(--accent-soft)} }
.page-row:focus-visible{box-shadow:var(--focus-ring)}

/* Thirty bars, one per day. A chart library for this would be more code than the question
   deserves — the only thing being asked is whether traffic is steady, growing, or stopped. */
.spark{
  display:flex;align-items:flex-end;gap:2px;height:64px;
  padding:var(--s-2);background:var(--field);border-radius:var(--r-md);
}
.spark-bar{
  flex:1;min-width:3px;background:var(--accent);border-radius:2px 2px 0 0;opacity:.85;
}
@media (hover:hover){ .spark-bar:hover{opacity:1} }

/* ---- Labels and field text ------------------------------------------------------------
   Form labels and the text people type were inheriting whatever colour their container had,
   which on some cards meant heading teal and on others meant body ink. One token, so a label
   reads the same on every screen — and so the contrast is decided once rather than per page. */
.lbl,label,legend{color:var(--muted);font-size:12.5px;font-weight:600;letter-spacing:.01em}
.field,input,textarea,select{color:var(--ink)}
.field::placeholder,input::placeholder,textarea::placeholder{color:var(--muted);opacity:.75}
.hint-text,.t-muted small,small.hint{color:var(--muted)}


/* ---- Neutral text surfaces ------------------------------------------------------------
   #757575 on headings, table cells, selects and secondary button labels, as asked.

   Stated once so the trade is on record: against the themes' tinted page and card colours
   this measures 4.02-4.39:1, under the 4.5 that body text needs. On pure white it is 4.61.
   The gap is small and the colour is a product decision, but it is the reason a lighter grey
   was not used before. Changing the two custom properties below reverses it.

   FILLED BUTTONS ARE EXCLUDED, and that one is not a preference. Grey on the derived button
   fills measures 1.01-1.58:1 — the label would be invisible, not merely low-contrast. Those
   keep white; ghost and secondary buttons, which sit on card backgrounds, take the grey. */
:root{
  --text-neutral:#757575;
  --text-neutral-strong:#5F5F5F;   /* table headers and anything that must out-weigh a row */
}

h1,h2,h3,h4,h5,h6,
.hello,.section-h,.dash-next-title,.qmb-title{ color:var(--text-neutral) }

/* Tables: cells take the neutral, headers a step darker so the header row still leads. */
table th{ color:var(--text-neutral-strong) }
table td,.report-table td,.list-table td{ color:var(--text-neutral) }

select,.field select,select.field{ color:var(--text-neutral) }
select option{ color:var(--text-neutral) }

/* Buttons that are NOT a filled accent — ghost, secondary, link-style. */
.btn.ghost,.btn.secondary,.btn.link,.btn:not(.primary):not(.rose):not(.btn-danger):not(.pay-btn){
  color:var(--text-neutral);
}

/* Filled buttons keep white. See the note above — grey here is 1.01:1. */
.btn.primary,.btn.rose,.cta,.pay-btn{ color:var(--on-accent) }
.btn-danger{ color:#fff }

/* ---- Type roles ------------------------------------------------------------------------
   Merriweather for headings, Montserrat for everything else at two weights: 500 for reading,
   600 for anything you act on.

   The 600 on links and buttons does real work. Prose at 500 with links at 600 means the
   things you can click look different from the things you only read, before the mouse moves
   — which is what a link is for. */
body,p,span,li,td,th,label,legend,input,select,textarea,option{
  font-family:var(--sans);
  font-weight:500;
}
a[href],button,.btn,.nav-link,.tab,input[type=submit],input[type=button]{
  font-family:var(--sans);
  font-weight:600;
}
/* The one exception: a filled button's label is already carried by its fill and size, and
   600 inside a heading would fight the serif. */
h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{font-family:inherit;font-weight:inherit}

/* A smaller button, for use inside a row where a full-size one would dominate the numbers
   beside it. */
.btn-sm{padding:4px 10px;font-size:12px;border-radius:var(--r-sm)}

/* The WhatsApp line in a user row — a second contact detail under the primary one. */
.wa-line{display:flex;align-items:center;gap:6px;flex-wrap:wrap;color:var(--muted);font-size:12px}
.wa-line .pill{font-size:10.5px;padding:1px 7px}

/* A value that is shown but not editable. Deliberately not a disabled <input>: a greyed-out
   field invites clicking and then does nothing, which reads as broken rather than as
   read-only. */
.field-readonly{
  background:var(--field);
  color:var(--ink);
  display:flex;align-items:center;
  cursor:default;
}

/* The conversation list scrolls on its own rather than growing the page. An expert with
   several hundred clients could not reach anyone past the fold, because the chat panel beside
   it set the height and the list simply ran past it. */
.msg-contact-scroll{max-height:min(62vh,560px);overflow-y:auto;overscroll-behavior:contain}
.msg-contact-scroll .msg-contact{display:flex;align-items:center;gap:10px}

/* ── Reconnect banner ──────────────────────────────────────────────────────────
   Blazor Server toggles the classes on #components-reconnect-modal: it is hidden
   until the connection drops, then gets components-reconnect-show while retrying,
   -failed once retries are exhausted, and -rejected when the server no longer has
   the circuit. Without a styled element the page just stops responding, which on a
   call page reads as a frozen browser rather than a lost connection.

   Above everything, including the call room, on purpose. This is the one message
   that explains why nothing else on screen is working.                          */
.hq-reconnect{display:none;position:fixed;inset:0;z-index:2147483000;
  background:rgba(11,16,20,.72);backdrop-filter:blur(2px);
  align-items:flex-start;justify-content:center;padding-top:14vh}
#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected{display:flex}

.hq-reconnect-card{background:#fff;color:#1F2B37;border-radius:16px;padding:22px 26px;
  max-width:420px;text-align:center;box-shadow:0 24px 60px rgba(0,0,0,.45);
  display:flex;flex-direction:column;align-items:center;gap:8px;font-size:14px;line-height:1.55}
.hq-reconnect-card b{font-size:16px;color:#0B3D38}

.hq-reconnect-dot{width:12px;height:12px;border-radius:50%;background:#C0553B;
  animation:hqReconnectPulse 1.1s ease-in-out infinite}
@keyframes hqReconnectPulse{0%,100%{opacity:.35;transform:scale(.85)}50%{opacity:1;transform:scale(1.15)}}
@media (prefers-reduced-motion:reduce){.hq-reconnect-dot{animation:none;opacity:.9}}

/* One message at a time — the retrying line and the gave-up line say opposite
   things, and showing both is worse than showing neither. */
.hq-reconnect-show,.hq-reconnect-failed,.hq-reconnect-rejected{display:none}
.components-reconnect-show .hq-reconnect-show{display:block}
.components-reconnect-failed .hq-reconnect-failed{display:block}
.components-reconnect-rejected .hq-reconnect-rejected{display:block}

/* Reload is only an answer once retrying has stopped. Offering it during a retry
   invites people to throw away a connection that was about to come back. */
.hq-reconnect-btn{display:none;margin-top:6px;border:0;border-radius:10px;cursor:pointer;
  background:#0B3D38;color:#fff;font-weight:600;font-size:13.5px;padding:10px 18px}
.components-reconnect-failed .hq-reconnect-btn,
.components-reconnect-rejected .hq-reconnect-btn{display:inline-block}

/* ── Care Plans ───────────────────────────────────────────────────────────────
   One screen, two tabs — the plans a member is on, and the plans they can join.
   These were two sidebar entries and two pages, which read as two features and
   buried the one that mattered most (their own plan) under the one that mattered
   least.                                                                       */
.hidden{display:none !important}

.cp-tabs{display:flex;gap:6px;margin:0 0 18px;border-bottom:1.5px solid var(--line)}
.cp-tab{appearance:none;background:none;border:0;cursor:pointer;font:inherit;font-size:14.5px;
  font-weight:600;color:var(--ink-soft);padding:10px 16px;border-radius:10px 10px 0 0;
  border-bottom:2.5px solid transparent;margin-bottom:-1.5px;display:flex;align-items:center;gap:8px}
.cp-tab:hover{color:var(--ink)}
.cp-tab.on{color:var(--brand);border-bottom-color:var(--brand)}
.cp-tab-n{font-size:11.5px;font-weight:700;background:var(--field);color:var(--ink-soft);
  border-radius:999px;padding:1px 8px}
.cp-tab.on .cp-tab-n{background:color-mix(in srgb,var(--brand) 12%,#fff);color:var(--brand)}

/* A plan is one object, so the card is one object — not a stack of unrelated
   blocks with a price floating at the top right, a screenful away from the button
   that charges it. */
.cp-card{background:var(--card,#fff);border:1.5px solid var(--line);border-radius:16px;
  padding:22px 24px;display:flex;flex-direction:column;gap:16px}

.cp-head{display:flex;justify-content:space-between;align-items:flex-start;gap:20px;flex-wrap:wrap}
.cp-head-main{min-width:0;flex:1 1 320px}
.cp-name{margin:0 0 8px;font-size:19px;line-height:1.35}

/* The facts people actually compare on, as separate items rather than one
   middle-dot-joined run of text that has to be read to be parsed. */
.cp-facts{display:flex;flex-wrap:wrap;gap:6px}
.cp-fact{font-size:12px;font-weight:600;color:var(--ink-soft);background:var(--field);
  border-radius:999px;padding:4px 11px;white-space:nowrap}

.cp-price{text-align:right;flex:0 0 auto;display:flex;flex-direction:column;align-items:flex-end;gap:2px}
.cp-price-now{font-size:22px;line-height:1.2;color:var(--ink)}
.cp-price-was{font-size:13px;color:var(--ink-soft)}
.cp-price-sub{font-size:11.5px;color:var(--ink-soft)}
.cp-save{font-size:11.5px;font-weight:700;color:#2d8560;background:rgba(45,133,96,.10);
  border-radius:999px;padding:2px 9px;margin-top:2px}

/* ── What is being bought ──────────────────────────────────────────────────────
   A row of same-sized chips reading "3 Psychologist" answers "how many of each"
   and never answers the question people were actually asking, which is "so what
   am I getting?". Three layers now, largest first: the total in words, the split
   as one proportional bar, then the per-expert detail.

   One colour per kind of expert, used in BOTH the bar and the chip, so the split
   can be read without a legend.                                                */
.cp-sessions .lbl,.cp-benefits .lbl{margin-bottom:8px}

.cp-sessions{
  --tone-1:#2E6D67; --tone-1-bg:#E7F1EF;   /* doctors        */
  --tone-2:#5464A8; --tone-2-bg:#ECEEF8;   /* mind           */
  --tone-3:#5A8570; --tone-3-bg:#EAF2ED;   /* nutrition      */
  --tone-4:#7B6BA8; --tone-4-bg:#F0EDF7;   /* movement       */
  --tone-5:#B08830; --tone-5-bg:#F8F1E2;   /* ayush          */
  --tone-6:#4A7C9B; --tone-6-bg:#EAF1F5;   /* anything else  */
  --tone-7:#C97D4F; --tone-7-bg:#F9EFE8;
  background:linear-gradient(180deg,color-mix(in srgb,var(--brand) 4%,#fff),transparent 70%);
  border:1px solid var(--line);border-radius:14px;padding:16px 18px}

/* The headline. One number, one sentence — no counting required. */
.cp-sum{display:flex;align-items:center;gap:14px;margin-bottom:14px}
.cp-sum-total{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:54px;height:54px;border-radius:16px;font-size:22px;font-weight:800;color:#fff;
  background:linear-gradient(135deg,var(--brand),color-mix(in srgb,var(--brand) 65%,#4A7C9B));
  box-shadow:0 8px 18px -10px color-mix(in srgb,var(--brand) 70%,transparent);
  animation:cpPop .45s cubic-bezier(.2,.9,.3,1.3) both}
.cp-sum-txt{display:flex;flex-direction:column;gap:2px;line-height:1.35}
.cp-sum-txt b{font-size:15.5px;color:var(--ink)}
.cp-sum-txt span{font-size:12.5px;color:var(--ink-soft)}

/* Proportional, not decorative: this is the one place the card shows that the plan
   is mostly doctor time. */
.cp-split{display:flex;height:9px;border-radius:999px;overflow:hidden;background:var(--field);
  margin-bottom:14px}
.cp-split-seg{width:var(--w);background:var(--seg,var(--brand));
  animation:cpGrow .6s cubic-bezier(.3,.8,.35,1) both}
.cp-split-seg + .cp-split-seg{box-shadow:inset 1.5px 0 0 #fff}

.cp-session-grid{display:flex;flex-wrap:wrap;gap:9px}
.cp-session{display:flex;align-items:center;gap:10px;background:var(--chip-bg,var(--field));
  border:1.5px solid color-mix(in srgb,var(--seg,var(--brand)) 22%,transparent);
  border-radius:12px;padding:8px 14px 8px 9px;
  /* Staggered by index so the breakdown assembles left to right rather than
     appearing all at once — it draws the eye along the row, which is the order
     the numbers should be read in. */
  animation:cpRise .38s cubic-bezier(.2,.85,.3,1) both;animation-delay:calc(var(--i,0) * 70ms);
  transition:transform .15s ease,box-shadow .15s ease}
.cp-session:hover{transform:translateY(-2px);
  box-shadow:0 10px 20px -14px color-mix(in srgb,var(--seg,var(--brand)) 80%,transparent)}
.cp-session-ic{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;
  border-radius:10px;background:#fff;font-size:17px;line-height:1;
  border:1px solid color-mix(in srgb,var(--seg,var(--brand)) 18%,transparent)}
.cp-session-txt{display:flex;flex-direction:column;line-height:1.3}
.cp-session-txt b{font-size:13.5px;color:var(--ink)}
/* "3 sessions", not a bare "3" — the unit was doing all the work and was missing. */
.cp-session-n{font-size:12px;font-weight:700;color:var(--seg,var(--brand));
  display:flex;align-items:center;gap:6px}
.cp-cadence{font-weight:600;color:var(--ink-soft);background:#fff;border-radius:999px;
  padding:1px 7px;font-size:10.5px;border:1px solid var(--line)}

.cp-sessions .tone-1{--seg:var(--tone-1);--chip-bg:var(--tone-1-bg)}
.cp-sessions .tone-2{--seg:var(--tone-2);--chip-bg:var(--tone-2-bg)}
.cp-sessions .tone-3{--seg:var(--tone-3);--chip-bg:var(--tone-3-bg)}
.cp-sessions .tone-4{--seg:var(--tone-4);--chip-bg:var(--tone-4-bg)}
.cp-sessions .tone-5{--seg:var(--tone-5);--chip-bg:var(--tone-5-bg)}
.cp-sessions .tone-6{--seg:var(--tone-6);--chip-bg:var(--tone-6-bg)}
.cp-sessions .tone-7{--seg:var(--tone-7);--chip-bg:var(--tone-7-bg)}

@keyframes cpPop{from{opacity:0;transform:scale(.7)}to{opacity:1;transform:scale(1)}}
@keyframes cpRise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes cpGrow{from{width:0}to{width:var(--w)}}

/* Movement is decoration here; the numbers are the information. Anyone who has asked
   their system not to animate still gets the whole card, immediately. */
@media (prefers-reduced-motion:reduce){
  .cp-sum-total,.cp-session,.cp-split-seg{animation:none}
  .cp-session:hover{transform:none}
}

.cp-benefit-list{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:5px}
.cp-benefit-list li{font-size:13.5px;line-height:1.55;color:var(--ink-2,var(--ink))}

/* The full clinical description is real detail and belongs on the card — but not
   ahead of the price and the sessions, which is where its sheer size put it. */
.cp-more{border-top:1px solid var(--line);padding-top:14px}
.cp-more summary{cursor:pointer;font-size:13.5px;font-weight:600;color:var(--brand);list-style:none}
.cp-more summary::-webkit-details-marker{display:none}
.cp-more summary::before{content:"▸ ";display:inline-block;transition:transform .15s ease}
.cp-more[open] summary::before{content:"▾ "}
.cp-more .cp-desc-preview{margin-top:12px}

.cp-actions{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  border-top:1px solid var(--line);padding-top:16px}
.cp-actions .btn{min-width:190px}

@media(max-width:640px){
  .cp-card{padding:18px 16px;gap:14px}
  /* The price moves under the name rather than being squeezed against it. */
  .cp-price{text-align:left;align-items:flex-start;width:100%}
  .cp-actions .btn{min-width:0;width:100%}
  .cp-tab{padding:10px 12px;font-size:13.5px}
}

/* ── Admin warning banner ─────────────────────────────────────────────────────
   For the case where the data is fine but a setting makes it invisible, and the
   screen would otherwise look entirely healthy. Amber rather than red: nothing is
   broken or lost, something just is not reaching the public site — and there is a
   button right here that fixes it.                                             */
.banner-warn{display:flex;align-items:center;justify-content:space-between;gap:18px;flex-wrap:wrap;
  background:#FCF6E8;border:1.5px solid #E9D9A8;border-left:5px solid #B08830;
  color:#5C4A16;border-radius:12px;padding:14px 18px}
.banner-warn b{color:#4A3A0F;font-size:14.5px}
.banner-warn .t-sm{max-width:78ch;line-height:1.55;color:#6B5A28}
.banner-warn .btn{flex:0 0 auto}

/* Care-plan reassignment UI on Plan Assignments. */
.cp-assign-current{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.cp-reassign{margin-top:8px;display:flex;flex-direction:column;gap:8px;max-width:520px}
.cp-reassign .banner-warn{font-size:12px;padding:8px 12px}

/* Default-expert rows in the care-plan editor. */
.cp-default-row{display:flex;align-items:center;gap:12px;margin-bottom:8px;flex-wrap:wrap}
.cp-default-what{min-width:150px;font-size:13.5px}
.cp-default-row .field{max-width:420px}

/* Error-log bulk selection. */
.bulk-check{width:17px;height:17px;flex:0 0 auto;cursor:pointer;accent-color:var(--brand)}
.bulk-selectall{display:flex;align-items:center;gap:10px;background:var(--field);border-radius:8px}
.doc-selected{background:color-mix(in srgb,var(--brand) 6%,#fff);border-color:color-mix(in srgb,var(--brand) 30%,var(--line))}
.btn.danger{background:#C0553B;color:#fff;border:0}
.btn.danger:hover{background:#a8462f}

/* ── Family coverage report (HR + super-admin) ──────────────────────────────── */
.fam-filters{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;align-items:end}
.fam-toggle{display:flex;align-items:center;gap:8px;font-size:13.5px;padding-bottom:10px}
.fam-toggle input{width:16px;height:16px;accent-color:var(--brand)}

.fam-summary{display:flex;gap:12px;flex-wrap:wrap}
.fam-stat{flex:1 1 140px;background:var(--card,#fff);border:1.5px solid var(--line);border-radius:14px;
  padding:14px 18px;display:flex;flex-direction:column;gap:2px}
.fam-stat b{font-size:24px;color:var(--brand);line-height:1.1}
.fam-stat span{font-size:12px;color:var(--ink-soft)}

.fam-row{display:grid;grid-template-columns:1.4fr auto 2fr;gap:14px;align-items:center;
  padding:12px 4px;border-bottom:1px solid var(--line)}
.fam-row:last-child{border-bottom:0}
.fam-emp{display:flex;flex-direction:column;min-width:0}
.fam-emp b{font-size:14px}
.fam-emp small{font-size:11.5px;color:var(--ink-soft)}
.fam-count{white-space:nowrap}
.fam-members{display:flex;flex-wrap:wrap;gap:6px}
.fam-chip{background:var(--field);border:1px solid var(--line);border-radius:10px;padding:4px 10px;
  font-size:12.5px;display:flex;flex-direction:column;line-height:1.25}
.fam-chip small{font-size:10.5px;color:var(--ink-soft)}

@media(max-width:640px){
  .fam-row{grid-template-columns:1fr;gap:6px}
  .fam-count{justify-self:start}
}

/* ── Colourful horizontal bar chart (pulse results, HR + employee) ──────────── */
.hbar-chart{display:flex;flex-direction:column;gap:10px}
.hbar-row{display:grid;grid-template-columns:minmax(120px,1.4fr) 3fr auto;gap:12px;align-items:center}
.hbar-label{font-size:13px;color:var(--ink,#1a2b4a);line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.hbar-track{height:22px;background:var(--field,#eef1f6);border-radius:11px;overflow:hidden;position:relative}
.hbar-fill{height:100%;border-radius:11px;min-width:6px;
  transform-origin:left;animation:hbarGrow .7s cubic-bezier(.22,1,.36,1) both}
.hbar-value{font-size:13px;font-weight:700;color:var(--ink,#1a2b4a);min-width:44px;text-align:right;font-variant-numeric:tabular-nums}
@keyframes hbarGrow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@media (prefers-reduced-motion:reduce){.hbar-fill{animation:none}}
@media(max-width:560px){.hbar-row{grid-template-columns:1fr auto;grid-template-areas:'label value' 'track track';row-gap:5px}
  .hbar-label{grid-area:label}.hbar-value{grid-area:value}.hbar-track{grid-area:track}}

/* ══ Pulse surveys — HR, admin, employee ══════════════════════════════════════ */
.ps-tabs{display:flex;gap:6px;border-bottom:2px solid var(--line);margin-bottom:4px}
.ps-tab{background:none;border:0;padding:10px 18px;font-size:14px;font-weight:600;color:var(--ink-soft);
  cursor:pointer;border-bottom:3px solid transparent;margin-bottom:-2px}
.ps-tab.active{color:var(--brand);border-bottom-color:var(--brand)}
.ps-head-row{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-top:16px}

.ps-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:16px}
.ps-card{background:#fff;border:1.5px solid var(--line);border-radius:16px;padding:18px;
  border-top:5px solid var(--accent,#2E6D67);display:flex;flex-direction:column;gap:6px;
  transition:transform .15s, box-shadow .15s}
.ps-card:hover{transform:translateY(-3px);box-shadow:0 10px 24px rgba(0,0,0,.08)}
.ps-card-top{display:flex;justify-content:space-between;align-items:center}
.ps-emoji{font-size:28px}
.ps-live{color:#2f9e6f;font-size:12px;font-weight:700}
.ps-card-title{font-size:16px;margin:2px 0 0;color:var(--ink)}
.ps-card-meta{font-size:12px;color:var(--ink-soft);margin:0}
.ps-card-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
.ps-template{background:linear-gradient(160deg,color-mix(in srgb,var(--accent) 8%,#fff),#fff)}

/* editor */
.ps-editor{max-width:720px;max-height:88vh;overflow-y:auto}
.ps-row2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.ps-swatches{display:flex;gap:6px;flex-wrap:wrap}
.ps-swatch{width:26px;height:26px;border-radius:50%;border:2px solid transparent;cursor:pointer}
.ps-swatch.sel{border-color:var(--ink);box-shadow:0 0 0 2px #fff inset}
.ps-q-head{display:flex;justify-content:space-between;align-items:center}
.ps-q-need{font-size:12px;font-weight:700;color:#B08830;background:#FCF6E8;border-radius:8px;padding:3px 10px}
.ps-q-ok{font-size:12px;font-weight:700;color:#2f9e6f}
.ps-q-row{display:grid;grid-template-columns:auto 1fr auto auto;gap:8px;align-items:center;margin-top:8px}
.ps-q-num{width:24px;height:24px;border-radius:50%;background:var(--field);display:flex;align-items:center;
  justify-content:center;font-size:12px;font-weight:700;color:var(--ink-soft)}
.ps-q-type{max-width:150px}

.ps-results-head{display:flex;align-items:center;gap:14px;border-left:5px solid var(--accent);padding-left:14px}

/* employee form */
.ps-intro{display:flex;gap:16px;align-items:flex-start;background:linear-gradient(160deg,color-mix(in srgb,var(--accent) 12%,#fff),#fff);
  border:1.5px solid var(--line);border-left:5px solid var(--accent);border-radius:16px;padding:20px}
.ps-intro-emoji{font-size:40px;line-height:1}
.ps-thanks{text-align:center;padding:50px 20px;background:linear-gradient(160deg,color-mix(in srgb,var(--accent) 10%,#fff),#fff);
  border-radius:20px;border:1.5px solid var(--line)}
.ps-thanks-emoji{font-size:64px}
.ps-form{display:flex;flex-direction:column;gap:14px}
.ps-fq{background:#fff;border:1.5px solid var(--line);border-radius:14px;padding:16px 18px;border-left:4px solid var(--accent)}
.ps-fq-head{display:flex;gap:10px;align-items:baseline;margin-bottom:12px}
.ps-fq-num{width:26px;height:26px;flex:0 0 auto;border-radius:50%;background:var(--accent);color:#fff;
  display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700}
.ps-fq-text{font-size:14.5px;font-weight:600;color:var(--ink)}
.ps-scale{display:flex;gap:6px;flex-wrap:wrap}
.ps-dot{width:40px;height:40px;border-radius:10px;border:1.5px solid var(--line);background:#fff;
  font-size:14px;font-weight:700;color:var(--ink-soft);cursor:pointer;transition:.12s}
.ps-dot:hover{border-color:var(--accent)}
.ps-dot.on{background:var(--accent);color:#fff;border-color:var(--accent);transform:scale(1.08)}
.ps-scale-ends{display:flex;justify-content:space-between;font-size:11px;color:var(--ink-soft);margin-top:5px;max-width:480px}
.ps-agree{display:flex;gap:6px;flex-wrap:wrap}
.ps-agree-btn{padding:8px 14px;border-radius:20px;border:1.5px solid var(--line);background:#fff;
  font-size:13px;cursor:pointer;transition:.12s}
.ps-agree-btn.on{background:var(--accent);color:#fff;border-color:var(--accent)}
.ps-yesno{display:flex;gap:10px}
.ps-yn{padding:10px 28px;border-radius:12px;border:1.5px solid var(--line);background:#fff;font-weight:700;cursor:pointer}
.ps-yn.on.yes{background:#4A9B8E;color:#fff;border-color:#4A9B8E}
.ps-yn.on.no{background:#C0553B;color:#fff;border-color:#C0553B}
.ps-submit{margin-top:8px;align-self:flex-start;padding:12px 32px;font-size:15px}
@media(max-width:560px){.ps-q-row{grid-template-columns:auto 1fr;gap:6px}.ps-q-type{max-width:100%}.ps-row2{grid-template-columns:1fr}}

/* ── Quiz result banner (certification final quiz) ─────────────────────────── */
.quiz-result-banner{display:flex;gap:18px;align-items:center;padding:18px 20px;border-radius:16px;margin:8px 0 20px}
.quiz-result-banner.fail{background:linear-gradient(135deg,#FCEDE8,#FDF6F3);border:1.5px solid #E8C4B8}
.quiz-result-banner.pass{background:linear-gradient(135deg,#E7F5EF,#F3FBF8);border:1.5px solid #B6E0CE}
.quiz-result-banner b{font-size:16px;color:var(--ink,#1a2b4a)}
.quiz-score-ring{--pct:0;width:74px;height:74px;flex:0 0 auto;border-radius:50%;display:flex;align-items:center;
  justify-content:center;font-weight:800;font-size:18px;color:#B0442C;
  background:conic-gradient(#C0553B calc(var(--pct)*1%), #EAD7D0 0);position:relative}
.quiz-score-ring::before{content:"";position:absolute;inset:8px;border-radius:50%;background:#fff}
.quiz-score-ring span{position:relative;z-index:1}
.quiz-result-banner.pass .quiz-score-ring{color:#2f9e6f;background:conic-gradient(#4A9B8E calc(var(--pct)*1%), #CFE9DF 0)}


/* ---- Share buttons keep their own colour. The generic link rule above paints every anchor
   with --link, and the share row's LinkedIn / WhatsApp / Facebook buttons ARE anchors — so
   their labels came out brand-orange on top of blue and green backgrounds, unreadable and
   nothing like the buttons they sit next to. Instagram escaped it only because it happens to
   be a <button>. Excluded above and restated here so the row cannot drift again. */
.share-bar .share-btn,
.share-bar .share-btn:hover,
.share-bar .share-btn:visited{color:#fff;text-decoration:none}
.share-bar .share-btn.copy,
.share-bar .share-btn.copy:hover,
.share-bar .share-btn.copy:visited{color:#123A34}
.share-bar .share-btn span{color:inherit}

/* ---- care plan store: coloured by category, trust row, guarantee ---- */
.cp-card{--pc:#0b6f69;--pc-ink:#0b6f69;--pc-soft:#e6f4f2;border:1px solid var(--line);border-top:5px solid var(--pc);position:relative;overflow:hidden;transition:transform .18s ease,box-shadow .18s ease,border-color .18s ease}
@media(hover:hover){.cp-card:hover{transform:translateY(-3px);box-shadow:0 22px 40px -20px rgba(0,0,0,.35);border-color:var(--pc)}}
.cp-card::before{content:"";position:absolute;right:-60px;top:-60px;width:220px;height:220px;border-radius:50%;background:radial-gradient(circle,var(--pc-soft),transparent 70%);opacity:.9;pointer-events:none;z-index:0}
.cp-card>*{position:relative;z-index:1}
.cp-eyebrow{display:flex;align-items:center;gap:8px;font-size:10.5px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;color:var(--pc-ink);margin-bottom:6px;flex-wrap:wrap}
.cp-icon{width:26px;height:26px;border-radius:8px;display:grid;place-items:center;background:var(--pc);color:#fff;font-size:13px;box-shadow:0 6px 14px -6px var(--pc)}
.cp-badge{font-size:10px;font-weight:800;letter-spacing:.04em;padding:3px 8px;border-radius:999px;background:var(--pc-soft);color:var(--pc-ink);text-transform:none}
.cp-badge.save{background:#e6f6ef;color:#177a52}
.cp-name{font-family:var(--display,Merriweather,serif);font-size:21px}
.cp-fact{background:var(--pc-soft);color:var(--pc-ink)}
.cp-price-now{font-family:var(--display,Merriweather,serif);font-size:28px;color:var(--pc-ink)}
.cp-trust{display:grid;grid-template-columns:1fr 1fr;gap:8px 16px;padding:12px 14px;border-radius:12px;background:var(--pc-soft);font-size:12px;line-height:1.5;color:var(--ink-soft)}
.cp-trust b{color:var(--ink)}
.cp-actions .btn.primary{background:var(--pc);border-color:var(--pc);box-shadow:0 10px 20px -12px var(--pc);padding:12px 22px;font-size:14px}
.cp-actions .btn.primary:hover{filter:brightness(1.06)}
.cpc-mind{--pc:#5b4bd6;--pc-ink:#4a3cc0;--pc-soft:#eeebfb}.cpc-relationships{--pc:#d64570;--pc-ink:#b8365e;--pc-soft:#fdeaf0}
.cpc-sleep{--pc:#2c3e8f;--pc-ink:#243475;--pc-soft:#e9ebf7}.cpc-work{--pc:#1f6fd6;--pc-ink:#1857ab;--pc-soft:#e8f0fc}
.cpc-women{--pc:#c2418a;--pc-ink:#9e3370;--pc-soft:#fbe8f2}.cpc-parenting{--pc:#e07b1f;--pc-ink:#b8620f;--pc-soft:#fdf0e3}
.cpc-body{--pc:#0b8f8a;--pc-ink:#086f6b;--pc-soft:#e3f5f4}.cpc-growth{--pc:#1f9d6a;--pc-ink:#177a52;--pc-soft:#e6f6ef}
@media(max-width:700px){.cp-trust{grid-template-columns:1fr}}
