/* =========================================================================
   The SUPER suite pages: super-presentation, superaac, supercoach, supervoca
   =========================================================================

   Loaded after style.css, which keeps the site's own type scale, bands,
   buttons, global bar and footer. This file adds what the three apps bring
   to their pages: their gradients, their wordmarks and icons, generic device
   frames around the apps' own screens, the PAACs cards, and the "stage"
   layout the four pages share -- the product's name set large in its own
   gradient, then its screens at full size with a line under each.

   Two themes (assets/js/theme.js sets <html data-theme>). The stage was
   designed on black, and dark is the site's default: with no data-theme
   (JavaScript off) or data-theme="dark", every band of these pages is the
   black stage. data-theme="light" is the all-white site of 2026-10-19, where
   the bands are .band-white (see "Light theme" below). A .band-dark band
   stays black in both themes. So the rules below come in three kinds:
     - plain rules: both themes, or the dark stage where nothing overrides;
     - "on black" rules, under :is(:root:not([data-theme="light"]),
       .band-dark): the dark theme everywhere, and a .band-dark band in the
       light theme;
     - light rules, under [data-theme="light"] .band-white (or .band-light,
       .band-grey where the site's light bands differ).

   Where a gradient may go. The light ends of the three gradients (#F28A35,
   #91D9F7, #25B975) fall under 3:1 on the near-white bands, so:
     - on black, the full gradient may colour LARGE type (every stop clears
       3:1 on black, the lowest #7738DA at 3.4: .gtext);
     - on a light band, large type may use only the stops that clear 3:1 on
       white (--grad-type, .gtext-l; on a white band .gtext takes it too);
     - body-size text is never a gradient. Accent text uses each app's
       deepest colour (--accent), 4.5:1 or better on --paper and --paper-2,
       and on black a lighter one of the same hue (--accent-d), 5:1 or better
       on the #1d1d1f panels.
   Elsewhere a gradient fills the panels that carry the apps' screens.

   The pages use no icon font: every picture is the apps' own screen.

   Class names here are checked against style.css and auth.css: none of them
   is defined there (the site already owns .pcard and .symcard, for one).
   ========================================================================= */

:root {
  --aac-1: #7738da;
  --aac-2: #b345b3;
  --aac-3: #f28a35;
  --coach-1: #91d9f7;
  --coach-2: #57bcee;
  --coach-3: #2997d6;
  --voca-1: #2468e8;
  --voca-2: #148fad;
  --voca-3: #25b975;

  --grad-aac: linear-gradient(118deg, var(--aac-1), var(--aac-2) 52%, var(--aac-3));
  --grad-coach: linear-gradient(118deg, var(--coach-1), var(--coach-2) 50%, var(--coach-3));
  --grad-voca: linear-gradient(118deg, var(--voca-1), var(--voca-2) 52%, var(--voca-3));

  /* The PAACs colour key (Fitzgerald), as the print pieces use it. */
  --fk-social: #efb6ce;
  --fk-people: #f2d98c;
  --fk-action: #a9d6a1;
  --fk-thing: #f3c08a;
  --fk-describe: #a8c8e8;

  /* Device frames and the panels on black. */
  --frame: #1d1d1f;
  --panel-dark: #1d1d1f;
  --rule-dark: #2c2c2e;

  /* The same rule on the white product pages. */
  --rule-light: #d2d2d7;

  /* Content keeps its own colours in both themes, whatever the page's
     tokens do: the white of the app icons' tiles, of the PAACs picture
     ground and of a phone screen behind its screenshot, and the ink on the
     PAACs colour-key tints (the symbols' own colours; 9.7:1 or better on
     all five). */
  --content-white: #ffffff;
  --ink-tint: #1d1d1f;
}

/* --grad-type: the stops of each gradient that clear 3:1 on white
   (#7738DA 6.3, #B345B3 4.8; #2997D6 3.2, #1E6FA8 5.4; #2468E8 5.0,
   #148FAD 3.8). --accent: 6.0, 5.2 and 4.8 to 1 on --paper. --accent-d, the
   accent on black: the AAC and VOCA accents lifted toward white (#A77EE7 5.4
   and #5B8EEE 5.3 on #1d1d1f, 6.8 and 6.6 on black), and SuperCoach's own
   middle stop (#57BCEE 7.9 and 9.8). */
