/* Klaro site components, on top of klaro.css (tokens, type, pills). Plan: design/brief/redesign.md
   §5–7 and §11–12; markup for each component: design/redesign/components.html.
   Logical properties only (the device bar and the showcase camera are left to right on purpose:
   they draw a browser and the app's own pixels). Arabic: the block near the end, plus [dir="rtl"]
   lines next to the few rules that need a mirror. Motion: the tokens and the block near the end;
   every duration and curve is a token, nothing moves with prefers-reduced-motion, nothing loops,
   transform and opacity only.

   Contents
     Header (.topbar, .nav, .mega, the phone sheet, .joinform)     Footer (.footer)
     Layout (.section, .section-head)    Chips (.chip, .chip-switch)    Hero (.hero, .mosaic)
     Illustrations (.ill, .ph, .strip)   Cards (.icard, .icard-grid, .carousel)
     .split-card and .stack   .band   .rcard (.rcards--one)   .panel   .faq   .closing
     Devices (.device, .plate, .plate--device)   The real app (.show, .rec, .caption, .show--full)
     .opening   .steps-sticky
     Utilities   Market support   Arabic   Motion */

/* ======================================================================== Header */
/* "Skip to content" (partials/header.*.html): the keyboard's first stop, out of sight until it has
   focus, then a dark pill over the header's start. Every page's <main id="main"> is its target. */
.skip-link { position: fixed; z-index: 90; inset-block-start: 10px; inset-inline-start: 10px; padding: 10px 18px; border-radius: var(--r-pill);
  background: var(--ink); color: #fff; font: 600 15px/1.2 var(--font); text-decoration: none; }
.skip-link:not(:focus) { width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip-link:focus-visible { outline-offset: 2px; }
.topbar { position: sticky; top: 0; z-index: 50; background: var(--page); border-block-end: 1px solid transparent;
  transition: background-color var(--dur-ui) var(--ease-ui), border-color var(--dur-ui) var(--ease-ui); }
.topbar.scrolled { background: rgb(243 243 241 / .84); -webkit-backdrop-filter: saturate(1.5) blur(14px); backdrop-filter: saturate(1.5) blur(14px); border-block-end-color: var(--line); }
.topbar-row { height: var(--header-h); display: flex; align-items: center; gap: 6px; }
.brand { display: inline-flex; margin-inline-end: 14px; border-radius: 8px; }
.brand img { height: 28px; width: auto; }
.nav { display: flex; align-items: center; gap: 2px; }
.nav-link { display: inline-flex; align-items: center; gap: 4px; height: 36px; padding-inline: 12px; border: 0; border-radius: var(--r-pill); background: none;
  font: 500 var(--fs-nav)/1 var(--font); color: var(--ink); text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-ui), color var(--dur-fast) var(--ease-ui); }
.nav-link:hover { background: rgb(23 23 23 / .05); }
.nav-link[aria-expanded="true"] { background: var(--ink); color: #fff; }   /* the open trigger turns into a dark pill */
.nav-link .chev { width: 14px; height: 14px; transition: rotate var(--dur-ui) var(--ease-ui); }
.nav-link[aria-expanded="true"] .chev { rotate: 180deg; }
.nav-lang { color: var(--text); }
.topbar-space { flex: 1; }
.topbar-row > .btn + .btn { margin-inline-start: 2px; }
.menu-btn { display: none; width: 44px; height: 44px; place-items: center; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--card); color: var(--ink); cursor: pointer; }
.menu-btn .i { grid-area: 1 / 1; width: 20px; height: 20px; }
.menu-btn .menu-close, .menu-btn[aria-expanded="true"] .menu-open { display: none; }
.menu-btn[aria-expanded="true"] .menu-close { display: block; }
/* The audience link. {aud} in the partials is the address of an audience home (tools/site.py), so
   its label follows the address: the students' home ends in students/, the tutors' home does not. */
.nav-aud[href$="students/"] .aud-tutors, .nav-aud:not([href$="students/"]) .aud-students { display: none; }

/* The floating bar (after Granola), off by default: add .topbar--float to the <header> in both
   header partials. After 120px of scroll site.js adds .is-float: the row becomes an inset white pill. */
.topbar--float { transition: none; }
.topbar--float.is-float { padding-block: 6px; background: transparent; border-block-end-color: transparent; -webkit-backdrop-filter: none; backdrop-filter: none; }
.topbar--float.is-float .topbar-row { width: min(calc(var(--container) + 24px), calc(100% - 2 * var(--gutter))); height: calc(var(--header-h) - 12px);
  padding-inline: 18px 8px; background: rgb(255 255 255 / .88); -webkit-backdrop-filter: saturate(1.5) blur(14px); backdrop-filter: saturate(1.5) blur(14px);
  border: 1px solid var(--line); border-radius: var(--r-pill); box-shadow: 0 10px 30px -18px rgb(30 12 60 / .3); }

/* The Products menu (desktop) and, at 1024px and under, the whole-screen menu sheet. One panel:
   .mega-main (sheet rows) · .mega-products (two columns by audience) · .mega-foot (the join code:
   on desktop only its field, when the bar's Join code opened the panel; on the sheet also Sign in
   and Try it free). site.js sets data-view="main" | "products" on the sheet. */
.mega-wrap { position: absolute; inset-inline: 0; inset-block-start: 100%; display: flex; justify-content: center; padding-inline: var(--gutter); pointer-events: none; }
.mega { pointer-events: auto; width: min(840px, 100%); margin-block-start: 8px; padding: 12px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-media); box-shadow: var(--shadow-float); }
.mega-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.mega-col { display: flex; flex-direction: column; gap: 2px; padding: 8px; }
.mega-col + .mega-col { border-inline-start: 1px solid var(--line); }
.mega-head { align-self: flex-start; padding: 6px 12px 10px; border-radius: 8px; font: 600 14px/1.3 var(--font); color: var(--muted); text-decoration: none; }
.mega-head:hover { color: var(--ink); }
.mega-item { display: flex; align-items: flex-start; gap: 14px; padding: 12px; border-radius: 16px; color: var(--ink); text-decoration: none; transition: background-color var(--dur-fast) var(--ease-ui); }
.mega-item:hover { background: var(--surface); }
.mega-icon { flex-shrink: 0; display: grid; place-items: center; width: 32px; height: 32px; border: 1px solid var(--line); border-radius: 10px; background: var(--card); color: var(--muted); }
.mega-icon .i { width: 18px; height: 18px; }
.mega-text b { display: block; font-size: 15px; font-weight: 600; line-height: 1.3; }
.mega-text small { display: block; margin-block-start: 3px; font-size: 13px; line-height: 1.45; color: var(--text); }
.mega-all { display: inline-flex; align-items: center; gap: 6px; margin: 4px 20px 6px; font: 600 14px/1.3 var(--font); color: var(--purple-dark); text-decoration: none; }
.mega-all:hover { text-decoration: underline; text-underline-offset: 4px; }
.mega-foot { display: flex; flex-wrap: wrap; gap: 8px; margin-block-start: 8px; padding: 12px 8px 4px; border-block-start: 1px solid var(--line); }
.mega-foot > [data-joincode], .mega-foot > .joinform { flex: 0 1 380px; }
.joinform { display: flex; align-items: center; gap: 6px; height: 52px; padding: 5px; padding-inline-start: 20px; border: 1px solid var(--purple-line); border-radius: var(--r-pill); background: var(--purple-tint); }
.joinform:focus-within { border-color: var(--purple); box-shadow: 0 0 0 3px var(--purple-soft); }
.joinform input { flex: 1; min-width: 0; height: 100%; padding: 0; border: 0; background: none; font: 600 16px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .06em; color: var(--purple-dark); }
.joinform input:focus { outline: none; }   /* the form shows the focus instead */
.joinform input::placeholder { color: var(--faint); }
.joinform .btn { height: 40px; }
@media (min-width: 1025px) {
  .mega-main, .mega-back, .mega-sheet-only { display: none; }
  /* One join-code entry on desktop: the bar's "Join code" button, which opens this panel with the
     field showing. The panel's own "Have a join code?" is for the phone sheet only. */
  .mega-foot > [data-joincode], .mega-foot:has(> .joinform[hidden]) { display: none; }
}
@media (max-width: 1024px) {
  .nav, .topbar-wide { display: none; }
  .menu-btn { display: grid; }
  .nav-lang { margin-inline-end: 4px; }
  html.menu-open { overflow: hidden; }
  html.menu-open .topbar { background: var(--page); border-block-end-color: var(--line); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .mega-wrap { position: fixed; inset-block: var(--header-h) 0; inset-inline: 0; display: block; padding: 0; }
  .mega { display: flex; flex-direction: column; width: 100%; height: 100%; margin: 0; padding: 8px var(--gutter) calc(16px + env(safe-area-inset-bottom, 0px));
    background: var(--page); border: 0; border-radius: 0; box-shadow: none; overflow-y: auto; overscroll-behavior: contain; }
  .mega[data-view="products"] .mega-main, .mega:not([data-view="products"]) .mega-products { display: none; }
  .mega-main { display: flex; flex-direction: column; }
  .mega-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; width: 100%; min-height: 64px; padding: 0; border: 0; border-block-end: 1px solid var(--line);
    background: none; font: 600 20px/1.25 var(--font); color: var(--ink); text-align: start; text-decoration: none; cursor: pointer; }
  .mega-row .chev, .mega-back .chev, .mega-all .chev { font-size: 1.3em; font-weight: 400; line-height: 1; color: var(--muted); }
  .mega-back { display: inline-flex; align-items: center; gap: 8px; height: 48px; padding: 0; border: 0; background: none; font: 600 16px/1 var(--font); color: var(--purple-dark); cursor: pointer; }
  .mega-cols { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .mega-col { padding: 12px 0; }
  .mega-col + .mega-col { border-inline-start: 0; border-block-start: 1px solid var(--line); }
  .mega-head { padding-inline: 0; }
  .mega-item { margin-inline: -8px; padding: 12px 8px; }
  .mega-all { margin-inline: 0; }
  .mega-foot { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; margin-block-start: auto; padding: 24px 0 0; border: 0; }
  .mega-foot > .joinform { height: 52px; }
}

/* ======================================================================== Footer */
.footer { background: var(--page); border-block-start: 1px solid var(--line); }
.footer > .wrap { padding-block: 64px 32px; }
.footer-brand { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; padding-block-end: 40px; border-block-end: 1px solid var(--line); }
.footer-brand a { display: inline-flex; border-radius: 8px; }
.footer-brand img { height: 30px; width: auto; }
.footer-line { max-width: 34em; font-size: var(--fs-lead); font-weight: 500; line-height: 1.45; color: var(--text); text-wrap: pretty; }   /* a plain line about Klaro; the motto is the deep panel's */
.footer-cols { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px 40px; padding-block: 40px 48px; }
.footer-h { margin-block-end: 16px; font-size: 20px; font-weight: 600; line-height: 1.3; letter-spacing: -.01em; }
.footer-h a { text-decoration: none; border-radius: 4px; }
.footer-h a:hover { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }
.footer ul { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
.footer-link { padding: 0; border: 0; background: none; font: 400 16px/1.4 var(--font); color: var(--text); text-align: start; text-decoration: none; cursor: pointer; }
.footer-link:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }
.footer-here { color: var(--ink); font-weight: 600; cursor: default; }
.footer-here:hover { text-decoration: none; }
.footer-base { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; padding-block-start: 24px; border-block-start: 1px solid var(--line); font-size: 14px; color: var(--muted); }
.footer-base > span:first-child { margin-inline-end: auto; }
.footer-base .footer-link { font-size: 14px; color: var(--muted); }
@media (max-width: 900px) { .footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ======================================================================== Layout */
.section { padding-block: var(--section); }
.section + .section { padding-block-start: 0; }
/* Section heading: H2 and at most one line under it. No kickers. Centred; --start aligns it to the
   inline start (carousel, FAQ, step lists). */
.section-head { display: flex; flex-direction: column; align-items: center; gap: 16px; max-width: 880px; margin-inline: auto; margin-block-end: var(--head-gap); text-align: center; }
.section-head--start { align-items: flex-start; margin-inline: 0; text-align: start; }
.section-sub { max-width: 40em; font-size: var(--fs-lead); line-height: 1.45; color: var(--text); text-wrap: pretty; }
.section-foot { display: flex; justify-content: center; margin-block-start: 40px; }

/* ======================================================================== Chips */
/* A grey pill: icon + label, hero only. <span class="chip"><svg class="i">…</svg>Marking <span class="chip-for">for tutors</span></span> */
.chip { display: inline-flex; align-items: center; gap: 8px; height: 36px; padding-inline: 12px 16px; border-radius: var(--r-pill); background: var(--chip); font: 500 14px/1 var(--font); color: var(--ink); white-space: nowrap; }
.chip .i { width: 18px; height: 18px; color: var(--muted); }
.chip-for { padding-inline-start: 8px; border-inline-start: 1px solid var(--border-strong); color: var(--chip-muted); }
/* The audience switch: "Klaro for… [Tutors] [Students and parents]", links to the two homes; the
   current page is a dark pill (aria-current="page"). */
.chip-switch { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 2px; padding: 4px; border-radius: 22px; background: var(--chip); font: 500 14px/1 var(--font); }
.chip-switch-label { padding-inline: 10px 6px; color: var(--chip-muted); }
.chip-switch a { display: inline-flex; align-items: center; height: 32px; padding-inline: 14px; border-radius: var(--r-pill); color: var(--ink); text-decoration: none; transition: background-color var(--dur-fast) var(--ease-ui); }
.chip-switch a:hover { background: rgb(23 23 23 / .06); }
.chip-switch a[aria-current="page"] { background: var(--ink); color: #fff; }
.spark { fill: var(--gold); }

/* ======================================================================== Hero (product pages, hub, research) */
/* Two columns, copy 5/12 and visual 7/12. .hero--center: one centred column (hub, research, 404). */
.hero { padding-block: 64px var(--section); }
.hero + .section { padding-block-start: 0; }   /* one section's space under a hero, not two */
.hero-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: center; gap: var(--s-8); }
.hero-copy { display: flex; flex-direction: column; align-items: flex-start; gap: 24px; }
.hero-copy .lead { max-width: 30em; }
.hero-grid .hero-copy .h1, .opening-copy .h1 { font-size: var(--fs-h1-split); }   /* full --fs-h1 only in a centred hero */
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; margin-block-start: 8px; }
.hero-note { display: inline-flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 500; color: var(--text); }
.hero-note .i { width: 14px; height: 14px; }
.hero--center .hero-grid { grid-template-columns: minmax(0, 1fr); justify-items: center; }
.hero--center .hero-copy { align-items: center; max-width: 880px; text-align: center; }
.hero--center .hero-copy .lead { margin-inline: auto; }
/* Illustration mosaic: two offset columns of two tiles each (1:1 over 4:3, then 4:3 over 1:1),
   at the inline end of the hero, at most 620px wide. */
.mosaic { display: grid; grid-template-columns: 1fr 1fr; align-items: start; gap: 16px; width: 100%; max-width: 620px; margin-inline-start: auto; }
.mosaic-col { display: grid; gap: 16px; }
.mosaic-col + .mosaic-col { margin-block-start: 56px; }
.mosaic-col:first-child > :first-child, .mosaic-col:last-child > :last-child { --ar: 1; }
.mosaic-col:first-child > :last-child, .mosaic-col:last-child > :first-child { --ar: 4 / 3; }
@media (max-width: 1024px) { .hero-grid { grid-template-columns: minmax(0, 1fr); gap: 48px; } }
@media (max-width: 640px) {
  .hero { padding-block-start: 32px; }
  .hero-actions .btn { flex: 1 1 auto; }
  .mosaic, .mosaic-col { gap: 12px; }
  .mosaic-col + .mosaic-col { margin-block-start: 32px; }
}

/* ======================================================================== Illustrations */
/* A tile holding one illustration (radius 28, the paper colour behind it). --ar sets the shape
   (1 by default; 4 / 5, 4 / 3, 16 / 10…). data-ph names the file, so pages are never blocked by a
   missing illustration: until the file exists the tile is a plain paper-coloured box.
     <figure class="ill" data-ph="img/ill/02-book-passage.webp"><img src="…" alt="…"></figure>  (site.js marks it .is-missing if the file 404s)
     <div class="ill" data-ph="img/ill/02-book-passage.webp"></div>                             (no file yet: passes tools/site.py --check)
   File names are for us, never for visitors: they show only with ?ph in the address (site.js
   then adds html.ph-on), on tiles, placeholder boxes and plates alike. */
.ill { position: relative; display: block; margin: 0; overflow: hidden; aspect-ratio: var(--ar, 1); border-radius: var(--radius-tile); background: var(--paper); }
.ill > img, .ill > picture > img { width: 100%; height: 100%; object-fit: cover; }
.ill--contain > img { object-fit: contain; }
.ill.is-missing > img { visibility: hidden; }
/* Any placeholder box: <div class="ph" data-ph="what will go here"></div> */
.ph { position: relative; display: block; aspect-ratio: var(--ar, auto); border-radius: inherit; background: var(--ph); }
html.ph-on :is(.ph, .ill.is-missing, .ill:not(:has(img, picture, video)))::before { content: attr(data-ph); position: absolute; inset: 0; z-index: 1; display: grid; place-items: center; padding: 12px;
  border: 1px dashed #c9c8c1; border-radius: inherit; background: var(--ph); color: var(--muted); font: 500 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
  text-align: center; overflow-wrap: anywhere; letter-spacing: 0; }
/* Subjects strip: one row of six 4:3 tiles and a one-line caption (three across on phones). */
.strip-tiles { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 16px; margin: 0; padding: 0; list-style: none; }
.strip-tiles > .ill { --ar: 4 / 3; }
.strip-caption { margin-block-start: 24px; font-size: 17px; color: var(--text); text-align: center; text-wrap: balance; }
@media (max-width: 900px) { .strip-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; } }

