/*
  Bozor Bot — landing page, shared by / and /ru/.

  It was inline while there was one home page. A second language made it a
  file: two inline copies of a thousand lines would drift the first time
  someone fixed one of them, and the page that drifted would be the one
  nobody on the team reads. No build step either way — see ../README.md.

  ## The palette has one rule

  Glazed ceramic: indigo and saffron on white clay. The product's idea is
  that it restores the market-stall exchange — "do you have something to
  froth milk?" and the trader walks you to it — to a shop where nobody is
  standing behind the counter, and the palette is warm for the same
  reason.

  The rule that constrains every change: **saffron fails WCAG contrast as
  text on clay**, at about 1.9:1. So it is a *graphic* colour on light
  ground and a *text* colour on the dark band, never the reverse. That is
  why the Q&A section is dark — it is the one place the warm accent can
  carry words.
*/
:root {
  --clay: oklch(0.985 0.008 85);
  --clay-sunk: oklch(0.955 0.013 85);
  --ink: oklch(0.24 0.055 265);
  --ink-deep: oklch(0.19 0.05 265);
  --ink-soft: oklch(0.46 0.03 265);
  --line: oklch(0.9 0.014 265);
  --indigo: oklch(0.47 0.16 262);
  --indigo-deep: oklch(0.37 0.14 262);
  --glaze: oklch(0.66 0.14 235);
  --saffron: oklch(0.78 0.15 72);
  --pomegranate: oklch(0.55 0.19 25);
  --white: #fff;

  /* Onest is here for Cyrillic only. Neither of the brand faces has it, and
     the browser falls back glyph by glyph, so Latin text keeps the brand
     face and Russian text picks up Onest instead of whatever the system
     has. Only /ru/ loads Onest; on the English page the name resolves to
     nothing and costs nothing. */
  --sans: "Instrument Sans", "Onest", ui-sans-serif, system-ui, -apple-system,
    "Segoe UI", Roboto, sans-serif;
  --display: "Bricolage Grotesque", "Onest", "Instrument Sans", ui-sans-serif,
    system-ui, sans-serif;

  /* Light only, and said out loud so a browser in dark mode does not
     restyle controls and scrollbars against a page that stays light. */
  color-scheme: light;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--clay);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1,
h2,
h3 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.028em;
  margin: 0;
  text-wrap: balance;
}

p {
  margin: 0;
  text-wrap: pretty;
}

a {
  color: var(--indigo);
}

/* One visible focus ring, keyboard only. A landing page is almost
   entirely links, so losing it fails WCAG 2.4.7 outright. */
:where(a, button, summary, [tabindex]):focus-visible {
  outline: 2.5px solid var(--indigo);
  outline-offset: 3px;
  border-radius: 7px;
}

.shell {
  width: 100%;
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 1.5rem;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
  background: var(--white);
  padding: 0.8rem 1.1rem;
  border-radius: 0 0 10px 0;
}
.skip:focus {
  left: 0;
}

.prose {
  max-width: 54ch;
  color: var(--ink-soft);
  font-size: 1.15rem;
}

/* ---------------------------------------------------------------- nav */

.top {
  position: sticky;
  top: 0;
  z-index: 10;
  background: oklch(0.985 0.008 85 / 0.82);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}

.top-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  height: 4.25rem;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.03em;
  color: var(--ink);
  text-decoration: none;
}

.seal {
  width: 2rem;
  height: 2rem;
  flex: none;
}