.aac { --grad: var(--grad-aac); --accent: #7738da; --accent-d: #a77ee7; --grad-type: linear-gradient(100deg, #7738da, #b345b3); }
.coach { --grad: var(--grad-coach); --accent: #1e6fa8; --accent-d: #57bcee; --grad-type: linear-gradient(100deg, #2997d6, #1e6fa8); }
.voca { --grad: var(--grad-voca); --accent: #2468e8; --accent-d: #5b8eee; --grad-type: linear-gradient(100deg, #2468e8, #148fad); }

.grad { background: var(--grad); }

:is(:root:not([data-theme="light"]), .band-dark) .gtext,
[data-theme="light"] .band-white .gtext,
.gtext-l {
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* On black, the full gradient; a .gtext-l (the light-ground gradient asked
   for by name) takes it too in the dark theme, where there is no light
   ground. */
[data-theme="light"] .band-white .gtext,
.gtext-l { background-image: var(--grad-type); }
:is(:root:not([data-theme="light"]), .band-dark) .gtext,
:root:not([data-theme="light"]) .gtext-l { background-image: var(--grad); }

/* A gradient behind transparent text disappears in forced-colours modes;
   there the text takes the system colour instead. */
@media (forced-colors: active) {
  :is(:root:not([data-theme="light"]), .band-dark) .gtext,
  :root:not([data-theme="light"]) .gtext-l,
  [data-theme="light"] .band-white .gtext,
  .gtext-l {
    background: none;
    color: CanvasText;
  }
}

/* ------------------------------------------------------- icons, wordmarks */

/* The app icons are white tiles, so they get the shape and the soft shadow
   an icon has on a home screen (22.37% is the radius the apps use). */
.app-icon {
  display: block;
  width: var(--icon, 96px);
  height: auto;
  aspect-ratio: 1;
  border-radius: 22.37%;
  background: var(--content-white);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06), 0 10px 28px rgba(0, 0, 0, 0.1);
}

/* On black the soft shadow is lost; a deeper one keeps the tile lifted off
   the ground. (.hero-logo is style.css's; its light shadow is there.) */
:is(:root:not([data-theme="light"]), .band-dark) .hero-logo,
:is(:root:not([data-theme="light"]), .band-dark) .app-icon {
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 14px 36px rgba(0, 0, 0, 0.55);
}

/* ------------------------------------------------------------- devices */

/* Generic frames with no maker's shape or mark, as in the print pieces. The
   bezel and corners are sized against the frame's own width through a
   container, so a frame keeps its proportions at any size. The screen takes
   its shape from the image (the tablet renders come at two sizes). */
.device {
  container-type: inline-size;
  width: 100%;
}

.device .bezel {
  background: var(--frame);
  box-shadow: 0 30px 60px -24px rgba(0, 0, 0, 0.45), inset 0 0 0 1px #3a3a3e;
}

/* On black: the darker shadow, and a lighter edge ring so the #1d1d1f bezel
   still has an outline against the ground and the #1d1d1f panels. */
:is(:root:not([data-theme="light"]), .band-dark) .device .bezel {
  box-shadow: 0 30px 70px -20px rgba(0, 0, 0, 0.9), inset 0 0 0 1px #48484c;
}

.device img {
  display: block;
  width: 100%;
  height: auto;
}

.device.tablet .bezel {
  padding: 2.6cqw;
  border-radius: 4.6cqw;
}

.device.tablet .screen {
  border-radius: 2.2cqw;
  overflow: hidden;
}

.device.phone .bezel {
  padding: 3.4cqw;
  border-radius: 15cqw;
}

.device.phone .screen {
  border-radius: 11.8cqw;
  overflow: hidden;
  aspect-ratio: 1608 / 3496;
  background: var(--content-white);
}

.device.phone .screen img {
  height: 100%;
  object-fit: cover;
  object-position: top;
}

/* ---------------------------------------------------------------- hero */

/* The three apps together: a tablet between two phones that overlap it. */
.family {
  max-width: 1080px;
  margin: clamp(48px, 6vw, 80px) auto 0;
  padding-inline: var(--gut);
  display: grid;
  grid-template-columns: 0.24fr 1fr 0.24fr;
  align-items: end;
}

/* A phone's width is left to the grid (not .device's 100%), so the negative
   margin below widens it over the tablet by the same amount on either side.
   At width: 100% the left phone's right margin moved nothing and only the
   right phone overlapped, which pulled the group off centre. */
.family .device.phone {
  position: relative;
  z-index: 2;
  width: auto;
}

.family .left { margin-right: -24%; margin-bottom: -2%; }
.family .right { margin-left: -24%; margin-bottom: -2%; }

.family .device.tablet {
  position: relative;
  z-index: 1;
}

/* A product's name, set large in its gradient, and on one line. On a phone
   the size follows the screen, so the widest name fits between the gutters
   rather than breaking ("SuperVOC / A" at 320px): "SuperCoach" is about 5.4
   times its size in SF Pro Bold and 5.6 in Arial Bold, so 5.8 leaves room
   for any system face. The break stays only as a last resort. */
.product-name {
  overflow-wrap: anywhere;
  font-size: min(clamp(56px, 10vw, 128px), calc((100vw - 2 * var(--gut)) / 5.8));
  line-height: 1;
  letter-spacing: -0.03em;
  font-weight: 700;
  padding-bottom: 0.08em;
}

/* German and Finnish compounds ("Kommunikationssoftware") are wider than a
   320px phone at these sizes: hyphenate, and break only as a last resort. */
.tagline,
.chapter .headline,
.hero .display {
  hyphens: auto;
  overflow-wrap: break-word;
}

.tagline {
  margin: 18px 0 0;
  font-size: clamp(26px, 3.4vw, 44px);
  line-height: 1.12;
  letter-spacing: -0.01em;
  font-weight: 600;
  text-wrap: balance;
}

:is(:root:not([data-theme="light"]), .band-dark) .tagline { color: var(--ink-d); }

.center .tagline { margin-inline: auto; max-width: 24ch; }

/* "Beta", beside a name. */
.beta {
  display: inline-block;
  margin-left: 0.4em;
  padding: 0.18em 0.6em;
  border-radius: var(--r-pill);
  border: 1px solid currentColor;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  line-height: 1.2;
  text-transform: uppercase;
  vertical-align: middle;
  color: var(--ink-2);
}

/* #a1a1a6: 8.2:1 on black, 6.5:1 on a #1d1d1f card. */
:is(:root:not([data-theme="light"]), .band-dark) .beta { color: var(--ink-d2); }

/* Inside a gradient name the badge must not inherit the transparent fill.
   The markup keeps a real space before it, so the name's accessible name is
   "SuperCoach Beta" rather than "SuperCoachBeta"; that space is the gap, so
   the badge drops its own margin. The colours are the black stage's; the
   light theme's are under "Light theme". */
.product-name .beta {
  margin-left: 0;
  font-size: clamp(13px, 1.4vw, 17px);
  vertical-align: 0.9em;
  color: var(--ink-d2);
  -webkit-text-fill-color: var(--ink-d2);
  border-color: var(--ink-d2);
}

/* Below about 410px the badge no longer fits beside the name and wraps; there
   it gets a line of its own, centred under the name. */
@media (max-width: 420px) {
  .product-name .beta { display: table; margin: 8px auto 0; }
}

/* -------------------------------------------------------------- stages */

/* One screen, large, with an optional line under it. */
.big-stage {
  margin: clamp(48px, 6vw, 80px) auto 0;
  padding-inline: var(--gut);
}

.big-stage .device.tablet { max-width: 980px; margin-inline: auto; }
.big-stage .device.phone { max-width: 340px; margin-inline: auto; }

.big-stage figcaption {
  max-width: 46ch;
  margin: 28px auto 0;
  font-size: 19px;
  line-height: 1.4;
  color: var(--ink-2);
}

:is(:root:not([data-theme="light"]), .band-dark) .big-stage figcaption { color: var(--ink-d2); }

.big-stage figcaption strong,
.pair figcaption strong {
  font-weight: 600;
  color: var(--ink);
}

:is(:root:not([data-theme="light"]), .band-dark) .big-stage figcaption strong,
:is(:root:not([data-theme="light"]), .band-dark) .pair figcaption strong { color: var(--ink-d); }

/* Two (or three) screens side by side, each on its own panel, each with a
   line under it. */
.pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2.4vw, 32px);
  margin-top: clamp(56px, 7vw, 96px);
  text-align: left;
}

.pair.three { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.pair figure {
  margin: 0;
  border-radius: var(--r-lg);
  background: var(--paper-2);
  padding: clamp(20px, 3vw, 36px) clamp(20px, 3vw, 36px) clamp(24px, 3vw, 32px);
  display: grid;
  gap: 22px;
  align-content: start;
}

.band-grey .pair figure { background: var(--white); }
/* On black: #1d1d1f; the caption's #a1a1a6 is 6.5:1 on it, the ink 15.5:1. */
:is(:root:not([data-theme="light"]), .band-dark) .pair figure { background: var(--panel-dark); }

.pair .device.phone { max-width: 240px; margin-inline: auto; }

.pair figcaption {
  margin: 0;
  font-size: 19px;
  line-height: 1.4;
  color: var(--ink-2);
}

:is(:root:not([data-theme="light"]), .band-dark) .pair figcaption { color: var(--ink-d2); }

/* A panel in the product's gradient behind a screen (in both themes: the
   on-black panel rule above is more specific, so it is named here too). */
.pair figure.on-grad,
:is(:root:not([data-theme="light"]), .band-dark) .pair figure.on-grad,
.big-stage.on-grad .stage-panel {
  background: var(--grad);
}

.big-stage .stage-panel {
  border-radius: var(--r-lg);
  padding: clamp(28px, 5vw, 64px);
  max-width: 1080px;
  margin-inline: auto;
}

/* Three short facts in a row: a number or a word, then one line. */
.stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  margin: clamp(56px, 7vw, 96px) 0 0;
  padding: 0;
  list-style: none;
}

.stats li {
  font-size: 17px;
  line-height: 1.4;
  color: var(--ink-2);
}

:is(:root:not([data-theme="light"]), .band-dark) .stats li { color: var(--ink-d2); }

/* A stat word in a long-compounding language ("Kansiokohtaisesti") is wider
   than its column at this size: let it hyphenate, and break as a last resort,
   rather than push the page sideways. */
.stats .n {
  hyphens: auto;
  overflow-wrap: anywhere;
  display: block;
  font-size: clamp(40px, 5vw, 64px);
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 8px;
  padding-bottom: 0.04em;
}

/* A thin rule between two black chapters (#2c2c2e on black: a quiet line,
   not a boundary anyone must find). The light theme's is under "Light
   theme". */
.chapter-rule {
  height: 1px;
  max-width: var(--page-wide);
  margin: 0 auto;
  background: var(--rule-dark);
}

.chapter + .chapter { padding-top: 0; }

/* A feature list: a dot in the product's gradient, the line in ink. */
.dots {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
  text-align: left;
}

.dots li {
  position: relative;
  padding-left: 22px;
  font-size: 19px;
  line-height: 1.35;
  color: var(--ink);
}

:is(:root:not([data-theme="light"]), .band-dark) .dots li { color: var(--ink-d); }

.dots li::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 0.5em;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--grad);
}

