/* Japandi carries the headings, as it does in the product. Inlined because
     it is licensed and has no CDN. */
  @font-face { font-family: "Japandi"; src: url("assets/71f96c4fc08b.otf") format("opentype"); font-weight: 300; font-display: swap; }
  @font-face { font-family: "Japandi"; src: url("assets/942ab42a7322.otf") format("opentype"); font-weight: 400; font-display: swap; }
  @font-face { font-family: "Japandi"; src: url("assets/8bb991ec1cda.otf") format("opentype"); font-weight: 700; font-display: swap; }

  :root {
    --background: 140 20% 97%;
    --foreground: 150 8% 10%;
    --card: 0 0% 100%;
    --card-foreground: 150 8% 10%;
    --popover: 0 0% 100%;
    --popover-foreground: 150 8% 10%;
    --primary: 150 19% 36%;
    --primary-foreground: 0 0% 100%;
    --secondary: 144 19% 95%;
    --secondary-foreground: 150 12% 24%;
    --muted: 144 19% 95%;
    --muted-foreground: 150 5% 40%;
    --accent: 146 18% 91%;
    --accent-foreground: 150 18% 16%;
    --destructive: 6 63% 46%;
    --destructive-foreground: 0 0% 100%;
    --border: 144 17% 89%;
    --input: 144 17% 89%;
    --ring: 150 15% 49%;
    --radius: 0.625rem;
    --success: 145 63% 42%;
    --warning: 33 91% 44%;
    color-scheme: light;
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --background: 144 14% 7%;
      --foreground: 140 16% 93%;
      --card: 150 12% 10%;
      --card-foreground: 140 16% 93%;
      --popover: 150 12% 12%;
      --popover-foreground: 140 16% 93%;
      --primary: 153 19% 58%;
      --primary-foreground: 144 14% 7%;
      --secondary: 150 12% 13%;
      --secondary-foreground: 145 14% 82%;
      --muted: 150 12% 13%;
      --muted-foreground: 150 7% 61%;
      --accent: 142 13% 17%;
      --accent-foreground: 140 16% 93%;
      --border: 147 12% 18%;
      --input: 150 12% 21%;
      --ring: 150 15% 49%;
      --success: 145 50% 58%;
      --warning: 33 68% 61%;
      color-scheme: dark;
    }
  }

  :root[data-theme="dark"] {
    --background: 144 14% 7%;
    --foreground: 140 16% 93%;
    --card: 150 12% 10%;
    --card-foreground: 140 16% 93%;
    --popover: 150 12% 12%;
    --popover-foreground: 140 16% 93%;
    --primary: 153 19% 58%;
    --primary-foreground: 144 14% 7%;
    --secondary: 150 12% 13%;
    --secondary-foreground: 145 14% 82%;
    --muted: 150 12% 13%;
    --muted-foreground: 150 7% 61%;
    --accent: 142 13% 17%;
    --accent-foreground: 140 16% 93%;
    --border: 147 12% 18%;
    --input: 150 12% 21%;
    --ring: 150 15% 49%;
    --success: 145 50% 58%;
    --warning: 33 68% 61%;
    color-scheme: dark;
  }

  html, body {
    background: hsl(var(--background));
    color: hsl(var(--foreground));
    font-family: "Outfit", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
    -webkit-font-smoothing: antialiased;
  }

  h1, h2, .display-face { font-family: "Japandi", "Outfit", sans-serif; letter-spacing: -0.015em; }
  html { scroll-behavior: smooth; overflow-x: clip; }

  /* Which lockup shows. Three states, same as the palette: an explicit
     stamp either way, and the OS preference when there is no stamp. */
  .dark-show { display: none; }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .dark-show { display: block; }
    :root:not([data-theme="light"]) .dark-hide { display: none; }
  }
  :root[data-theme="dark"] .dark-show { display: block; }
  :root[data-theme="dark"] .dark-hide { display: none; }
  :root[data-theme="light"] .dark-show { display: none; }
  :root[data-theme="light"] .dark-hide { display: block; }
  body { overflow-x: clip; }

  /* ── The dotted ground under the hero, faded at its edges ─────────────── */
  .dotfield {
    background-image: radial-gradient(hsl(var(--foreground) / 0.13) 1px, transparent 1px);
    background-size: 22px 22px;
    -webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 0%, #000 35%, transparent 78%);
    mask-image: radial-gradient(ellipse 90% 70% at 50% 0%, #000 35%, transparent 78%);
  }

  /* ── Aurora: slow drifting light behind the hero and the close ────────── */
  .aurora { position: absolute; border-radius: 9999px; filter: blur(80px); will-change: transform; }
  @keyframes drift-a { 0%,100% { transform: translate3d(0,0,0) scale(1); } 33% { transform: translate3d(7%,-5%,0) scale(1.12); } 66% { transform: translate3d(-5%,6%,0) scale(.92); } }
  @keyframes drift-b { 0%,100% { transform: translate3d(0,0,0) scale(1.05); } 40% { transform: translate3d(-8%,4%,0) scale(.9); } 75% { transform: translate3d(5%,7%,0) scale(1.15); } }
  @keyframes drift-c { 0%,100% { transform: translate3d(0,0,0) scale(.95); } 50% { transform: translate3d(4%,-8%,0) scale(1.2); } }
  .aurora-a { animation: drift-a 26s ease-in-out infinite; }
  .aurora-b { animation: drift-b 34s ease-in-out infinite; }
  .aurora-c { animation: drift-c 30s ease-in-out infinite; }

  /* The dotfield creeps, so the ground is never quite still. */
  @keyframes creep { to { background-position: 22px 22px; } }
  .dotfield-live { animation: creep 9s linear infinite; }

  /* ── Rising lines ───────────────────────────────────────────────────────
     Hairline rails with a lit segment climbing each one. Every column runs at
     its own speed on a negative delay, so the field is already in motion on the
     first frame instead of starting together. */
  .rails { background-image: repeating-linear-gradient(to right, hsl(var(--foreground)/.05) 0 1px, transparent 1px 9.09%); }
  .riser {
    position: absolute; bottom: -32%; width: 1px; height: 32%;
    background: linear-gradient(to top, transparent, hsl(var(--primary)/.5) 40%, hsl(var(--primary)/.95));
    animation-name: rise; animation-timing-function: linear; animation-iteration-count: infinite;
    will-change: transform;
  }
  @keyframes rise { to { transform: translateY(-420%); } }

  /* The field is masked away from wherever the words are: below them when the
     hero stacks, and to the left of them once it goes two up. */
  .riser-field {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, transparent 46%, #000 72%, transparent 99%);
    mask-image: linear-gradient(to bottom, transparent 0%, transparent 46%, #000 72%, transparent 99%);
  }
  @media (min-width: 1024px) {
    .riser-field {
      -webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 44%, #000 64%);
      mask-image: linear-gradient(to right, transparent 0%, transparent 44%, #000 64%);
    }
  }

  /* ── The wash that follows the pointer across a card ────────────────────
     Two layers, both driven by --mx/--my written on mousemove. One lifts the
     card's ground toward the brand; the other lights the border, drawn as a
     masked ring so only the edge shows. Both fade in on hover, so a card at
     rest is completely flat. */
  @property --mx { syntax: "<percentage>"; inherits: true; initial-value: 50%; }
  @property --sweep { syntax: "<number>"; inherits: true; initial-value: -1; }
  @keyframes row-sweep { from { --sweep: -.12; } to { --sweep: 1.12; } }

  /* The play button breathes, so it reads as the one thing on the row that
     does something rather than another filter pill. */
  /* The client app is on both stores, and one phone glyph can only say one of
     them. Two marks in the same square, trading places. */
  @keyframes plat-a { 0%, 42% { opacity: 1; } 50%, 92% { opacity: 0; } 100% { opacity: 1; } }
  @keyframes plat-b { 0%, 42% { opacity: 0; } 50%, 92% { opacity: 1; } 100% { opacity: 0; } }
  .plat > * { position: absolute; inset: 0; margin: auto; }
  .plat-a { animation: plat-a 6s ease-in-out infinite; }
  .plat-b { animation: plat-b 6s ease-in-out infinite; }
  @media (prefers-reduced-motion: reduce) {
    .plat-a, .plat-b { animation: none; }
    .plat-b { opacity: 0; }
  }

  @keyframes demo-ping { 0% { transform: scale(1); opacity: .45; } 70%, 100% { transform: scale(2); opacity: 0; } }
  .demo-ping { animation: demo-ping 2.6s cubic-bezier(0,0,.2,1) infinite; }
  /* The dialog carries a display class, which outranks the user agent rule for
     [hidden]. Say it here rather than depending on the host page to say it. */
  #demo-modal[hidden] { display: none !important; }
  @media (prefers-reduced-motion: reduce) { .demo-ping { animation: none; opacity: 0; } }

  .spot { position: relative; isolation: isolate; }
  .spot::before,
  .spot::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    opacity: 0;
    transition: opacity .35s ease;
    pointer-events: none;
  }
  .spot::before {
    z-index: -1;
    background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 50%), hsl(var(--primary) / .14), transparent 72%);
  }
  .spot::after {
    padding: 1px;
    background: radial-gradient(300px circle at var(--mx, 50%) var(--my, 50%), hsl(var(--primary) / .85), transparent 68%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
  }
  .spot:hover::before, .spot:focus-within::before { opacity: 1; }
  .spot:hover::after, .spot:focus-within::after { opacity: 1; }

  /* ── One light, crossing the whole row ────────────────────────────────
     The group animates a single position from before its left edge to past
     its right. A card converts that row position into its own coordinates
     with the share of the row it occupies, so the light simply passes
     through it. Nothing fades in or out: the gradient is always on, and it
     is only visible while it is over the card. */
  .beam-row { animation: row-sweep 7s linear infinite; }
  .beam-row:hover { animation-play-state: paused; }
  .beam::before, .beam::after { opacity: 1; transition: none; }
  .beam::before {
    background: radial-gradient(300px circle at calc((var(--sweep) - var(--start, 0)) / var(--span, 1) * 100%) 26%, hsl(var(--primary) / .16), transparent 70%);
  }
  .beam::after {
    background: radial-gradient(280px circle at calc((var(--sweep) - var(--start, 0)) / var(--span, 1) * 100%) 26%, hsl(var(--primary) / .9), transparent 66%);
  }
  /* A pointer takes the light over: its own card follows the cursor, and the
     travelling one stops so there are never two. */
  .beam-row:hover .beam::before,
  .beam-row:hover .beam::after { opacity: 0; }
  .beam-row:hover .beam:hover::before,
  .beam-row:hover .beam:hover::after {
    opacity: 1;
    background: radial-gradient(300px circle at var(--mx, 50%) var(--my, 50%), hsl(var(--primary) / .16), transparent 70%);
  }
  .beam-row:hover .beam:hover::after {
    background: radial-gradient(280px circle at var(--mx, 50%) var(--my, 50%), hsl(var(--primary) / .9), transparent 66%);
  }
  @media (prefers-reduced-motion: reduce) {
    .beam-row { animation: none; }
    .beam::before, .beam::after { opacity: 0; }
  }

  /* ── The comet that runs the circuit ──────────────────────────────────── */
  .beam { stroke-dasharray: 110 3000; stroke-dashoffset: 0; animation: beam 9s linear infinite; }
  @keyframes beam { to { stroke-dashoffset: -3110; } }

  /* ── The six days nobody is in the room ───────────────────────────────── */
  @keyframes fade-up-cell { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
  .cell-in { animation: fade-up-cell .5s cubic-bezier(.16,1,.3,1) both; }

  /* ── Scroll reveal ────────────────────────────────────────────────────── */
  [data-reveal] {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity .6s cubic-bezier(.16,1,.3,1), transform .6s cubic-bezier(.16,1,.3,1);
    transition-delay: var(--delay, 0ms);
  }
  [data-reveal].shown { opacity: 1; transform: none; }

  /* ── Marquee: two rows, opposite directions, halting under the pointer ── */
  @keyframes marquee-left  { from { transform: translateX(0); } to { transform: translateX(-50%); } }
  @keyframes marquee-right { from { transform: translateX(-50%); } to { transform: translateX(0); } }
  .marquee { display: flex; width: max-content; }
  .marquee > * { margin-right: 12px; }
  .marquee-l { animation: marquee-left 46s linear infinite; }
  .marquee-r { animation: marquee-right 52s linear infinite; }
  .marquee-track:hover .marquee,
  .marquee-track:focus-within .marquee { animation-play-state: paused; }
  .marquee-track { overflow: hidden; }
  .marquee-mask {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
    mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
  }

  /* ── The pulse that runs the loop ─────────────────────────────────────── */
  .flow { stroke-dasharray: 7 9; animation: flow 1.4s linear infinite; }
  @keyframes flow { to { stroke-dashoffset: -16; } }

  /* ── Notifications arriving ───────────────────────────────────────────── */
  @keyframes slide-in { from { opacity: 0; transform: translateY(10px) scale(.97); } to { opacity: 1; transform: none; } }
  .toast-in { animation: slide-in .45s cubic-bezier(.16,1,.3,1) both; }

  /* The audit feed. Both halves are identical, so travelling exactly half the
     track's height lands the second copy where the first began and the seam
     never shows. Hovering stops it, so a line can actually be read. */
  .audit-dot {
    position: relative; z-index: 1; flex: none;
    margin-top: 7px; height: .5rem; width: .5rem; border-radius: 9999px;
  }
  .audit-dot.is-success { background: hsl(var(--success)); box-shadow: 0 0 0 4px hsl(var(--card)), 0 0 0 7px hsl(var(--success) / .17); }
  .audit-dot.is-primary { background: hsl(var(--primary)); box-shadow: 0 0 0 4px hsl(var(--card)), 0 0 0 7px hsl(var(--primary) / .17); }
  .audit-dot.is-warning { background: hsl(var(--warning)); box-shadow: 0 0 0 4px hsl(var(--card)), 0 0 0 7px hsl(var(--warning) / .17); }
  .audit-dot.is-admin   { background: hsl(var(--muted-foreground) / .5); box-shadow: 0 0 0 4px hsl(var(--card)), 0 0 0 7px hsl(var(--muted-foreground) / .1); }

  /* ── A practice's own page, in three of the twelve templates ─────────── */
  .tpl-stage > * { grid-area: 1 / 1; }
  /* The three layouts are nothing like each other, so a true cross-fade reads
     as a double exposure. The outgoing panel clears first and the incoming one
     waits for it, which hands over cleanly without a hard cut. */
  .tpl-panel {
    opacity: 0; transform: scale(.985); pointer-events: none;
    transition: opacity .26s ease, transform .26s ease;
  }
  .tpl-panel.is-on {
    opacity: 1; transform: none; pointer-events: auto;
    transition: opacity .42s ease .24s, transform .52s cubic-bezier(.16,1,.3,1) .24s;
  }
  .tpl-ink  { color: hsl(var(--tpl)); }
  .tpl-bg   { background: hsl(var(--tpl)); color: #fff; }
  .tpl-bd   { border-color: hsl(var(--tpl) / .5); }
  .tpl-tint { background: hsl(var(--tpl) / .12); }
  /* An answer that opens by growing its row, so the height animates without
     anybody measuring anything in script. */
  .faq-answer.is-open { grid-template-rows: 1fr; }
  .faq-chip[aria-pressed="true"] { background: hsl(var(--primary)); border-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
  .faq-item[hidden] { display: none !important; }
  mark { background: hsl(var(--primary) / .2); color: inherit; border-radius: 3px; padding: 0 2px; }

  .tpl-url  { transition: opacity .25s ease; }
  .tpl-url.is-out { opacity: 0; }
  /* The frame belongs to the practice, so the chrome carries its colour too. */
  .tpl-bar  { background: hsl(var(--tpl) / .09); transition: background-color .55s ease; }
  .tpl-dot  { background: hsl(var(--tpl) / .3); transition: background-color .55s ease; }
  .tpl-addr {
    background: hsl(var(--tpl) / .07);
    border-color: hsl(var(--tpl) / .28);
    color: hsl(var(--tpl));
    transition: background-color .55s ease, border-color .55s ease, color .55s ease;
  }
  @media (prefers-reduced-motion: reduce) {
    .tpl-panel { transition: none; }
  }

  @keyframes audit-scroll { to { transform: translateY(-50%); } }
  .audit-track { animation: audit-scroll 46s linear infinite; }
  .audit-view:hover .audit-track { animation-play-state: paused; }
  .audit-view {
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 7%, #000 93%, transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 7%, #000 93%, transparent);
  }
  @media (prefers-reduced-motion: reduce) {
    .audit-track { animation: none; }
  }

  @keyframes pulse-ring {
    0%   { box-shadow: 0 0 0 0 hsl(var(--success) / .5); }
    70%  { box-shadow: 0 0 0 7px hsl(var(--success) / 0); }
    100% { box-shadow: 0 0 0 0 hsl(var(--success) / 0); }
  }
  .live-dot { animation: pulse-ring 2.2s infinite; }

  .draw { stroke-dasharray: 500; stroke-dashoffset: 500; }
  .draw.shown { animation: draw 1.5s cubic-bezier(.16,1,.3,1) forwards; }
  @keyframes draw { to { stroke-dashoffset: 0; } }

  @media (prefers-reduced-motion: reduce) {
    .aurora { animation: none !important; }
    *, *::before, *::after {
      animation-duration: .01ms !important; animation-iteration-count: 1 !important;
      transition-duration: .01ms !important; scroll-behavior: auto !important;
    }
    [data-reveal] { opacity: 1; transform: none; }
    .draw { stroke-dashoffset: 0; }
  }