.top-nav {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.top-links {
  display: flex;
  gap: 0.25rem;
}

/* The section anchors are the first thing to go on a narrow screen. The
   two that matter stay, and a hamburger for three links is machinery for
   its own sake. */
@media (max-width: 54rem) {
  .top-links {
    display: none;
  }
}

.top-nav a {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 500;
  padding: 0.5rem 0.75rem;
  border-radius: 9px;
  transition: color 0.18s, background 0.18s;
}

.top-nav a:hover {
  color: var(--ink);
  background: oklch(0.47 0.16 262 / 0.07);
}

/* ------------------------------------------------------------ buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: 0.975rem;
  font-weight: 600;
  padding: 0.7rem 1.25rem;
  border-radius: 11px;
  text-decoration: none;
  border: 1px solid transparent;
  white-space: nowrap;
  transition: transform 0.16s, box-shadow 0.16s, background 0.16s;
}

.btn-solid {
  background: var(--indigo);
  color: var(--white);
  box-shadow: 0 1px 2px oklch(0.24 0.055 265 / 0.3),
    0 8px 20px oklch(0.47 0.16 262 / 0.22);
}

.btn-solid:hover {
  background: var(--indigo-deep);
  transform: translateY(-1px);
  box-shadow: 0 2px 4px oklch(0.24 0.055 265 / 0.3),
    0 12px 26px oklch(0.47 0.16 262 / 0.28);
}

.btn-quiet {
  border-color: var(--line);
  background: var(--white);
  color: var(--ink);
}

.btn-quiet:hover {
  border-color: var(--indigo);
  color: var(--indigo);
}

/*
  Specificity, not preference. `.top-nav a` is (0,1,1) and `.btn-solid`
  is (0,1,0), so the muted link colour above wins inside the header and
  paints the button's label `--ink-soft` on `--indigo` — about 1:1, an
  invisible button. The colour has to be restated at matching weight.
*/
.top-nav a.btn-solid {
  color: var(--white);
}

.top-nav .btn {
  padding: 0.55rem 1.05rem;
  font-size: 0.925rem;
}

.btn-big {
  font-size: 1.05rem;
  padding: 0.9rem 1.6rem;
}

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

.hero {
  padding: clamp(3rem, 7vw, 6rem) 0 clamp(3.5rem, 8vw, 7rem);
}

.hero-grid {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4.5rem);
  /*
    `start`, not `center`. Centring ties the headline's position to the
    height of the column beside it, so every time the chat panel changed
    size — which it does on every turn, as the typing indicator comes and
    goes — the headline slid up and down with it.
  */
  align-items: start;
  /* Asymmetric on purpose: the conversation is the product, so it gets
     the larger share rather than sitting under a centred headline. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
}

@media (max-width: 60rem) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Says in plain words what the headline only implies. The h1 is written for
   a person and names no product category, so without this line the page's
   most prominent text tells a search engine nothing about what it sells. */
.eyebrow {
  margin-bottom: 1.1rem;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--indigo);
}

.hero h1 {
  font-size: clamp(2.7rem, 6.2vw, 4.6rem);
  font-weight: 800;
  letter-spacing: -0.04em;
}

.hero-sub {
  margin-top: 1.4rem;
  max-width: 34ch;
  font-size: 1.2rem;
  color: var(--ink-soft);
}

.hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

.hero-note {
  margin-top: 1.1rem;
  font-size: 0.925rem;
  color: var(--ink-soft);
}

/* ------------------------------------------------- the chat, as a demo

   Markup rather than a screenshot: it stays correct when the widget
   changes, it reflows on a phone, a screen reader can read it, and the
   transcript is in the page text where a crawler can see it.          */

.chat {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: 0 1px 2px oklch(0.24 0.055 265 / 0.06),
    0 30px 60px -20px oklch(0.24 0.055 265 / 0.22);
  overflow: hidden;
}

/* A glazed edge, the way a tile catches light. One decorative flourish,
   and it is the only one on the page. */
.chat::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: linear-gradient(
    90deg,
    var(--indigo) 0%,
    var(--glaze) 45%,
    var(--saffron) 100%
  );
}

.chat-top {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1rem 1.15rem;
  border-bottom: 1px solid var(--line);
  font-weight: 600;
  font-size: 0.95rem;
}

.live {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: oklch(0.6 0.15 150);
  flex: none;
  box-shadow: 0 0 0 0 oklch(0.6 0.15 150 / 0.5);
  animation: breathe 2.6s ease-out infinite;
}

@keyframes breathe {
  0% {
    box-shadow: 0 0 0 0 oklch(0.6 0.15 150 / 0.45);
  }
  70%,
  100% {
    box-shadow: 0 0 0 7px oklch(0.6 0.15 150 / 0);
  }
}

.chat-log {
  display: grid;
  gap: 0.7rem;
  padding: 1.25rem;
  /*
    Messages sit at the bottom and grow upwards, the way every chat
    client works. While the sequence plays this puts the empty room above
    the conversation instead of below it, which reads as "nobody has said
    much yet" rather than as a panel that failed to load.
  */
  flex: 1;
  align-content: end;
}

.turn {
  max-width: 86%;
  padding: 0.7rem 0.95rem;
  font-size: 0.975rem;
  line-height: 1.5;
}