/* Three rules or promises in a row, on black (the light theme's panels are
   under "Light theme"). */
.promises {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  margin: clamp(40px, 5vw, 64px) 0 0;
  padding: 0;
  list-style: none;
  text-align: left;
}

.promises li {
  border-radius: var(--r-lg);
  background: var(--panel-dark);
  padding: 26px 26px 28px;
}

.promises h3 { font-size: 21px; line-height: 1.2; color: var(--ink-d); }
.promises p { margin: 8px 0 0; color: var(--ink-d2); }

/* One highlighted item on its own: the home page's calls line. */
.promises.promises-one {
  grid-template-columns: minmax(0, 1fr);
  max-width: 720px;
  margin-inline: auto;
}
/* Its link sits on a --paper-2 panel in the light theme, where --blue is
   4.3:1 (the tiles' rule, style.css). */
[data-theme="light"] .promises-one .link-arrow { color: var(--blue-surface); }

/* The suite: the three apps, one card each, linking to their pages. */
.suite-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  margin: clamp(40px, 5vw, 64px) 0 0;
  padding: 0;
  list-style: none;
  text-align: left;
}

.suite-cards a {
  display: grid;
  gap: 14px;
  height: 100%;
  border-radius: var(--r-lg);
  background: var(--panel-dark);
  padding: 26px 26px 28px;
  color: var(--ink-d);
}

