:root{
  /* ── Palette 11 · charcoal + ONE signal colour ────────────────────────────
     The whole discipline of this palette: charcoal carries every structural
     thing, and the signal appears ONLY where something is actionable or
     confirmed. If the signal shows up decoratively it stops meaning anything.
     Mint dimmed one step from #2BB6A0 — the original buzzed against white. */
  --signal:#23A18C;   --signal-d:#1B8674;   --signal-soft:#DFF2EE;
  --charcoal:#1D2226; --charcoal-d:#12161A; --charcoal-soft:#ECEEEF;

  /* The logo's letter colour. Near-black with the signal's hue in it, so the
     mark reads as one family rather than as black dropped onto mint. Dark
     enough to stay open at 29px, which plain charcoal was not. */
  --ink-teal:#08211D;

  /* ── Apricot · DECORATIVE ONLY ────────────────────────────────────────────
     Warmth for empty states, illustrations and invitations. It must NEVER sit
     on a button, link, badge or anything else the user can press — the moment
     two colours can mean "act here", neither of them means it. */
  --apricot:#C97F35; --apricot-soft:#FAF3EA; --apricot-line:#EEDFCB;

  /* Legacy aliases — the whole app still speaks in --orange/--blue. */
  --orange:var(--signal);  --orange-d:var(--signal-d); --orange-soft:var(--signal-soft);
  --blue:var(--charcoal);  --blue-d:var(--charcoal-d); --blue-soft:var(--charcoal-soft);

  /* --bg was a cool #F8F9F9 -- fine on its own, but it sat every authenticated
     page (dashboard, admin, provider profile) on a visibly different, colder
     ground than the marketing site's warm cream (#FBF7F0), which is a large
     part of why the logged-in app read as an older, disconnected product even
     though it already shares the marketing site's colour tokens, radius and
     shadow everywhere else. One token, one line: the whole authenticated app
     inherits the same warm ground the moment this changes, no per-page edits
     needed. --card stays pure white -- cards are meant to lift off this warm
     ground, not blend into it. */
  --ink:#191D20; --muted:#6E767B; --line:#E4E6E8; --bg:#FBF7F0; --card:#fff;
  /* The whole site's type system, marketing and authenticated app alike --
     Manrope carries both headings and body copy, with Cairo behind it for
     real Arabic glyphs (Manrope has none) and the system stack as the last
     resort. Both are self-hosted (see the @font-face rules below, right
     after Yellowtail's) specifically so this could stop being scoped to
     .mkt-shell/.authwrap -- when they were still loaded live from Google,
     confining them to marketing/auth pages was the only way to keep the
     authenticated app from sending every signed-in visitor's IP to a third
     party on every page load. Self-hosting removes that cost entirely, the
     same reasoning that already applied to Yellowtail, so the two token
     names below now apply everywhere rather than describing a marketing-only
     identity. */
  --mkt-font-head:'Manrope','Cairo','Segoe UI',Tahoma,Arial,sans-serif;
  --mkt-font-body:'Manrope','Cairo','Segoe UI',system-ui,-apple-system,'Helvetica Neue',Arial,sans-serif;
  /* The warm wash behind every marketing/auth screen in the finalized design
     — apricot top-left, mint bottom-right, both fading into the same cream
     the section bands already use. Values (colour stops, radii, angle) are
     copied directly from the design file, not approximated. */
  --mkt-wash:
    radial-gradient(120% 90% at 6% 2%,#FAE7C6 0%,#FCF1DC 22%,#FDF8EE 44%,rgba(253,251,246,0) 68%),
    radial-gradient(105% 95% at 100% 78%,#CFE6D8 0%,#DFEFE4 32%,#EDF5EC 58%,rgba(247,251,246,0) 82%),
    linear-gradient(150deg,#FDF6EA 0%,#FCFAF4 38%,#F6FAF4 66%,#E9F3EB 100%);
  --ok:#0E9F6E; --ok-soft:#E4F6EE; --warn:#B45309; --warn-soft:#FEF3E2; --bad:#D9453D; --bad-d:#B93A32; --bad-soft:#FDECEB;
  --r:16px; --rs:10px; --shadow:0 1px 2px rgba(22,35,46,.05),0 8px 24px -16px rgba(22,35,46,.28);

  /* ── motion ───────────────────────────────────────────────────────────────
     Every duration and curve in the app comes from here, so the whole
     interface moves with one hand. Nothing exceeds 260ms: past roughly a
     quarter-second a response stops feeling like a response and starts
     feeling like a wait, and this is software a mother opens at 11pm to find
     out whether tomorrow's session is confirmed.

     --ease-out is the standard: fast at the start, settling at the end, the
     way a real object with mass arrives. --ease-press is near-linear because
     a press must register instantly. --ease-pop overshoots ~4% and is used in
     exactly one place, a count appearing, where a tiny bounce is the point. */
  --t-press:90ms; --t-fast:130ms; --t-mid:190ms; --t-reveal:260ms;
  --ease-out:cubic-bezier(.22,.61,.36,1);
  --ease-press:cubic-bezier(.4,0,.6,1);
  --ease-pop:cubic-bezier(.34,1.4,.64,1);

  /* Two elevations, one step apart. Hover raises a surface by exactly one
     step — never two, or the page looks like it is inflating. */
  --lift-1:0 1px 2px rgba(22,35,46,.05),0 8px 24px -16px rgba(22,35,46,.28);
  --lift-2:0 2px 5px rgba(22,35,46,.07),0 16px 32px -18px rgba(22,35,46,.36);
}
/* Self-hosted, not linked live from Google -- the wordmark (.wm, below)
   renders on every single page including the whole authenticated app, so
   unlike Cairo (which only loads on marketing/auth pages) this one couldn't
   be scoped down to avoid the cost of an external request. Self-hosting
   removes it entirely instead: no visitor's IP goes to Google just to render
   the logo. Single file -- the "latin" subset covers every character the
   wordmark ever renders ("MyJalsa" is plain ASCII in every locale, Arabic
   included -- see .wm's own font-family comment) -- and the only weight
   Yellowtail ships in is 400, confirmed against Google's own served CSS
   before downloading anything. */
@font-face{
  font-family:'Yellowtail';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('/fonts/yellowtail-v25-latin-400.woff2') format('woff2');
}
/* Manrope and Cairo, self-hosted the same way and for the same reason as
   Yellowtail above -- see the --mkt-font-head/--mkt-font-body comment. Both
   files came straight off Google's own served CSS (fetched, not
   re-exported or reprocessed) and turned out to already be variable fonts:
   one file each covers the whole 400-800 weight range this app actually
   uses (confirmed against every font-weight value in this stylesheet
   before downloading anything, the same way Yellowtail's single 400 weight
   was confirmed rather than assumed), so a single ranged font-weight
   descriptor is enough -- no need for five separate static files per
   family. Each is subset to exactly the script it exists for: Manrope to
   latin (it has no Arabic glyphs at all, same as Yellowtail has no reason
   to carry one), Cairo to arabic (its own latin subset is redundant here --
   Manrope is always listed first in both stacks, so plain-ASCII text such
   as numerals or an English word inside an Arabic sentence is already
   served by Manrope before Cairo is ever consulted). Neither declares an
   italic face: the two places this stylesheet uses font-style:italic are
   incidental (a "message removed" note, one tiny badge label), not a real
   typographic need, and a browser's synthesised oblique is an acceptable
   trade for not shipping a third weight axis nobody asked for. */
@font-face{
  font-family:'Manrope';
  font-style:normal;
  font-weight:400 800;
  font-display:swap;
  src:url('/fonts/manrope-v20-latin-400to800.woff2') format('woff2');
}
@font-face{
  font-family:'Cairo';
  font-style:normal;
  font-weight:400 800;
  font-display:swap;
  src:url('/fonts/cairo-v31-arabic-400to800.woff2') format('woff2');
}
*{box-sizing:border-box;margin:0;padding:0}
/* Was a plain system-font stack, with a second RTL-only override right
   below repeating the same idea in Arabic -- both predate Manrope/Cairo
   existing as tokens at all. Now that they're self-hosted and available
   everywhere (see --mkt-font-body and the @font-face rules above), the one
   token stack replaces both rules outright: it already carries Cairo
   behind Manrope for exactly the Arabic case the old override existed for,
   so a separate html[dir=rtl] rule has nothing left to do. */
body{background:var(--bg);color:var(--ink);font-family:var(--mkt-font-body);font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased}
a{color:var(--signal-d);text-decoration:none}
/* Ziad's call: nothing underlines on hover, anywhere. Underline-on-hover is a
   1990s affordance — it moves the text baseline and makes a calm page twitch.
   What replaces it is at the bottom of this file: a restrained lift. */
a:hover{text-decoration:none}

/* layout */
/* A2/A3: the header is a defined bar, and it spans the window rather than the
   narrower content column beneath it — so the logo sits at one edge and the
   navigation at the other. */
/* Same border token and padding as .mkt-nav below (not a coincidental match --
   this was a hardcoded #DCE1E3/34px that predated --line/the marketing nav's
   own 40px, and the two headers reading as even slightly different bars is
   exactly the kind of thing that makes the authenticated app feel like a
   different product one scroll below its own logo). */
.top{background:#fff;border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20;
     box-shadow:0 1px 3px rgba(20,26,30,.04)}
.top .in{max-width:1440px;margin:0 auto;padding:13px 40px;display:flex;align-items:center;gap:18px}
/* The tagline sits INSIDE .logo now, baseline-aligned against the wordmark —
   not centered against it. Two different font sizes centered by box height
   land a couple of pixels off from each other; centered by text baseline
   they read as one line. .logo itself still centers by height against the
   icon, which has no text baseline of its own to align to. */
.wm-group{display:flex;align-items:baseline;gap:10px}
.navkicker{color:var(--signal-d);font-weight:700;font-size:12.5px}
/* The tagline is a nice-to-have beside a roomy desktop logo, not something a
   phone-width header has space for -- with it forced to fit, .top/.mkt-nav-in
   (neither of which wrap) pushed wider than the viewport and forced a
   horizontal scrollbar on the whole page. */
@media(max-width:640px){.navkicker{display:none}}
.logo{display:flex;align-items:center;gap:9px;font-weight:700;font-size:19px;color:var(--charcoal)}
.logo svg{flex:none}
/* ~15% larger in the header specifically (icon 26->30px, wordmark's
   ambient font-size 19->22px, scaled together so their proportions and the
   existing 9px gap stay exactly as they were) -- the wide desktop header
   was the one place the lockup read as slightly undersized. Scoped to
   .mkt-nav/.top so the footer's own smaller lockup (which shares the same
   .logo class) is untouched, and to a desktop-width media query so mobile
   stays exactly as compact as it already was. */
@media(min-width:900px){
  .mkt-nav .logo,.top .logo{font-size:22px}
  .mkt-nav .logo svg,.top .logo svg{width:30px;height:30px}
}
/* Wordmark, script treatment: "My" and "alsa" in the surrounding ink colour,
   "J" tinted with the same --signal token the primary button uses -- still
   the only place that colour appears non-functionally, allowed because the
   logo is not a control. Yellowtail ships in exactly one weight (400, no
   bold cut at all), so it's pinned explicitly rather than left to inherit
   the heading's own weight -- inheriting 700/800 here would make the browser
   synthesise a fake bold on cursive strokes, which is far more damaging to a
   script face than to a sans one. No letter-spacing: a script font's
   letterforms are drawn to flow into each other at zero extra spacing: the
   0.06em seam-opener from the sans version doesn't apply and looked wrong
   when tried here. The two "inherit the heading's font" rules that used to
   make the sans wordmark match Cairo/body headings are gone too -- the point
   now is the opposite: this word is always Yellowtail, never whatever font
   happens to surround it. */
.wm{font-family:'Yellowtail',"Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;font-weight:400}
.wm .j{color:var(--signal)}
/* At the logo's own 19-22px, --signal next to the surrounding ink reads as
   barely distinguishable from it on an actual screen. Brightening/saturating
   the same colour fixed it (a heavier weight made no difference, and isn't
   available here anyway -- see above). Scoped to the logo specifically --
   headings at 34px+ don't have this legibility problem. */
.logo .wm .j{filter:saturate(1.7) brightness(1.18)}
.nav{display:flex;gap:18px;margin-inline-start:auto;align-items:center;font-size:14px}
.nav a{color:var(--muted);font-weight:500}
.nav a.on{color:var(--ink);font-weight:600}
/* This is the one nav a signed-in role actually has to fit -- five text
   links plus three icon buttons plus the language toggle, sign-out and
   avatar, all in one row, none of them optional the way the marketing
   page's own links are (those just disappear under 900px, see
   .mkt-links). A flex row has no floor on how narrow it'll let itself get
   asked to go: without min-width:0 here .nav refuses to shrink below its
   own content's width and pushes .top -- and the whole page -- wider than
   the screen instead. min-width:0 lets it actually shrink to the space
   .logo leaves it; overflow-x:auto is the fallback once even that space
   runs out, so the row scrolls in place (nothing hidden, nothing shrunk
   into unreadability) instead of the page gaining a horizontal scrollbar.
   The logo itself sits outside .nav and never moves.
   900px, not a narrower phone-only cutoff: self-hosting Manrope (see the
   @font-face rules near the top of this file) measurably widened this
   exact row -- its letter metrics aren't identical to the system font it
   replaced -- and a real measured pass at 768px started intermittently
   overflowing by ~35px right at the edge of a tighter breakpoint. 900px
   matches the header logo's own upsize threshold just above, so "compact"
   and "roomy" now mean the same width everywhere in this header rather
   than two breakpoints that almost, but don't quite, agree. */
@media(max-width:900px){
  .nav{min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;padding-bottom:2px}
  .nav::-webkit-scrollbar{display:none}
  .nav>*{flex-shrink:0}
}
.wrap{max-width:1080px;margin:0 auto;padding:24px 20px 80px}
.narrow{max-width:620px}
.grid{display:grid;gap:16px}
.g2{grid-template-columns:1fr 1fr}
.g3{grid-template-columns:repeat(3,1fr)}
/* Six pages (booking, therapist profile, provider dashboard/profile/
   availability) set a fixed-width sidebar column with an INLINE style, which
   no stylesheet media query can ever override — inline always wins. That
   meant a 320-360px sidebar never collapsed on a phone, squeezing the real
   content into whatever was left of a ~375px screen. The column width still
   varies per page via the --sw custom property (inline styles may set that
   safely), but the actual grid-template-columns declaration now lives here,
   where the media query below can reach it. */
.sidegrid{grid-template-columns:1fr var(--sw, 320px)}
@media(max-width:820px){.g2,.g3,.sidegrid{grid-template-columns:1fr}.side{position:static!important}}

/* type */
h1{font-size:26px;letter-spacing:-.02em;margin-bottom:6px}
h2{font-size:19px;letter-spacing:-.01em;margin-bottom:10px}
h3{font-size:15px;margin-bottom:6px}
.lede{color:var(--muted);margin-bottom:18px}
.sec{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:700;margin:22px 0 10px}
.muted{color:var(--muted)}
.small{font-size:13px}
.tiny{font-size:12px}

/* card */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:18px;box-shadow:var(--shadow)}
.card.tight{padding:14px}
.card+.card{margin-top:14px}

/* buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;background:var(--orange);color:#fff;border:1px solid var(--orange);
  border-radius:var(--rs);padding:11px 18px;font-size:14.5px;font-weight:600;cursor:pointer;font-family:inherit}
.btn:hover{background:var(--orange-d);border-color:var(--orange-d);text-decoration:none;color:#fff}
.btn.blue{background:var(--blue);border-color:var(--blue)}
.btn.blue:hover{background:var(--blue-d);border-color:var(--blue-d)}
.btn.ghost{background:#fff;color:var(--charcoal);border-color:var(--line)}
.btn.ghost:hover{background:var(--blue-soft);color:var(--blue-d)}
/* Solid, not outlined — Decline/Cancel/Report sat as white buttons with a
   thin red frame, which read as timid next to Mint's solid fill and made the
   red itself easy to miss. A real problem gets a real red. Hover states an
   explicit color:#fff too — without it, .pop>summary:hover's mint text rule
   (below) wins that one property even though this rule wins everything else,
   because the cascade is resolved per-property: a red button was briefly
   turning its text mint on hover, which is exactly the "act here" signal
   this button must never send. */
.btn.danger{background:var(--bad);color:#fff;border-color:var(--bad)}
.btn.danger:hover{background:var(--bad-d);border-color:var(--bad-d);color:#fff}
.btn.sm{padding:8px 13px;font-size:13.5px}
.btn.full{width:100%}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn[disabled]{opacity:.5;pointer-events:none}
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.spread{display:flex;justify-content:space-between;align-items:center;gap:12px}

/* form */
label{display:block;font-size:13px;font-weight:600;color:var(--ink);margin:14px 0 6px}
/* `search` and `time` were missing from this list, so those two fell through to
   the browser's own control: square corners and text hard against the left edge,
   next to fields that were rounded and padded. That is the whole reason the
   search box looked like it belonged to a different site. */
input[type=text],input[type=email],input[type=password],input[type=number],
input[type=date],input[type=search],input[type=time],select,textarea{
  width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:var(--rs);font-size:14.5px;font-family:inherit;background:#fff;color:var(--ink)}
input[type=search]{-webkit-appearance:none;appearance:none}
input[type=search]::-webkit-search-decoration,
input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}
input:focus,select:focus,textarea:focus{outline:2px solid var(--blue-soft);border-color:var(--blue)}
textarea{min-height:84px;resize:vertical}
.hint{font-size:12px;color:var(--muted);margin-top:5px}

/* Custom checkbox -- the native box has never been styled anywhere in the
   app (chips already hide theirs entirely and paint the checked state on
   the label itself; .ackbox/.docpick left theirs as the bare OS default).
   Same mint-fill-plus-mark language as every other "checked" state already
   in the system (.tab.on, .chip:has(input:checked)). */
input[type=checkbox]{-webkit-appearance:none;appearance:none;width:19px;height:19px;
  border:1.5px solid var(--line);border-radius:6px;background:#fff;flex:none;
  cursor:pointer;display:inline-block;vertical-align:middle;position:relative;margin:0}
input[type=checkbox]:checked{background:var(--signal);border-color:var(--signal)}
input[type=checkbox]:checked::after{content:'';position:absolute;left:5px;top:2px;
  width:6px;height:10px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(40deg)}
input[type=checkbox]:focus-visible{outline:2px solid var(--signal);outline-offset:2px}

/* Custom chevron on every <select> -- the browser's own default arrow is
   heavier and darker than the rest of the icon set. Colour is --muted's own
   hex baked into the data URI (a background-image can't reference a CSS
   custom property), so it's still the real token's value, not a new one. */
select{-webkit-appearance:none;appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%236E767B' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.8 4.6L6 7.8l3.2-3.2'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 12px center;padding-inline-end:32px}
html[dir=rtl] select{background-position:left 12px center}

/* chips */
.chips{display:flex;gap:7px;flex-wrap:wrap}
.chip{display:inline-flex;align-items:center;gap:5px;font-size:13px;padding:7px 12px;border-radius:999px;background:#fff;border:1px solid var(--line);color:var(--muted);cursor:pointer}
.chip.on{background:var(--orange);border-color:var(--orange);color:#fff;font-weight:600}
.chip.b.on{background:var(--blue);border-color:var(--blue)}

/* badges */
.badge{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;padding:3px 9px;border-radius:999px;letter-spacing:.01em}
.badge.ok{background:var(--ok-soft);color:var(--ok)}
.badge.warn{background:var(--warn-soft);color:var(--warn)}
.badge.bad{background:var(--bad-soft);color:var(--bad)}
.badge.info{background:var(--blue-soft);color:var(--blue-d)}
.badge.new{background:var(--orange-soft);color:var(--orange-d)}

/* provider card */
.pcard{display:flex;gap:14px;background:#fff;border:1px solid var(--line);border-radius:var(--r);padding:16px;box-shadow:var(--shadow)}
.pcard+.pcard{margin-top:12px}
.av{width:52px;height:52px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;
  background:var(--blue-soft);color:var(--blue-d);font-weight:700;font-size:20px}
.av.lg{width:76px;height:76px;font-size:29px}
.av.xs{width:26px;height:26px;font-size:11px}
.navavatar{display:inline-flex}
.pname{font-weight:700;font-size:16px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.meta{display:flex;gap:10px;flex-wrap:wrap;color:var(--muted);font-size:13px;margin-top:4px}
.price{font-weight:800;font-size:17px}
.price span{font-weight:500;color:var(--muted);font-size:12.5px}

/* stats */
.stats{display:flex;border:1px solid var(--line);border-radius:var(--r);overflow:hidden;background:#fff}
.stats>div{flex:1;padding:13px 8px;text-align:center;border-inline-end:1px solid var(--line)}
.stats>div:last-child{border:none}
.stats b{display:block;font-size:18px}
.stats span{display:block;font-size:11.5px;color:var(--muted);margin-top:2px}

/* calendar */
.cal{display:grid;grid-template-columns:repeat(7,1fr);gap:6px}
.cal .dow{text-align:center;font-size:11px;color:var(--muted);font-weight:700;padding-bottom:4px}
.cal a,.cal span{display:block;text-align:center;padding:10px 0;border-radius:var(--rs);font-size:14px;border:1px solid transparent}
.cal a{background:#fff;border-color:var(--line);color:var(--ink);font-weight:600}
.cal a:hover{border-color:var(--orange);text-decoration:none}
.cal a.on{background:var(--orange);border-color:var(--orange);color:#fff}
.cal span{color:#C3CDD8}
.slots{display:flex;flex-wrap:wrap;gap:8px}
.slots a{padding:9px 14px;border:1px solid var(--line);border-radius:var(--rs);background:#fff;font-size:14px;font-weight:600;color:var(--ink)}
.slots a:hover{border-color:var(--orange);text-decoration:none}
.slots a.on{background:var(--orange);border-color:var(--orange);color:#fff}

/* long-form legal/informational pages (terms, privacy) */
.doc h1{margin-bottom:6px}
.doc h2{margin-top:28px;font-size:16px}
.doc p{margin-top:8px;color:var(--ink)}
.doc{padding-bottom:20px}

/* ── auth screens — sign in, sign up, forgot/reset password ───────────────
   Same tokens as the rest of the app, including the homepage: standard
   --bg for the page, and the white .card is still the only crisp-white
   surface, which is what makes the form pop against the sidebar. The warm
   wash and Manrope/Cairo type system are the finalized design's global
   visual language, so every auth screen gets them — not just the two
   (signup, create-account) the design actually mocks up. */
.authbody{background-color:var(--bg);background-image:var(--mkt-wash)}
.authwrap{font-family:var(--mkt-font-body)}
/* Capped and centered as a whole, not stretched edge-to-edge — on a wide
   window, a full-bleed split pulls the left content toward the far-left edge
   and the right card toward the far-right edge, leaving a dead gap of empty
   background between them in the middle. Capping the total width and
   centering it pulls both sides in toward the center together. The page
   background matches both panels already, so the margin this leaves on a
   wide screen reads as intentional, not as a seam. */
.authwrap{display:grid;grid-template-columns:1fr clamp(420px, 40vw, 560px);
  min-height:calc(100vh - 62px);align-items:stretch;max-width:1240px;margin:0 auto}
/* flex-start, not center: the trust column's own content (three short
   points) is usually much shorter than the card beside it, and centering it
   inside a row stretched to match the card left a large dead gap below it
   instead of the even top/bottom margins centering implies. Anchoring it to
   the top keeps it level with the card's own heading instead. */
/* No background of its own — .authbody's --mkt-wash gradient (above) already
   covers the whole page. A solid fill here used to paint over it just in
   this column, which is what made the trust panel read as a separate pale
   block next to the card instead of one continuous field. */
.authside{padding:52px 32px;display:flex;flex-direction:column;justify-content:flex-start}
.authhead{font-family:var(--mkt-font-head);font-weight:700;font-size:32px;line-height:1.25;
  letter-spacing:-.01em;max-width:420px;margin-bottom:28px}
.authtrust{display:flex;flex-direction:column;gap:26px;max-width:420px}
.authtrust>div{display:flex;gap:14px;align-items:flex-start}
.authtrust-ic{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex:none;
  border-radius:var(--rs);background:var(--signal-soft);color:var(--signal-d)}
.authtrust b{display:block;font-family:var(--mkt-font-head);font-weight:700;font-size:15px;margin-bottom:4px}
.authtrust p{font-size:13px;color:var(--muted);line-height:1.5;margin:0}

/* The left panel (logo, headline, illustration, trust row, demo box) is much
   taller than the right one (a short form). Centering the card inside a grid
   row that tall put it far down an otherwise-empty column instead of in view.
   Sticky + full viewport height keeps it centered in whatever part of the
   page is actually on screen as the taller side scrolls past it. */
/* min-height, not height: a fixed height centered the card inside it, but on
   a short viewport with a tall form (or a validation error adding a flash
   message above it) the centered content simply overflowed top and bottom --
   clipping the heading under the sticky header with no bottom margin at all
   below the card. min-height keeps the centering when it fits, and lets the
   box (and the page) grow past the viewport instead of clipping when it
   doesn't, so the card's own 40px padding is never lost. */
.authmain{position:sticky;top:56px;min-height:calc(100vh - 56px);display:flex;align-items:center;justify-content:center;padding:40px 32px}
.authcard{width:100%;max-width:400px}
.authcard h1{font-size:25px;margin-bottom:4px}
.authcard .lede{margin-bottom:22px}
/* An icon sitting inside an input. Wraps ONLY the input — not the label
   above it — so top:50% centers on the input itself regardless of how tall
   the label happens to be. */
.field-ic{position:relative}
.field-ic>.ico{position:absolute;top:50%;transform:translateY(-50%);color:var(--muted);pointer-events:none}
html:not([dir=rtl]) .field-ic>.ico{left:12px}
html[dir=rtl] .field-ic>.ico{right:12px}
.field-ic input{padding-inline-start:38px;margin-top:0}
.field-ic button.eyebtn{position:absolute;top:50%;transform:translateY(-50%);background:none;border:0;padding:8px;color:var(--muted);cursor:pointer;line-height:0}
html:not([dir=rtl]) .field-ic button.eyebtn{right:2px}
html[dir=rtl] .field-ic button.eyebtn{left:2px}
html:not([dir=rtl]) .field-ic:has(button.eyebtn) input{padding-inline-end:38px}
html[dir=rtl] .field-ic:has(button.eyebtn) input{padding-inline-end:38px}
/* Password strength -- progressive enhancement only, matching the site's
   own rule that every feature works with JS off (§4). The container ships
   `hidden` in the markup, so with no JS it simply never appears; the
   password field's own required/minlength validation still gates
   submission on its own regardless. Three segments, not a continuous bar --
   easier to read at a glance than a percentage would be. */
.pwmeter{display:flex;align-items:center;gap:8px;margin-top:10px}
.pwmeter[hidden]{display:none}
.pwmeter-bars{display:flex;gap:6px;flex:1}
.pwmeter-bars span{flex:1;height:3px;border-radius:2px;background:var(--line)}
.pwmeter[data-score="1"] .pwmeter-bars span:first-child{background:var(--bad)}
.pwmeter[data-score="2"] .pwmeter-bars span:nth-child(-n+2){background:var(--warn)}
.pwmeter[data-score="3"] .pwmeter-bars span{background:var(--signal)}
.pwmeter-label{font-size:12px;color:var(--muted);flex:none}
.authdivider{display:flex;align-items:center;gap:12px;color:var(--muted);font-size:12.5px;margin:18px 0}
.authdivider::before,.authdivider::after{content:'';flex:1;height:1px;background:var(--line)}
.authfoot{text-align:center;font-size:13px;color:var(--muted);margin-top:18px}
/* Role selection ("Join MyJalsa") -- the finalized design gives this its own
   wide page rather than the narrow split-panel signup form uses, since two
   large photo cards need real room. Same slim auth header (logo + language
   toggle) as every other auth screen; only the body below it is wide.
   grid-column:1/-1 pulls it out of .authwrap's two-column split (built for
   the OTHER auth pages' side-panel+form layout) -- without it, .rolepick
   was landing in column 1 only, capped at ~680px with the form column's
   ~560px sitting empty beside it. Harmless at the ≤860px breakpoint where
   .authwrap itself collapses to one column already. */
.rolepick{grid-column:1/-1;max-width:1100px;margin:0 auto;padding:64px 32px 80px;font-family:var(--mkt-font-body)}
.rolepick-head{max-width:52ch;margin-bottom:40px}
.rolepick-head h1{font-family:var(--mkt-font-head);font-weight:800;font-size:40px;letter-spacing:-.018em;margin-bottom:12px}
.rolepick-head p{color:var(--muted);font-size:16px}
.rolepick-grid{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-bottom:32px}
a.rolecard-photo{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:22px;
  background:#fff;overflow:hidden;text-decoration:none;color:inherit}
.rolecard-photo-img{position:relative;height:220px;overflow:hidden;background:var(--signal-soft)}
.rolecard-photo-img img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.rolecard-photo-body{padding:30px;display:flex;flex-direction:column;flex:1}
.rolecard-photo-body b{display:block;font-family:var(--mkt-font-head);font-weight:700;font-size:21px;margin-bottom:9px}
.rolecard-photo-body p{color:var(--muted);margin-bottom:0}
.rolecard-photo-cta{margin-top:auto;padding-top:20px;display:flex;align-items:center;gap:10px;font-weight:700;color:var(--signal-d)}
html[dir=rtl] .rolecard-photo-cta svg{transform:scaleX(-1)}
@media(max-width:760px){
  .rolepick{padding:36px 20px 56px}
  .rolepick-head h1{font-size:30px}
  .rolepick-grid{grid-template-columns:1fr}
}

@media(max-width:860px){
  .authwrap{grid-template-columns:1fr}
  .authside{padding:28px 22px 8px;justify-content:flex-start}
  .authhead{font-size:23px;margin-bottom:20px}
  .authtrust{gap:18px}
  /* Single column on mobile — the sticky/full-height trick above is only for
     the two-column desktop layout; here the card just follows normally
     underneath the hero, like any other stacked content. */
  .authmain{position:static;height:auto;padding:24px 22px 44px}
}

/* ── the public marketing homepage ("/", logged out) ───────────────────────
   Same five tokens as the authenticated app (ink / bg / card / signal /
   muted, plus muted's border expression --line) -- nothing new added to the
   palette. .mkt-shell resets the default .wrap so each section can run
   full-bleed and set its own inner max-width via .mkt-in.

   Alignment rule for the whole page: everything shares ONE left edge, the
   start edge of .mkt-in. Nothing on this page is centered except .mkt-final
   (the belief statement) -- that is the one deliberate break in rhythm.

   Background rule: the page background carries every section except two --
   .mkt-final (cream) and .mkt-pro (a cream/pale-mint wash as of the Sept
   2026 section-differentiation refinement below; it was solid brand teal,
   --signal-d, before that). Those are the page's only two full-bleed
   colour moments; near-black does not appear anywhere on this page. */
.mkt-shell{max-width:none;margin:0;padding:0;background-color:var(--bg);font-family:var(--mkt-font-body)}
/* Applied per-section, not to the whole (very tall) page -- the gradient's
   percentage-based sizing needs a viewport-scale box to read as the
   finalized design's warm wash rather than fading out within the first few
   hundred pixels of a 3000px+ page. Hero keeps the full two-colour wash;
   every section below it (Sept 2026 refinement) uses a quieter, mint-led
   variant of the same tokens plus soft organic accents, so the page reads
   as one continuous field instead of gradient/white/gradient/beige blocks
   stacked on top of each other -- see each section's own rule below for
   why its particular treatment was chosen. */
.mkt-hero{background-image:var(--mkt-wash)}
.mkt-in{max-width:1400px;margin:0 auto;padding:0 40px}

/* ── organic background accents (Sept 2026) ────────────────────────────────
   Large, soft-edged circles used as low-opacity atmosphere behind a
   section's content, never as a foreground graphic. Pure CSS radial
   gradients rather than an SVG asset -- a soft edge just needs a feathered
   colour stop, and this avoids a new image dependency for something nobody
   is meant to consciously notice. Every section using one of these sets
   position:relative + overflow:hidden on itself (so a circle positioned
   partly off-edge can never cause the page to scroll sideways) and puts its
   real content in the existing .mkt-in / .mkt-about-grid wrapper at a
   higher stacking context, which is why the ::before/::after below never
   need z-index math of their own beyond "under everything else". */

/* WHAT IS MYJALSA -- the most-flagged section: flat white/cream before,
   with two large mint circles bleeding in from opposite corners at low
   opacity, plus a very faint corner-to-corner wash so the cream doesn't
   read as a hard stop against Hero's wash above it or Explore's wash
   below. */
#about{position:relative;overflow:hidden;background-color:#FDFBF7;
  background-image:radial-gradient(120% 100% at 100% 0%,#E6F4F1 0%,rgba(230,244,241,0) 55%)}
#about::before{content:"";position:absolute;top:-200px;right:-180px;width:560px;height:560px;
  border-radius:50%;background:radial-gradient(circle,#D7EBE6 0%,rgba(215,235,230,0) 68%);
  opacity:.55;pointer-events:none;z-index:0}
#about::after{content:"";position:absolute;bottom:-220px;left:-160px;width:480px;height:480px;
  border-radius:50%;background:radial-gradient(circle,#E6F4F1 0%,rgba(230,244,241,0) 68%);
  opacity:.45;pointer-events:none;z-index:0}
#about>.mkt-in{position:relative;z-index:1}

/* EXPLORE CARE -- the cards themselves are untouched (still opaque white
   .card surfaces); only the section field behind them softens from Hero's
   full wash to a quieter mint/cream pairing, so it reads as a continuation
   of About rather than a second, unrelated gradient moment. */
.mkt-explore{background-color:#FBF7F0;
  background-image:radial-gradient(130% 100% at 0% 0%,#D7EBE6 0%,rgba(215,235,230,0) 50%),
                   radial-gradient(120% 100% at 100% 100%,#E6F4F1 0%,rgba(230,244,241,0) 55%)}

/* HOW IT WORKS -- stays the flat warm beige it was (the brief is explicit
   that this section's contrast is doing useful work and must not become
   "gradient-heavy"). The only change is a ~90px feather at the very top and
   bottom edges blending from the neighbours' tones into the beige, instead
   of a hard one-pixel colour cut -- everything past that first/last 90px is
   still the same flat #F3EEE4 it always was. */
#how-it-works{background-color:#F3EEE4;
  background-image:linear-gradient(to bottom,#D7EBE6 0,rgba(215,235,230,0) 90px),
                    linear-gradient(to top,#FBF7F0 0,rgba(251,247,240,0) 90px)}

/* WHY PEOPLE TRUST -- deliberately closer to About's treatment than to
   Hero's full wash, since the brief asks for it to feel related to the
   Hero AND About sections specifically, not to repeat Hero's stronger
   apricot-forward moment a third time down the page. */
/* A calm linear wash rather than About's corner-radial blooms -- adjacent
   sections using the same technique (radial circles in a corner) is
   exactly what read as repetitive. One small, restrained circle at the
   opposite corner from About's pair (bottom-right here; About uses
   top-right + bottom-left) so the two sections are never mistaken for
   the same background repeated twice. The mint arrives late (78%, not
   55%) specifically so this stays flatter than .mkt-pro's own diagonal
   wash right below it -- an earlier version used a near-identical cutoff
   to Pro's and the two sections met at a visible, near-duplicate seam. */
#trust{position:relative;overflow:hidden;background-color:#FBF7F0;
  background-image:linear-gradient(125deg,#FBF7F0 0%,#FBF7F0 78%,#E6F4F1 100%)}
#trust::before{content:"";position:absolute;bottom:-170px;right:-150px;width:400px;height:400px;
  border-radius:50%;background:radial-gradient(circle,#D7EBE6 0%,rgba(215,235,230,0) 68%);
  opacity:.4;pointer-events:none;z-index:0}
#trust>.mkt-in{position:relative;z-index:1}

.mkt-nav{position:sticky;top:0;z-index:20;background:#fff;border-bottom:1px solid var(--line);
  box-shadow:0 1px 3px rgba(20,26,30,.04)}
.mkt-nav-in{max-width:1400px;margin:0 auto;padding:13px 40px;display:flex;align-items:center;gap:24px}
.mkt-links{display:flex;gap:22px;margin-inline-start:auto;font-size:14px}
.mkt-links a{color:var(--muted);font-weight:500}
.mkt-links a:hover{color:var(--ink)}
.mkt-cta{display:flex;align-items:center;gap:14px}
.mkt-cta a:not(.btn){color:var(--muted);font-weight:500;font-size:14px}

/* One padding value, on every section, no exceptions. .mkt-pro and
   .mkt-final are full-bleed sections in their own right now (round two:
   they used to be rounded cards inset on the page background, which stacked
   an extra 44px of the card's own padding on top of the section padding and
   was the actual cause of the uneven gap the brief measured). */
.mkt-section,.mkt-pro,.mkt-final{padding:64px 0;scroll-margin-top:70px}
.mkt-head{max-width:60ch;margin-bottom:32px}
.mkt-head h2{font-family:var(--mkt-font-head);font-weight:700;font-size:42px;letter-spacing:-.01em;margin-bottom:8px}
.mkt-head p{color:var(--muted);max-width:60ch}
/* Small uppercase label above a section heading -- the finalized design's
   own device, added only where it doesn't just repeat the heading text
   sitting right below it (About/Explore/How-it-works keep their existing,
   shorter headings, which already read the same as the design's eyebrow
   for that slot -- stacking a second, near-identical line there would look
   like a mistake, not a fix). */
.mkt-eyebrow{display:block;font-size:11px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--signal-d);margin-bottom:12px}
/* Heading scale by section, matching the finalized design's own hierarchy
   instead of one flat size everywhere. clamp() keeps each in proportion
   down to mobile without touching the shared @media(max-width:900px) rule
   above (which still governs every other, unlisted .mkt-head h2). */
#about .mkt-head h2{font-size:clamp(24px,3vw,34px)}
.mkt-explore .mkt-head h2{font-size:clamp(28px,5vw,52px);font-weight:800}
#how-it-works .mkt-head h2{font-size:clamp(26px,4.5vw,46px);font-weight:800}
#trust .mkt-head h2{font-size:clamp(26px,4.5vw,46px);font-weight:800}

/* type scale, top to bottom of the page:
     hero        84 / 800   -- roughly double a section heading
     section h2  42 / 700
     sub-heading 22 / 700   -- pillar labels, step titles, trust labels
     body        15.5 / 400
   Weight steps down with size at every level, which is what actually reads
   as hierarchy -- making everything bold flattens it right back out. */
.mkt-pillar b,.mkt-step h3,.mkt-trust b{font-family:var(--mkt-font-head);font-weight:700;font-size:22px}

/* the belief statement -- the one section allowed to break the left-aligned
   rhythm, and one of the two full-bleed colour moments. No button: this
   closes the page as a statement, not another conversion point. Balanced
   wrapping and a tighter line-height on the lead line, which at display
   size looked loose under the app's own 1.55 body line-height and was
   stranding its last word on its own line.

   MOTTO TREATMENT (Sept 2026 refinement): this is meant to read as the
   page's philosophical conclusion, not one more informational section, so
   it earns more air than the shared .mkt-section padding gives every
   section above it, and a lead line with more presence than a plain
   section h2. clamp() keeps that extra room from ever costing mobile
   anything -- at narrow widths it resolves to the same 64px every other
   section already uses. Centering, copy, typography and spacing are
   untouched; only scale/space (here) and background (below) changed.

   BACKGROUND (background-only refinement): this section states what
   MyJalsa believes, and is deliberately the one section that earns the
   strongest mint/teal presence on the page -- everywhere else the wash is
   a corner accent (#about, .mkt-explore, #trust) or a late, mostly-cream
   diagonal (.mkt-pro); here it's a soft, centered, top-to-bottom bloom
   using the exact same two teal stops those already use (#D7EBE6 pale
   teal, #E6F4F1 soft mint) plus the page's own cream, layered instead of
   flattened so the ink text above it stays exactly as readable as it was
   on the flat apricot-soft it replaces. No new color, no shape, no image --
   a background-image only, same technique as every wash on this page. */
.mkt-final{text-align:center;padding:clamp(64px,11vw,132px) 0;
  background-color:#FBF7F0;
  background-image:
    radial-gradient(120% 95% at 50% 0%, #D7EBE6 0%, #E6F4F1 38%, rgba(230,244,241,.45) 64%, rgba(251,247,240,0) 84%),
    radial-gradient(100% 90% at 50% 100%, #D7EBE6 0%, rgba(215,235,230,0) 62%);
}
.mkt-believe{max-width:42ch;margin:0 auto}
.mkt-believe-lead{font-family:var(--mkt-font-head);font-size:36px;font-weight:800;
  line-height:1.25;letter-spacing:-.01em;margin-bottom:22px;text-wrap:balance}
.mkt-believe p:not(.mkt-believe-lead):not(.mkt-believe-close){color:var(--muted);margin-bottom:22px;max-width:52ch;margin-inline:auto}
.mkt-believe-close{font-weight:600}
/* The one place the brand's own cursive touches running text outside the
   logo itself -- see .wm above for the header/footer wordmark this echoes.
   Wraps only the literal word "MyJalsa" already present in both language
   strings (landing.ejs), never a fabricated addition. Solid teal rather
   than the wordmark's gradient, and no tile/background behind it, so it
   reads as a typographic accent -- not a second, competing logo. */
.mkt-believe-mark{font-family:'Yellowtail',cursive;font-weight:400;
  font-size:1.35em;line-height:1;color:var(--signal-d)}

/* hero -- text column plus a real photo, the finalized design's own split
   (roughly 1fr text / 0.86fr image). Below the point the image would be
   narrower than the text feels comfortable, it drops under the text instead
   of squeezing sideways -- a stacked hero is fine on a phone, a thin
   sliver of photo down one edge is not. */
/* Scoped to .mkt-hero .mkt-in, not .mkt-hero itself -- .mkt-hero is now the
   full-bleed <section> (background only, no max-width), matching every
   other section on this page; the grid these lines lay out belongs to the
   .mkt-in wrapper actually holding the two-column content. */
.mkt-hero .mkt-in{padding-block:64px;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,0.86fr);gap:56px;align-items:center}
/* line-height 1.1, not the flat 1 it used to be -- the typography audit
   traced the "homepage doesn't feel like the sign-up pages" impression to
   this one property: every other heading on the site (including this
   section's own sibling two sections down) inherits the relaxed 1.55 body
   default, while this was the only one crushed to zero line-gap. 1.1 keeps
   it reading as a tight display headline without the same jarring
   contrast against the rest of the page. Family/weight/size/letter-spacing/
   colour all unchanged. */
.mkt-hero-seat{font-family:var(--mkt-font-head);font-weight:800;font-size:76px;line-height:1.1;letter-spacing:-.02em;margin-bottom:22px}
.mkt-hero-beginning{font-size:26px;font-weight:700;color:var(--ink);margin-bottom:20px;white-space:nowrap}
.mkt-hero-support{max-width:65ch;margin-bottom:30px}
.mkt-hero-support p{font-size:17px;color:var(--muted);margin-bottom:8px}
.mkt-hero-support p:last-child{margin-bottom:0}
.mkt-hero-cta{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:28px}
.mkt-stats{display:flex;gap:28px;flex-wrap:wrap;max-width:65ch;border-top:1px solid var(--line);padding-top:20px}
.mkt-stats span{display:flex;align-items:center;gap:8px;font-size:13.5px;color:var(--muted);font-weight:600}
.mkt-stats span .ico{color:var(--signal)}
.mkt-hero-media{position:relative;border-radius:22px;overflow:hidden;min-height:420px;background:var(--signal-soft)}
.mkt-hero-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 72%}
@media(max-width:980px){
  .mkt-hero .mkt-in{grid-template-columns:1fr}
  .mkt-hero-media{order:-1;min-height:260px}
}

/* What is MyJalsa -- heading + a small illustrative "your care team" card on
   the left, the same three Find/Connect/Keep items as before on the right.
   The card is decorative only (no real names -- generic role labels built
   from the same specialty strings the explore-care grid already uses, and
   abstract avatar circles rather than invented portraits), matching the
   finalized design's own preview card without fabricating identities. */
/* gap tightened from 64->48px and align-items start->center: the Care Team
   preview card is shorter than the Find/Connect/Keep column beside it, and
   top-aligning the two under the old wider gap left the card looking small
   and adrift in its own column instead of part of one composition.
   Centering the shorter card against the taller one, with less air between
   them, is the whole fix -- no copy, layout structure, or breakpoint
   changed. */
.mkt-about-grid{display:grid;grid-template-columns:minmax(0,0.9fr) minmax(0,1.1fr);gap:48px;align-items:center}
.mkt-careteam-card{position:relative;margin-top:32px;width:100%;max-width:330px;padding:22px;
  background:#fff;border:1px solid var(--line);border-radius:16px;box-shadow:var(--lift-2)}
.mkt-careteam-label{font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-bottom:13px}
.mkt-careteam-row{display:flex;align-items:center;gap:10px}
.mkt-careteam-row+.mkt-careteam-row{margin-top:11px}
.mkt-careteam-avatar{flex:none;width:28px;height:28px;border-radius:999px;background:var(--signal-soft)}
.mkt-careteam-role{font-size:12.5px;font-weight:600}
.mkt-careteam-spec{font-size:11px;color:var(--muted)}
.mkt-careteam-add{display:flex;align-items:center;gap:10px;padding-top:11px;margin-top:11px;border-top:1px solid var(--line)}
.mkt-careteam-add-ic{display:flex;align-items:center;justify-content:center;flex:none;width:28px;height:28px;
  border-radius:999px;background:var(--signal-soft);color:var(--signal-d);font-weight:700;font-size:14px}
.mkt-careteam-add span:last-child{font-size:12.5px;font-weight:500;color:var(--signal-d)}
/* What is MyJalsa -- three plain text columns, deliberately not cards: no
   border, no background, no icon tile. Three genuinely parallel items is
   the one case a grid earns its place without becoming a card. */
.mkt-pillars{display:grid;grid-template-columns:repeat(3,1fr);gap:26px;margin-top:8px}
.mkt-pillar b{display:block;margin-bottom:8px}
.mkt-pillar p{color:var(--muted);max-width:38ch}
/* 3, not 4 -- the featured ABA card spans 2 of these, and 8 specialties
   (2+1+1+1+1+1+1+1 = 9 column-units) divide evenly into 3 columns as three
   full rows. At 4 columns the same 9 units left a lone eighth card sitting
   by itself in a final row nothing else occupied. */
.g4{grid-template-columns:repeat(3,1fr)}
@media(max-width:980px){
  .mkt-about-grid{grid-template-columns:1fr}
  .mkt-pillars{grid-template-columns:1fr}
}

/* how it works -- the one place numbers are allowed, because these four
   steps are an actual sequence rather than an arbitrary list. */
.mkt-steps{display:grid;grid-template-columns:repeat(4,1fr);gap:22px;margin-top:8px}
.mkt-step-n{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;
  border-radius:50%;background:var(--signal);color:#fff;font-weight:700;font-size:13.5px;margin-bottom:14px}
.mkt-step h3{margin-bottom:6px}
.mkt-step p{font-size:13.5px;color:var(--muted)}

/* why people trust MyJalsa -- its own class rather than reusing .authtrust:
   that one is hidden outright below 860px on the sign-in screen (where the
   trust row is expendable next to the actual sign-in form), which silently
   deleted this section's whole content on mobile the first time it reused
   that class. All three points use an enclosed-shape icon (lock / shield /
   eye) at the same visual weight -- a bare checkmark glyph was the odd one
   out next to the other two's solid outline shapes. */
.mkt-trust{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
.mkt-trust b{display:block;margin-bottom:4px}
.mkt-trust p{font-size:13.5px;color:var(--muted)}

/* the professional band -- full-bleed brand teal, the deeper of the two
   signal shades so it reads as substantial without being another black.
   The button sits directly under the copy rather than pinned to a far
   corner across a wide flex row, which read as two unrelated things
   sharing a box rather than one offer. */
/* FOR PROFESSIONALS -- was the page's one solid-teal block (--signal-d,
   white text), which the brief calls out as the most disconnected-feeling
   section. Replaced with the same cream/mint atmosphere as About and Trust,
   plus one soft teal-tinted circle so the section still nods at "this is
   the professional side" without going back to a flat brand-colour fill.
   Text and the ghost button switch to the same ink/border tokens every
   other ghost button on this page already uses on a light ground (see
   .btn.ghost's base rule) -- removed the white-on-dark overrides entirely
   rather than inventing a third colour pairing. */
/* A plain diagonal linear wash, not another dual-corner radial pair --
   Explore Care already uses that technique, and repeating it here (just at
   different corners) was itself starting to look like the same background
   recurring a third time. The soft teal circle below is what gives this
   section its own "for professionals" identity instead. */
.mkt-pro{position:relative;overflow:hidden;
  background-image:linear-gradient(135deg,#FBF7F0 0%,#FBF7F0 45%,#E6F4F1 100%);
  color:var(--ink)}
.mkt-pro::before{content:"";position:absolute;top:-180px;left:-160px;width:480px;height:480px;
  border-radius:50%;background:radial-gradient(circle,rgba(35,161,140,.16) 0%,rgba(35,161,140,0) 68%);
  pointer-events:none;z-index:0}
.mkt-pro>.mkt-in{position:relative;z-index:1}
.mkt-pro h2{font-family:var(--mkt-font-head);font-weight:700;font-size:42px;max-width:20ch;margin-bottom:10px}
.mkt-pro p{color:var(--muted);max-width:60ch;margin-bottom:20px}

/* speciality card hover / focus -- the one interactive colour moment beyond
   buttons, and deliberately the same solid brand teal a button uses, not
   the pale --signal-soft tint (white text on the pale tint fails contrast).
   .proscard is shared with the authenticated Home page, so every rule here
   is scoped under .mkt-section -- Home's identical cards keep their
   original lighter hover untouched. */
.proscard-ic{color:var(--signal);margin-bottom:9px}
@media (prefers-reduced-motion: no-preference){
  .mkt-section .proscard,.mkt-section .proscard *{
    transition:background-color .15s ease-out,border-color .15s ease-out,color .15s ease-out;
  }
  /* The app-wide card hover (translateY + scale + a bigger shadow) is meant
     for cards sitting freely in open space. In a tight, evenly-aligned grid
     it lifts one card out of line with its neighbours -- so on this page the
     colour change on .proscard IS the whole hover state, nothing moves. */
  .mkt-section .proscard:hover,.mkt-section .proscard:focus-visible{
    transform:none;box-shadow:var(--shadow);
  }
}
.mkt-section .proscard:hover,.mkt-section .proscard:focus-visible{background:var(--signal);border-color:var(--signal)}
.mkt-section .proscard:hover .proscard-ic,.mkt-section .proscard:focus-visible .proscard-ic,
.mkt-section .proscard:hover .prof-spec,.mkt-section .proscard:focus-visible .prof-spec,
.mkt-section .proscard:hover .prof-generic,.mkt-section .proscard:focus-visible .prof-generic,
.mkt-section .proscard:hover .tiny,.mkt-section .proscard:focus-visible .tiny{color:#fff}
/* The default focus ring is signal-on-signal once the card fills teal --
   invisible exactly where it matters most. White reads against both the
   page background and the teal fill, whichever the 3px offset lands on. */
.mkt-section .proscard:focus-visible{outline-color:#fff}

/* the one motion moment on the page. Cards start hidden only inside the
   no-preference query, so a reduced-motion visitor has nothing gated at
   all -- see the equivalent note on the site-wide reveal in kinetic.js. */
@media (prefers-reduced-motion: no-preference){
  .mkt-reveal-item{opacity:0;transform:translateY(10px);
    transition:opacity .22s var(--ease-out),transform .22s var(--ease-out)}
  .mkt-reveal-item.mkt-reveal-in{opacity:1;transform:translateY(0)}
}

.mkt-foot{border-top:1px solid var(--line);padding:36px 0}
.mkt-foot-in{display:flex;justify-content:space-between;align-items:flex-end;flex-wrap:wrap;gap:18px}
.mkt-stores{display:flex;gap:10px;margin-top:16px}
.mkt-store-badge{display:inline-flex;align-items:center;height:40px;padding:0 14px;
  border:1px solid var(--line);border-radius:var(--rs);font-size:12.5px;font-weight:600;color:var(--muted)}
.mkt-store-badge:hover{border-color:#CFD6DA;color:var(--ink)}
.mkt-foot-links{display:flex;gap:20px;flex-wrap:wrap;font-size:13.5px}
.mkt-foot-links a{color:var(--muted)}
.mkt-foot-links a:hover{color:var(--ink)}
.mkt-foot-bottom{margin-top:22px;padding-top:18px;border-top:1px solid var(--line);
  font-size:12.5px;color:var(--muted)}

@media(max-width:900px){
  .mkt-head h2{font-size:32px}
  .mkt-hero-seat{font-size:52px}
  .mkt-hero-beginning{white-space:normal}
  .mkt-pro h2{font-size:30px}
  .mkt-pillars{grid-template-columns:1fr;gap:26px}
  .mkt-trust{grid-template-columns:1fr;gap:22px}
  .mkt-steps{grid-template-columns:1fr 1fr}
  .g4{grid-template-columns:1fr 1fr}
  .mkt-links{display:none}
  .mkt-foot-in{align-items:flex-start}
}
@media(max-width:520px){
  .mkt-hero-seat{font-size:38px}
  .mkt-steps{grid-template-columns:1fr}
  .g4{grid-template-columns:1fr}
  .mkt-foot-in{flex-direction:column;align-items:flex-start}
  /* At this width, .mkt-links is already hidden (see the 900px query above)
     and only the logo and .mkt-cta remain -- but the desktop 40px side
     padding left them an EXACT, zero-slack fit at 390px (logo + gap + cta
     summed to precisely 310px against a 310px content area). No slack at
     all meant "Sign in" lost a sub-pixel rounding tie and wrapped onto two
     lines. Trimming the side padding, not the content, restores real room
     without touching font sizes, gaps, or hiding anything further. */
  .mkt-nav-in{padding-inline:20px}
}

/* money */
/* No overflow:hidden here on purpose — the fee row embeds the "booking
   protection" (i) popover (partials/protection.ejs), which opens as an
   absolutely-positioned box wider than this container. overflow:hidden used
   to clip that box entirely: the popover opened and was instantly invisible,
   in all three places this component is used (book.ejs, book_confirm.ejs,
   booking.ejs). Rounded corners come from the end rows instead. */
.lines{border:1px solid var(--line);border-radius:var(--r);background:#fff}
.lines>div{display:flex;justify-content:space-between;padding:12px 15px;border-bottom:1px solid var(--line);font-size:14.5px}
.lines>div:first-child{border-top-left-radius:var(--r);border-top-right-radius:var(--r)}
.lines>div:last-child{border:none;font-weight:800;background:#FAFCFE;border-bottom-left-radius:var(--r);border-bottom-right-radius:var(--r)}
.protect{background:var(--blue-soft);border:1px solid #CFE3F3;border-radius:var(--r);padding:14px;margin-top:12px}
.protect b{display:block;margin-bottom:6px;font-size:14px;color:var(--blue-d)}
.protect li{list-style:none;font-size:13px;color:#2B4F6B;padding:2px 0 2px 18px;position:relative}
html[dir=rtl] .protect li{padding:2px 18px 2px 0}
.protect li:before{content:"✓";position:absolute;inset-inline-start:0;color:var(--ok);font-weight:700}

/* timeline */
.tl{position:relative;padding-inline-start:22px}
.tl:before{content:"";position:absolute;inset-inline-start:6px;top:6px;bottom:6px;width:2px;background:var(--line)}
.tl .step{position:relative;padding:6px 0 14px}
.tl .step:before{content:"";position:absolute;inset-inline-start:-22px;top:11px;width:12px;height:12px;border-radius:50%;background:#fff;border:2px solid var(--line)}
.tl .step.done:before{background:var(--ok);border-color:var(--ok)}
.tl .step.now:before{background:var(--orange);border-color:var(--orange);box-shadow:0 0 0 4px var(--orange-soft)}
.tl b{font-size:14px}
.tl p{font-size:13px;color:var(--muted)}

/* misc */
.flash{padding:12px 15px;border-radius:var(--rs);margin-bottom:16px;font-size:14px}
.flash.ok{background:var(--ok-soft);color:#08694A}
.flash.err{background:var(--bad-soft);color:#9B241D}
.empty{text-align:center;padding:44px 20px;color:var(--muted)}
.side{position:sticky;top:78px}
.code{font-family:ui-monospace,Menlo,monospace;font-size:30px;letter-spacing:.35em;font-weight:700;color:var(--blue-d);background:var(--blue-soft);padding:14px;border-radius:var(--rs);text-align:center}
table{width:100%;border-collapse:collapse;font-size:14px}
th,td{text-align:start;padding:10px 12px;border-bottom:1px solid var(--line)}
th{font-size:11.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
.hero{background:linear-gradient(135deg,var(--blue-d),var(--blue));color:#fff;border-radius:var(--r);padding:26px 24px;margin-bottom:20px}
.hero h1{color:#fff}.hero p{color:#B9C2C6}
.bubble{max-width:78%;padding:10px 14px;border-radius:14px;font-size:14px;margin-bottom:8px}
.bubble.bot{background:#fff;border:1px solid var(--line)}
.bubble.me{background:var(--blue);color:#fff;margin-inline-start:auto}

/* ── Apricot: decorative only ────────────────────────────────────────────────
   Warmth for invitations, empty states and illustration. The rule is simple:
   apricot may tint a SURFACE, never a CONTROL. Every button, link and badge on
   an apricot surface still uses the signal colour, so "act here" keeps meaning
   exactly one thing. */
.invite{background:var(--apricot-soft);border-color:var(--apricot-line);box-shadow:none}
/* The next appointment, set apart from an otherwise flat list — a tinted
   border and a slightly heavier shadow, not a new colour and not a badge
   competing with the status badge already on the row. */
a.nextappt{border-color:var(--signal);box-shadow:0 1px 2px rgba(22,35,46,.05),0 10px 26px -16px rgba(35,161,140,.35)}

.inboxrow{display:flex;gap:12px;align-items:center}

/* Same eyebrow-over-heading device the marketing page uses (.mkt-eyebrow,
   also --signal-d, also a small uppercase-weight label above the real
   heading) -- the greeting used to sit in plain --ink, which is what made a
   page that already shares every other token still read as a different
   design system the instant you looked at its own hero. */
.homehero{margin-bottom:28px}
.homehero-eyebrow{color:var(--signal-d);font-weight:700;font-size:13.5px;margin-bottom:8px}
.homehero h1{font-size:30px;margin-bottom:8px}
.empty{border:1px dashed var(--apricot-line);background:var(--apricot-soft);border-radius:var(--r);
  padding:30px 22px;text-align:center;color:var(--muted)}
.empty .mk{width:44px;height:44px;margin:0 auto 12px;border-radius:12px;
  background:var(--apricot);opacity:.22}
.empty b{display:block;color:var(--ink);font-size:15.5px;margin-bottom:4px}


/* ── added after Ziad's first hands-on review ──────────────────────────────
   Apricot was defined and then barely used. It marks a WARM SURFACE — never a
   control. Every button inside an apricot box is still mint, so "act here"
   keeps exactly one meaning. */

/* A7 · profession cards were different heights because the names wrap onto a
   second line at different lengths. Fixed height, text centred to it. */
/* height:100%, not min-height alone. The grid rows are already equal, but a
   card with a short name stopped at its own content height and sat at the
   bottom of its track — so "ABA / Behavior Therapy", whose name runs to three
   lines, drew a visibly taller frame than every card beside it. Filling the
   track makes every frame identical regardless of how long the name is.
   align-self:stretch does NOT do this here; it was measured. */
.proscard{display:flex;flex-direction:column;justify-content:space-between;
  min-height:96px;height:100%}

/* The finalized design gives the most-booked specialty (ABA) a real photo set
   BESIDE the text, not squeezed into a strip under it, and a wider frame --
   a modifier, not a change to .proscard itself, since that class is shared
   with the authenticated Home page (comment above) and Home's grid has no
   photography to show. Scoped under .mkt-section like every other .proscard
   interaction rule, so it only ever applies on the marketing page.

   grid-column:span 2 only makes sense at a width where .g4 actually has a
   second column to spare it (see .g4's own column counts below) -- taking
   the wide span at a width with no room for it is what used to leave the
   eighth, un-featured card alone in a final row nothing else occupied. */
.mkt-section .proscard-feature{grid-column:span 2;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,0.8fr);padding:0;overflow:hidden}
.mkt-section .proscard-feature .proscard-feature-body{padding:28px;display:flex;
  flex-direction:column;justify-content:space-between}
.mkt-section .proscard-feature .proscard-photo{position:static;min-height:210px;
  background-size:cover;background-position:50% 50%}
.mkt-section .proscard-feature:hover .proscard-photo,
.mkt-section .proscard-feature:focus-visible .proscard-photo{opacity:.92}
/* Below the width .g4 drops to two columns, a spanning ABA card would be the
   only thing in its row and leave the other seven an odd number to fill two
   columns with -- span 1 keeps every row a clean multiple, and the body/photo
   stack as one column instead of sitting side by side, which is the
   responsive adaptation the wide treatment was always meant to have. */
@media(max-width:900px){
  .mkt-section .proscard-feature{grid-column:span 1;grid-template-columns:1fr}
  .mkt-section .proscard-feature .proscard-photo{min-height:150px}
}

/* Base title size for every specialty card (standard AND featured/peer
   start here) -- .proscard-feature/.proscard-feature-peer below raise it.
   A class now, not the inline style it used to be, specifically so this
   override has something to win against without an !important. */
.prof-title{font-size:15px;line-height:1.35}
/* The two cards sharing Explore Care's taller first row (ABA's own feature
   treatment, and Speech sitting beside it at the same stretched row
   height -- see the "peer" comment in landing.ejs) get a noticeably more
   confident internal scale, so their content reads as intentionally
   featured rather than standard text stranded in a taller frame. Standard
   cards elsewhere in the grid are untouched. */
.mkt-section .proscard-feature .prof-title,
.mkt-section .proscard-feature-peer .prof-title{font-size:19px;line-height:1.3}
.mkt-section .proscard-feature .tiny,
.mkt-section .proscard-feature-peer .tiny{font-size:13.5px}
.mkt-section .proscard-feature .proscard-ic .ico,
.mkt-section .proscard-feature-peer .proscard-ic .ico{width:38px;height:38px}
@media(max-width:900px){
  /* Below the width the grid drops the featured span (see the media query
     above), ABA and Speech are just two more cards in the stack -- their
     enlarged type only makes sense next to each other in the same tall
     row, so it reverts to the standard scale there too. */
  .mkt-section .proscard-feature .prof-title,
  .mkt-section .proscard-feature-peer .prof-title{font-size:15px;line-height:1.35}
  .mkt-section .proscard-feature .tiny,
  .mkt-section .proscard-feature-peer .tiny{font-size:12px}
  .mkt-section .proscard-feature .proscard-ic .ico,
  .mkt-section .proscard-feature-peer .proscard-ic .ico{width:32px;height:32px}
}

/* A8 · the specialty carries the colour, the generic job word stays in ink. */
.prof-spec{color:var(--signal);font-weight:700}
.prof-generic{color:var(--ink);font-weight:600}

/* A9 · a clock sits beside every time and duration. */
/* No width/height here: the partial sets them per use, and a 1em rule silently
   overrode every explicit size — a 32px icon was rendering at 15. Only the
   alignment and spacing belong to the class. */
.ico{vertical-align:-.13em;flex:none}
/* Icons that sit inline with text get the trailing space and a little restraint;
   standalone ones (the specialty cards) get neither. */
.ico-inline{margin-inline-end:5px;opacity:.8}

/* G7 · booking protection is reassurance, not an instruction — the one place
   on the booking screen that should feel warm rather than transactional. */
.protection{background:var(--apricot-soft);border:1px solid var(--apricot-line);
            border-radius:var(--r);padding:16px 18px}
.protection b{color:#8A5A22}

/* E8 · filter panel picks up the same warmth so it reads as a helper rather
   than another form to fill in. */
/* G17 · the collapsed-by-default filter panel. A native <details> — no JS
   needed for it to open or remember state across the two form submits inside
   it (the actual filter apply, and Clear). */
.filters-toggle{padding:0;overflow:hidden}
.filters-toggle summary{list-style:none;display:flex;align-items:center;gap:8px;
  padding:14px 18px;cursor:pointer}
.filters-toggle summary::-webkit-details-marker{display:none}
.filters-toggle .chev{margin-inline-start:auto;color:var(--muted);font-size:12px}
.filters-toggle[open] .chev{transform:rotate(180deg)}
.filters-toggle summary:hover{background:var(--charcoal-soft)}
.filters-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
  gap:14px;padding:4px 18px 18px;border-top:1px solid var(--line)}
.filters-actions{display:flex;gap:10px;align-items:flex-end}

/* B7/A9 · duration chip in a booking row. */
.dur{display:inline-flex;align-items:center;font-size:12.5px;color:var(--muted);
     background:var(--charcoal-soft);border-radius:20px;padding:2px 9px;margin-inline-start:6px}

/* I1 · report flag. Quiet until hovered — it should be findable, not inviting. */
.flag{color:#B9C0C4;display:inline-flex;align-items:center;padding:4px}
.flag:hover{color:var(--bad)}

/* I1 · the flag that MARKS a booking as reported, in any list that shows one.
   Red only while the case is live; once resolved or expired it goes muted. The
   record should stay visible without continuing to alarm someone who is just
   scrolling back through their own history. */
.repflag{display:inline-flex;align-items:center;gap:5px;margin-top:5px;
         font-size:11.5px;font-weight:600;line-height:1;padding:3px 8px;
         border-radius:20px;background:var(--bad-soft);color:var(--bad)}
.repflag.done{background:var(--charcoal-soft);color:var(--muted);font-weight:500}

/* G1 · the cancellation policy, shown before booking rather than linked to.
   Apricot, because this is the warm-caution register — it is not an error and
   it is not a sales point; it is the platform being straight with someone about
   what happens if things go wrong. */
.policy{background:var(--apricot-soft);border:1px solid var(--apricot-line);
        border-radius:12px;padding:14px 16px;margin-top:16px}
.policy-h{display:flex;align-items:center;gap:6px;font-size:13.5px;margin-bottom:8px}
.policy ul{margin:0;padding-inline-start:18px;display:flex;flex-direction:column;gap:6px}
.policy li{font-size:13px;line-height:1.5;color:var(--ink)}
.policy li.muted{color:var(--muted);font-size:12.5px}
.policy.compact li{font-size:12.5px}
.policy.compact{padding:12px 14px}

/* G2 · the confirmation screen. */
.lines.review div{align-items:flex-start;gap:18px}
.lines.review div span:first-child{color:var(--muted);white-space:nowrap;min-width:88px}
.lines.review div span:last-child{text-align:end}
/* .lines>div:last-child (further down) bolds the row, drops its background
   and removes its border — exactly right for a price breakdown's "Total"
   line, and wrong here: whichever fact happens to be listed last (often
   "Where") is not a total and was reading as one purely by coincidence of
   position. Every row in a review list carries equal weight, full stop —
   restore the plain border and undo the unwanted bold. */
.lines.review>div:last-child{font-weight:400;background:none;border-bottom:0}
.lines.review>div:not(:last-child){border-bottom:1px solid var(--line)}
.ackbox{display:flex;align-items:flex-start;gap:9px;margin-top:14px;
        font-size:13px;line-height:1.45;cursor:pointer}
.ackbox input{margin:2px 0 0}
.btn.ghost.full{display:block;text-align:center;text-decoration:none}

/* F8 · reliability, shown instead of a bare failure count. */
.reliability{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--muted)}
.reliability b{color:var(--ink)}

/* A4 · bell with its unread count beside it, not instead of it. */
.bell{display:inline-flex;align-items:center;gap:4px}
.bell .dot{background:var(--signal);color:#fff;font-size:11px;font-weight:700;
           border-radius:20px;padding:1px 6px;line-height:1.5}

/* G3 · month heading over each calendar block. */
.calmonth{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
          color:var(--muted);margin:16px 0 8px}
.calmonth:first-of-type{margin-top:4px}

/* The inbox. Messages sit on opposite sides so a thread is scannable without
   reading names — the oldest trick in messaging UI and still the best one. */
.thread{display:flex;flex-direction:column;gap:10px;margin-top:14px}
.msg{max-width:74%;align-self:flex-start;background:#fff;border:1px solid var(--line);
     border-radius:14px;padding:10px 13px}
.msg.mine{align-self:flex-end;background:var(--signal-soft);border-color:#BFE4DB}
.msg .body{font-size:14.5px;white-space:pre-wrap;line-height:1.5}
.msg .when{font-size:10.5px;color:var(--muted);margin-top:5px}

/* Attachments in a thread. Images preview inline because a photographed report
   is unreadable as a filename; everything else stays a labelled link. */
.att-img{max-width:280px;border-radius:10px;display:block}
.att-file{display:flex;align-items:center;gap:7px;font-size:13.5px;color:var(--ink);
          text-decoration:none;padding:2px 0}
.att-file:hover{color:var(--signal-d)}
.msg .body.removed{color:var(--muted);font-style:italic}
.linkbtn{background:none;border:0;padding:0;margin-inline-start:8px;color:var(--muted);
         font-size:10.5px;cursor:pointer;text-decoration:none;border-bottom:1px solid currentColor}
.linkbtn:hover{color:var(--bad)}
details summary{cursor:pointer}

/* Progressive disclosure with no JavaScript: <details> as a popover. The
   trigger is an icon button; the panel floats so it never pushes the
   conversation around when it opens. */
.pop{position:relative}
/* THE actual cause of every "Decline"/"Cancel"/"Remove" button reading as
   clipped across this whole app: this rule forces every <summary> inside a
   .pop into a fixed 40x40 icon-button box, with nothing to override it for
   confirm.ejs's summary — which is a real labeled .btn, not an icon. A
   40px-wide box around "Cancel booking" can only ever show a fragment of it.
   Restructuring the surrounding rows (done earlier) helped some cases but
   never touched this. Fixed below: any summary that is ALSO a .btn sizes to
   its own content instead. */
.pop>summary.btn{width:auto;height:auto;border-radius:var(--rs)}
.pop>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;
  justify-content:center;width:40px;height:40px;border:1px solid var(--line);
  border-radius:11px;background:#fff;color:var(--muted)}
.pop>summary::-webkit-details-marker{display:none}
.pop>summary:hover{color:var(--signal);border-color:var(--signal)}
.pop>summary.danger-hover:hover{color:var(--bad);border-color:var(--bad)}
.pop[open]>summary{color:var(--signal);border-color:var(--signal)}
.pop[open]>summary.danger-hover{color:var(--bad);border-color:var(--bad)}

/* The report flag: solid red and smaller than the standard 40px popover
   trigger, and — on purpose — identical at rest, on hover and while open.
   "Turns red when you touch it" undersells what pressing it actually does.
   Block sits right beside it, same fixed height so the two read as one
   matched pair — Block is simply wider because "Block" is a word and the
   flag is not. Both scoped to this one row only. */
.chatflags .pop>summary.flagbtn{width:26px;height:26px;border-radius:8px;background:var(--bad);border-color:var(--bad);color:#fff}
.chatflags .pop>summary.flagbtn:hover,.chatflags .pop[open]>summary.flagbtn{background:var(--bad);border-color:var(--bad);color:#fff}
.chatflags .btn.sm{height:26px;padding:0 10px;font-size:12px}

/* The document row's options trigger sits beside two .btn.sm buttons — the
   default 40px popover box would tower over them the same way the flag/block
   pair once did, so it is sized to match .btn.sm instead. */
.docrow .pop>summary{width:34px;height:34px;border-radius:10px}
.pop-body{position:absolute;z-index:40;width:310px;background:#fff;
  border:1px solid var(--line);border-radius:14px;padding:15px;box-shadow:var(--shadow);
  inset-inline-start:0;top:48px}
.pop-body.end{inset-inline-start:auto;inset-inline-end:0}
.pop-body.up{top:auto;bottom:48px}
.pop-body input[type=file]{font-size:13px;max-width:100%}

/* Client specialty foundation (Find Care, views/find_care.ejs). Deliberately
   its own classes, not .proscard: the public Explore Care page keeps its
   own editorial treatment (including ABA's featured photo layout) exactly
   as approved, untouched by anything below. One card shape rendered eight
   times from one EJS loop (see the view) -- structure, padding, badge size
   and CTA are identical for every specialty; only the icon, name,
   description and tint differ, and none of the eight is featured/larger.

   The eight tints are all muted, low-chroma washes in the same lightness
   family as the app's existing --signal-soft/--apricot-soft tokens --
   deliberately none of them IS --signal-soft: mint already means
   "actionable/confirmed" everywhere else (buttons, the active nav item),
   and putting that exact color on one specialty card would quietly borrow
   that "this one matters more" meaning even with the structure otherwise
   identical. This is presentation only -- no palette token changed, no new
   brand color, just eight small washes scoped to this one page. */
.specgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:26px}
@media(max-width:900px){.specgrid{grid-template-columns:repeat(2,1fr)}}

.clientspec{display:flex;flex-direction:column;height:100%;position:relative}
.clientspec .tophead{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}
.clientspec-name{font-size:15px;line-height:1.35;margin-top:10px;display:block}
.clientspec-desc{margin:6px 0 12px}
.clientspec .btn.ghost{margin-top:auto;align-self:flex-start;background:#fff}

.t-rose{background:#F5E6E4}
.t-blue{background:#E4EEF7}
.t-sage{background:#E9EFE1}
.t-apricot{background:#F7E9D7}
.t-yellow{background:#FBF1D6}
.t-lilac{background:#EFE8F0}
.t-sand{background:#F1E9DA}
.t-slate{background:#E7ECEF}

/* The illustrated badge: the icon lifted onto its own small white plate
   with a soft offset accent circle behind it, rather than sitting bare on
   the tinted card. Same icon paths as everywhere else in the app
   (partials/icons.ejs) -- only the surrounding plate is new. */
.badge-wrap{position:relative;width:52px;height:52px;margin-bottom:4px}
.badge-wrap .accent{position:absolute;width:26px;height:26px;border-radius:50%;
  background:rgba(255,255,255,.55);top:-5px;inset-inline-end:-7px}
.badge-wrap .plate{position:relative;width:52px;height:52px;border-radius:16px;
  background:rgba(255,255,255,.62);display:flex;align-items:center;justify-content:center;
  color:var(--signal);box-shadow:0 1px 2px rgba(22,35,46,.05)}

.clientspec-info>summary{width:24px;height:24px;border-radius:999px;
  font-size:12px;font-style:italic;font-family:Georgia,serif;background:rgba(255,255,255,.65)}
/* .pop-body's default position (48px below its own trigger, at a fixed
   310px width) is right for a document row or a chat header, where there is
   plain page background underneath. Inside this small card that same
   offset landed the box across the middle of the description text --
   correctly opaque, but only tall enough to cover its first line, leaving
   the rest of the sentence visible below it. Overriding .clientspec-info
   to NOT establish its own positioning context (position:static cancels
   .pop's default) means the open panel anchors to .clientspec itself
   instead, so it can cover the card's whole body cleanly -- one clear
   answer, not a box cutting through a paragraph. */
.clientspec-info{position:static}
.clientspec-info .pop-body{
  position:absolute;top:44px;bottom:14px;left:14px;right:14px;width:auto;
  display:flex;align-items:center;justify-content:center;text-align:center;
  background:#fff;
}

/* The assessments hand-off card -- visually distinct from the specialty
   grid (dashed border, no tint, spans full width below it) so it reads
   immediately as "a different kind of thing", not a ninth specialty
   squeezed into the same row. */
.asmt{display:flex;align-items:center;gap:16px;background:#fff;
  border:1px dashed var(--apricot-line);border-radius:var(--r);padding:18px 20px;
  margin-bottom:22px;text-decoration:none;color:inherit;box-shadow:var(--shadow)}
.asmt b{font-size:15.5px;display:block;margin-bottom:2px}
.asmt-arrow{margin-inline-start:auto;color:var(--signal-d);font-weight:700;font-size:14px;white-space:nowrap}
@media(max-width:600px){.asmt{flex-direction:column;align-items:flex-start;gap:10px}.asmt-arrow{margin-inline-start:0}}

.composer{display:flex;gap:10px;align-items:flex-start;margin-top:14px}
.composer .grow{flex:1}

/* ── the guidance chips ────────────────────────────────────────────────────
   Selection is APRICOT, not the signal colour. That is deliberate and it is the
   one place the rule bends: these are not actions, they are things the person
   is telling us about themselves, and apricot is the app's warm surface. Using
   mint here would make "I am worried about my child's speech" look like a
   button that does something. */
.qblock{margin-bottom:12px}
.qlabel{font-size:15px;font-weight:600;display:block;margin-bottom:8px}
.chips{display:flex;flex-wrap:wrap;gap:8px}
.chip{display:inline-flex;align-items:center;border:1px solid var(--line);
      border-radius:22px;padding:8px 15px;font-size:13.5px;cursor:pointer;
      background:#fff;transition:background .12s,border-color .12s,color .12s;
      user-select:none}
.chip input{position:absolute;opacity:0;width:0;height:0;margin:0}
.chip:hover{border-color:var(--apricot-line)}
.chip:has(input:checked){background:var(--apricot-soft);border-color:var(--apricot);
      color:#8A5A22;font-weight:600}
.chip:has(input:focus-visible){outline:2px solid var(--signal);outline-offset:2px}

/* Follow-ups appear only once the option that needs them is chosen. Done with
   :has() rather than JavaScript, so it works before any script loads and
   cannot get out of step with what is actually selected. */
.reveal{display:none;margin-top:12px}
.qblock:has(input[value="__other"]:checked) .other-reveal{display:block}
.qblock:has(input[value="18+"]:checked) .age-reveal{display:block}

/* Name search above the filter panel — the fast path for someone who already
   knows who they are looking for. */
.namesearch{display:flex;gap:9px;margin-bottom:16px}
.namesearch input{flex:1}

/* G6 · when a service can only happen one way, say so instead of showing a
   dropdown with a single option — a select the user cannot change is a control
   that lies about being a choice. */
.onlyfmt{margin:6px 0 0;font-size:14px}
.onlyfmt .muted{font-size:12.5px}

/* ── G5 · the therapist's weekly hours ─────────────────────────────────────
   Seven rows that have to stay scannable. The day name and the hours in words
   sit on one line at the top, so a therapist can read their whole week without
   parsing seven sets of time inputs — the inputs are for editing, the summary
   line is for reading, and those are different jobs. */
.dayrow+.dayrow{margin-top:10px}
.daytop{align-items:baseline;margin-bottom:10px}
.winrows{display:flex;flex-wrap:wrap;gap:8px 14px}
.winrow{display:flex;align-items:center;gap:7px}
.winrow input[type=time]{width:120px;padding:8px 10px;border:1px solid var(--line);
  border-radius:var(--rs);font-size:14px;font-family:inherit;background:#fff;color:var(--ink)}
.winrow .dash{color:var(--muted);font-size:13px}
.copyform{margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
.copyform .chips{margin-top:6px}
.copyform .chip{cursor:pointer}
.copyform .chip:has(input:checked){background:var(--charcoal);border-color:var(--charcoal);color:#fff;font-weight:600}

.offlist{margin-top:12px;display:flex;flex-direction:column}
.offrow{display:flex;justify-content:space-between;align-items:center;gap:12px;
  padding:9px 0;border-bottom:1px solid var(--line)}
.offrow:last-child{border-bottom:0}

/* ── F1/F2/F6/F7/F10 · the therapist's profile editor ─────────────────────── */
.agerange{display:flex;align-items:center;gap:9px}
.agerange input{width:88px}
.credlist{display:flex;flex-direction:column;margin-bottom:6px}
/* flex-wrap added on purpose: the services row grew a 4th control (Edit) on
   top of the badge/Pause/Remove that were already here, and a row that
   cannot wrap has nowhere to put content that no longer fits beside the
   name — it just runs past the edge instead. */
.credrow{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 14px;
  padding:11px 0;border-bottom:1px solid var(--line)}
.credrow:last-child{border-bottom:0}
.addcred{margin-top:14px;padding-top:16px;border-top:1px solid var(--line)}
.profphoto{width:100%;aspect-ratio:1;object-fit:cover;border-radius:var(--r);
  border:1px solid var(--line);display:block}
.profvideo{width:100%;border-radius:var(--r);border:1px solid var(--line);display:block;background:#000}
input[type=file]{width:100%;font-size:13px;font-family:inherit;color:var(--muted)}

/* The public profile: a real face where initials used to be. Sized to match the
   .av.lg circle it replaces so no other layout has to change. */
.av.photo{padding:0;overflow:hidden;background:var(--line)}
.av.photo img{width:100%;height:100%;object-fit:cover;display:block}
.provvideo{width:100%;max-width:420px;border-radius:var(--r);border:1px solid var(--line);
  display:block;background:#000;margin-top:12px}
.credmeta{display:block;color:var(--muted);font-size:12px;margin-top:1px}

/* F8 · reliability. Quiet by default — this is a good-news line for almost
   everyone who sees it, and it should not shout. It only takes the warning
   colour when the number genuinely warrants a second look. */
.reliability{display:flex;align-items:center;gap:7px;margin-top:14px;padding:9px 13px;
  border-radius:var(--rs);background:var(--ok-soft);color:var(--ok);
  font-size:13px;font-weight:600}
.reliability.concern{background:var(--warn-soft);color:var(--warn)}

/* The child's record. Rows, not cards — a record is a list you scan for the one
   thing you need, not a gallery. */
.doclist{display:flex;flex-direction:column;margin-top:8px}
.docrow{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 14px;
  padding:11px 0;border-bottom:1px solid var(--line)}
.docrow:last-child{border-bottom:0}

/* K3 · audit detail as facts, not JSON. Key in the muted colour, value in ink,
   so the eye lands on the number rather than on the field name. */
.adetail{display:flex;flex-wrap:wrap;gap:5px 14px;margin-top:4px}
.apair{display:inline-flex;align-items:baseline;gap:5px;font-size:12px}
.apair .ak{color:var(--muted)}
.apair .av2{color:var(--ink);font-weight:600}

/* K4–K7 · the admin numbers. */
.daterange{align-items:center;gap:10px}
.daterange input[type=date]{width:auto;padding:8px 10px;font-size:13.5px}
.daterange label{font-size:12.5px;color:var(--muted);font-weight:600}
.scrollx{overflow-x:auto}
.atable{border-collapse:collapse;width:100%;min-width:420px;background:#fff;
  border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.atable th{text-align:start;font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);padding:10px 14px;background:var(--charcoal-soft);border-bottom:1px solid var(--line)}
.atable td{padding:10px 14px;border-bottom:1px solid var(--line);font-size:13.5px;
  font-variant-numeric:tabular-nums}
.atable tbody tr:last-child td{border-bottom:0}
/* Numbers sit centred UNDER their own heading. They used to be end-aligned
   while the headings were start-aligned, so "3" floated a column-width away
   from the word "Therapists" it belonged to and the eye had to guess which
   number went with which heading. Header and cells are aligned together here
   precisely so they can never drift apart again. */
.atable th:not(:first-child),
.atable td:not(:first-child){text-align:center}
.atable .empty-row td{color:var(--muted)}

/* ══════════════════════════════════════════════════════════════════════════
   Hover: lift, never underline
   ══════════════════════════════════════════════════════════════════════════
   Ziad's call, and the right one. Underline-on-hover shifts the baseline and
   makes a calm page twitch under the cursor. A small lift says "this responds"
   without moving anything around it.

   Kept deliberately small: 1.5% on cards, 2% on small controls. Anything more
   and a page of cards ripples as the mouse crosses it, which reads as a toy.
   Transform-only so nothing reflows — the element scales in place and its
   neighbours never move. Anyone who has asked their system not to animate gets
   the colour change and no movement at all. */
@media (prefers-reduced-motion: no-preference){
  .btn,.chip,.pcard,.proscard,.card.invite,.docrow .btn,.slots label,
  .cal a,.badge.new,.att-file,.repflag,a.rolecard-photo{
    transition:transform .13s ease, box-shadow .13s ease,
               background-color .13s ease, border-color .13s ease, color .13s ease;
  }
  .btn:hover:not([disabled]){transform:translateY(-1px) scale(1.02)}
  .btn:active:not([disabled]){transform:translateY(0) scale(.99)}
  .chip:hover{transform:scale(1.03)}
  .cal a:hover{transform:scale(1.06)}
  .slots label:hover{transform:scale(1.03)}
  /* Whole cards that are themselves links. Barely there — 1.5% across a big
     surface is already visible, where 3% would look like it jumped. */
  a.pcard:hover,a.proscard:hover,a.rolecard-photo:hover{transform:translateY(-2px) scale(1.005);
    box-shadow:0 2px 4px rgba(22,35,46,.06),0 14px 30px -18px rgba(22,35,46,.34)}
}
/* Colour-only fallbacks, so hover still reads with motion turned off. */
.nav a:hover{color:var(--ink)}
a.pcard:hover,a.proscard:hover,a.rolecard-photo:hover{border-color:#CFD6DA}

/* ── the (i) beside "Booking protection" ───────────────────────────────── */
.feelab{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--muted)}
.ibadge{display:inline-flex;align-items:center;justify-content:center;
  width:13px;height:13px;border-radius:50%;border:1px solid var(--muted);
  color:var(--muted);font-size:9px;font-weight:700;font-style:italic;line-height:1}
.protpop>summary{border:0;padding:0}
.protpop>summary:hover .ibadge{border-color:var(--signal);color:var(--signal)}
.protlist{margin:8px 0 0;padding:0;display:flex;flex-direction:column;gap:6px}
.protlist li{list-style:none;font-size:12.5px;line-height:1.45;color:var(--ink);
  padding-inline-start:18px;position:relative}
.protlist li:before{content:"✓";position:absolute;inset-inline-start:0;
  color:var(--ok);font-weight:700}
.feeline{display:flex;align-items:center;gap:6px;margin:6px 0 0}

/* ── stats row: the unit rides small beside the number ─────────────────── */
/* display:inline is the whole fix. `.stats span{display:block}` above catches
   EVERY span inside a stat, including this one — so "8" and "س" stacked on top
   of each other instead of reading as "8س". The unit is part of the number,
   not a second line under it. */
.stats b .unit{display:inline;font-size:12px;font-weight:600;color:var(--muted);
  margin-inline-start:1px;white-space:nowrap}

/* ── credentials, grouped Education → Certificates → Courses ───────────── */
.credgroup{font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);font-weight:700;margin:16px 0 6px}
.credgroup:first-of-type{margin-top:8px}
.credline{display:flex;gap:9px;align-items:flex-start;padding:7px 0;
  border-bottom:1px solid var(--line)}
.credline:last-child{border-bottom:0}
.credtick{flex:none;line-height:1.5}

/* ── calendar: two real months, side by side ───────────────────────────── */
.calpair{display:grid;grid-template-columns:1fr 1fr;gap:22px}
@media(max-width:720px){.calpair{grid-template-columns:1fr}}
.calblock .calmonth{margin-top:0}
.cal .pad{border:0;background:none}
.cal .off{color:#C7CFD4;background:#FBFCFC;border-color:transparent;font-weight:500}
.monthpick{width:auto;padding:7px 10px;font-size:13px}

/* ── a file picker that does not look like 1998 ────────────────────────── */
/* The native control cannot be styled, so it is hidden and its LABEL becomes
   the button. The input stays in the DOM and keeps working with the keyboard —
   this is a costume, not a replacement. */
.filepick{display:block;margin-top:10px}
.filepick input[type=file]{position:absolute;width:1px;height:1px;padding:0;
  margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.filepick .fpbtn{display:flex;align-items:center;gap:9px;cursor:pointer;
  border:1px dashed #C9D3D8;border-radius:var(--rs);padding:12px 14px;
  background:#FBFCFC;color:var(--muted);font-size:13.5px;
  transition:border-color .13s ease,background-color .13s ease,color .13s ease}
.filepick:hover .fpbtn{border-color:var(--signal);color:var(--signal-d);background:var(--signal-soft)}
.filepick input[type=file]:focus-visible + .fpbtn{outline:2px solid var(--signal);outline-offset:2px}
.filepick .fpname{margin-inline-start:auto;color:var(--ink);font-weight:600;
  max-width:48%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ── bookings: three tabs and a filter row ─────────────────────────────── */
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:14px}
.tab{display:inline-flex;align-items:center;gap:7px;padding:10px 15px;
  font-size:14px;font-weight:600;color:var(--muted);border-bottom:2px solid transparent;
  margin-bottom:-1px;border-radius:var(--rs) var(--rs) 0 0}
.tab:hover{color:var(--ink);background:var(--charcoal-soft)}
.tab.on{color:var(--ink);border-bottom-color:var(--signal)}
.tabn{display:inline-flex;align-items:center;justify-content:center;min-width:19px;
  height:19px;padding:0 6px;border-radius:999px;background:var(--charcoal-soft);
  color:var(--muted);font-size:11.5px;font-weight:700}
.tab.on .tabn{background:var(--signal-soft);color:var(--signal-d)}
.bkfilters{display:flex;flex-wrap:wrap;gap:9px;align-items:center;margin-bottom:16px}
.bkfilters select,.bkfilters input[type=date]{width:auto;padding-block:8px;padding-inline-start:11px;font-size:13.5px}
.bkfilters select{padding-inline-end:30px}
.bkfilters input[type=date]{padding-inline-end:11px}

/* Guide results: the suggested specialty, then the actual people. */
.guidematches{margin-top:14px;padding-top:14px;border-top:1px solid var(--line)}

/* ── Records as folders ────────────────────────────────────────────────── */
.folder{background:#fff;border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow);margin-bottom:12px;overflow:hidden}
.folderhead{display:flex;align-items:center;gap:12px;padding:15px 18px;color:inherit}
.folderhead:hover{background:var(--charcoal-soft)}
.folder.open .folderhead{border-bottom:1px solid var(--line)}
.foldertoggle{color:var(--muted);font-size:18px;line-height:1}
.foldercontents{padding:4px 18px 14px}
.docpick{display:flex;align-items:flex-start;gap:10px;margin:0;font-weight:400;cursor:pointer;flex:1}
.docpick input{margin-top:3px}
.sharebar{position:sticky;bottom:12px;display:flex;flex-wrap:wrap;gap:12px;align-items:center;
  background:var(--charcoal);color:#fff;border-radius:var(--r);padding:14px 18px;margin-top:18px;
  box-shadow:0 8px 28px -12px rgba(20,26,30,.5)}
.sharebar .tiny.muted{color:#A9B4BA}
.sharebar select{width:auto}

/* Who first brought a document into the app. A document is filed once, under
   the therapist whose conversation it arrived in, and forwarding never makes a
   second copy — so the folder says WHICH relationship, and this says WHO. */
.origin{display:inline-block;padding:1px 7px;border-radius:999px;
  background:var(--charcoal-soft);color:var(--ink);font-weight:600;font-size:11px}

/* ── the therapist's month ─────────────────────────────────────────────────
   Two states carry colour and no more: mint for a session that is happening,
   amber for a request waiting on an answer. Apricot is deliberately not used
   here — the palette reserves it for decoration, and a request needing action
   is the opposite of decorative. */
.calwork .daycell{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:3px;padding:8px 0 6px;border:1px solid var(--line);border-radius:var(--rs);
  background:#fff;color:var(--ink);font-weight:600;font-size:14px;min-height:46px}
.calwork .daycell:hover{border-color:var(--signal)}
.calwork .daycell.on{background:var(--charcoal);border-color:var(--charcoal);color:#fff}
.calwork .dots{display:flex;gap:3px;height:6px;align-items:center}
/* Provider dashboard summary row -- the finalized design's "needs your
   answer" / "next up" pair, built entirely from data the /p route already
   computes (the pending count, and upcoming[0]) so no query or data shape
   changed. Deliberately a summary and a link to the real Requests page,
   not a second copy of its accept/decline form -- that form's ownership
   and status-transition logic (booking.js, §14 known risks) stays in
   exactly one place. */
.dash-row{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px}
.dash-card{border-radius:22px}
.dash-needs{background:var(--signal-soft);border-color:var(--signal-soft)}
.dash-needs-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.dash-needs-eyebrow{font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--signal-d)}
.dash-needs-count{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 7px;
  border-radius:999px;background:var(--signal);color:#fff;font-weight:700;font-size:12px}
.dash-needs .btn{margin-top:16px}
.dash-next-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.dash-next-eyebrow{font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.dash-next-time{display:flex;align-items:baseline;gap:9px;margin-bottom:4px}
.dash-next-time b{font-size:30px;letter-spacing:-.02em}
.dash-next-time span{font-size:13px;color:var(--muted)}
.dash-next-what{font-weight:600;font-size:15px;margin-bottom:2px}
@media(max-width:820px){.dash-row{grid-template-columns:1fr}}
.dot-c,.dot-n{display:inline-block;width:6px;height:6px;border-radius:50%}
.dot-c{background:var(--signal)}
.dot-n{background:var(--warn)}
.calwork .daycell.on .dot-c{background:#6FE3C7}
.calwork .daycell.on .dot-n{background:#F0B45E}
.callegend{display:flex;gap:18px;margin-top:12px;font-size:12px;color:var(--muted)}
.callegend span{display:inline-flex;align-items:center;gap:6px}

.slotcard{border:1px solid var(--line);border-left:3px solid var(--line);
  border-radius:var(--rs);padding:12px 14px;margin-top:10px;background:#fff}
html[dir=rtl] .slotcard{border-left:1px solid var(--line);border-right:3px solid var(--line)}
.slotcard.live{border-inline-start-color:var(--signal);background:var(--signal-soft)}
.slotcard.needs{border-inline-start-color:var(--warn);background:var(--warn-soft)}
.slotcard.past{opacity:.75}
.dayoff{background:var(--charcoal-soft);color:var(--muted);border-radius:var(--rs);
  padding:10px 13px;font-size:13px;font-weight:600}

/* ══════════════════════════════════════════════════════════════════════════
   KINETIC LAYER
   ══════════════════════════════════════════════════════════════════════════
   One rule governs everything below: motion has to be doing a job. Each
   effect here answers a question the user is actually asking.

     "Is this thing pressable?"        → it lifts under the cursor
     "Did my click land?"              → it presses in, in 90ms
     "Where am I on this page?"        → sections arrive as you reach them
     "Which of these is the new one?"  → the count pops once, then stops

   Nothing loops. Nothing bounces except a single arriving count. Nothing
   runs longer than 260ms. Anything decorative was cut.

   Everything that MOVES lives inside the reduced-motion guard, and every
   moving effect has a colour or shadow twin outside it — so a user who has
   asked their system to stop animating still gets the whole answer, just
   without the travel. That is not a downgrade path; it is the same
   information delivered statically.                                        */

/* ── 1 · scroll reveal ─────────────────────────────────────────────────────
   Direct children of the page column only. Reveal the CONTAINER, not each
   item inside it: a records page with 40 documents should breathe in as
   folders, not flicker forty times.

   The hidden state is gated on html.kin, which a two-line script in <head>
   sets before first paint — so nothing is ever painted visible and then
   yanked away. If the script that reveals them fails to load, a timer in
   that same inline block shows everything anyway. Content is never left
   invisible because a stylesheet won a race. */
@media (prefers-reduced-motion: no-preference){
  /* :where() is load-bearing, not stylistic. Written as a plain
     `*:not(.sharebar):not(script)` this selector scores HIGHER than the rule
     below that reveals things — the two :not() arguments each contribute — so
     the hidden state won permanently and every page shipped with invisible
     blocks even though the reveal class had been applied correctly. :where()
     contributes nothing to specificity, which puts the reveal safely on top.
     Do not "simplify" this back. */
  html.kin main.wrap > :not(:where(.sharebar, script)){
    opacity:0; transform:translate3d(0,10px,0);
    /* will-change is deliberately NOT set here. Promoting every block on the
       page to its own layer costs more than the animation saves; the browser
       already handles opacity+transform on the compositor. */
  }
  html.kin main.wrap > .kin-in{
    opacity:1; transform:none;
    transition:opacity var(--t-reveal) var(--ease-out),
               transform var(--t-reveal) var(--ease-out);
    /* The cascade delay, set per element by the reveal script and defaulting
       to none. Kept in a custom property rather than an inline transition so
       that it cannot leak into any other transition this element has. */
    transition-delay:var(--kin-d,0ms);
  }
}

/* The sticky header, once content has scrolled beneath it. Shadow only — the
   bar itself must not move, because a header that shifts while you scroll is
   the thing every user notices and nobody likes. */
.top.lifted{box-shadow:0 1px 3px rgba(20,26,30,.06),0 10px 24px -20px rgba(20,26,30,.5)}
@media (prefers-reduced-motion: no-preference){
  .top{transition:box-shadow var(--t-mid) var(--ease-out)}
}

/* ── 2 · press ─────────────────────────────────────────────────────────────
   The single highest-value effect in this file. A press that answers in 90ms
   is the difference between an interface that feels connected to your hand
   and one you have to trust. It is also the honest signal on a slow Egyptian
   mobile connection: the tap registered, the network is what is slow. */
@media (prefers-reduced-motion: no-preference){
  .tab,.slots a,.calwork .daycell,.folderhead,.pop>summary,
  .filepick .fpbtn,.bell,.logo,.docrow,.slotcard,.nav a,.repflag,.att-file{
    transition:transform var(--t-fast) var(--ease-out),
               box-shadow var(--t-fast) var(--ease-out),
               background-color var(--t-fast) var(--ease-out),
               border-color var(--t-fast) var(--ease-out),
               color var(--t-fast) var(--ease-out);
  }
  .btn:active:not([disabled]),
  .chip:active,.tab:active,.slots a:active,.calwork .daycell:active,
  .folderhead:active,.pop>summary:active,.filepick:active .fpbtn{
    transform:scale(.978);
    transition-duration:var(--t-press);
    transition-timing-function:var(--ease-press);
  }
}

/* ── 3 · hover, on everything that can be pressed ──────────────────────────
   Consistency is the point. Before this, buttons and cards lifted while tabs,
   folders, calendar days and the header sat dead — so the page taught you
   that some things respond and left you guessing about the rest. */
@media (prefers-reduced-motion: no-preference){
  .nav a:hover{transform:translateY(-1px)}
  .logo:hover{transform:scale(1.015)}
  .bell:hover{transform:scale(1.06)}
  .tab:hover{transform:translateY(-1px)}
  .slots a:hover{transform:scale(1.03)}
  .calwork .daycell:hover{transform:scale(1.05);box-shadow:var(--lift-1)}
  .att-file:hover{transform:translateY(-1px)}
  /* Whole-surface hovers: one elevation step, no scale. A card that is 700px
     wide cannot scale even 1% without its edge visibly sliding. */
  .slotcard:hover{box-shadow:var(--lift-2)}
  .folder:has(.folderhead:hover){box-shadow:var(--lift-2)}
}
/* Static twins — these apply with motion off, and carry the same meaning. */
.slotcard:hover{border-color:#CFD6DA}
/* Records rows.
   Two separate things, and they were conflated before:

   WHAT TRIGGERS the highlight — hovering the document itself (its tick box,
   name and details), not the row of Preview/Download/Rename buttons on the
   far side. Those buttons are their own controls with their own hover; tinting
   the entire row when the cursor is over one of them said "this whole line is
   one thing to click", which it is not.

   HOW FAR IT REACHES — all the way to the frame on both sides. The row sits
   inside 18px of folder padding, so a plain background stopped short of the
   edges and read as a floating grey block dropped into the folder rather than
   as a highlighted line of it. The negative margin pushes it back out to the
   frame and the matching padding puts the content back where it was. Inline
   properties, so Arabic bleeds to the correct edges without a second rule. */
.docrow{margin-inline:-18px;padding-inline:18px}
.docrow:has(.docmain:hover){background:var(--charcoal-soft)}
.atable tbody tr:hover,.table tbody tr:hover{background:var(--charcoal-soft)}
.repflag:hover{filter:brightness(.97)}

/* ── 4 · focus, for anyone not using a mouse ───────────────────────────────
   :focus-visible only, so a click never draws a ring, but Tab always does.
   This was missing app-wide, which made the site unusable by keyboard for
   anyone who could not see where they were. */
.btn:focus-visible,.tab:focus-visible,.slots a:focus-visible,
.calwork .daycell:focus-visible,.folderhead:focus-visible,
.pop>summary:focus-visible,.nav a:focus-visible,.bell:focus-visible,
.att-file:focus-visible,.linkbtn:focus-visible,a.pcard:focus-visible,
a.proscard:focus-visible,.mkt-links a:focus-visible,.mkt-cta a:focus-visible,
.mkt-foot-links a:focus-visible,.logo:focus-visible,.mkt-store-badge:focus-visible{
  outline:2px solid var(--signal); outline-offset:3px; border-radius:var(--rs);
}
input:focus,select:focus,textarea:focus{
  border-color:var(--signal); outline:none;
  box-shadow:0 0 0 3px var(--signal-soft);
}
@media (prefers-reduced-motion: no-preference){
  input,select,textarea{
    transition:border-color var(--t-fast) var(--ease-out),
               box-shadow var(--t-fast) var(--ease-out);
  }
}

/* ── 5 · a count arriving ──────────────────────────────────────────────────
   The ONE overshoot in the app, on the one thing whose whole purpose is to
   catch the eye: a number that was not there before. It fires once on load
   and never again. A badge that pulses forever stops being noticed within a
   day and becomes something the user resents. */
@keyframes kin-pop{
  from{opacity:0;transform:scale(.55)}
  to  {opacity:1;transform:scale(1)}
}
@media (prefers-reduced-motion: no-preference){
  .bell .dot,.tabn,.badge.new{
    animation:kin-pop var(--t-mid) var(--ease-pop) both;
  }
}

/* ── 6 · popovers and flash ────────────────────────────────────────────────
   A panel that appears instantly reads as a rendering glitch; one that takes
   160ms to arrive reads as a panel. Transform-origin is set per side so it
   grows out of the control that opened it rather than out of nowhere, and it
   is direction-aware so Arabic does not open backwards. */
@keyframes kin-panel{
  from{opacity:0;transform:translateY(-4px) scale(.985)}
  to  {opacity:1;transform:none}
}
@media (prefers-reduced-motion: no-preference){
  details[open]>.pop-body{
    animation:kin-panel var(--t-mid) var(--ease-out) both;
    transform-origin:top left;
  }
  html[dir=rtl] details[open]>.pop-body{transform-origin:top right}
  details[open]>.pop-body.up{transform-origin:bottom left}
  html[dir=rtl] details[open]>.pop-body.up{transform-origin:bottom right}

  /* The flash is the app answering something you just did. It comes from
     above the content, which is where an answer belongs. */
  .flash{animation:kin-panel var(--t-reveal) var(--ease-out) both}
}

/* ── the way in ────────────────────────────────────────────────────────────
   Sign in, sign up, and the choose-your-side landing page sit in the middle of
   the window rather than pinned under the header with the whole lower half of
   the screen empty. These are the only three pages in the app with one small
   decision on them and nothing else — every other page is a list that starts
   at the top and keeps going, and would be wrong centred.

   min-height, not height: if the viewport is short, or a validation message
   pushes the form taller, the block grows downward and scrolls normally rather
   than being clipped. dvh over vh so a phone's collapsing address bar does not
   make the page jump as it scrolls. */
.wrap.center{
  min-height:calc(100dvh - 62px);   /* 62px = the header bar */
  display:flex; flex-direction:column; justify-content:center;
  padding-top:24px; padding-bottom:48px;
}
/* The flash sits above the form rather than being centred with it — it is an
   answer to what just happened, not part of the decision. */
.wrap.center > .flash{flex:none}

/* ── an unfinished guide ───────────────────────────────────────────────────
   Red on the FRAME, not on the text. The question itself is not wrong and the
   options are not wrong — the box is simply empty — so the colour goes around
   the block and the words inside stay in their normal ink. Colouring the
   question red would read as "this question is the problem".

   The tint is barely there. A page with three of these on it must still be
   readable; three loud red panels reads as an error page rather than a form
   with gaps in it. */
.qblock.blank{border-color:var(--bad);box-shadow:0 0 0 3px var(--bad-soft)}
.blankmsg{color:var(--bad);font-size:12px;font-weight:600;margin-bottom:8px}
.needsans{border-color:var(--bad);background:var(--bad-soft);margin-bottom:14px}
.needsans b{color:var(--bad)}
/* The asterisk marks what is required from the start, so the red frames are a
   reminder rather than a surprise. Muted: it is a note, not a warning. */
.qlabel .req{color:var(--muted);font-weight:400;margin-inline-start:3px}
/* The sticky header must not sit on top of the question we just scrolled to. */
.qblock{scroll-margin-top:86px}

/* ── the booking calendar's closed days ────────────────────────────────────
   A day with nothing on it now says so from across the room. Before this it
   was drawn identically to a bookable day, and the only way to find out was to
   click it and read an empty list — which for a therapist working three
   afternoons a week is four wasted clicks in every seven.

   Two closed states, told apart deliberately:
     .shut       nothing left — either not a working day or already full.
     .shut.away  the therapist marked the day off. It gets the warmer tint and
                 carries their reason on hover, because "Eid" and "full" are
                 different news to a parent trying to plan a week.
   Neither is red. A therapist taking a day off has done nothing wrong, and red
   is reserved in this app for things that actually went wrong. */
.cal .off.shut{background:var(--charcoal-soft);border-color:transparent;color:#9AA4AA;
  cursor:not-allowed;text-decoration:line-through;text-decoration-thickness:1px}
.cal .off.shut.away{background:var(--apricot-soft);border-color:var(--apricot-line);
  color:#A9895F;text-decoration:none}

/* The legend under the grid. Same swatch shapes as the therapist's own month,
   so the two calendars in the app read with one vocabulary. */
.callegend i.lg-open,.callegend i.lg-away,.callegend i.lg-full{
  display:inline-block;width:11px;height:11px;border-radius:3px;border:1px solid var(--line)}
.callegend i.lg-open{background:#fff}
.callegend i.lg-away{background:var(--apricot-soft);border-color:var(--apricot-line)}
.callegend i.lg-full{background:var(--charcoal-soft);border-color:transparent}

/* While the times for a newly picked day are on their way. Dimmed, not
   emptied: the previous day's times stay legible for the ~50ms this takes, so
   the block does not collapse and shove the rest of the form up the page. */
#slotzone{min-height:44px}
#slotzone.loading{opacity:.45}
@media (prefers-reduced-motion: no-preference){
  #slotzone{transition:opacity var(--t-fast) var(--ease-out)}
}

/* ── the time picker ───────────────────────────────────────────────────────
   dir="ltr" on the wrapper, always. A clock reads 4:30 in Cairo exactly as it
   does anywhere else — the hour comes before the minutes in Arabic too — so
   these three controls must not be reordered by the page's RTL flow. The
   Arabic labels inside them still render right-to-left; only the running order
   of hour, minute, am/pm is pinned. */
.timepick{display:inline-flex;align-items:center;gap:6px}
/* Opts out of the new custom chevron: these three sit side by side as one
   compact, centered control, not a typical field-width dropdown, and the
   32px reserved for the chevron elsewhere would force them wider than the
   numbers inside need. */
.timepick select{-webkit-appearance:auto;appearance:auto;background-image:none;
  width:auto;min-width:66px;padding-inline:10px;text-align:center}
.timepick select:last-child{min-width:74px}
.tpsep{color:var(--muted);font-weight:700}

/* ── confirmation panels ───────────────────────────────────────────────────
   Deliberately plain. A panel that is itself alarming — red background, big
   warning icon — gets clicked past on reflex; one that reads like a short
   sentence of fact gets read. Only the heading carries the colour. */
.confirmpop > summary{list-style:none}
.confirmpop > summary::-webkit-details-marker{display:none}
.confirmhead{color:var(--bad)}
.confirmpop .btn.ghost:hover{background:var(--charcoal-soft)}
/* What survives the action. Green because it is reassurance, and it sits below
   the cost so the order read is "here is what happens, here is what is safe". */
.keepsline{color:var(--ok)}
.confirmnote{margin-top:9px;padding:9px 11px;border-radius:var(--rs);
  background:var(--apricot-soft);border:1px solid var(--apricot-line);
  font-size:12px;line-height:1.5}
/* The report acknowledgement. A tick box rather than a second panel: the
   reason box is already a deliberate act of writing, and stacking a popover
   inside a popover would be unusable on a phone. */
.ackline{display:flex;align-items:flex-start;gap:8px;margin:10px 0 0;
  font-size:12px;font-weight:500;line-height:1.45;cursor:pointer}
.ackline input{width:auto;margin-top:2px;flex:none}

/* A grid spaces its children with `gap`. The stacking rule `.card + .card
   {margin-top:14px}` is for cards flowing down a page, and inside a grid it
   does not separate anything — it just shoves every card except the first one
   14px down inside its own track. On the Home page that is why the ABA card
   appeared to sit higher than the two beside it even after all eight were made
   the same height: they were the same size, hanging at different heights. */
.grid > .card + .card{margin-top:0}

/* A <details> inside a flex row is a block-level flex item, and flex items
   shrink below their content by default — so the Rename control in Records
   collapsed to a sliver and its label ran underneath the button next to it.
   Buttons never wrap their own text either: a control labelled "Who has opened
   this" breaking across two lines makes the row ragged. */
.row > details{flex:none}
.btn{white-space:nowrap}