/* ======================================================================== Cards */
/* Illustrated card (SchoolAI's "There's a Space for that"): the whole card is one link; white,
   hairline, radius 40, illustration 1:1 at radius 28, a title and one line. */
.icard { display: flex; flex-direction: column; gap: 24px; height: 100%; padding: 16px 16px 32px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card);
  color: var(--ink); text-decoration: none; transition: border-color var(--dur-fast) var(--ease-ui); }
.icard:hover { border-color: #c8c7c0; }
.icard:focus-visible { outline-offset: 4px; }
.icard > .ill { --ar: 1; border-radius: var(--radius-media); }
.icard .ill img { transition: scale var(--dur-slow) var(--ease-ui); }
.icard-body { display: flex; flex-direction: column; gap: 8px; padding-inline: 16px; }
.icard-title { font-size: var(--fs-h3); font-weight: 600; line-height: 1.15; letter-spacing: var(--tr-title); }
.icard-line { font-size: 17px; line-height: 1.5; color: var(--text); }
.icard-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; margin: 0; padding: 0; list-style: none; }
@media (max-width: 1024px) { .icard-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .icard-grid { grid-template-columns: minmax(0, 1fr); gap: 16px; } .icard { gap: 20px; padding-block-end: 24px; } .icard-body { padding-inline: 8px; } }

/* Carousel: a scroll-snap track that starts at the container's edge and bleeds off the inline end
   (the left in Arabic). Arrows, swipe and the arrow keys move it; it never moves by itself.
   The arrow pair is hidden until site.js runs (<div class="carousel-nav" hidden>). */
.carousel { --card-w: min(384px, 78vw); --edge: max(var(--gutter), (100% - var(--container)) / 2); }
.carousel-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 24px; margin-block-end: var(--head-gap); }
.carousel-head > .section-head { margin-block-end: 0; }
.carousel-nav { display: flex; gap: 8px; }
.carousel-btn { display: grid; place-items: center; width: 64px; height: 44px; border: 0; border-radius: var(--r-pill); background: var(--ink); color: #fff; cursor: pointer;
  transition: opacity var(--dur-ui) var(--ease-ui), background-color var(--dur-fast) var(--ease-ui), scale var(--dur-fast) var(--ease-ui); }