/* The light theme's cards: white, lifted by a soft shadow. */
[data-theme="light"] :is(.band-light, .band-grey, .band-white) .suite-cards a {
  background: var(--white);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), 0 12px 32px rgba(0, 0, 0, 0.07);
}

.suite-cards a:hover { text-decoration: none; }
.suite-cards a:hover .link-arrow { text-decoration: underline; }
.suite-cards .app-icon { --icon: 56px; }
.suite-cards strong { display: block; font-size: 21px; line-height: 1.2; font-weight: 600; }
.suite-cards p { margin: 0; color: var(--ink-d2); font-size: 17px; }
[data-theme="light"] :is(.band-light, .band-grey, .band-white) .suite-cards p { color: var(--ink-2); }
.suite-cards .link-arrow { min-height: 0; align-self: end; }

/* On black (the dark theme, or a .band-dark band) the cards and the promise
   panels are the stage's #1d1d1f, said again with the theme's weight so that
   a site-wide dark rule for links or text cannot outrank the panels' own
   colours (the card is a link: without this it would turn link-blue). */
:is(:root:not([data-theme="light"]), .band-dark) .suite-cards a {
  background: var(--panel-dark);
  color: var(--ink-d);
  box-shadow: none;
}

:is(:root:not([data-theme="light"]), .band-dark) .suite-cards p { color: var(--ink-d2); }
:is(:root:not([data-theme="light"]), .band-dark) .promises li { background: var(--panel-dark); }
:is(:root:not([data-theme="light"]), .band-dark) .promises h3 { color: var(--ink-d); }
:is(:root:not([data-theme="light"]), .band-dark) .promises p { color: var(--ink-d2); }

/* The card is a link: on black its focus ring is the bright blue (7.0:1 on
   black, where the ring falls, 4 px outside the card). */
:is(:root:not([data-theme="light"]), .band-dark) .suite-cards a:focus-visible { outline-color: var(--blue-dark); }

/* A product page links the suite's other two apps: two cards, not three.
   (The three product pages each kept a copy of these rules until they moved
   here.) */
.suite-cards.two {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 820px;
  margin-inline: auto;
}

/* Icon, name, line, link: the line takes the slack, so the names of the two
   cards sit level and the links stay at the bottom. */
.suite-cards.two a { grid-template-rows: auto auto 1fr auto; }

/* The card is the link, so its arrow label is a span and does not get the
   dark band's link colour; --blue on the dark panel is under 4.5:1
   (--blue-dark, #2997ff, is 5.6:1 on it). */
:is(:root:not([data-theme="light"]), .band-dark) .suite-cards .link-arrow { color: var(--blue-dark); }

/* style.css stretches and centres every .link-arrow below 834px; inside a
   card it stays with the card's left-aligned text. */
.suite-cards .link-arrow { width: auto; justify-content: flex-start; }

/* "SuperCoach Beta" keeps a real space for the link's accessible name; the
   badge drops its own margin so the gap looks the same. */
.suite-cards strong .beta { margin-left: 0; }

/* Where the plan cards go on a product page: shop.js renders into it, and
   until it does (or without JavaScript) the line inside says the prices. */
.plans-host { margin-top: clamp(40px, 5vw, 64px); }

.plans-fallback {
  max-width: 52ch;
  margin: 0 auto;
  text-align: center;
  font-size: 19px;
  line-height: 1.45;
  color: var(--ink-2);
}

:is(:root:not([data-theme="light"]), .band-dark) .plans-fallback { color: var(--ink-d2); }

/* The plans, as three cards of prices. The numbers are filled per language
   from the app's price list at build time (localize.prices). */