.turn-ask {
  justify-self: end;
  background: var(--indigo);
  color: var(--white);
  border-radius: 16px 16px 5px 16px;
}

.turn-say {
  justify-self: start;
  background: var(--clay-sunk);
  border-radius: 16px 16px 16px 5px;
}

.turn-say b {
  color: var(--indigo-deep);
  font-weight: 600;
}

/* Shown between turns while the sequence plays; removed when it ends. */
.thinking {
  justify-self: start;
  display: inline-flex;
  gap: 0.28rem;
  padding: 0.95rem 1rem;
  background: var(--clay-sunk);
  border-radius: 16px 16px 16px 5px;
}

.thinking i {
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: var(--ink-soft);
  opacity: 0.45;
  animation: blink 1.25s ease-in-out infinite;
}

.thinking i:nth-child(2) {
  animation-delay: 0.16s;
}
.thinking i:nth-child(3) {
  animation-delay: 0.32s;
}

@keyframes blink {
  0%,
  100% {
    opacity: 0.25;
    transform: translateY(0);
  }
  50% {
    opacity: 0.9;
    transform: translateY(-2px);
  }
}

/*
  The played sequence.

  Default is everything visible: no JavaScript, no reduced motion, and a
  crawler all see the finished transcript. The script opts *in* to the
  animation by setting `data-play`, so a failure leaves the content
  intact rather than blank.
*/
/*
  Taken out of flow rather than made transparent. At `opacity: 0` the
  unsaid turns still reserve their space, so the panel was full height
  from the first frame with a large blank area in the middle of it and
  nothing to fill. Removed from flow, the log grows from the bottom as
  the conversation arrives — and the script pins the panel's height
  first, so nothing around it moves while that happens.
*/
.chat[data-play] .turn {
  display: none;
}

.chat[data-play] .turn.shown {
  display: block;
}

.chat[data-play] .turn.shown,
.chat[data-play] .cards.shown {
  animation: rise 0.42s cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
}

/* The cards are the result of the first answer, so they wait for it
   rather than sitting there before anything has been asked. */