.carousel-btn:hover { background: #33332f; }
.carousel-btn:active { scale: .97; }
.carousel-btn[aria-disabled="true"] { opacity: .3; cursor: default; }
.carousel-btn[aria-disabled="true"]:hover { background: var(--ink); }
.carousel-btn .i { width: 20px; height: 20px; }
.carousel-track { display: flex; gap: 24px; margin: 0; padding: 8px var(--edge) 12px; list-style: none; overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-padding-inline: var(--edge); scrollbar-width: none; }
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-item { flex: 0 0 var(--card-w); scroll-snap-align: start; }
@media (max-width: 640px) { .carousel-track { gap: 16px; } .carousel-head { align-items: flex-start; } }

/* ======================================================================== Split card ("in action") and stacked feature cards */
/* Product pages. One white card, radius 40: a row of words (the title on the inline start; the
   two or three sentences and a text link beside it), then the product across the card's whole
   width, so the real app is as large as the page allows (redesign.md §7.9: its text at ≥ 14px).
   Phones and tablets: the words, then the product.
     <div class="split-card"><div class="split-copy"><h2 class="h3">…</h2><p>…</p><a class="tlink">…</a></div><figure class="rec">…</figure></div>
   .stack: the feature cards, the same card with an H3; up to five stack as you scroll (desktop),
   each sticking 16px under the one before while the covered one settles back to 96%. A plain
   stack on phones and with reduced motion. .stack-media holds a plate or an illustration
   (shown whole on the paper colour, in the plates' shape). */
.split-card, .stack-card { display: flex; flex-direction: column; gap: 8px; padding: 16px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.split-copy, .stack-copy { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); grid-auto-flow: dense; align-items: start; gap: 0 var(--s-8); padding: 32px 32px 24px; }
.split-copy > *, .stack-copy > * { grid-column: 2; justify-self: start; }
.split-copy > * + *, .stack-copy > * + * { margin-block-start: 14px; }
.split-copy > :is(h2, h3), .stack-copy > :is(h2, h3) { grid-column: 1; grid-row: 1 / span 9; }
.split-copy > :is(h2, h3) + *, .stack-copy > :is(h2, h3) + * { margin-block-start: 0; }
.split-copy p, .stack-copy p { color: var(--text); }
.split-card > :is(.show, .rec) > :not(.plate), .stack-card .stack-media > :not(.plate, .ill) { padding-inline: 16px; }
.split-card > :is(.show, .rec), .stack-media { padding-block-end: 8px; }
.stack { display: grid; gap: 24px; }
.stack-card { transform-origin: 50% 0; }
.stack-media > .ill { --ar: var(--pa, 2.4); border-radius: var(--radius-media); }
.stack-media > .ill > img { object-fit: contain; }
.stack-card:nth-child(2) { --i: 1; } .stack-card:nth-child(3) { --i: 2; } .stack-card:nth-child(4) { --i: 3; } .stack-card:nth-child(5) { --i: 4; }
@media (min-width: 1025px) and (prefers-reduced-motion: no-preference) { .stack-card { position: sticky; inset-block-start: calc(var(--sticky-top) + var(--i, 0) * 16px); } }
@media (max-width: 1024px) {
  .split-copy, .stack-copy { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; padding: 16px 8px 16px; }
  .split-copy > * + *, .stack-copy > * + * { margin-block-start: 0; }
  .split-card > :is(.show, .rec) > :not(.plate), .stack-card .stack-media > :not(.plate, .ill) { padding-inline: 8px; }
}
@media (max-width: 640px) { .stack-media > .ill { --ar: 4 / 3; } }
/* A feature card with no picture of the app (its .stack-media holds only an .ill): the words and
   one small square illustration beside them, not a wide box that would letterbox a square drawing
   into a "missing screenshot". The markup is the same; phones put the tile above the words. */
.stack-card:has(> .stack-media > .ill:only-child) { flex-direction: row; align-items: center; gap: 0; }
.stack-card:has(> .stack-media > .ill:only-child) > .stack-copy { flex: 1; min-width: 0; }
.stack-media:has(> .ill:only-child) { flex: 0 0 auto; width: 216px; padding: 16px; }
.stack-media > .ill:only-child { --ar: 1; }
.stack-media > .ill:only-child > img { object-fit: cover; }
@media (max-width: 1024px) { .stack-media:has(> .ill:only-child) { width: 176px; padding: 8px; } }
@media (max-width: 640px) {
  .stack-card:has(> .stack-media > .ill:only-child) { flex-direction: column; align-items: stretch; }
  .stack-media:has(> .ill:only-child) { order: -1; width: 136px; padding: 8px 8px 0; }
}

/* ======================================================================== Band (one pale band per page) */
.band { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: center; gap: var(--s-8); padding: var(--s-8); background: var(--band); border-radius: var(--radius-card); }
.band-copy { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
.band-copy p { font-size: var(--fs-lead); line-height: 1.45; color: var(--text); }
@media (max-width: 900px) { .band { grid-template-columns: minmax(0, 1fr); gap: 24px; padding: 16px 16px 32px; } .band-copy { padding-inline: 8px; } }

/* ======================================================================== Research cards */
.rcards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
.rcard { display: flex; flex-direction: column; gap: 20px; padding: 16px 16px 32px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.rcard > .ill { --ar: 16 / 10; border-radius: var(--radius-media); }
.rcard-body { display: flex; flex: 1; flex-direction: column; gap: 12px; padding-inline: 16px; }
.rcard-source { font-size: 14px; font-weight: 600; line-height: 1.4; color: var(--purple-dark); }
.rcard-title { font-size: clamp(22px, 2vw, 26px); font-weight: 600; line-height: 1.2; letter-spacing: var(--tr-title); text-wrap: balance; }
.rcard-body p:not(.rcard-source) { color: var(--text); }
.rcard-in { margin-block-start: auto; padding-block-start: 16px; border-block-start: 1px solid var(--line); font-size: 15px; font-weight: 500; color: var(--ink); }
@media (max-width: 1024px) { .rcards { grid-template-columns: minmax(0, 1fr); max-width: 640px; margin-inline: auto; } }
/* One finding on its own (the product pages' "Powered by research"): one centred card, the
   illustration shown whole in a wide strip. */
.rcards--one { grid-template-columns: minmax(0, 1fr); max-width: 760px; margin-inline: auto; }
.rcards--one .rcard > .ill { --ar: 5 / 2; }
.rcards--one .rcard > .ill > img { object-fit: contain; }

/* ======================================================================== The deep panel (the motto, as a promise) */
/* SchoolAI's "Built on trust" slot: one per page, deep aubergine, radius 96, a white H2, one line,
   four plain promises in a 2×2, one white button. An optional .ill.panel-ill sits above the H2. */
.panel { display: flex; flex-direction: column; align-items: center; gap: 24px; padding: var(--s-9); background: var(--deep); color: #fff; border-radius: var(--radius-panel); text-align: center; }
.panel-ill { width: 120px; }
.panel .h2 { max-width: 20ch; }
.panel-sub { max-width: 36em; font-size: var(--fs-lead); line-height: 1.45; color: var(--on-deep); text-wrap: pretty; }
.panel-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px 64px; width: 100%; max-width: 920px; margin: 24px 0 16px; padding: 0; list-style: none; text-align: start; }
.panel-item { display: flex; flex-direction: column; gap: 8px; padding-block-start: 20px; border-block-start: 1px solid rgb(255 255 255 / .18); }
.panel-item-title { font-size: 22px; font-weight: 600; line-height: 1.25; letter-spacing: var(--tr-title); }
.panel-item p { font-size: 17px; line-height: 1.5; color: var(--on-deep); }
.panel :focus-visible { outline-color: #fff; }
@media (max-width: 640px) { .panel { gap: 20px; padding: 48px 20px; } .panel-grid { grid-template-columns: minmax(0, 1fr); gap: 24px; margin-block-start: 8px; } }

/* ======================================================================== FAQ */
/* H2 on the inline start, questions on the other side. klaro.js opens one answer at a time
   (.faq-item > button, then .answer); tools/site.py reads the same markup for FAQPage JSON-LD. */
.faq { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); align-items: start; gap: 24px var(--s-8); }
.faq-head { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
.faq-list { border-block-start: 1px solid var(--line); }
.faq-item { border-block-end: 1px solid var(--line); }
.faq-q { display: flex; align-items: center; justify-content: space-between; gap: 24px; width: 100%; padding: 24px 0; border: 0; background: none;
  font: 600 20px/1.35 var(--font); letter-spacing: -.01em; color: var(--ink); text-align: start; cursor: pointer; }
.faq-q:focus-visible { outline-offset: 2px; border-radius: 8px; }
.faq-icon { flex-shrink: 0; display: grid; place-items: center; width: 36px; height: 36px; border: 1px solid var(--line); border-radius: 50%; color: var(--ink);
  transition: rotate var(--dur-ui) var(--ease-ui), background-color var(--dur-fast) var(--ease-ui), border-color var(--dur-fast) var(--ease-ui), color var(--dur-fast) var(--ease-ui); }
.faq-icon .i { width: 18px; height: 18px; }
.faq-q[aria-expanded="true"] .faq-icon { rotate: 45deg; background: var(--ink); border-color: var(--ink); color: #fff; }
.answer { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-ui) var(--ease-inout); }
.answer.open { grid-template-rows: 1fr; }
.answer > div { overflow: hidden; visibility: hidden; transition: visibility 0s var(--dur-ui); }
.answer.open > div { visibility: visible; transition-delay: 0s; }
.answer p { max-width: 56ch; padding-block-end: 24px; font-size: 17px; line-height: 1.6; color: var(--text); }
@media (max-width: 900px) { .faq { grid-template-columns: minmax(0, 1fr); } .faq-q { font-size: 18px; padding-block: 20px; } }

/* ======================================================================== Closing call */
/* A centred H2, one line, buttons; up to eight illustration tiles float round it (four a side,
   placed by order). The upper tiles hang from the top and the lower ones stand on the bottom, each
   clear of the edge by more than its drift (--depth, px), so none is ever cut by the section's
   edge, whatever its height. Phones: the first four in a row above the words. */
.closing { position: relative; padding-block: var(--section); overflow: clip; }
.closing-copy { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 20px; max-width: 640px; margin-inline: auto; padding-inline: var(--gutter); text-align: center; }
.closing-copy .lead { margin-inline: auto; }
.closing-copy .hero-actions { justify-content: center; }
.closing-tiles { position: absolute; inset: 0; width: min(1440px, 100%); margin: 0 auto; padding: 0; list-style: none; pointer-events: none; }
.closing-tile { position: absolute; width: var(--w, 140px); --ar: 1; }
.closing-tile:nth-child(1) { --w: 150px; --depth: 28; inset-inline-start: 3%; inset-block-start: calc(2% + 28px); rotate: -4deg; }
.closing-tile:nth-child(2) { --w: 118px; --depth: 14; inset-inline-start: 16%; inset-block-start: 34%; rotate: 3deg; }
.closing-tile:nth-child(3) { --w: 168px; --depth: 36; inset-inline-start: 4%; inset-block-end: calc(2% + 40px); rotate: 2deg; }
.closing-tile:nth-child(4) { --w: 124px; --depth: 20; inset-inline-start: 18%; inset-block-end: calc(2% + 24px); rotate: -3deg; }
.closing-tile:nth-child(5) { --w: 140px; --depth: 24; inset-inline-end: 4%; inset-block-start: calc(2% + 24px); rotate: 3deg; }
.closing-tile:nth-child(6) { --w: 120px; --depth: 34; inset-inline-end: 17%; inset-block-start: 31%; rotate: -2deg; }
.closing-tile:nth-child(7) { --w: 164px; --depth: 18; inset-inline-end: 3%; inset-block-end: calc(2% + 22px); rotate: -4deg; }
.closing-tile:nth-child(8) { --w: 126px; --depth: 30; inset-inline-end: 19%; inset-block-end: calc(2% + 34px); rotate: 2deg; }
@media (max-width: 1240px) { .closing-tile:nth-child(even) { display: none; } }
/* .closing-tiles--four: a page with only four illustrations to spare (/) keeps all four at every
   width, one up and one down on each side (the places of tiles 1, 3, 5 and 7 above). */
@media (min-width: 901px) {
  .closing-tiles--four .closing-tile:nth-child(2) { --w: 168px; --depth: 36; inset-inline: 4% auto; inset-block: auto calc(2% + 40px); rotate: 2deg; }
  .closing-tiles--four .closing-tile:nth-child(3) { --w: 140px; --depth: 24; inset-inline: auto 4%; inset-block: calc(2% + 24px) auto; rotate: 3deg; }
  .closing-tiles--four .closing-tile:nth-child(4) { --w: 164px; --depth: 18; inset-inline: auto 3%; inset-block: auto calc(2% + 22px); rotate: -4deg; }
}
.closing-tiles--four .closing-tile:nth-child(even) { display: block; }
@media (max-width: 900px) {
  .closing-tiles { position: static; display: flex; justify-content: center; gap: 12px; margin-block-end: 32px; padding-inline: var(--gutter); }
  .closing-tile, .closing-tile:nth-child(even) { position: relative; inset: auto; display: block; width: 72px; rotate: none; }
  .closing-tile:nth-child(n+5) { display: none; }
}

/* ======================================================================== Devices */
/* CSS-drawn frames, no brand (§7.9). A phone frame only ever holds the app's own phone layout.
     browser: <div class="device device--browser"><div class="device-bar" aria-hidden="true"><i></i><i></i><i></i><span></span></div><div class="device-screen">…</div></div>
     phone:   <div class="device device--phone"><div class="device-body"><div class="device-screen">…</div></div></div>
     tablet:  <div class="device device--tablet"><div class="device-body"><div class="device-screen">…</div></div></div>
   The screen holds one <img>, <video>, the showcase's .show-screen or a .ph placeholder. --screen
   sets its shape (1440 / 900 by default; phone 390 / 844; tablet 16 / 10), --focus the crop
   (object-position, top left by default). Phone and tablet frames scale with their width
   (--device-w, or the width their parent gives them). */
.device { position: relative; margin: 0; }
.device--browser { overflow: hidden; background: var(--card); border: 1px solid rgb(0 0 0 / .08); border-radius: var(--radius-frame); box-shadow: 0 24px 50px -28px rgb(30 12 60 / .3); }
.device-bar { position: relative; direction: ltr; display: flex; align-items: center; gap: 7px; height: 36px; padding-inline: 14px; background: #f8f8f6; border-block-end: 1px solid rgb(0 0 0 / .06); }
.device-bar > i { width: 10px; height: 10px; border-radius: 50%; background: #dddcd6; }
.device-bar > span { position: absolute; inset-inline: 0; width: 36%; height: 20px; margin-inline: auto; border-radius: var(--r-pill); background: #edece8; }
.device-screen { position: relative; overflow: hidden; aspect-ratio: var(--screen, 1440 / 900); background: var(--surface); }
.device-screen > img, .device-screen > video, .device-screen > picture > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 0 0); }
.device-screen > .ph { position: absolute; inset: 0; }
.device--phone, .device--tablet { container-type: inline-size; width: var(--device-w, 100%); max-width: 100%; }
.device--phone { --device-w: 300px; }
.device-body { position: relative; background: #151515; box-shadow: 0 24px 50px -28px rgb(30 12 60 / .4); }
.device--phone .device-body { padding: 2.6cqi; border-radius: 12.4cqi; }
.device--phone .device-screen { --screen: 390 / 844; border-radius: 9.8cqi; }
.device--phone .device-body::before { content: ""; position: absolute; z-index: 2; inset-inline: 0; inset-block-start: 4.8cqi; width: 28%; height: 7.4cqi; margin-inline: auto; border-radius: var(--r-pill); background: #151515; }
.device--phone .device-body::after { content: ""; position: absolute; inset-inline-end: -.7cqi; inset-block-start: 24%; width: .8cqi; height: 12%; border-radius: 2px; background: #151515; }
.device--tablet .device-body { padding: 1.8cqi; border-radius: 4cqi; }
.device--tablet .device-screen { --screen: 16 / 10; border-radius: 2.2cqi; }

/* A scene plate: a wide illustration behind a device, the device at the inline end where the
   illustration leaves room (§7.9). The plate is flipped in Arabic so that room stays at the
   inline end; the device is not. Until the plate exists it is a flat lavender field (its file
   name, data-ph, shows only with ?ph). --plate-ar sets the shape (16 / 10 by default;
   --plate-ar-sm under 640px, 4 / 3), --plate-pos the crop, --device-share the device's width.
     <div class="plate" data-ph="img/plates/P4-marking.webp"><img class="plate-img" src="…" alt=""> <div class="device …">…</div></div>
   .plate--center centres the device; .plate--bleed lets it run off the bottom and inline-end edges. */
.plate { position: relative; isolation: isolate; container-type: size; display: flex; align-items: center; justify-content: flex-end; aspect-ratio: var(--plate-ar, 16 / 10);
  padding: var(--plate-pad, 6%); overflow: hidden; border-radius: var(--radius-media); background: var(--plate); }
.plate-img, .plate > picture > img { position: absolute; inset: 0; z-index: -1; width: 100%; height: 100%; object-fit: cover; object-position: var(--plate-pos, 50% 50%); }
.plate > picture { display: contents; }
[dir="rtl"] :is(.plate-img, .plate > picture > img) { scale: -1 1; }
.plate > .device--browser { flex: 0 0 auto; width: min(var(--device-share, 86%), calc(88cqh * 1.6 - 58px)); box-shadow: var(--shadow-device); }
.plate > .device--tablet { flex: 0 0 auto; width: min(var(--device-share, 80%), calc(88cqh * 1.6)); }
.plate > .device--phone { flex: 0 0 auto; width: calc(88cqh * 390 / 844 * .94); }
.plate :is(.device--phone, .device--tablet) .device-body { box-shadow: var(--shadow-device); }
.plate--center { justify-content: center; }
.plate--bleed { align-items: flex-end; padding-block-end: 0; padding-inline-end: 0; }
.plate--bleed > .device--browser { width: 92%; margin-block-end: -6%; margin-inline-end: -6%; }
.plate.is-missing > .plate-img { visibility: hidden; }
html.ph-on :is(.plate[data-ph].is-missing, .plate[data-ph]:not(:has(.plate-img, picture)))::before { content: attr(data-ph); position: absolute; z-index: 0; inset-block-start: 14px; inset-inline-start: 14px;
  max-width: 44%; padding: 6px 10px; border: 1px dashed rgb(46 23 88 / .3); border-radius: 10px; color: rgb(46 23 88 / .72);
  font: 500 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0; overflow-wrap: anywhere; }
@media (max-width: 640px) {
  .plate { --plate-pad: 5%; aspect-ratio: var(--plate-ar-sm, 4 / 3); }   /* the plates' 1200×900 phone crop */
  .device-bar { gap: 5px; height: 24px; padding-inline: 10px; }
  .device-bar > i { width: 7px; height: 7px; }
  .device-bar > span { height: 12px; }
}

/* A scene plate with the device drawn in it (the owner's decision, 2026-10-08): the illustration
   draws the laptop, monitor, phone or tablet facing the viewer, its screen a flat dark field, and
   the real app (a recording, its poster, a still, or the live frame) sits exactly on that screen.
     <div class="plate plate--device" data-plate="P4-marked-papers-full" data-ph="img/plates/P4-marked-papers-full.webp">
       <div class="device device--browser">…the usual bar and screen…</div>
     </div>
   The whole device is always in the picture, with some of the scene round it (the owner: a cut
   laptop gains nothing). So the pictures are 4:3 (P1 3:2), never the 12:5 files, at every width, and
   the plate is as large as its column and the screen's height allow: --plate-room is the height
   kept for the header and the words under the plate.
   Each plate's box, measured on its picture, is set below as unitless percentages of the
   picture: --sx, --sy (the screen's top left), --sw, --sh (its size), --sr (its corner radius,
   in % of the picture's width), and --pa (the picture's aspect, width / height). A plate may crop
   its picture round the screen (--frac is the share of the picture's width shown, --x0 where the
   crop starts; the box follows the crop), but only where the device stays whole: the Minis hero,
   and on phones each plate's --frac-sm (the device's own width plus a little of the scene).
   Arabic: the picture is not mirrored (that would move the box).
   The opt-in: a plate's picture is used only once its rule below says --drawn: 1 (0 by default).
   With 1, site.js loads img/plates/<data-plate>.webp and adds .is-drawn once it has: the CSS
   frame's chrome goes and the screen takes the drawn screen's corners; until then (still loading,
   file missing, no JavaScript) the CSS frame waits in the same box, so nothing moves when the
   picture arrives. With 0 (no picture yet, or its box not measured) the page never asks for the
   file, and the CSS frame sits centred on the plain field (--bx, --by), at the box's size. */
.plate--device { --frac: 1; --x0: clamp(0, (var(--sx) + var(--sw) / 2) / 100 - var(--frac) / 2, 1 - var(--frac)); --drawn: 0; --plate-room: 240px;
  display: block; width: min(100%, calc((100vh - var(--plate-room)) * var(--pa) * var(--frac))); width: min(100%, calc((100svh - var(--plate-room)) * var(--pa) * var(--frac)));
  margin-inline: auto; padding: 0; aspect-ratio: calc(var(--pa) * var(--frac));
  --bx: calc(var(--sx) * var(--drawn) + (100 - var(--sw)) / 2 * (1 - var(--drawn)));
  --by: calc(var(--sy) * var(--drawn) + (100 - var(--sh)) / 2 * (1 - var(--drawn))); }
.plate--device > .plate-img { inset-block: 0; inset-inline: auto; inset-inline-start: calc(-100% * var(--x0) / var(--frac)); width: calc(100% / var(--frac)); max-width: none; object-fit: fill; }   /* wider than the plate when it is cropped: not capped by img { max-width: 100% } */
[dir="rtl"] .plate--device { direction: ltr; }   /* the picture is never mirrored, so the box is placed from its left */
[dir="rtl"] .plate--device > .plate-img { scale: none; }
.plate--device > .device { position: absolute; display: flex; flex-direction: column; margin: 0;
  inset-block-start: calc(var(--by) * 1%); inset-inline-start: calc((var(--bx) / 100 - var(--x0)) / var(--frac) * 100%);
  width: calc(var(--sw) / var(--frac) * 1%); height: calc(var(--sh) * 1%); }
.plate--device > .device--browser { box-shadow: var(--shadow-device); }
.plate--device > .device .device-body { display: flex; flex-direction: column; height: 100%; }
.plate--device > .device .device-screen { flex: 1; min-height: 0; aspect-ratio: auto; }
.plate--device.is-drawn > .device { overflow: hidden; background: none; border: 0; border-radius: calc(var(--sr) * 1cqw / var(--frac)); box-shadow: none; }
.plate--device.is-drawn > .device .device-bar { display: none; }
.plate--device.is-drawn > .device .device-body { padding: 0; border-radius: inherit; background: none; box-shadow: none; }
.plate--device.is-drawn > .device .device-body::before, .plate--device.is-drawn > .device .device-body::after { display: none; }
.plate--device.is-drawn > .device .device-screen { border-radius: inherit; background: #1b1722; }
/* The drawn picture fades in when it arrives (it is lazy), instead of popping over the CSS frame. */
.plate--device > .plate-img { opacity: 0; transition: opacity var(--dur-slow) var(--ease-ui); }
.plate--device.is-drawn > .plate-img { opacity: 1; }
@media (max-width: 640px) {
  html.ph-on .plate--device[data-ph]::before { display: none; }   /* no room for the file name beside the device */
  .plate--device { --frac: var(--frac-sm, 1); }
  .plate--device[data-plate^="P2-"] { --plate-room: 140px; }   /* the phone as tall as the screen allows; the words follow under it */
}
/* The boxes, measured on each picture (design/brief/illustrations.md, "Screen boxes"; the -full
   files are new crops of the uncropped originals, so the whole laptop or monitor is in them).
   Replace the numbers, keep the names; --drawn: 1 opts a plate in (site.js then loads it). The
   screens are 1.43–1.57:1 (laptops, monitors), 1.32:1 (the tablet) or 0.45:1 (the phone), and
   the camera fills any shape. */
.plate[data-plate] { --pa: 1.3333; --sx: 12; --sy: 15; --sw: 76; --sh: 70; --sr: .8; }
.plate[data-plate="P1-evening-desk-full"] { --pa: 1.5; --sx: 21.18; --sy: 13.15; --sw: 55.9; --sh: 58.33; --sr: 0; --frac-sm: .9; --drawn: 1; }   /* laptop, 3:2: at 4:3 the keyboard would touch the edges */
/* P2 is a phone (9:19.5), only ever holding the app's phone layout (the Minis feed). */
.plate[data-plate="P2-kitchen-tea-phone"] { --pa: 1.3333; --sx: 36.5; --sy: 11; --sw: 27; --sh: 78; --sr: 3; --frac-sm: .4; --drawn: 1; }
.plate[data-plate="P3-chalkboard-phone"] { --pa: 1.3333; --sx: 11.25; --sy: 16.33; --sw: 77.58; --sh: 65.89; --sr: 0; --frac-sm: .88; --drawn: 1; }   /* monitor */
.plate[data-plate="P4-marked-papers-full2"] { --pa: 1.3333; --sx: 26.75; --sy: 15.67; --sw: 46.5; --sh: 40; --sr: 0; --frac-sm: .74; --drawn: 1; }   /* laptop, drawn whole (4:3 original, v2) */
.plate[data-plate="P5-dusk-window-full2"] { --pa: 1.3333; --sx: 26.25; --sy: 23.11; --sw: 47.67; --sh: 41.78; --sr: 0; --frac-sm: .82; --drawn: 1; }   /* laptop, drawn whole (4:3 original, v2) */
.plate[data-plate="P6-library-full"] { --pa: 1.3333; --sx: 15.04; --sy: 16.54; --sw: 69.63; --sh: 59.77; --sr: 0; --frac-sm: .8; --drawn: 1; }   /* monitor */
.plate[data-plate="P7-sofa-corner-phone"] { --pa: 1.3333; --sx: 14.58; --sy: 14.22; --sw: 70.75; --sh: 71.67; --sr: .83; --frac-sm: .82; --drawn: 1; }   /* a tablet, 4:3 landscape */

/* ======================================================================== The real app: showcase frames and recordings */
/* The markup is design/brief/showcase.md §11.1; showcase.js drives it and styles nothing. The
   classes it adds or toggles, and what they look like:
     .show / .rec                the <figure>; .is-ready (the frame has drawn), .is-free (the visitor's turn), .is-playing
     .show-screen / .rec-screen  the screen inside a .device (it is also .device-screen); holds the poster <img>
     .show-cam                   the layer the live frame sits in, scaled by the driver to fit its screen whole
     .plate--device, .pr-shot-field
                                 the camera's frame: showcase.js moves its children (the plate's picture and the
                                 device, with the app in it) as one, with inline transforms, while it is in; the
                                 frame itself stays put and clips. Nothing ever zooms inside a screen.
     .show-cursor, .show-tap     the drawn cursor and its tap ripple; .show-hint (.is-on), .show-ring, .show-bubble
     .show-steps / .rec-cues     the caption track: one <li> is .is-on (the first shows until one is)
     .show-under, .show-actions  the row under the device: the track, then .show-try / .show-replay (pills)
     .rec-play                   the Play button over a poster (reduced motion, or play refused)
     .show-sheet, .show-sheet-bar  the phone's full-screen "Try it" (a fixed dialog with a second frame) */
.show, .rec { position: relative; display: flex; flex-direction: column; gap: 16px; margin: 0; }
.show-screen, .rec-screen { direction: ltr; }   /* the live frame is placed in app pixels from the top left; the app mirrors itself inside */
/* No will-change: the frame's scale never animates, so it is drawn sharp at its size. */
.show-cam { position: absolute; inset-block-start: 0; inset-inline-start: 0; width: 1440px; height: 900px; transform-origin: 0 0; }
.show-cam > iframe { display: block; width: 1440px; height: 900px; border: 0; opacity: 0; pointer-events: none; transition: opacity var(--dur-slow) var(--ease-ui); }
/* showcase.js reports state in data-state: the frame shows once the app has drawn */
.show:is(.is-ready, [data-state="live"], [data-state="story"], [data-state="free"], [data-state="done"]) .show-cam > iframe { opacity: 1; }
.show:is(.is-free, [data-state="free"]) .show-cam > iframe { pointer-events: auto; }
.show .device { outline: 3px solid transparent; outline-offset: 0; transition: outline-color var(--dur-slow) var(--ease-ui); }
.show:is(.is-free, [data-state="free"]) .device { outline-color: var(--purple); }   /* the hand-over: the ring fades in */
/* A recording is always whole in its screen (the owner, 2026-10-09): contained, at the top and
   centred across, on the app's own page colour, so a drawn screen that isn't 16:10 shows a strip of
   that colour under the app instead of cutting it. A phone recording fills its phone (cover, centred
   across): the difference there is under 2% and falls in the phone layout's side margins. */
.rec .device-screen, .rec .plate--device.is-drawn > .device .device-screen { background: var(--page); }
.rec .device-screen > :is(img, video), .rec .device-screen > picture > img { object-fit: contain; object-position: 50% 0; }
.rec .device--phone .device-screen > :is(img, video), .rec .device--phone .device-screen > picture > img { object-fit: cover; object-position: 50% 0; }
/* The video fades in over its poster once it has a frame (showcase.js sets data-shown). */
.rec .device-screen > video { opacity: 0; transition: opacity var(--dur-slow) var(--ease-ui); }
.rec .device-screen > video[data-shown] { opacity: 1; }
.show-cursor { position: absolute; inset-block-start: 0; inset-inline-start: 0; z-index: 2; opacity: 0; pointer-events: none; will-change: transform, opacity; }
.show-tap { position: absolute; inset-block-start: -18px; inset-inline-start: -18px; width: 36px; height: 36px; border-radius: 50%; background: rgb(146 84 224 / .35); opacity: 0; }
.show-hint { position: absolute; inset-block-start: 0; inset-inline-start: 0; z-index: 3; opacity: 0; pointer-events: none; transition: opacity var(--dur-slow) var(--ease-ui); }
.show-hint:is(.is-on, :not([hidden])) { opacity: 1; }
.show-ring, .show-hint-ring { position: absolute; inset-block-start: -22px; inset-inline-start: -22px; width: 44px; height: 44px; border: 3px solid var(--purple); border-radius: 50%; }
.show-bubble { position: absolute; inset-block-start: -24px; inset-inline-start: 38px; width: max-content; max-width: 260px; padding: 10px 14px; border-radius: 14px;
  background: var(--ink); color: #fff; font: 600 15px/1.35 var(--font); box-shadow: 0 10px 24px rgb(23 23 23 / .18); }
.show-bubble:lang(ar) { direction: rtl; inset-inline: auto 38px; }   /* in Arabic the book is on the right: the bubble opens leftwards */
.show-under { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 16px; min-height: 44px; }
.show-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.show-steps, .rec-cues { display: grid; flex: 1 1 260px; margin: 0; padding: 0; list-style: none; }
/* The captions share one cell. The one leaving fades out quickly, then the next fades in, so two
   lines never show on top of each other. */
.show-steps > li, .rec-cues > li { grid-area: 1 / 1; font-size: 18px; font-weight: 600; line-height: 1.4; letter-spacing: -.01em; color: var(--ink); opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-ui); }
/* showcase.js marks the current step with [data-current]; .is-on is kept for hand-written markup */
.show-steps > li:first-child, .rec-cues > li:first-child,
.show-steps > li:is(.is-on, [data-current]), .rec-cues > li:is(.is-on, [data-current]) { opacity: 1; transition: opacity var(--dur-ui) var(--ease-ui) var(--dur-fast); }
.show-steps:has(> :is(.is-on, [data-current])) > li:not(.is-on, [data-current]),
.rec-cues:has(> :is(.is-on, [data-current])) > li:not(.is-on, [data-current]) { opacity: 0; transition: opacity var(--dur-fast) var(--ease-ui); }
.rec-play { position: absolute; z-index: 2; inset: 0; display: grid; place-items: center; width: 64px; height: 64px; margin: auto; border: 0; border-radius: 50%;
  background: rgb(255 255 255 / .94); color: var(--purple-dark); box-shadow: 0 10px 30px -10px rgb(30 12 60 / .45); cursor: pointer; }
.rec-play .i { width: 26px; height: 26px; margin-inline-start: 3px; }
/* "Use the sample app with the keyboard" (showcase.js, during the visitor's turn): the live app is
   out of the tab order until it is chosen, so this shows only when the keyboard reaches it. */
.show-use:not(:focus, :focus-visible) { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
.show-sheet { position: fixed; inset: 0; z-index: 80; display: flex; flex-direction: column; background: var(--page); }
.show-sheet-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 60px; padding: 8px 16px; padding-block-start: max(8px, env(safe-area-inset-top, 0px));
  background: var(--card); border-block-end: 1px solid var(--line); font-size: 15px; font-weight: 600; color: var(--text); }
.show-sheet iframe { flex: 1; width: 100%; border: 0; }
/* A frame with no device that fills what its parent gives it (/try/): showcase.js's data-fit="fill"
   draws the app at that size. The parent sets the height (a flex column). */
.show--full { flex: 1; min-height: 0; gap: 0; }
.show--full .show-screen { position: relative; flex: 1; min-height: 0; overflow: hidden; background: var(--surface); border-block-start: 1px solid var(--line); }
.show--full .show-screen > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 0 0; }

/* One plain caption under every picture of the real app: what it is, whose view, and that it is a
   sample course. <figcaption class="caption"><b>Sample course.</b> The tutor's marking queue…</figcaption> */
.caption { max-width: 62ch; font-size: 15px; font-weight: 500; line-height: 1.45; color: var(--text); text-wrap: pretty; }
.caption b { font-weight: 600; color: var(--ink); }

/* ======================================================================== The opening scroll (§7.8) */
/* The pinned hero of / and /students/, after Granola. Copy on the inline start; on the other side a
   tall track whose stage sticks for about one screen of scroll. As the visitor scrolls, the device
   settles from slightly large to its place and three illustration tiles (the visitor's materials)
   gather behind it, shrink and fade. Four invisible markers (.opening-marks > i[data-step]) let the
   showcase driver pick the story's step when one crosses the middle of the screen.
   CSS scroll-driven animations where supported; site.js otherwise sets --od (how far the opening
   has scrolled, in vh) and the same motion follows it. Reduced motion: no pin, no movement; the
   tiles sit still round the stage (hidden on phones). Phones: copy, then the pinned stage.
     <section class="opening">
       <div class="wrap opening-grid">
         <div class="opening-copy">chip-switch, h1, lead, .hero-actions, .hero-note</div>
         <div class="opening-track">
           <div class="opening-stage">
             <div class="opening-collage" aria-hidden="true"><div class="ill opening-tile">…</div> ×3</div>
             <figure class="show only-wide" …>browser device</figure>
             <figure class="rec only-narrow" …>phone device</figure>
           </div>
           <div class="opening-marks" aria-hidden="true"><i data-step="1"></i><i data-step="2"></i><i data-step="3"></i><i data-step="4"></i></div>
         </div>
       </div>
     </section>
   .opening--pin-copy also pins the copy column beside the stage (desktop). */
.opening { position: relative; overflow-x: clip; padding-block-start: 16px; view-timeline-inset: 0; }
.opening-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: start; gap: var(--s-8); }
.opening-copy { position: relative; z-index: 2; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; gap: 24px;
  min-height: calc(100vh - var(--sticky-top) - 16px); min-height: calc(100svh - var(--sticky-top) - 16px); padding-block: 24px; }
.opening-copy .lead { max-width: 30em; }
.opening-track { --pin-len: calc(90svh + var(--sticky-top) + 16px); position: relative; min-height: 190vh; min-height: 190svh; }
.opening-stage { position: sticky; inset-block-start: var(--sticky-top); isolation: isolate; display: flex; flex-direction: column; justify-content: center;
  height: calc(100vh - var(--sticky-top) - 16px); height: calc(100svh - var(--sticky-top) - 16px); }
.opening-stage > :is(.show, .rec) { position: relative; z-index: 1; width: min(88%, calc((100svh - 270px) * 1.6)); margin-inline: auto; }
.opening-stage .device { --settle-s: 1.03; --settle-x: 0%; --settle-y: -1vh; }   /* a barely-there settle as the visitor scrolls; the app inside never zooms */
.opening-collage { position: absolute; inset: 0 0 64px; z-index: 0; pointer-events: none; }
.opening-tile { --ar: 1; position: absolute; inset-inline: 0; inset-block-start: 50%; width: 26%; margin: -13% auto 0;
  translate: calc(var(--fx) * var(--k-dir)) var(--fy); rotate: calc(var(--fr) * var(--k-dir)); box-shadow: 0 18px 40px -24px rgb(30 12 60 / .4); }
.opening-tile:nth-child(1) { --fx: 152%; --fy: -150%; --fr: 6deg; --g0: 0vh; --g1: 50vh; --g0n: 0; }
.opening-tile:nth-child(2) { --fx: -158%; --fy: -75%; --fr: -5deg; --g0: 6vh; --g1: 56vh; --g0n: 6; }   /* upper left, half behind the frame: clear of the H1 and the captions, with or without motion */
.opening-tile:nth-child(3) { --fx: 156%; --fy: 80%; --fr: 4deg; --g0: 12vh; --g1: 62vh; --g0n: 12; }
.opening-marks { position: absolute; inset: 0; pointer-events: none; }
.opening-marks > i { position: absolute; inset-inline: 0; height: 1px; inset-block-start: calc(50svh - var(--sticky-top) + var(--at, 0) * var(--pin-len)); }
.opening-marks > i:nth-child(1) { --at: .15; } .opening-marks > i:nth-child(2) { --at: .35; } .opening-marks > i:nth-child(3) { --at: .6; } .opening-marks > i:nth-child(4) { --at: .85; }
@media (min-width: 1025px) and (prefers-reduced-motion: no-preference) {
  .opening--pin-copy .opening-copy { position: sticky; inset-block-start: var(--sticky-top); height: calc(100svh - var(--sticky-top) - 16px); min-height: 0; }
}
@media (max-width: 1024px) {
  .opening-grid { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .opening-copy { min-height: 0; padding-block: 16px 0; }
  .opening-track { --sticky-top: 72px; --pin-len: calc(100svh + 88px); min-height: 200vh; min-height: 200svh; }
  .opening-stage { height: calc(100svh - 88px); }
  .opening-stage > :is(.show, .rec) { width: min(100%, 560px); }
  .opening-stage > :is(.show, .rec):has(.device--phone) { width: auto; align-items: center; }
  .opening-stage .device--phone { --device-w: min(64vw, calc((100svh - 230px) * 390 / 844 * .94)); }
  .opening-collage { inset-block-end: 80px; }
  .opening-tile { width: 32%; margin-block-start: -16%; }
  .opening-tile:nth-child(1) { --fx: 112%; --fy: -118%; }
  .opening-tile:nth-child(2) { --fx: -114%; --fy: 18%; }
  .opening-tile:nth-child(3) { --fx: 112%; --fy: 112%; }
}
/* A live scene with a phone recording (data-phone-recording): under 900px showcase.js plays that
   recording of the app's phone layout instead of loading the app, so the browser frame turns into
   a phone there (its poster is the <picture>'s phone source). */
@media (max-width: 899px) {
  .opening-stage > .show[data-phone-recording] { width: auto; align-items: center; }
  /* The screen starts under the app's own header and toolbar (the top 200 of its 844 px), so the
     book, the lecture and the question fill it, and the phone can be wider for the same height. */
  .show[data-phone-recording] { --phone-screen: 390 / 644; }
  .show[data-phone-recording] .device--browser { --device-w: min(86vw, calc((100svh - 190px) * 390 / 644 * .94)); width: var(--device-w); max-width: 100%;
    padding: calc(var(--device-w) * .026); background: #151515; border: 0; border-radius: calc(var(--device-w) * .124); box-shadow: 0 24px 50px -28px rgb(30 12 60 / .4); }
  .show[data-phone-recording] .device--browser::before { content: ""; position: absolute; z-index: 2; inset-inline: 0; inset-block-start: calc(var(--device-w) * .048);
    width: 28%; height: calc(var(--device-w) * .074); margin-inline: auto; border-radius: var(--r-pill); background: #151515; }
  .show[data-phone-recording] .device--browser .device-bar { display: none; }
  .show[data-phone-recording] .device--browser .device-screen { --screen: var(--phone-screen); --focus: 50% 100%; border-radius: calc(var(--device-w) * .098); }
  .show[data-phone-recording] .show-video { object-position: 50% 100%; }
}
@media (max-width: 899px) and (min-width: 641px) { .show[data-phone-recording] .device--browser { --device-w: min(340px, calc((100svh - 240px) * 390 / 644 * .94)); } }
@media (max-width: 1024px) and (min-width: 641px) {
  .opening-stage .device--phone { --device-w: 300px; }
  /* Tablets: a landscape device with room on both sides, so the tiles sit beside it, not under the caption. */
  .opening-tile { width: min(26%, 220px); margin-block-start: max(-13%, -110px); }
  .opening-tile:nth-child(1) { --fx: 175%; --fy: -80%; }
  .opening-tile:nth-child(2) { --fx: -175%; --fy: 10%; }
  .opening-tile:nth-child(3) { --fx: 175%; --fy: 70%; }
}
@media (max-width: 899px) and (min-width: 641px) {
  /* Round the phone that plays the recording there, not a landscape frame. */
  .opening:has(.show[data-phone-recording]) .opening-tile:nth-child(odd) { --fx: 140%; }
  .opening:has(.show[data-phone-recording]) .opening-tile:nth-child(2) { --fx: -140%; }
}

/* ======================================================================== Sticky step list (/ and /students/, §7.8) */
/* Each step is a panel: its copy (h3, two lines, a text link) and its framed recording.
     <div class="steps-sticky" data-steps aria-label="What Klaro takes off your plate">
       <div class="steps-panels">
         <article class="steps-panel" id="step-lives">
           <div class="steps-copy"><h3 class="h3">Teach live from your schedule</h3><p>…</p><a class="tlink" href="…">About live sessions …</a></div>
           <figure class="rec" data-recording="lives"><div class="plate">…device…</div><div class="show-under"><ol class="rec-cues">…</ol></div><figcaption class="caption">…</figcaption></figure>
         </article> …
       </div>
     </div>
   Desktop with motion: site.js builds .steps-nav from the panels' copy (titles pinned on the inline
   start; the active one opens and its bar fills with its recording) and hides the panels' own copy.
   Phones, no JavaScript or reduced motion: a plain stack, copy above each recording. */
.steps-panels { display: grid; gap: var(--s-9); }
.steps-panel { display: grid; gap: 24px; scroll-margin-block: calc(var(--sticky-top) + 24px); }
.steps-copy { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; max-width: 40em; }
.steps-copy p { color: var(--text); }
.steps-nav { display: none; }
.steps-nav-item > h3 { margin: 0; font: inherit; }
.steps-nav-title { display: block; width: 100%; padding: 0; border: 0; background: none; font: 600 24px/1.25 var(--font); letter-spacing: var(--tr-title); color: var(--muted); text-align: start; cursor: pointer; transition: color var(--dur-ui) var(--ease-ui); }
.steps-nav-title:hover, .steps-nav-item.is-active .steps-nav-title { color: var(--ink); }
.steps-nav-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-slow) var(--ease-inout); }   /* the open step eases open as the next one eases shut */
.steps-nav-body > div { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; overflow: hidden; visibility: hidden; transition: visibility 0s var(--dur-slow); }
.steps-nav-item.is-active .steps-nav-body { grid-template-rows: 1fr; }
.steps-nav-item.is-active .steps-nav-body > div { visibility: visible; transition-delay: 0s; }
.steps-nav-body p { padding-block-start: 10px; font-size: 17px; color: var(--text); }
.steps-nav-bar { display: block; height: 2px; margin-block-start: 20px; overflow: hidden; border-radius: 2px; background: var(--line); }
.steps-nav-bar > i { display: block; height: 100%; background: var(--purple); transform: scaleX(var(--p, 0)); transform-origin: 0 50%; transition: transform var(--dur-ui) linear; }   /* linear on purpose: it follows the recording's clock */
[dir="rtl"] .steps-nav-bar > i { transform-origin: 100% 50%; }
@media (min-width: 1025px) {
  /* The recordings as large as they can be (§7.9): the plate's column is as wide as a 4:3 plate
     that fits the screen's height (with the words under it), up to 78%; the titles take the rest. */
  .steps-sticky.has-nav { display: grid; grid-template-columns: minmax(240px, 1fr) minmax(0, min(78%, calc((100svh - 240px) * 4 / 3))); align-items: start; gap: var(--s-8); }
  .steps-sticky.has-nav .steps-nav { position: sticky; inset-block-start: calc(var(--sticky-top) + 32px); display: flex; flex-direction: column; gap: 24px; margin: 0; padding: 0; list-style: none; }
  .steps-sticky.has-nav .steps-copy { display: none; }
  .steps-sticky.has-nav .steps-panels { gap: 160px; padding-block: 4vh 12vh; }
}

/* ======================================================================== Utilities */
/* Showcase frames load only where they are shown: a display:none figure is never mounted. */
@media (max-width: 900px) { .only-wide { display: none !important; } }
@media (min-width: 901px) { .only-narrow { display: none !important; } }

/* ======================================================================== Market support */
/* Only /market/ still uses these (market/market.css builds on them): the old product-page hero
   band, restyled flat, its chip, the draft marker and the small label. New pages don't use them. */
.p-hero { padding-block: 28px 0; }
.p-top { display: flex; flex-direction: column; gap: 18px; padding-block-start: 28px; }
.p-chip { align-self: flex-start; display: inline-flex; align-items: center; gap: 9px; height: 36px; padding-inline: 5px 14px; border-radius: var(--r-pill); background: var(--chip); font: 500 14px/1 var(--font); }
.p-chip .icon-tile { display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: var(--card); color: var(--muted); }
.p-chip .p-for { padding-inline-start: 9px; border-inline-start: 1px solid var(--border-strong); color: var(--chip-muted); }
.p-head { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); align-items: end; gap: 24px 56px; }
.p-head-side { display: flex; flex-direction: column; gap: 20px; padding-block-end: 6px; }
.p-draft { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border: 1px dashed #f0b58c; border-radius: var(--r-pill); background: #fff0e6; font-size: 12px; font-weight: 600; color: #a5521b; }
.kicker { font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--muted); }
.kicker.spark { display: inline-flex; align-items: center; gap: 7px; color: var(--purple-dark); fill: inherit; }
.star { display: inline-block; flex-shrink: 0; }
@media (max-width: 980px) { .p-head { grid-template-columns: minmax(0, 1fr); } }

/* ======================================================================== Arabic */
/* <html lang="ar" dir="rtl">. Letter spacing is off everywhere in Arabic (klaro.css). Here: more
   leading (Arabic letters reach higher and lower), H3-level titles at 500 (Readex Pro), a little
   more room in the menu, and the few mirrors logical properties can't do (marked [dir="rtl"] above:
   the plate flip, the bar fills; --k-dir mirrors all horizontal motion). Rules are written
   .x:lang(ar), so English course material marked lang="en" keeps its English styling. */
:is(.icard-title, .rcard-title, .panel-item-title, .steps-nav-title):lang(ar) { font-weight: 500; line-height: 1.45; }
.footer-line:lang(ar) { line-height: 1.75; }
:is(.section-sub, .band-copy p, .panel-sub):lang(ar) { line-height: 1.75; }
:is(.icard-line, .rcard-body p, .panel-item p, .steps-copy p, .steps-nav-body p, .answer p, .split-copy p, .stack-copy p):lang(ar) { line-height: 1.75; }
:is(.faq-q, .show-steps > li, .rec-cues > li, .caption, .mega-text small, .strip-caption):lang(ar) { line-height: 1.6; }
.mega-row:lang(ar) { line-height: 1.5; }
.mega:lang(ar) { width: min(900px, 100%); }
.chip-switch-label:lang(ar) { padding-inline: 10px 6px; }

/* ======================================================================== Motion */
/* One motion system (the owner, 2026-10-09: calm and smooth, nothing snappy, nothing that zooms
   inside a screen). Every duration and curve on the site is one of these tokens; showcase.js reads
   the camera's (and --ease-inout, --dur-slow) from here, so the script moves on the CSS's curves.
     --ease-ui     a soft ease-out (cubic) for UI answering the visitor: hover, menus, captions, fades
     --ease-inout  a slow sine ease-in-out for what opens or settles: the opening, the accordions,
                   the step list, the hint's ring, the pointer fading in and out
     --ease-camera a cubic ease-in-out for anything that travels across the picture: the camera,
                   both ways, and the pointer's glide in the hero. It leaves at zero speed, is
                   fastest halfway and lands at zero speed, like a camera on a dolly
     --dur-fast    .15s  colour, border and press feedback; a caption leaving
     --dur-ui      .3s   menus, captions, small turns, the FAQ
     --dur-slow    .6s   reveals, the live app and its hint fading in, the step list opening
     --dur-camera      2.2s  the camera walking in to the screen; then it holds
     --dur-camera-out  1.8s  the camera walking back out to the whole scene
   The camera is one scale about one fixed point per walk (showcase.js), so the picture never
   drifts one way and then the other. No overshoot and no bounce anywhere. Linear only where
   something follows a clock or the scroll one to one (a progress bar, the parallax drift, the
   stacked cards). klaro.css's --ease-out and --ease-settle point at --ease-ui here, so its buttons
   and links follow the system too. */
:root {
  --ease-ui: cubic-bezier(.33, 1, .68, 1);
  --ease-inout: cubic-bezier(.37, 0, .63, 1);
  --ease-camera: cubic-bezier(.65, 0, .35, 1);
  --dur-fast: .15s;
  --dur-ui: .3s;
  --dur-slow: .6s;
  --dur-camera: 2.2s;
  --dur-camera-out: 1.8s;
  --ease-out: var(--ease-ui);
  --ease-settle: var(--ease-ui);
}
/* Calm and once. site.js adds html.motion only when motion is allowed, and hides (opacity) only
   what starts below the first screen, until any part of it is on screen: nothing visible ever
   waits, and with JavaScript off or reduced motion everything is simply there.
     .k-rv  rises 12px into place once (--k-d staggers what comes in together) */
@keyframes k-rise { from { opacity: 0; translate: 0 12px; } }
@keyframes k-draw { from { transform: scaleX(0); } }
@keyframes k-pop { from { opacity: 0; scale: .6; } }
@keyframes k-in { from { opacity: 0; translate: 0 -6px; } }
@keyframes k-out { to { opacity: 0; translate: 0 -4px; } }
@keyframes k-ring { 50% { scale: 1.35; opacity: .35; } }   /* breathes out and back to the ring at rest */
@keyframes k-settle { from { scale: var(--settle-s); translate: calc(var(--settle-x) * var(--k-dir)) var(--settle-y); } }
@keyframes k-gather { to { translate: 0 0; rotate: 0deg; scale: .75; } }
@keyframes k-fade { to { opacity: 0; } }
@keyframes k-drift { from { translate: 0 calc(var(--depth, 24) * 1px); } to { translate: 0 calc(var(--depth, 24) * -1px); } }
@keyframes k-covered { to { scale: .96; } }

@media screen {
  html.motion .k-rv:not(.k-in) { opacity: 0; }
  html.motion .k-rv.k-in { animation: k-rise var(--dur-slow) var(--ease-ui) var(--k-d, 0s) backwards; }
}
@media print { html.motion .k-rv { opacity: 1 !important; animation: none !important; } }

@media (prefers-reduced-motion: no-preference) {
  /* The H1's underline draws in from the inline start, then its spark appears. */
  .uline::after { transform-origin: 0 50%; animation: k-draw calc(var(--dur-slow) * 1.5) var(--ease-inout) .35s both; }
  [dir="rtl"] .uline::after { transform-origin: 100% 50%; }
  .uline::before { animation: k-pop var(--dur-slow) var(--ease-ui) 1.05s both; }
  /* Menus (site.js waits for k-out to end before it hides the panel) */
  .mega:not([hidden]) { animation: k-in var(--dur-ui) var(--ease-ui) both; }
  .mega.is-closing { animation: k-out var(--dur-fast) var(--ease-ui) both; pointer-events: none; }
  /* Hover and focus */
  .icard:is(:hover, :focus-visible) .ill img { scale: 1.02; }
  /* The hint ring breathes three times, slowly, then rests. */
  .show-hint:is(.is-on, :not([hidden])) :is(.show-ring, .show-hint-ring) { animation: k-ring calc(var(--dur-camera) * 1.2) var(--ease-inout) var(--dur-slow) 3; }
}

/* Scroll-linked motion: only where the browser has scroll-driven animations (the opening also has
   site.js's fallback below). Transform and opacity only, on the compositor. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .opening { view-timeline: --opening block; view-timeline-inset: 0; }
    .opening-stage .device { animation: k-settle var(--ease-inout) both; animation-timeline: --opening; animation-range: exit-crossing 0px exit-crossing 100vh; }
    .opening-tile { animation: k-gather var(--ease-inout) both, k-fade var(--ease-inout) both; animation-timeline: --opening, --opening;
      animation-range: exit-crossing var(--g0) exit-crossing var(--g1), exit-crossing 60vh exit-crossing 80vh; }
    .mosaic-col + .mosaic-col { animation: k-drift linear both; animation-timeline: view(); animation-range: cover; --depth: 20; }
    .closing-tile { animation: k-drift linear both; animation-timeline: view(); animation-range: cover; }
  }
  @media (prefers-reduced-motion: no-preference) and (max-width: 1024px) {
    .opening-stage .device { animation: none; }   /* phones and tablets: the device stays as it is */
  }
  @media (prefers-reduced-motion: no-preference) and (max-width: 900px) {
    .closing-tile { animation: none; }
  }
}
/* The covered stacked card settles back as the next one slides over it (desktop). */
@supports (animation-timeline: view()) and (timeline-scope: --a) {
  @media (min-width: 1025px) and (prefers-reduced-motion: no-preference) {
    .stack { timeline-scope: --stack-2, --stack-3, --stack-4, --stack-5; }
    .stack-card:nth-child(2) { view-timeline-name: --stack-2; }
    .stack-card:nth-child(3) { view-timeline-name: --stack-3; }
    .stack-card:nth-child(4) { view-timeline-name: --stack-4; }
    .stack-card:nth-child(5) { view-timeline-name: --stack-5; }
    .stack-card:not(:last-child) { animation: k-covered linear both; animation-range: entry 60% contain 70%; }
    .stack-card:nth-child(1) { animation-timeline: --stack-2; }
    .stack-card:nth-child(2) { animation-timeline: --stack-3; }
    .stack-card:nth-child(3) { animation-timeline: --stack-4; }
    .stack-card:nth-child(4) { animation-timeline: --stack-5; }
  }
}
/* The opening without scroll-driven animations: site.js sets html.k-sd-js and, on .opening, --od
   (how far the opening has scrolled past the top of the screen, in vh). Same ranges as above, and
   the same gentle in-and-out (a smoothstep, close to --ease-inout). */
@media (prefers-reduced-motion: no-preference) {
  html.k-sd-js .opening-stage .device {
    --q: clamp(0, var(--od, 0) / 100, 1);
    --p: calc(var(--q) * var(--q) * (3 - 2 * var(--q)));
    scale: calc(var(--settle-s) - (var(--settle-s) - 1) * var(--p));
    translate: calc(var(--settle-x) * var(--k-dir) * (1 - var(--p))) calc(var(--settle-y) * (1 - var(--p)));
  }
  html.k-sd-js .opening-tile {
    --t: clamp(0, (var(--od, 0) - var(--g0n)) / 50, 1);
    --e: calc(var(--t) * var(--t) * (3 - 2 * var(--t)));
    translate: calc(var(--fx) * var(--k-dir) * (1 - var(--e))) calc(var(--fy) * (1 - var(--e)));
    rotate: calc(var(--fr) * var(--k-dir) * (1 - var(--e)));
    scale: calc(1 - .25 * var(--e));
    opacity: calc(1 - clamp(0, (var(--od, 0) - 60) / 20, 1));
  }
}
@media (prefers-reduced-motion: no-preference) and (max-width: 1024px) {
  html.k-sd-js .opening-stage .device { scale: none; translate: none; }
}
/* Reduced motion: the opening is an ordinary hero (no pin); the tiles sit still round the stage on
   desktop and are hidden on phones. */
@media (prefers-reduced-motion: reduce) {
  .opening-track { min-height: 0; }
  .opening-stage { position: relative; inset-block-start: auto; height: auto; padding-block: 72px 24px; }
  .opening-marks { display: none; }
}
@media (prefers-reduced-motion: reduce) and (max-width: 1024px) {
  .opening-collage { display: none; }
  .opening-stage { padding-block: 0; }
}

/* ======================================================================== Product page layouts (product-pages agent) */
/* The eight product pages share the system above (chip, H1, split-card, plates, FAQ, panel) but
   not one sequence: each page picks the beats that fit its product (qa.md P1-3). Every class here
   starts with pr-. Logical properties only, so all of it mirrors in Arabic.
     Heroes   .hero.pr-hero-stack  copy centred, the real app full width under it (.pr-hero-media)
              .hero.pr-hero-row    chip and H1 on the inline start, lead and button beside them (.pr-row), the app under both
              .hero-grid with .pr-hero-phone (a phone plate cropped round the phone), .pr-hero-rec (a recording on
                            the band colour), .pr-shot, or .pr-hero-ill (one large illustration)
     Blocks   .pr-shot      a crisp crop of the app in a white frame on the band colour (--shot: its aspect;
                            .pr-shot--bleed runs it off the inline-end and bottom edges)
              .pr-legend    three short columns, each topped by a rule
              .pr-finding   the research line: "Powered by research" (a link), one finding, the full reference (.pr-cite)
              .pr-spec      a heading beside a list of hairline rows (what to upload, what to choose)
              .pr-duo       two cards side by side (.pr-duo-ill a small illustration, .pr-duo-icon an icon, .pr-words the app's own words)
              .pr-trio      three phones in a row, each with a title and a line (a swipeable row under 900px)
              .pr-feature   words on one side, a picture on the other (.pr-feature--flip puts the picture first)
              .pr-aside     a wide picture with two short cards beside it
              .pr-timeline  when things happen, as stops along a line (a vertical line on phones)
              .pr-compare   two halves of one card, compared row by row
              .panel.pr-close  the page's closing call on the deep field, with the motto and two related pages */

/* Heroes */
.pr-hero-stack .hero-grid { grid-template-columns: minmax(0, 1fr); justify-items: center; }
.pr-hero-stack .hero-copy { align-items: center; max-width: 860px; text-align: center; }
.pr-hero-stack .hero-copy .lead { margin-inline: auto; }
.pr-hero-stack .hero-copy .h1 { font-size: var(--fs-h1-split); }
.pr-hero-stack .hero-actions { justify-content: center; }
.pr-hero-media { width: min(100%, 1100px); margin-block-start: var(--s-8); margin-inline: auto; }
.pr-row { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: end; gap: 24px var(--s-8); }
.pr-row-head { display: flex; flex-direction: column; align-items: flex-start; gap: 24px; }
.pr-row-head .h1 { font-size: var(--fs-h1-split); }
.pr-row-side { display: flex; flex-direction: column; align-items: flex-start; gap: 20px; padding-block-end: 6px; }
.pr-row + .pr-hero-media { width: 100%; }
.pr-hero-ill { --ar: 1; width: 100%; max-width: 540px; margin-inline-start: auto; }
@media (max-width: 1024px) {
  .pr-row { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .pr-hero-ill { max-width: 420px; margin-inline: auto; }
}

/* A crop of the app (a still) at its own scale: a white frame on the band colour. */
.pr-shot { display: flex; flex-direction: column; gap: 16px; margin: 0; }
.pr-shot-field { position: relative; padding: 6%; overflow: hidden; background: var(--band); border-radius: var(--radius-media); }
.pr-shot-screen { position: relative; overflow: hidden; aspect-ratio: var(--shot, 16 / 10); background: var(--card); border: 1px solid rgb(0 0 0 / .06); border-radius: 14px; box-shadow: var(--shadow-float); }
.pr-shot-screen > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 0 0); }
.pr-shot--bleed .pr-shot-field { padding-inline-end: 0; padding-block-end: 0; }
.pr-shot--bleed .pr-shot-screen { border-start-end-radius: 0; border-end-start-radius: 0; border-end-end-radius: 0; border-inline-end: 0; border-block-end: 0; }
@media (max-width: 640px) { .pr-shot-field { padding: 5%; } .pr-shot--bleed .pr-shot-field { padding-inline-end: 0; padding-block-end: 0; } }

/* Three short columns, each topped by a rule. */
.pr-legend { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 32px 40px; margin: 0; padding: 0; list-style: none; }
.pr-legend > li { display: flex; flex-direction: column; gap: 10px; padding-block-start: 20px; border-block-start: 2px solid var(--ink); }
.pr-legend p { color: var(--text); }
@media (max-width: 900px) { .pr-legend { grid-template-columns: minmax(0, 1fr); gap: 28px; } }

/* The research line. The reference follows the research page's format, with its DOI. */
.pr-finding { display: flex; flex-direction: column; gap: 8px; max-width: 46em; padding-block-start: 16px; border-block-start: 1px solid var(--line); }
.pr-finding-line { font-size: 16px; line-height: 1.55; color: var(--ink); }
.pr-finding-label { margin-inline-end: 4px; font-weight: 700; color: var(--purple-dark); text-decoration: none; }
.pr-finding-label:hover { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.pr-cite { font-size: 13px; line-height: 1.5; color: var(--muted); }
.pr-cite a { color: var(--muted); overflow-wrap: anywhere; }
.pr-block-foot { margin-block-start: 40px; }
.split-copy > .pr-finding { margin-block-start: 22px; }

/* A heading beside a list of hairline rows. */
.pr-spec { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); align-items: start; gap: 24px var(--s-8); }
.pr-spec-head { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
.pr-spec-head p { color: var(--text); }
.pr-spec-list { margin: 0; border-block-start: 1px solid var(--line); }
.pr-spec-list > div { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: 8px 32px; padding-block: 22px; border-block-end: 1px solid var(--line); }
.pr-spec-list dt { font-size: 19px; font-weight: 600; line-height: 1.35; letter-spacing: -.01em; }
.pr-spec-list dd { margin: 0; color: var(--text); }
.band .pr-spec-list, .band .pr-spec-list > div { border-color: rgb(46 23 88 / .14); }
@media (max-width: 900px) {
  .pr-spec { grid-template-columns: minmax(0, 1fr); }
  .pr-spec-list > div { grid-template-columns: minmax(0, 1fr); padding-block: 18px; }
}

/* Two cards side by side. */
.pr-duo { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; margin: 0; padding: 0; list-style: none; }
.pr-duo > li { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; padding: 40px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.pr-duo > li p { color: var(--text); }
.pr-duo > li .tlink { margin-block-start: auto; padding-block-start: 6px; }
.pr-duo-ill { --ar: 1; width: 132px; margin-block-end: 10px; border-radius: 22px; }
.pr-duo-icon { display: grid; place-items: center; width: 44px; height: 44px; margin-block-end: 6px; border-radius: 14px; background: var(--purple-soft); color: var(--purple-dark); }
.pr-duo-icon .i { width: 22px; height: 22px; }
.pr-words { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 0; padding: 0; list-style: none; }
.pr-words > li { padding: 6px 12px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--surface); font-size: 14px; font-weight: 500; color: var(--ink); }
@media (max-width: 900px) { .pr-duo { grid-template-columns: minmax(0, 1fr); gap: 16px; } .pr-duo > li { padding: 28px 24px; } }

/* Three phones in a row, each with a title and a line; a swipeable row under 900px. */
.pr-trio { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; margin: 0; padding: 0; list-style: none; }
.pr-trio > li { display: flex; flex-direction: column; gap: 10px; }
.pr-trio-stage { display: grid; place-items: center; margin-block-end: 14px; padding: 32px 20px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }   /* white, so the page's one pale band stays the only one */
.pr-trio .device--phone { --device-w: min(290px, 100%); }
.pr-trio p { color: var(--text); }
@media (max-width: 900px) {
  .pr-trio { display: flex; gap: 16px; margin-inline: calc(-1 * var(--gutter)); padding-inline: var(--gutter); padding-block-end: 8px; overflow-x: auto;
    scroll-snap-type: x mandatory; scroll-padding-inline: var(--gutter); overscroll-behavior-x: contain; scrollbar-width: none; }
  .pr-trio::-webkit-scrollbar { display: none; }
  .pr-trio > li { flex: 0 0 min(80vw, 320px); scroll-snap-align: start; }
  .pr-trio .device--phone { --device-w: min(240px, 100%); }
}

/* Words on one side, a picture on the other. */
.pr-feature { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: center; gap: 32px var(--s-8); }
.pr-feature-copy { display: flex; flex-direction: column; align-items: flex-start; gap: 18px; }
.pr-feature-copy > p { color: var(--text); }
.pr-feature--flip { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
.pr-feature--flip > .pr-feature-copy { order: 2; }
@media (max-width: 900px) { .pr-feature { grid-template-columns: minmax(0, 1fr); } .pr-feature--flip > .pr-feature-copy { order: 0; } }

/* A wide picture with two short cards beside it. */
.pr-aside { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: stretch; gap: 24px; }
.pr-aside-cards { display: grid; gap: 24px; margin: 0; padding: 0; list-style: none; }
.pr-aside-cards > li { display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 12px; padding: 32px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.pr-aside-cards p { color: var(--text); }
@media (max-width: 900px) { .pr-aside { grid-template-columns: minmax(0, 1fr); gap: 16px; } .pr-aside-cards { gap: 16px; } .pr-aside-cards > li { padding: 24px; } }

/* When things happen: stops along a line. */
.pr-timeline { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px; margin: 0; padding: 0; list-style: none; }
.pr-timeline > li { position: relative; display: flex; flex-direction: column; gap: 10px; padding-block-start: 40px; }
.pr-timeline > li::before { content: ""; position: absolute; inset-block-start: 7px; inset-inline: 0 -32px; height: 2px; background: var(--line); }
.pr-timeline > li:last-child::before { inset-inline-end: 0; }
.pr-timeline > li::after { content: ""; position: absolute; inset-block-start: 0; inset-inline-start: 0; width: 16px; height: 16px; box-sizing: border-box; border: 3px solid var(--purple); border-radius: 50%; background: var(--page); }
.pr-timeline-when { font-size: 14px; font-weight: 700; line-height: 1.3; color: var(--purple-dark); }
.pr-timeline p { color: var(--text); }
@media (max-width: 900px) {
  .pr-timeline { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .pr-timeline > li { padding-block: 0 32px; padding-inline-start: 36px; }
  .pr-timeline > li::before { inset-block: 8px 0; inset-inline: 7px auto; width: 2px; height: auto; }
  .pr-timeline > li:last-child { padding-block-end: 0; }
  .pr-timeline > li:last-child::before { display: none; }
}

/* Two halves of one card. */
.pr-compare { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); }
.pr-compare > div { display: flex; flex-direction: column; gap: 16px; padding: 40px; }
.pr-compare > div + div { border-inline-start: 1px solid var(--line); }
.pr-compare > div > p { color: var(--text); }
.pr-compare dl { margin: 0; }
.pr-compare dl > div { padding-block: 14px; border-block-start: 1px solid var(--line); }
.pr-compare dt { font-size: 14px; font-weight: 600; color: var(--muted); }
.pr-compare dd { margin: 4px 0 0; color: var(--ink); }
@media (max-width: 900px) {
  .pr-compare { grid-template-columns: minmax(0, 1fr); }
  .pr-compare > div { padding: 28px 24px; }
  .pr-compare > div + div { border-inline-start: 0; border-block-start: 1px solid var(--line); }
}

/* The closing call on the deep field: this product's line and the motto (the motto leads where it
   is the product itself: Course builder, Study view), the button, two related pages. On product
   pages it replaces the separate panel, carousel and floating tiles. */
.pr-close .h2 { max-width: 18ch; }
.pr-close .hero-actions { justify-content: center; margin-block-start: 8px; }
.pr-close-motto > span { display: inline-block; }   /* the motto under a page's own line breaks between its two sentences, never inside one */
.pr-close-note { font-size: 15px; font-weight: 500; color: var(--on-deep); }
.pr-close-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 28px; width: 100%; max-width: 720px; margin: 16px 0 0; padding: 24px 0 0; list-style: none; border-block-start: 1px solid rgb(255 255 255 / .18); }
.pr-close-links a { display: inline-flex; align-items: center; gap: 6px; font-size: 16px; font-weight: 600; color: #fff; text-decoration: none; }
.pr-close-links a:hover { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }
.pr-close-links .i { width: 18px; height: 18px; }

/* Per-page pieces of the above */
/* The study view's hero frame: under 900px it plays the phone recording in a phone (site.css above). */
@media (max-width: 899px) {
  .pr-hero-media.show[data-phone-recording] { align-items: center; }
  .pr-hero-media.show[data-phone-recording] > :not(.device) { align-self: stretch; }
}
/* Minis: the drawn phone of plate P2, cropped round the phone so it is as large as the hero allows
   (--frac: the share of the picture's width shown; --x0: where the crop starts). */
.hero-grid > .pr-hero-phone { width: 100%; max-width: 500px; margin-inline-start: auto; }
.pr-hero-phone .plate--device { --frac: .52; }
.pr-hero-phone .plate-img { max-width: none; }   /* klaro.css caps every img at 100%, which would undo the crop's wider picture */
@media (max-width: 1024px) { .hero-grid > .pr-hero-phone { max-width: 460px; margin-inline: auto; } }
@media (max-width: 640px) { .pr-hero-phone .plate--device { --frac: .4; } }
/* Quizzes: the recording in a browser frame on the band colour, beside the copy. */
.pr-hero-rec .pr-shot-field { padding: 5%; }
.pr-hero-rec .device--browser { box-shadow: var(--shadow-float); }
/* A still shown whole (the course page): the picture sets the frame's shape. */
.pr-shot--wide .pr-shot-field { padding: 3%; }
.pr-shot--wide .pr-shot-screen { aspect-ratio: auto; }
.pr-shot--wide .pr-shot-screen > img { position: static; display: block; height: auto; }
/* On phones the whole page would be too small to read: show its inline-end part (the course's
   lectures, book and questions) at a readable size instead. */
@media (max-width: 640px) {
  .pr-shot--wide .pr-shot-screen { aspect-ratio: 480 / 560; }
  .pr-shot--wide .pr-shot-screen > img { position: absolute; height: 100%; object-position: 100% 0; }
}
@media (max-width: 640px) { .hero-grid > .pr-hero-ill { max-width: 300px; } }
.pr-legend--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 900px) { .pr-legend--two { grid-template-columns: minmax(0, 1fr); } }
.pr-trio-caption { margin-block-start: 24px; }
.band-copy .pr-band-note { font-size: 17px; color: var(--ink); }
/* Practice's choices in the band. */
@media (max-width: 900px) { .band.pr-spec { padding: 32px 24px; } }

/* Arabic */
:is(.pr-legend p, .pr-spec-list dd, .pr-duo p, .pr-trio p, .pr-feature-copy p, .pr-aside-cards p, .pr-timeline p, .pr-compare dd, .pr-finding-line):lang(ar) { line-height: 1.75; }