.price-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  margin: clamp(40px, 5vw, 64px) 0 0;
  padding: 0;
  list-style: none;
}

.price-cards li {
  border-radius: var(--r-lg);
  background: var(--white);
  padding: 28px 28px 30px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), 0 12px 32px rgba(0, 0, 0, 0.07);
}

.price-cards h3 { font-size: 24px; line-height: 1.2; }

.price-cards .price-main {
  margin: 14px 0 0;
  font-size: clamp(28px, 3vw, 36px);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--accent);
}

.price-cards .price-alt {
  margin: 4px 0 0;
  font-size: 17px;
  color: var(--ink-2);
}

.price-cards p:last-child {
  margin: 16px 0 0;
  font-size: 17px;
  line-height: 1.45;
  color: var(--ink);
}

/* On black the cards are the stage's panels, #1d1d1f with no shadow (the
   panel itself separates from the ground); the price takes the app's accent
   for black (5:1 or better on the panel), the rest the light ink ramp. */
:is(:root:not([data-theme="light"]), .band-dark) .price-cards li {
  background: var(--panel-dark);
  color: var(--ink-d);
  box-shadow: none;
}

:is(:root:not([data-theme="light"]), .band-dark) .price-cards .price-main { color: var(--accent-d, var(--blue-dark)); }
:is(:root:not([data-theme="light"]), .band-dark) .price-cards .price-alt { color: var(--ink-d2); }
:is(:root:not([data-theme="light"]), .band-dark) .price-cards p:last-child { color: var(--ink-d); }

/* ---------------------------------------------------------- light theme */

/* In the light theme every page is white from top to bottom (owner,
   2026-10-15 for the product pages, 2026-10-19 for the whole site), and the
   stage's bands are .band-white. What is black on the stage turns light
   there: the panels take --paper-2, the rules --rule-light, the "Beta" badge
   in a gradient name the light ink ramp. Everything else has its light
   default above (or its own [data-theme="light"] rule beside it), and the
   gradients follow the light-band rule at the top. In the dark theme, and in
   a .band-dark band, none of this applies: the stage stays black. The ground
   itself and the footer's hairline come from style.css for every page; what
   follows is what the stage needs on it. */

[data-theme="light"] .band-white .chapter-rule { background: var(--rule-light); }

/* The hero clips its overflow (style.css), and the device frames' shadow
   reaches 66 px below them (30 px offset + 60 px blur - 24 px spread). On
   black that edge was invisible; on white a narrow hero's 40 px bottom
   padding cut the shadow into a grey strip with a hard edge. 72 px holds it.
   Both themes keep it, so switching never moves the page. */
.hero.band-white { padding-bottom: 72px; }

[data-theme="light"] .band-white .product-name .beta {
  color: var(--ink-2);
  -webkit-text-fill-color: var(--ink-2);
  border-color: var(--ink-2);
}

[data-theme="light"] .band-white .promises li { background: var(--paper-2); }
[data-theme="light"] .band-white .promises h3 { color: var(--ink); }
[data-theme="light"] .band-white .promises p { color: var(--ink-2); }

/* ---------------------------------------------------------- PAACs cards */

.paacs-row {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: clamp(8px, 1.4vw, 16px);
  max-width: 900px;
  margin: clamp(36px, 5vw, 56px) auto 0;
  padding: 0;
  list-style: none;
}

.ps-card {
  margin: 0;
  border-radius: var(--r-md);
  border: 3px solid var(--fk);
  overflow: hidden;
  display: grid;
  grid-template-rows: 1fr auto;
  background: var(--content-white);
}

.ps-card img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
  padding: 8%;
  background: var(--content-white);
}

/* The cards are content, the same in both themes: white picture ground,
   colour-key border and label, dark ink on the tint. */
.ps-card figcaption {
  margin: 0;
  background: var(--fk);
  color: var(--ink-tint);
  font-size: clamp(15px, 1.5vw, 19px);
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  padding: 6px 4px 8px;
}

.fk-social { --fk: var(--fk-social); }
.fk-people { --fk: var(--fk-people); }
.fk-action { --fk: var(--fk-action); }
.fk-thing { --fk: var(--fk-thing); }
.fk-describe { --fk: var(--fk-describe); }

.fk-key {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 22px;
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
  font-size: 17px;
  color: var(--ink-2);
}

:is(:root:not([data-theme="light"]), .band-dark) .fk-key { color: var(--ink-d2); }

.fk-key li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.fk-key li::before {
  content: '';
  width: 16px;
  height: 16px;
  border-radius: 5px;
  background: var(--fk);
}

/* Japanese has no spaces, so a heading can otherwise break inside a word
   ("あ / なた"). Break between phrases instead (Chrome and Edge; elsewhere
   the normal rules apply). The home page's news card's summary is a plain
   paragraph, so it is named here too. */
:lang(ja) :is(h1, h2, h3, .tagline, .intro, figcaption, .news-latest-summary) {
  word-break: auto-phrase;
}

/* The suite's motto in Chinese is three five-character sentences
   (您的话语。…), and Chinese breaks between any two characters: a measure of
   about five characters puts one sentence on each line instead of breaking
   inside one. */
:lang(zh) .motto {
  max-width: 5.6em;
  margin-inline: auto;
}