.chat[data-play] .cards {
  opacity: 0;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(9px) scale(0.985);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.cards {
  display: flex;
  gap: 0.5rem;
  padding: 0 1.25rem 1.25rem;
}

.mini {
  flex: 1;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.6rem 0.7rem;
  font-size: 0.8rem;
  line-height: 1.35;
  background: var(--white);
}

.mini span {
  display: block;
  color: var(--ink-soft);
  font-size: 0.75rem;
}

.mini b {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.swatch {
  height: 2.1rem;
  border-radius: 7px;
  margin-bottom: 0.45rem;
}

/* ----------------------------------------------------------- sections */

.band {
  padding: clamp(3.5rem, 8vw, 7rem) 0;
}

.band-sunk {
  background: var(--clay-sunk);
  border-block: 1px solid var(--line);
}

.band-dark {
  background: var(--ink-deep);
  color: oklch(0.93 0.015 265);
}

.band-dark h2,
.band-dark h3 {
  color: var(--white);
}

.band-head h2 {
  font-size: clamp(2rem, 4.2vw, 3.1rem);
}

.band-head .prose {
  margin-top: 1.1rem;
}

.band-dark .prose {
  color: oklch(0.84 0.025 265);
}

/* -------------------------------------------------- how it works, 1-3

   Numbered because the content really is a sequence — you cannot read a
   shop you have not connected. Numbering anything else would be
   decoration pretending to be structure.                              */

.steps {
  display: grid;
  gap: 1.75rem;
  margin-top: 3rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.step {
  position: relative;
  padding-top: 1.5rem;
  border-top: 2px solid var(--line);
}

.step-n {
  position: absolute;
  top: -0.85rem;
  left: 0;
  width: 1.7rem;
  height: 1.7rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--clay);
  color: var(--indigo);
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.9rem;
  box-shadow: 0 0 0 2px var(--indigo);
}

.band-sunk .step-n {
  background: var(--clay-sunk);
}

.step h3 {
  font-size: 1.2rem;
  margin-bottom: 0.5rem;
}

.step p {
  color: var(--ink-soft);
  font-size: 0.975rem;
}

/* ------------------------------------------------------ the lattice

   The index drawn as tilework. A bozor's tiles are a grid of glazed
   squares, and so is a search index: your pages, cut up and set where
   they can be found. The animation runs once, on scroll-in, and walks
   left to right in the direction content actually flows.              */

.lattice-wrap {
  margin-top: 3rem;
  display: grid;
  gap: 2rem;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  align-items: center;
}

@media (max-width: 56rem) {
  .lattice-wrap {
    grid-template-columns: minmax(0, 1fr);
  }
}

.lattice {
  width: 100%;
  height: auto;
}

.lattice .tile {
  opacity: 0.16;
}

.lattice.lit .tile {
  animation: glaze 0.5s ease-out forwards;
}

@keyframes glaze {
  to {
    opacity: 1;
  }
}

.lattice .flow {
  stroke-dasharray: 220;
  stroke-dashoffset: 220;
}

.lattice.lit .flow {
  animation: draw 1.1s ease-out forwards;
}

@keyframes draw {
  to {
    stroke-dashoffset: 0;
  }
}

.legend {
  display: grid;
  gap: 1.1rem;
}

/*
  Child combinator, not descendant. `.legend div` also matches the inner
  text wrapper, which turns it into a second two-column grid and drops
  the heading into the 0.55rem swatch track.
*/
.legend > div {
  display: grid;
  grid-template-columns: 0.55rem 1fr;
  gap: 0.85rem;
  align-items: start;
}

.legend i {
  margin-top: 0.45rem;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 2px;
}

.legend b {
  font-weight: 600;
}

.legend p {
  color: var(--ink-soft);
  font-size: 0.95rem;
}

/* ------------------------------------------------------------ feature

   Not a six-card grid. The first item is the argument and gets the room;
   the rest are support and sit in a tighter list.                     */

.feature-lead {
  margin-top: 3rem;
  display: grid;
  gap: 2.5rem;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  align-items: center;
  padding: clamp(1.75rem, 3vw, 2.75rem);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 22px;
}

@media (max-width: 56rem) {
  .feature-lead {
    grid-template-columns: minmax(0, 1fr);
  }
}

.feature-lead h3 {
  font-size: clamp(1.4rem, 2.6vw, 1.9rem);
  margin-bottom: 0.9rem;
}

.feature-lead p {
  color: var(--ink-soft);
}

.lanes {
  display: grid;
  gap: 0.75rem;
}

.lane {
  border: 1px solid var(--line);
  border-left: 3px solid var(--indigo);
  border-radius: 0 12px 12px 0;
  padding: 0.85rem 1rem;
  background: var(--clay);
}

.lane:nth-child(2) {
  border-left-color: var(--saffron);
}

.lane q {
  display: block;
  font-weight: 600;
  font-size: 0.975rem;
  quotes: "“" "”";
}

.lane small {
  display: block;
  margin-top: 0.3rem;
  color: var(--ink-soft);
  font-size: 0.875rem;
}

.rest {
  display: grid;
  gap: 1.5rem 2.5rem;
  margin-top: 2.5rem;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

.rest > div {
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}

.rest h3 {
  font-size: 1.08rem;
  margin-bottom: 0.4rem;
}

.rest p {
  color: var(--ink-soft);
  font-size: 0.95rem;
}

/* ------------------------------------------------------- the gap loop

   The differentiator, so it gets the dark band and the warm accent —
   the one place saffron passes contrast as text.                      */

.loop {
  margin-top: 3rem;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  counter-reset: none;
}

.node {
  position: relative;
  padding: 1.4rem;
  border: 1px solid oklch(0.4 0.05 265);
  border-radius: 16px;
  background: oklch(0.23 0.055 265);
}

.node h3 {
  font-size: 1.05rem;
  margin-bottom: 0.45rem;
}

.node p {
  color: oklch(0.82 0.025 265);
  font-size: 0.925rem;
}

.node-end {
  border-color: var(--saffron);
  background: oklch(0.26 0.06 70);
}

.node-end h3 {
  color: var(--saffron);
}

/* The arrow between nodes. Decorative, and gone on a narrow screen
   where the cards stack and the reading order carries the sequence. */
.node:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -0.78rem;
  width: 0.56rem;
  height: 0.56rem;
  border-top: 2px solid var(--saffron);
  border-right: 2px solid var(--saffron);
  transform: translateY(-50%) rotate(45deg);
}

@media (max-width: 62rem) {
  .node:not(:last-child)::after {
    display: none;
  }
}

/* ------------------------------------------------------------ pricing */

.tiers {
  display: grid;
  gap: 1rem;
  margin-top: 3rem;
  grid-template-columns: repeat(auto-fit, minmax(14.5rem, 1fr));
  align-items: stretch;
}

.tier {
  display: flex;
  flex-direction: column;
  padding: 1.6rem;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: var(--white);
}

/* Structurally different, not just outlined: the recommended plan is a
   different object on the page rather than the same card with a badge. */
.tier-pick {
  background: var(--ink-deep);
  border-color: var(--ink-deep);
  color: oklch(0.92 0.02 265);
  box-shadow: 0 24px 50px -22px oklch(0.24 0.055 265 / 0.55);
}

.tier h3 {
  font-size: 1.05rem;
}

.tier-pick h3,
.tier-pick .cost {
  color: var(--white);
}

.cost {
  font-family: var(--display);
  font-size: 2.4rem;
  font-weight: 700;
  letter-spacing: -0.04em;
  margin: 0.6rem 0 0.1rem;
}

.cost .per {
  font-family: var(--sans);
  font-size: 0.9rem;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ink-soft);
}

.tier-pick .per {
  color: oklch(0.8 0.03 265);
}

.blurb {
  color: var(--ink-soft);
  font-size: 0.9rem;
  min-height: 2.6rem;
}

.tier-pick .blurb {
  color: oklch(0.82 0.03 265);
}

.tier ul {
  list-style: none;
  padding: 0;
  margin: 1.3rem 0;
  display: grid;
  gap: 0.55rem;
  font-size: 0.925rem;
  flex: 1;
  align-content: start;
}

.tier li {
  display: grid;
  grid-template-columns: 1rem 1fr;
  gap: 0.55rem;
  align-items: start;
}

.tier li svg {
  margin-top: 0.34rem;
  color: var(--indigo);
}

.tier-pick li svg {
  color: var(--saffron);
}

.tier b {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.tier-pick .btn-quiet {
  background: var(--saffron);
  border-color: var(--saffron);
  color: oklch(0.24 0.06 70);
}

.tier-pick .btn-quiet:hover {
  background: oklch(0.83 0.15 72);
  border-color: oklch(0.83 0.15 72);
  color: oklch(0.24 0.06 70);
}

.tag {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.18rem 0.5rem;
  border-radius: 999px;
  background: var(--saffron);
  color: oklch(0.24 0.06 70);
  margin-left: 0.5rem;
  vertical-align: 0.2em;
  letter-spacing: 0;
}

.fineprint {
  margin-top: 1.5rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
  max-width: 60ch;
}

/* --------------------------------------------------------------- faq */

.faq {
  margin-top: 2.5rem;
  display: grid;
  gap: 0.6rem;
  max-width: 52rem;
}

details {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 1.1rem 1.35rem;
}

details[open] {
  border-color: oklch(0.47 0.16 262 / 0.35);
}

summary {
  cursor: pointer;
  font-weight: 600;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

summary::-webkit-details-marker {
  display: none;
}

/* Rotates when the panel opens: motion that answers what the person
   just did, which is the kind worth having. */
.chev {
  flex: none;
  color: var(--indigo);
  transition: transform 0.22s ease;
}

details[open] .chev {
  transform: rotate(180deg);
}

details p {
  color: var(--ink-soft);
  font-size: 0.975rem;
  margin-top: 0.8rem;
  max-width: 62ch;
}

/* --------------------------------------------------------- close/foot */

/*
  The measure goes on the text, not on `.shell`. `class="shell close"` is
  one element, so a max-width here overrides the shell's own and the
  `margin: 0 auto` then centres the whole block — which reads as a
  different page from the left-aligned sections above it.
*/
.close h2 {
  font-size: clamp(2rem, 4.5vw, 3.2rem);
  max-width: 18ch;
}

.close .prose {
  margin-top: 1.1rem;
}

.close .hero-cta {
  justify-content: flex-start;
}

.foot {
  border-top: 1px solid var(--line);
  padding: 2.5rem 0;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.foot-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  align-items: center;
}

.foot nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

/* Everything that moves on its own, switched off together. The chat
   sequence checks this in script as well, so the transcript appears
   complete rather than mid-conversation. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .lattice .tile {
    opacity: 1;
  }

  .lattice .flow {
    stroke-dashoffset: 0;
  }
}