/* Each Japanese or Chinese character is a full em wide, so the display line
   at the Latin size leaves one or two characters alone on a phone. */
:lang(ja) .hero .display,
:lang(zh) .hero .display {
  font-size: clamp(34px, 7.4vw, 80px);
}

/* Four of the site's tiles (style.css .tile) as two rows of two; the
   site's own auto-fit grid leaves the fourth alone on a row at desktop
   widths. */
.tiles.tiles-2x2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* A tile title breaks a long word at its soft hyphen only where the phone
   layout needs it ("narrow", below); here every word fits whole. */
.tiles-2x2 .tile h3 { hyphens: none; }

/* ----------------------------------------------------------- Open Beta

   The product pages' bar (owner, 2026-10-26: "a small section shaped like
   a bar ... that states the applications are now in Open Beta together
   with a button at the right"), in the hero under its words and above its
   picture since 2026-10-27, and the home page's, under the hero's
   sentence. A pill of the site's --paper-2 surface, as the tiles and the
   news card use, with the words at its start and the blue pill button at
   its end; on a phone the two stack, centred, the button full width.

   The button opens a form that needs a script (open-beta.js), so it shows
   only where scripts run: theme.js puts data-theme on <html> before the
   first paint, so the button is there from the first frame, and nothing
   moves when the module arrives. Without scripts the bar is its sentence. */
.beta-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
  max-width: 980px;
  margin-inline: auto;
  padding: 12px 12px 12px 30px;
  border-radius: var(--r-pill);
  background: var(--paper-2);
  color: var(--ink);
  text-align: left;
}

/* In a hero: under its words, before its picture. */
.hero .beta-bar {
  margin-top: 30px;
}

.beta-bar-text {
  margin: 0;
  font-size: 19px;
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: -0.012em;
  text-wrap: balance;
}

/* Where the sentence wraps (a phone), Japanese breaks between phrases, not
   inside a word, and never starts a line with the long-vowel mark: not
   "オープンベ / ータ版" (auto-phrase in Chrome and Edge; strict everywhere).
   Korean breaks between words, which its spaces mark, not between
   syllables; Chinese at the space after the product's name rather than
   inside 推出 ("现已推 / 出"), and anywhere only if a run is longer than
   the line. */
:lang(ja) .beta-bar-text {
  word-break: auto-phrase;
  line-break: strict;
}

:lang(ko) .beta-bar-text,
:lang(zh) .beta-bar-text {
  word-break: keep-all;
  overflow-wrap: anywhere;
}

.beta-bar-btn {
  display: none;
  flex: 0 0 auto;
}

:root[data-theme] .beta-bar-btn {
  display: inline-flex;
}

@media (max-width: 640px) {
  .beta-bar {
    flex-direction: column;
    justify-content: center;
    gap: 14px;
    padding: 20px 20px;
    border-radius: var(--r-lg);
    text-align: center;
  }

  .beta-bar-text { font-size: 17px; }

  :root[data-theme] .beta-bar-btn { width: 100%; }
}

@media print {
  .beta-bar-btn { display: none !important; }
}

/* -------------------------------------------------------------- narrow */

@media (max-width: 760px) {
  .pair,
  .pair.three,
  .price-cards,
  .tiles.tiles-2x2,
  .stats,
  .promises,
  .suite-cards,
  .suite-cards.two { grid-template-columns: 1fr; }

  .family { grid-template-columns: 0.3fr 1fr 0.3fr; }
  .paacs-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  /* The four tiles in one column. A title word wider than the column
     (German "Einstellungsbildschirm", 245 px at 24 px) widened the column
     with it and pushed a 320 px page 11 px sideways: minmax(0, 1fr) keeps
     the column to the page. Where such a word may break, its catalogue
     gives it a soft hyphen (&shy;, at the compound's seam: de, sv, nb),
     which every browser honours the same way; overflow-wrap is the last
     resort for a word without one. Not hyphens: auto: without
     hyphenate-limit-chars (Safari, so every browser on iOS) it also splits
     ordinary words that wrap whole. */
  .tiles.tiles-2x2 { grid-template-columns: minmax(0, 1fr); }

  .tiles-2x2 .tile h3 {
    max-width: 100%;
    hyphens: manual;
    overflow-wrap: break-word;
  }
}

/* ------------------------------------------------- Overview animations
   The home page's two app-screen animations (index.html): SuperAAC's "It
   listens, too" in the first figure of #superaac, and SuperCoach's model,
   point and praise, the lead figure of #supercoach. The scenes and their
   own stylesheet are assets/overview/ (a copy of Marketing/campaign/source/
   web/, the source of truth; INTEGRATION.md there). These rules are the
   page's chrome around them, and they must hold BEFORE the scene loads:
   the poster in the frame, the room for the Pause / Play button and the
   coach's step captions, the coach's two frames and the still link between
   them. Every class is prefixed ov-; none is defined in style.css,
   auth.css or the scene's overview.css. The button is the site's own
   .duo-toggle (style.css, "news").

   Nothing moves until the figure is .ov-ready: the poster (the frame's own
   <img class="ov-poster">) keeps the frame's size and shows until the
   first frame is drawn; the moving layer (.ov-live) is laid out over it
   but not seen while it loads (.ov-loading). Room for the line under the
   stage (the button; the coach's steps) is held only where the scene can
   come: with JavaScript (theme.js marks <html> with data-theme) and
   without reduced motion; the coach's line carries its step captions laid
   out (index.html, unseen until the scene is up), so it is as tall at
   first paint as they make it in any language and font. So nothing below
   a figure moves when its animation arrives, or if it never does. --------------------------------- */

/* The link's colours: the accent on black (the campaign's), the light
   theme's blue on white; the step dashes in the chapter's accent. */
.ov-figure {
  --ov-link: rgba(41, 151, 255, 0.78);
  --ov-pulse: #f5f5f7;
  --ov-glow: rgba(87, 188, 238, 0.7);
  --ov-ring: #57bcee;
  --ov-bt: var(--blue);
  --ov-step: var(--accent-d, var(--blue));
}

[data-theme="light"] .ov-figure {
  --ov-link: rgba(0, 113, 227, 0.62);
  --ov-pulse: #0071e3;
  --ov-glow: rgba(41, 151, 255, 0.45);
  --ov-ring: #2997ff;
  --ov-bt: var(--blue);
  --ov-step: var(--accent, var(--blue));
}

/* The moving layer sits over the frame's poster, inside its screen. */
.ov-figure .device .screen {
  position: relative;
}

.ov-live {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.ov-live[hidden] {
  display: none;
}

.ov-loading .ov-live {
  visibility: hidden;
}

/* Drawn: the poster keeps its room (the frame's size) but is not seen,
   or heard (the moving layer, or the coach's stage, says what it shows). */
.ov-ready .ov-poster {
  visibility: hidden;
}

/* ---------------------------------------------------- the line under it */

/* The Pause / Play button (and the coach's step captions): a 44 px
   button at the line's start, away from the floating Symbols switch at
   the window's end, the captions centred on the stage. */
.ov-line {
  display: none;
  grid-template-columns: 44px minmax(0, 1fr) 44px;
  align-items: center;
  gap: 10px;
  max-width: 980px;
  min-height: 44px;
  margin: 20px auto 0;
  visibility: hidden;
}

@media (prefers-reduced-motion: no-preference) {
  :root[data-theme] .ov-figure:not(.ov-reduced) .ov-line {
    display: grid;
  }
}

.ov-ready .ov-line {
  visibility: visible;
}

/* The button keeps its place in the tab order before its scene is drawn
   (start.js marks the figure .ov-armed where the scene can come): unseen
   and not clickable until then, seen with its focus ring when Tab reaches
   it, which loads the scene. Not if the scene failed (.ov-off). */
.ov-armed:not(.ov-ready):not(.ov-off) .ov-line .duo-toggle {
  visibility: visible;
  opacity: 0;
  pointer-events: none;
}

.ov-armed:not(.ov-ready):not(.ov-off) .ov-line .duo-toggle:focus {
  opacity: 1;
}

.ov-line .duo-toggle {
  grid-column: 1;
}

.ov-steps {
  grid-column: 2;
  min-width: 0;
}

/* The coach's step captions: three short dashes over the step's words, the
   step playing fills, the ones before it are full (player.js moves them).
   The words are stacked in one grid cell and cross-fade, so the line keeps
   the height of the longest. Here, not in overview.css, because the page
   carries them laid out before the scene loads. Site font, as the page's
   text. */
.ov-dashes {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin-bottom: 10px;
}

.ov-dash {
  position: relative;
  width: 28px;
  height: 3px;
  border-radius: 2px;
  overflow: hidden;
  background: var(--track);
}

.ov-dash-fill {
  position: absolute;
  inset: 0;
  border-radius: 2px;
  background: var(--ov-step);
  transform-origin: 0 50%;
  transform: scaleX(0);
}

.ov-caps {
  display: grid;
  justify-items: center;
}

.ov-cap {
  grid-area: 1 / 1;
  max-width: 100%;
  font-size: 19px;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-align: center;
  text-wrap: balance;
}

/* Narrow: the stage is one frame above the other and the captions sit
   under a small phone, so a size nearer the page's body text. */
@media (max-width: 640px) {
  .ov-cap {
    font-size: 16px;
  }
}

/* SuperAAC: the button alone. On a wide screen it sits under the frame's
   start, beside the centred caption, so it takes no room of its own;
   where the caption is as wide as the frame, it gets its own line. */
.ov-listens .ov-line {
  max-width: 980px;
}

@media (min-width: 700px) {
  .ov-listens .ov-line {
    position: relative;
    min-height: 0;
    height: 0;
    margin-top: 0;
  }

  .ov-listens .ov-line .duo-toggle {
    position: absolute;
    top: 18px;
    inset-inline-start: 0;
  }
}

@media (max-width: 699px) {
  @media (prefers-reduced-motion: no-preference) {
    :root[data-theme] .ov-listens:not(.ov-reduced) figcaption {
      margin-top: 18px;
    }
  }
}

/* ------------------------------------------------ the coach's two frames */

/* The coach's phone and the person's tablet side by side, the link's cell
   between them, both about as tall (a 240 px phone, 120 px of link and a
   760 px tablet at full width). */
.ov-duet {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 24fr) minmax(0, 12fr) minmax(0, 76fr);
  align-items: center;
  max-width: 1120px;
  margin-inline: auto;
}

.big-stage .ov-duet .device.phone,
.big-stage .ov-duet .device.tablet {
  max-width: none;
  margin: 0;
}

.ov-link {
  position: relative;
  align-self: stretch;
}

/* Without the scene (no JavaScript, reduced motion, while it loads): the
   link drawn still, between the frames, across its cell. */
.ov-link-still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.ov-link-still path {
  fill: none;
  stroke: var(--ov-link);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 0.01 9;
  vector-effect: non-scaling-stroke;
}

.ov-link-still .ov-link-v {
  display: none;
}

/* The Bluetooth mark at the still link's middle (32% down its cell side
   by side; 43% across, halfway down, stacked): the rune on a disc of the
   page's colour, so the dotted line stops round it. The scene draws its
   own on the moving link (overview.css .ov-link-mark). */
.ov-link-bt {
  position: absolute;
  left: 50%;
  top: 32%;
  box-sizing: border-box;
  display: grid;
  place-items: center;
  width: clamp(18px, 2.3vw, 26px);
  aspect-ratio: 1;
  border: 1.6px solid var(--ov-link);
  border-radius: 50%;
  background: var(--paper);
  transform: translate(-50%, -50%);
}

.ov-link-bt svg {
  width: 62%;
  height: 62%;
  fill: var(--ov-bt);
}

.ov-ready .ov-link-still,
.ov-ready .ov-link-bt {
  display: none;
}

.ov-coach .ov-line {
  max-width: 1120px;
  margin-top: clamp(24px, 3vw, 36px);
}

/* Its caption keeps no word alone on its last line, as the page's
   Latest-news summary does. Not in Japanese, Chinese or Korean, where a
   line may end inside a word and evening the lines out only moves that
   break into another word. */
.ov-coach > figcaption:not(:lang(ja), :lang(zh), :lang(ko)) {
  text-wrap: pretty;
}

/* A phone, or a narrow window: one above the other, the phone first (the
   coach acts, the person's tablet answers), the link between them. Side
   by side holds down to 641 px (a 128 px phone beside a 405 px tablet);
   narrower, the phone's remote would be too small to read beside it. */
@media (max-width: 640px) {
  .ov-duet {
    grid-template-columns: minmax(0, 1fr);
  }

  .big-stage .ov-duet .device.phone {
    width: min(40%, 180px);
    margin-inline: auto;
  }

  .ov-link {
    height: clamp(60px, 19vw, 100px);
  }

  .ov-link-still .ov-link-h {
    display: none;
  }

  .ov-link-still .ov-link-v {
    display: inline;
  }

  .ov-link-bt {
    left: 43%;
    top: 50%;
  }

  .ov-coach .ov-line {
    grid-template-columns: 44px minmax(0, 1fr) 0;
    gap: 12px;
  }
}

/* --------------------------------------------------------------- print */

/* Paper is white whatever the screen shows, and a browser drops background
   colours when it prints: style.css prints every page in the light theme's
   tokens, so the stage prints as the light theme draws it too. These repeat
   the "Light theme" values for the dark theme (keep the two in step); each
   is weighted to outrank the on-black rule it undoes. */
@media print {
  :root:not([data-theme="light"]) .band-white .gtext,
  :root:not([data-theme="light"]) .gtext-l { background-image: var(--grad-type); }

  :root:not([data-theme="light"]) .hero-logo { box-shadow: 0 1px 2px rgba(0, 0, 0, .06), 0 8px 24px rgba(0, 0, 0, .08); }
  :root:not([data-theme="light"]) .app-icon { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06), 0 10px 28px rgba(0, 0, 0, 0.1); }
  :root:not([data-theme="light"]) .device .bezel { box-shadow: 0 30px 60px -24px rgba(0, 0, 0, 0.45), inset 0 0 0 1px #3a3a3e; }

  :root:not([data-theme="light"]) :is(.tagline, .dots li, .big-stage figcaption strong, .pair figcaption strong,
    .promises h3, .price-cards li, .price-cards p:last-child) { color: var(--ink); }

  :root:not([data-theme="light"]) :is(.beta, .big-stage figcaption, .pair figcaption, .stats li, .plans-fallback,
    .fk-key, .price-cards .price-alt, .promises p, .suite-cards p) { color: var(--ink-2); }

  :root:not([data-theme="light"]) .product-name .beta {
    color: var(--ink-2);
    -webkit-text-fill-color: var(--ink-2);
    border-color: var(--ink-2);
  }

  :root:not([data-theme="light"]) :is(.pair figure:not(.on-grad), .promises li) { background: var(--paper-2); }

  :root:not([data-theme="light"]) :is(.price-cards li, .suite-cards a) {
    background: var(--white);
    color: var(--ink);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), 0 12px 32px rgba(0, 0, 0, 0.07);
  }

  :root:not([data-theme="light"]) .price-cards .price-main { color: var(--accent); }
  :root:not([data-theme="light"]) .suite-cards .link-arrow { color: var(--blue); }
  :root:not([data-theme="light"]) .chapter-rule { background: var(--rule-light); }
}
