/* fieldedpartners.com. Plain CSS, no frameworks.
   The base rules are the still version of the page. Motion sits at the end of the file, inside
   @supports (animation-timeline: view()) and prefers-reduced-motion: no-preference. */

/* ---------- Fonts (self-hosted, Latin subset, SIL Open Font License) ---------- */

@font-face {
  font-family: "Hanken Grotesk";
  src: url("../fonts/hanken-grotesk-600.woff2?v=44b72139") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Public Sans";
  src: url("../fonts/public-sans-400.woff2?v=c026fbf5") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Public Sans";
  src: url("../fonts/public-sans-600.woff2?v=c138842b") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
/* Bold is for the section labels only. */
@font-face {
  font-family: "Public Sans";
  src: url("../fonts/public-sans-700.woff2?v=a56a676f") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */

:root {
  --navy: #10193C;
  --slate: #3E3D52;
  --teal: #3D7472;
  --teal-on-navy: #8CC2BD;
  --slate-on-navy: #C9C8D6;
  --muted: #5B5A6E;
  --muted-on-navy: #A9A8BC;
  --rule: #DCDCE3;
  --rule-on-navy: #34406A;
  --quiet: #F4F4F7;
  --white: #FFFFFF;
  --focus: #3D7472;
  --font-head: "Hanken Grotesk", "Segoe UI", Arial, sans-serif;
  --font-body: "Public Sans", "Segoe UI", Arial, sans-serif;
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-6: 24px; --space-8: 32px; --space-12: 48px; --space-16: 64px;

  /* Layout */
  --gutter: 16px;
  --content: 1080px;
  --text: 720px;
  --section-pad: 56px;
  --logo-w: 150px;                              /* never under 120px */
  --logo-h: calc(var(--logo-w) * 98 / 870);
  --header-h: 72px;                             /* the header's height: anchor offsets and the pinned hero */

  /* The pinned hero (motion 1). Change these by number. */
  --hero-track: 3000px;                         /* how far you scroll through the story */
  --hero-zoom: 1.28;                            /* the image's size at the end of the track */
  --hero-type: clamp(2rem, 1.2rem + 2.6vw, 3.25rem);   /* the size of the hero's story lines */

  /* The pinned list (motion 3, desktop only). */
  --does-track: 2900px;                         /* how far you scroll while the list is pinned */
  --does-top: 24px;                             /* the gap between the header and the pinned list: short windows */
}
@media (min-height: 720px) {
  :root { --does-top: 40px; }                   /* and taller ones */
}
@media (max-width: 1199px) and (max-height: 767px) {
  :root { --does-top: 24px; }                   /* narrower windows count as short up to 768px: the panes run longer */
}
@media (min-width: 768px) {
  :root {
    --gutter: 48px;
    --section-pad: 64px;
    --logo-w: 190px;
    --header-h: 80px;
    --hero-track: 2900px;
  }
}

/* ---------- Base ---------- */

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

html {
  scroll-padding-top: var(--header-h);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--white);
  color: var(--slate);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
}
@media (min-width: 768px) {
  body { font-size: 17px; }
}

h1, h2, h3, p, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, video, picture { display: block; max-width: 100%; height: auto; }

/* Every grid on the page starts as one column exactly as wide as its container. Without
   minmax(0, 1fr) a grid column can grow to fit its widest content, and Safari on iPhone then
   makes the whole page wider than the screen. */
.split, .columns, .does__item, .hero__frames, .hero__story, .site-footer__row {
  grid-template-columns: minmax(0, 1fr);
}
.split > *, .columns > *, .does__item > * { min-width: 0; }

h1, h2, h3 {
  font-family: var(--font-head);
  font-weight: 600;
  color: var(--navy);
  text-wrap: balance;
}
h2 {
  font-size: 26px;
  line-height: 1.18;
  letter-spacing: -0.005em;
}
/* The item heading: one style for every item in a list of items (What we do, What we put in,
   What the assistant does, What we won't do). How it goes is the one exception, set larger
   further down. */
h3,
.limits > li {
  font-size: 22px;
  line-height: 1.25;
}
@media (min-width: 768px) {
  h2 { font-size: 34px; }
}
@media (min-width: 1024px) {
  h3,
  .limits > li { font-size: 24px; }
}
p { text-wrap: pretty; }
.nowrap { display: inline-block; white-space: nowrap; }

a { color: inherit; }
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.wrap {
  width: 100%;
  max-width: calc(var(--content) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* The section label: sentence case, one to three words, Public Sans Bold. Teal on white,
   white on the teal sections, teal-on-navy on navy. The only place teal is a text color. */
.label {
  font-family: var(--font-body);
  font-size: 20px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0;
  color: var(--teal);
  margin-bottom: var(--space-4);
}

.skip {
  position: absolute;
  left: var(--gutter);
  top: -100px;
  z-index: 30;
  padding: var(--space-3) var(--space-4);
  background: var(--navy);
  color: var(--white);
  font-weight: 600;
}
.skip:focus { top: var(--space-2); outline-color: var(--white); outline-offset: -4px; }

/* ---------- Buttons (square, flat) ---------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 var(--space-6);
  border: 1px solid transparent;
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
}
.button:hover { text-decoration: underline; text-underline-offset: 3px; }
.button--navy { background: var(--navy); border-color: var(--navy); color: var(--white); }
.button--white { background: var(--white); border-color: var(--white); color: var(--navy); }
/* The outline button sits over the hero picture with no fill of its own (George, Nov 13): its
   words count as text over the picture, and the contrast tool measures them. On short upright
   screens it takes a see-through fill (motion 1, further down). */
.button--outline { background: transparent; border-color: var(--white); color: var(--white); }

/* ---------- 1. Header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--white);
  border-bottom: 1px solid var(--slate);
}
.site-header__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: calc(var(--header-h) - 1px);   /* with the rule under it, the header is exactly --header-h */
  padding-block: var(--space-3) 10px;
}
.brand__logo { display: block; width: var(--logo-w); }
.brand__logo img { width: 100%; }
/* The tagline starts under the F of the name (146.68 / 870 of the lockup's width) and sits
   half the symbol's height below the lockup, which is the logo's clear space. */
.brand__tagline {
  margin-top: calc(var(--logo-h) / 2);
  margin-left: calc(var(--logo-w) * 0.1686);
  font-size: 15px;
  line-height: 1.3;
  color: var(--muted);
}
.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.site-nav__link {
  display: none;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--space-3);
  font-size: 15px;
  font-weight: 600;
  color: var(--navy);
  text-decoration: none;
}
.site-nav__link:hover { text-decoration: underline; text-underline-offset: 4px; }
.site-nav .button { margin-left: var(--space-3); padding-inline: 20px; }
@media (min-width: 768px) {
  .brand__tagline { font-size: 16px; }
}
@media (min-width: 900px) {
  .site-nav__link { display: inline-flex; }
}

/* ---------- 2. Story hero (still version) ---------- */

.hero {
  background: var(--navy);
  color: var(--white);
}
.hero :focus-visible { outline-color: var(--white); }
.hero__stage {
  position: relative;
  overflow: hidden;
  padding-block: var(--space-12) 290px;   /* room under the text for the desk, and the caption */
}
/* Image 1, the desk at night. In the still version on a window wider than it is tall the
   picture sits behind the text, cropped sideways. The crop goes by the window's width, because
   the stacked text is a tall block and the picture is scaled to its height (George, Nov 13):
     1,366px and wider    61% across: the text has the dark wall and the lamp is whole (its
                          shade ends 21px inside the right edge at 1,366px wide, 50px at
                          1,440px; the desk phone beside it is cut).
     under 1,366px        40% across: the picture is moved so the text sits on the wall. The
                          lamp is out of the window.
   Nothing is darkened at any width (George, Nov 13: a 62.3% crop with an even darkening
   behind the text was tried at 1,366 to 1,439px and dropped, because its edge showed). */
.hero__image {
  position: absolute;
  inset: 0;
}
.hero__image picture,
.hero__image img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
}
.hero__image img {
  object-fit: cover;
  object-position: 61% 50%;
}
@media (max-width: 1365px) {
  .hero__image img { object-position: 40% 50%; }
}
/* The still version on a window taller than it is wide (phones, tablets held upright): the
   text sits on navy and the desk is shown under it, the bottom of the tall picture in a band
   as wide as the window. The navy blends gradually into the picture's dark wall, so the band
   has no top edge (George, Nov 13). The picture file carries extra floor below its bottom
   edge (17.27% of its width, for the pinned hero); here that part is pushed out of sight, so
   the band ends where the picture itself ends. */
@media (orientation: portrait) {
  .hero__stage { padding-bottom: 90vw; }
  .hero__image {
    top: auto;
    height: 90vw;
  }
  .hero__image img { object-position: 50% calc(100% + 17.27vw); }
  .hero__image::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 42vw;
    background: linear-gradient(var(--navy), rgb(16 25 60 / 0));   /* navy to nothing */
  }
}
.hero__frames {
  position: relative;
  z-index: 1;                     /* the buttons stay on top of whatever sits at the foot of the stage */
  display: grid;
  gap: var(--space-12);
}
.hero__line {
  max-width: 17em;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: var(--hero-type);
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--white);
  text-wrap: balance;
}
.hero__line > span:not(.nowrap) { display: block; }
.hero__quiet { color: var(--slate-on-navy); }
.hero__sub {
  max-width: 30em;
  margin-top: var(--space-4);
  font-size: 18px;
  line-height: 1.5;
  color: var(--slate-on-navy);
}
/* The line under the descriptor has two wordings. The still version shows "One night shift at
   a senior living community."; "Scroll to follow one night shift…" is shown only where the
   hero pins and there is a scroll to follow (motion 1). */
.hero__sub--pinned { display: none; }
.hero__story {
  display: grid;
  gap: var(--space-4);
}
.hero__buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}
/* The rotating copy of the middle line stays out of the page until site.js turns it on
   (motion 2, at the end of this file). */
.rot__live { display: none; }
/* Phones: the two buttons share one row (down to 360px wide), which keeps the final frame
   clear of the caption on a short screen. The tap height stays 48px. */
@media (max-width: 479px) {
  .hero__buttons .button {
    padding-inline: var(--space-4);
    font-size: 15px;
  }
}
/* The foot of the stage: the caption on the left and, on its line at the right, the pause
   control. */
.hero__foot {
  position: absolute;
  inset: auto 0 0 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
}
.hero__caption {
  grid-area: 1 / 1;
  font-size: 14px;
  color: var(--slate-on-navy);
}
/* The pause control: the page's one round element (CLAUDE.md, Design tokens). A 44px tap
   area holding a 38px circle with a thin light outline and a flat, part-see-through navy
   fill; inside it two bars (pause) or a triangle (play). No visible text: its name is in
   aria-label and title. Hidden until site.js turns it on. The negative margins keep the
   caption where it was and line the circle's edge up with the page gutter. */
.hero__pause {
  grid-area: 1 / 2;
  position: relative;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: -11px -3px -11px 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.hero__pause[hidden] { display: none; }
.hero__pause::before {
  content: "";
  position: absolute;
  inset: 3px;
  border: 1px solid var(--slate-on-navy);
  border-radius: 50%;
  background: rgb(16 25 60 / 0.6);   /* navy, 60% */
}
.hero__pause:focus-visible { outline: none; }
.hero__pause:focus-visible::before {
  outline: 2px solid var(--white);
  outline-offset: 2px;
}
.hero__pause-icon {
  position: relative;
  width: 10px;
  height: 12px;
  border-inline: 3px solid var(--white);
}
.hero__pause.is-paused .hero__pause-icon {
  width: 11px;
  margin-left: 2px;                  /* a triangle looks centered a little right of center */
  border: 0;
  background: var(--white);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
@media (min-width: 768px) {
  .hero__sub { font-size: 20px; }
  .hero__foot {
    align-items: end;
    padding-bottom: var(--space-8);
  }
}
@media (min-width: 768px) and (orientation: landscape) {
  .hero__stage { padding-bottom: 300px; }
}

/* ---------- Sections ---------- */

.section { padding-block: var(--section-pad); }
/* Consecutive white sections share one gap, not two. */
.what + .starting,
.starting + .put-in,
.put-in + .does,
.does + .demo { padding-top: 0; }

.split {
  display: grid;
  gap: var(--space-8);
}
.prose > h2 { margin-bottom: var(--space-4); }
.prose > p { max-width: 34em; }
.prose > p + p { margin-top: var(--space-4); }

@media (min-width: 900px) {
  .split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--space-16);
    align-items: start;
  }
}

/* Lists divided by rules */
.ruled > li {
  padding-block: var(--space-4);
  border-top: 1px solid var(--teal);
}
.ruled > li:last-child { padding-bottom: 0; }
.ruled h3 { margin-bottom: var(--space-2); }

.columns {
  display: grid;
  gap: var(--space-6);
}
.ruled-columns > li {
  padding-top: var(--space-4);
  border-top: 1px solid var(--slate);
}
.ruled-columns h3 { margin-bottom: var(--space-2); }
@media (min-width: 900px) {
  .columns {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--space-6);
  }
}

/* ---------- 3. What we do ---------- */

.statement {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: clamp(1.5rem, 1.1rem + 1.4vw, 1.9375rem);
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--navy);
  text-wrap: pretty;
}
.posture {
  margin-top: var(--space-6);
  font-size: 19px;
  line-height: 1.45;
}

/* ---------- 4. Where we're starting ---------- */

.starting__image {
  width: 100%;
  max-width: 100%;
}
/* Image 3, the Fort Worth skyline, in a frame with one flat angled edge: along the top on
   phones, down the left side on desktop. The picture is wider than the frame; on desktop it
   is moved right (22%) so the angled edge clears the left tower. */
.starting__clip {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  clip-path: polygon(0 12%, 100% 0, 100% 100%, 0 100%);
}
.starting__clip img {
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: 50% 50%;
}
@media (min-width: 900px) {
  .split--image { grid-template-columns: minmax(0, 6fr) minmax(0, 6fr); }
  .starting__clip {
    aspect-ratio: 5 / 4;
    clip-path: polygon(18% 0, 100% 0, 100% 100%, 0 100%);
  }
  .starting__clip img { object-position: 22% 50%; }
}

/* ---------- 5. What we put in ---------- */

.put-in .label { margin-bottom: var(--space-4); }   /* label, then the three columns, as in How it goes */
.optional {
  margin-bottom: var(--space-2);
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
}

/* ---------- 6. What the assistant does (still version: everything open) ---------- */

.does .label { margin-bottom: var(--space-4); }   /* label, then the items, as in How it goes */
.does__list { border-bottom: 1px solid var(--teal); }
.does__item {
  display: grid;
  gap: var(--space-4);
  padding-block: var(--space-4) 20px;
  border-top: 1px solid var(--teal);
}
.does__text { padding-left: 26px; }
.does__text h3 {
  position: relative;
  margin-bottom: var(--space-2);
}
.does__text h3::before {   /* the teal square dot */
  content: "";
  position: absolute;
  left: -26px;
  top: 0.45em;
  width: 10px;
  height: 10px;
  background: var(--teal);
}
/* The pane: one real exchange from the recording. Who is speaking, then what they said; a
   rule between the question and the reply. */
.does__pane {
  margin: 0;
  padding-inline: var(--space-4);
  border: 1px solid var(--rule);
}
.does__turn { padding-block: var(--space-3) var(--space-4); }
.does__turn + .does__turn { border-top: 1px solid var(--rule); }
.does__pane dt {
  margin-bottom: var(--space-1);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--navy);
}
.does__pane dd { margin: 0; }
.does__steps {
  padding-left: 1.3em;
  list-style: decimal;
}
.does__steps > li + li { margin-top: var(--space-2); }
/* A reply can open with a sentence before its numbered lines and close with one after them. */
p + .does__steps,
.does__steps + p { margin-top: var(--space-2); }
.does__source {
  margin-top: var(--space-2);
  color: var(--muted);
}
@media (min-width: 1024px) {
  .does__item {
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    column-gap: var(--space-16);
    align-items: start;
  }
  .does__pane { padding-inline: var(--space-6); }
  .does__turn { padding-block: var(--space-4) 20px; }
}

/* ---------- 7. The demo ---------- */

/* The video takes its width from the column and its height from the width (16:9), so the
   space is held before the still frame arrives and nothing shifts when it does. */
.demo__video {
  width: 100%;
  max-width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  background: var(--navy);
}
@media (min-width: 900px) {
  .split--video { grid-template-columns: minmax(0, 4fr) minmax(0, 6fr); }
}

/* "Read the exchange": everything said in the video, as text, under it. A plain <details>, so
   it opens and closes with no script. Closed, it is one line; open, the six exchanges are set
   like the panes above (who is speaking, then what they said), with a rule above each question. */
.exchange { border-bottom: 1px solid var(--rule); }
.exchange > summary {
  width: fit-content;
  padding-block: 10px;                             /* 46px tall with its line: a full tap target */
  padding-right: var(--space-2);                   /* so the focus ring does not touch the last letter */
  font-size: 16px;
  font-weight: 600;
  color: var(--navy);
  cursor: pointer;
}
.exchange > summary:hover { text-decoration: underline; }
.exchange__list { margin: 0; }
.exchange__turn { padding-bottom: var(--space-3); }
.exchange__turn:nth-child(odd) {
  padding-top: var(--space-3);
  border-top: 1px solid var(--rule);
}
.exchange__turn:last-child { padding-bottom: var(--space-4); }
.exchange dt {
  margin-bottom: var(--space-1);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--navy);
}
.exchange dd {
  margin: 0;
  font-size: 16px;
}

/* ---------- 8 and 10. Teal sections: all text white ---------- */

.section--teal {
  background: var(--teal);
  color: var(--white);
}
.section--teal h2,
.section--teal h3,
.section--teal .label { color: var(--white); }
.section--teal :focus-visible { outline-color: var(--white); }
.section--teal .ruled-columns > li { border-top-color: var(--white); }

.how .label { margin-bottom: var(--space-4); }
.how .columns { row-gap: var(--space-8); }
.how .ruled-columns > li { padding-top: var(--space-4); }
.step {
  margin-bottom: var(--space-3);
  font-size: 14px;
  font-weight: 600;
}
/* The deliberate exception to the item-heading size: see CLAUDE.md, Design tokens. */
.how h3 {
  font-size: 26px;
  line-height: 1.2;
  margin-bottom: var(--space-3);
}

.data h2 { max-width: 14em; }
.data__line {
  font-size: 19px;
  line-height: 1.5;
}
@media (min-width: 900px) {
  .data { padding-block: var(--space-12); }
  .data__line { padding-top: 30px; }  /* lines up with the heading, under the label */
}

/* ---------- 9. What we won't do ---------- */

.limits { border-top: 1px solid var(--teal); }
.limits > li {
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--teal);
  font-family: var(--font-head);
  font-weight: 600;
  color: var(--navy);
  text-wrap: balance;   /* like the other item headings: no single word left alone on a line */
}
@media (min-width: 900px) {
  .split--wont { grid-template-columns: minmax(0, 4fr) minmax(0, 6fr); }
}

/* ---------- 11. Angled bands: three flat bands stepping down into the navy ---------- */

.bands {
  position: relative;
  height: clamp(64px, 8vw, 116px);
  margin-bottom: -1px;            /* no hairline between the bands and the navy below */
  background: var(--white);
}
.bands > div { position: absolute; inset: 0; }
.bands__teal  { background: var(--teal);  clip-path: polygon(0 62%, 100% 6%, 100% 100%, 0 100%); }
.bands__slate { background: var(--slate); clip-path: polygon(0 74%, 100% 20%, 100% 100%, 0 100%); }
.bands__navy  { background: var(--navy);  clip-path: polygon(0 96%, 100% 44%, 100% 100%, 0 100%); }

/* ---------- 12. Who we are, contact, footer (navy) ---------- */

.section--navy {
  background: var(--navy);
  color: var(--white);
}
.section--navy .label { color: var(--teal-on-navy); }
.section--navy :focus-visible { outline-color: var(--teal-on-navy); }

/* Who we are is text only at launch (George, Nov 13): two columns of words, no photo and no
   placeholder. If a photo is added later it goes under the text in the first column. */
.close { padding-block: var(--space-8) var(--section-pad); }
.close__text {
  max-width: 24em;
  font-size: 19px;
  line-height: 1.5;
}
.close__links { margin-top: calc(-1 * var(--space-2)); }
.close__links a {
  display: block;
  width: fit-content;
  max-width: 100%;
  padding-block: var(--space-2);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 21px;
  line-height: 1.35;
  overflow-wrap: anywhere;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
.close__address {
  margin-top: var(--space-3);
  color: var(--slate-on-navy);
}
@media (min-width: 900px) {
  .close { padding-top: var(--space-12); }
  .close__text { font-size: 20px; }
}

.site-footer__row {
  display: grid;
  gap: var(--space-4);
  padding-block: var(--space-6);
  border-top: 1px solid var(--rule-on-navy);
  font-size: 14px;
  color: var(--muted-on-navy);
}
.site-footer__logo { width: 150px; }
@media (min-width: 768px) {
  .site-footer__row {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    justify-items: end;
  }
}

/* ---------- The 404 page ----------

   The header as on the home page (lockup and tagline, no links), then a navy ground down to
   the foot of the window with the one line and the one button, set like the hero's first
   frame. Nothing moves. */

.missing {
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100svh - var(--header-h));
  padding-block: var(--space-12);
  background: var(--navy);
}
.missing :focus-visible { outline-color: var(--white); }
.missing .button { margin-top: var(--space-6); }

/* ---------- Motion 1: the pinned zooming story hero ----------

   Everything above is the still page. This block only runs where the browser can tie an
   animation to the scroll position (Chrome, Edge, Safari 26 and later) and the reader has not
   asked for reduced motion. Very short windows (a phone on its side) keep the still version.

   The hero section becomes a tall track (--hero-track). Inside it the stage sticks under the
   header and fills the window while the track scrolls past. The track's progress, 0 to 100%,
   drives the image's zoom and the six text frames. Only transform and opacity are animated.

   Where the track's length goes: frame 0 and the four lines take about a sixth each of the
   first 83%; the final frame fades in from 83 to 87%; the last 13% (about 300px, half a
   swipe) is the tail before the stage lets go. The zoom runs the whole way and ends as the
   stage lets go. */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-height: 440px) {

    .hero {
      height: var(--hero-track);
      overflow: clip;                              /* trims the zoomed image; unlike "hidden", it lets the stage stick */
      background: var(--navy);
      view-timeline-name: --hero;
      view-timeline-axis: block;
      view-timeline-inset: var(--header-h) 0px;    /* progress is 0% when the stage pins under the header */
    }

    /* The stage: pinned under the header, as tall as the window with the browser's bars showing
       (100svh), so it never resizes mid-scroll. If the final frame needs more room than a short
       phone has, the stage grows to fit and its last lines scroll into view as it unpins. */
    .hero__stage {
      position: sticky;
      top: var(--header-h);
      min-height: calc(100svh - var(--header-h));
      padding-block: var(--hero-pad) var(--hero-foot);
      overflow: visible;
      --hero-pad: var(--space-12);
      --hero-foot: var(--space-16);                /* room for the caption under the final frame */
      --hero-under: calc(100lvh - 100svh);         /* what a phone uncovers when its bars slide away */
    }

    /* The picture grows from 1 to --hero-zoom, evenly, across the whole track.
       The wide picture fills the stage (and reaches below it by what a browser's bars uncover
       when they hide), cropped sideways at 65%. It grows from the point midway between the
       chair and the lamp. */
    .hero__image {
      inset: 0 0 calc(-1 * var(--hero-under)) 0;
      transform-origin: 66.1% 57.8%;
      animation: hero-zoom 1ms linear both;
      animation-timeline: --hero;
      animation-range: contain 0% contain 100%;
    }
    .hero__image img { object-position: 65% 50%; }
    /* Small windows that are wider than tall (under 900px wide, or under 1,024px wide and
       under 600px tall; a browser snapped to half a laptop screen is one). There the final
       frame's text nearly fills the stage and at 65% the lamp or the lit desk lands under it.
       The picture is moved so the text sits on the wall, 40% across; the lamp is out of the
       window and nothing is darkened (George, Nov 13). */
    @media (max-width: 899px), (max-width: 1023px) and (max-height: 599px) {
      .hero__image img { object-position: 40% 50%; }
    }
    /* The tall picture is placed from the foot of the stage, never by a percentage: the
       picture's own bottom edge sits 50px below the foot, whatever the phone's bars are
       doing. Below that edge the file carries more floor (the floor mirrored, 17.27% of the
       picture's width), so that when the bars slide away and the screen shows more under the
       stage, it shows floor and not the hero's navy ground (George, Nov 13). The picture
       grows from the spot midway between the binder and the lamp: 57.5% across, and 36% of
       the picture's width above its own bottom edge. */
    @media (orientation: portrait) {
      .hero__image {
        inset: 0 0 calc(-50px - 17.27vw) 0;
        height: auto;
        transform-origin: 57.5% calc(100% - 53.27vw);
      }
      .hero__image img { object-position: 50% 100%; }
      .hero__image::before { content: none; }
    }

    /* "Watch the demo (2:23)" on a short upright screen. The button is outline only, but on a
       short phone the final frame reaches down to the desk and the button lands on the lamp,
       where its words cannot be read (1.6:1 on a small iPhone). There, and only there, it
       takes a see-through dark fill inside its outline: navy at 65%, flat, so the lamp shows
       faintly through (George, Nov 13). At 65% white words pass 4.5:1 over anything, pure
       white included (5.4:1); over the lamp they measure about 7:1.

       "There" is worked out from the layout, not listed phone by phone: the button is filled
       when the room under the final frame is less than the height the lamp reaches at the end
       of the zoom, plus 6px of air.
         --lamp-top   how far above the stage's foot the top of the lamp ends up. The picture's
                      bottom edge is 50px below the foot; the zoom point is 36% of the
                      picture's width above that edge and the lamp's top 9% above the zoom
                      point, which the zoom stretches.
         --final-h    the final frame's height. It goes by how its lines wrap, so it steps
                      with the window's width (the list below). Measured in Chromium and
                      WebKit on Nov 13; measure again if the final frame's words or its type
                      change (scripts/hero-contrast.mjs fails if the button is left on the lamp).
         100svh       the window's height with the browser's bars showing, which is what the
                      stage is built from. A media query on height would follow the bars as
                      they slide away, and the fill would come and go with them.
       tan(atan2(a, 1px)) turns the length a into a plain number, so --demo-fill is 1 where
       the room is short and 0 where it is not; the fill's opacity is 65% of it. From 640px
       wide the lamp is to the right of the buttons at every upright height. */
    @media (orientation: portrait) and (max-width: 639px) {
      .hero__stage {
        --final-h: 471px;                          /* up to 354px wide: the buttons on two rows */
        /* On a short phone the final frame and the caption need more room than the stage has
           (20px more on a small iPhone with Safari's bars showing, 375 x 548), and the caption
           would sit cut by the bottom of the window for as long as the hero is pinned. The
           space above the text gives way first: from 48px down to no less than 24px, by
           exactly what is missing (George, Nov 13). Shorter than that, the stage grows as
           before and the caption comes into view as the stage lets go. */
        --hero-pad: clamp(var(--space-6), calc(100svh - var(--header-h) - var(--final-h) - var(--hero-foot)), var(--space-12));
        --lamp-top: calc(36vw + 9vw * var(--hero-zoom) - 50px);
        --final-room: calc(100svh - var(--header-h) - var(--hero-pad) - var(--final-h));
        --demo-fill: clamp(0, tan(atan2(calc(var(--lamp-top) + 6px - var(--final-room)), 1px)), 1);
      }
      .hero__buttons .button--outline { background-color: rgb(16 25 60 / calc(0.65 * var(--demo-fill))); }   /* navy */
    }
    @media (orientation: portrait) and (min-width: 355px) { .hero__stage { --final-h: 411px; } }
    @media (orientation: portrait) and (min-width: 361px) { .hero__stage { --final-h: 384px; } }
    @media (orientation: portrait) and (min-width: 418px) { .hero__stage { --final-h: 313px; } }
    @media (orientation: portrait) and (min-width: 424px) { .hero__stage { --final-h: 278px; } }
    @media (orientation: portrait) and (min-width: 502px) { .hero__stage { --final-h: 263px; } }
    /* Upright windows 396 to 427px wide that are short. There the lamp ends up taller than the
       button row, and the last line of the small paragraph ("…goes to the nurse.") is long
       enough to reach it, so the end of that line would sit on the lamp's shade. The final
       frame keeps more room under it, as much as the lamp needs: the paragraph ends 6px above
       the lamp's top (the buttons, 48px, and the 24px above them are the 72px between). The
       room comes out of the space above the text, as above. The buttons then sit on the lamp
       and take their see-through fill. In a window with room to spare nothing changes
       (George, Nov 13). Narrower than this the lamp's top stays inside the button row; wider,
       the paragraph's last line is too short to reach the lamp. */
    @media (orientation: portrait) and (min-width: 396px) and (max-width: 427px) {
      .hero__stage { --hero-foot: max(var(--space-16), calc(var(--lamp-top) - 72px + 6px)); }
    }

    /* The six frames share one spot; one shows at a time. The line under the descriptor takes
       its "Scroll to follow…" wording. */
    .hero__sub--pinned { display: block; }
    .hero__sub--still { display: none; }
    .hero__frames { gap: 0; }
    .hero__story { display: contents; }
    .hero__frames .frame {
      grid-area: 1 / 1;
      align-self: start;
      animation: hero-line 1ms linear both;
      animation-timeline: --hero;
    }
    .hero__frames .frame--intro {
      animation-name: hero-intro;
      animation-range: contain 0% contain 18%;
    }
    .hero__story > .frame:nth-child(1) { animation-range: contain 17% contain 34.5%; }
    .hero__story > .frame:nth-child(2) { animation-range: contain 33.5% contain 51%; }
    .hero__story > .frame:nth-child(3) { animation-range: contain 50% contain 67.5%; }
    .hero__story > .frame:nth-child(4) { animation-range: contain 66.5% contain 84%; }
    .hero__frames .frame--final {
      animation-name: hero-final;
      animation-range: contain 0% contain 100%;
    }

    /* Keyboard: the only links in the hero are in the final frame. If one takes the focus
       before the reader has scrolled that far, show that frame and hide the others, so the
       focus ring is never on something invisible. */
    .hero__stage:has(:focus-visible) .frame { opacity: 0 !important; }
    .hero__stage:has(:focus-visible) .frame--final,
    .hero__stage:has(:focus-visible) .hero__pause {
      opacity: 1 !important;
      transform: none !important;
    }

    /* The pause control arrives and leaves with the final frame. */
    .hero__pause {
      animation: hero-final 1ms linear both;
      animation-timeline: --hero;
      animation-range: contain 0% contain 100%;
    }

    /* ---------- Motion 2: the rotating phrase ----------
       site.js puts "rotating" on <html> where this block applies, and every 3.5 seconds moves
       "is-on" to the next phrase: a half-second roll, then a 3 second hold. The phrase on
       show rolls up and out ("is-out") while the next rolls up from below, each with its
       underline. The written-out sentence stays for screen readers (the rotating copy is
       aria-hidden). The three phrases share one spot as wide as the longest, so nothing else
       on the line moves when they change. */
    .rotating .rot__still {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
    .rotating .rot__live { display: block; }
    /* The spot is a window one line tall: a phrase rolling in or out is trimmed at its top
       and bottom edges. The padding makes room for the underline inside the window and the
       matching negative margin keeps the line the same height. */
    .rot__slot {
      display: inline-grid;
      overflow-x: visible;
      overflow-y: clip;
      padding-bottom: 0.2em;
      margin-bottom: -0.2em;
    }
    .rot__phrase {                     /* waiting below the window */
      grid-area: 1 / 1;
      justify-self: start;
      white-space: nowrap;
      opacity: 0;
      transform: translateY(100%);
      text-decoration: underline;
      text-decoration-color: var(--teal-on-navy);
      text-decoration-thickness: 3px;
      text-underline-offset: 0.18em;
      text-decoration-skip-ink: none;
    }
    .rot__phrase.is-on,
    .rot__phrase.is-out {
      transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1);
    }
    .rot__phrase.is-on { opacity: 1; transform: none; }
    .rot__phrase.is-out {              /* fades a little faster than it travels, so it is gone before the window's top edge trims it */
      transform: translateY(-100%);
      transition-duration: 0.5s, 0.35s;
    }

    @keyframes hero-zoom {
      to { transform: scale(var(--hero-zoom)); }
    }
    /* Frame 0 is there from the start, then fades and rises away. */
    @keyframes hero-intro {
      0%, 80% { opacity: 1; transform: none; }
      100% { opacity: 0; transform: translateY(-16px); }
    }
    /* Lines 1 to 4: fade in rising 16px, hold, fade out rising 16px. */
    @keyframes hero-line {
      0% { opacity: 0; transform: translateY(16px); }
      20%, 80% { opacity: 1; transform: none; }
      100% { opacity: 0; transform: translateY(-16px); }
    }
    /* The final frame arrives at 83% and stays. Until then it is parked below the window, so
       its buttons can't be pressed while they can't be seen. */
    @keyframes hero-final {
      0%, 82.9% { opacity: 0; transform: translateY(100vh); }
      83% { opacity: 0; transform: translateY(16px); }
      87%, 100% { opacity: 1; transform: none; }
    }
  }

  @media (prefers-reduced-motion: no-preference) and (min-height: 440px) and (min-width: 768px) {
    .hero__stage {
      --hero-pad: clamp(64px, 13svh, 128px);
      --hero-foot: 80px;
    }
  }
}

/* ---------- Motion 3: the pinned list, "What the assistant does" ----------

   Desktop only (1,024px and wider, and a window tall enough to hold the list and its tallest
   pane: 620px, or 700px where the window is narrower than 1,200px and the panes run longer),
   in the same browsers as the hero and never with reduced motion. Everywhere else the list
   above stays as it is: every item open, its pane beside or under it.

   The section gains a blank track (--does-track) under the stage, and the stage sticks under
   the header while that track scrolls past. Progress along it, 0 to 100%, picks the item:
   item 1 until 22%, item 2 from 28 to 46%, item 3 from 51 to 71%, item 4 from 76%, changing
   over in between (22-28, 46-51, 71-76%). The item that is on has a navy title, a teal dot, a
   short teal bar on its rule and its description under it; its pane shows on the right.

   Only opacity and transform are animated, so nothing is laid out again while scrolling. The
   titles keep their places in the page; each description hangs under its title, outside the
   flow; and the items below an open description are moved down by its height. site.js
   measures the four descriptions (--does-h1 to --does-h4). Without it, every item makes room
   for the tallest (--does-lines). */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 1200px) and (min-height: 620px),
         (prefers-reduced-motion: no-preference) and (min-width: 1024px) and (min-height: 700px) {

    .does {
      view-timeline-name: --does;
      view-timeline-axis: block;
      view-timeline-inset: calc(var(--header-h) + var(--does-top)) 0px;   /* progress is 0% when the stage pins */
    }
    .does::after {                                 /* the track */
      content: "";
      display: block;
      height: var(--does-track);
    }
    .does__stage {
      position: sticky;
      top: calc(var(--header-h) + var(--does-top));
    }

    /* One grid for the whole list: the four titles down the left, and all four panes in the
       same spot on the right, inside one frame. The fifth row is the room an open description
       needs; it also takes up the slack when the tallest pane is taller than the list. */
    .does__list {
      --does-lines: 3;
      --does-h: calc(var(--does-lines) * 1lh);
      /* How far an open description pushes the items under it: its height, less the 12px it
         overlaps its title's row, plus 20px before the next rule. */
      --does-o1: calc(var(--does-h1, var(--does-h)) + 8px);
      --does-o2: calc(var(--does-h2, var(--does-h)) + 8px);
      --does-o3: calc(var(--does-h3, var(--does-h)) + 8px);
      --does-o4: calc(var(--does-h4, var(--does-h)) + 8px);
      display: grid;
      grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
      grid-template-rows: repeat(4, auto) minmax(max(var(--does-o1), var(--does-o2), var(--does-o3), var(--does-o4)), 1fr);
      column-gap: var(--space-16);
      border-bottom: 0;
    }
    .does__item { display: contents; }

    .does__text {
      grid-column: 1;
      position: relative;
      padding: 20px 0 20px 26px;
      border-top: 1px solid var(--teal);
    }
    .does__text::before {                          /* the short bar on the open item's rule */
      content: "";
      position: absolute;
      top: -1px;
      left: 0;
      width: 80px;
      height: 3px;
      background: var(--teal);
    }
    .does__text h3 {
      margin-bottom: 0;
      --does-off: 0.6;                             /* a title that is not on: still easy to read, clearly quieter */
    }
    .does__text h3::before { --does-off: 0; }
    .does__text p {
      position: absolute;
      top: calc(100% - 12px);
      right: 0;
      left: 26px;
    }
    .does__list::before {                          /* the rule under the list; it moves with the open description */
      content: "";
      grid-area: 5 / 1;
      align-self: start;
      border-top: 1px solid var(--teal);
    }

    .does__pane {
      grid-area: 1 / 2 / -1 / 2;
      align-self: start;
      border-color: transparent;                   /* the frame is drawn once, below, and stays put */
    }
    .does__list::after {
      content: "";
      grid-area: 1 / 2 / -1 / 2;
      border: 1px solid var(--rule);
      pointer-events: none;
    }
    /* A short window (under 720px, or under 768px where the window is narrower than 1,200px and
       the panes run longer): less air inside the pane, so the longest reply, the 6 a.m. one,
       still ends above the bottom of the window at the smallest sizes that pin. */
    @media (max-height: 719px), (max-width: 1199px) and (max-height: 767px) {
      .does__turn { padding-block: var(--space-3); }
      .does__pane .does__steps > li + li,
      .does__pane p + .does__steps,
      .does__pane .does__steps + p { margin-top: var(--space-1); }
    }

    /* Everything that changes follows the same timeline, start to end of the pin. */
    .does__text h3,
    .does__text h3::before,
    .does__text::before,
    .does__text p,
    .does__pane,
    .does__item + .does__item > .does__text,
    .does__list::before {
      animation: 1ms linear both;
      animation-timeline: --does;
      animation-range: contain 0px contain var(--does-track);
    }
    .does__item:nth-child(1) h3,
    .does__item:nth-child(1) h3::before,
    .does__item:nth-child(1) > .does__text::before { animation-name: does-on-1; }
    .does__item:nth-child(2) h3,
    .does__item:nth-child(2) h3::before,
    .does__item:nth-child(2) > .does__text::before { animation-name: does-on-2; }
    .does__item:nth-child(3) h3,
    .does__item:nth-child(3) h3::before,
    .does__item:nth-child(3) > .does__text::before { animation-name: does-on-3; }
    .does__item:nth-child(4) h3,
    .does__item:nth-child(4) h3::before,
    .does__item:nth-child(4) > .does__text::before { animation-name: does-on-4; }

    .does__item:nth-child(1) .does__text p { animation-name: does-note-1; }
    .does__item:nth-child(2) .does__text p { animation-name: does-note-2; }
    .does__item:nth-child(3) .does__text p { animation-name: does-note-3; }
    .does__item:nth-child(4) .does__text p { animation-name: does-note-4; }

    .does__item:nth-child(1) > .does__pane { animation-name: does-pane-1; }
    .does__item:nth-child(2) > .does__pane { animation-name: does-pane-2; }
    .does__item:nth-child(3) > .does__pane { animation-name: does-pane-3; }
    .does__item:nth-child(4) > .does__pane { animation-name: does-pane-4; }

    .does__item:nth-child(2) > .does__text { animation-name: does-shift-2; }
    .does__item:nth-child(3) > .does__text { animation-name: does-shift-3; }
    .does__item:nth-child(4) > .does__text { animation-name: does-shift-4; }
    .does__list::before { animation-name: does-shift-end; }
    /* site.js puts "is-sizing" on the list for an instant when it has measured the
       descriptions again, so these animations start afresh with the new distances. */
    .does__list.is-sizing::before,
    .does__list.is-sizing .does__text { animation-name: none; }

    /* The title, the dot and the bar: on or off, changing evenly across each change-over. */
    @keyframes does-on-1 {
      0%, 22% { opacity: 1; }
      28%, 100% { opacity: var(--does-off, 0); }
    }
    @keyframes does-on-2 {
      0%, 22% { opacity: var(--does-off, 0); }
      28%, 46% { opacity: 1; }
      51%, 100% { opacity: var(--does-off, 0); }
    }
    @keyframes does-on-3 {
      0%, 46% { opacity: var(--does-off, 0); }
      51%, 71% { opacity: 1; }
      76%, 100% { opacity: var(--does-off, 0); }
    }
    @keyframes does-on-4 {
      0%, 71% { opacity: var(--does-off, 0); }
      76%, 100% { opacity: 1; }
    }

    /* The descriptions: the open one fades out at the start of a change-over, the items move
       in the middle of it (below), and the next one fades in at the end. */
    @keyframes does-note-1 {
      0%, 22% { opacity: 1; }
      24.4%, 100% { opacity: 0; }
    }
    @keyframes does-note-2 {
      0%, 25.6% { opacity: 0; }
      28%, 46% { opacity: 1; }
      48%, 100% { opacity: 0; }
    }
    @keyframes does-note-3 {
      0%, 49% { opacity: 0; }
      51%, 71% { opacity: 1; }
      73%, 100% { opacity: 0; }
    }
    @keyframes does-note-4 {
      0%, 74% { opacity: 0; }
      76%, 100% { opacity: 1; }
    }

    /* The panes: the old one is nearly gone before the new one is half there, so two
       exchanges are never readable on top of each other. */
    @keyframes does-pane-1 {
      0%, 22% { opacity: 1; }
      25.6%, 100% { opacity: 0; }
    }
    @keyframes does-pane-2 {
      0%, 24.4% { opacity: 0; }
      28%, 46% { opacity: 1; }
      49%, 100% { opacity: 0; }
    }
    @keyframes does-pane-3 {
      0%, 48% { opacity: 0; }
      51%, 71% { opacity: 1; }
      74%, 100% { opacity: 0; }
    }
    @keyframes does-pane-4 {
      0%, 73% { opacity: 0; }
      76%, 100% { opacity: 1; }
    }

    /* Items 2 to 4 and the rule under the list: each sits below the open description until
       its own turn comes, then moves up under the title above it. */
    @keyframes does-shift-2 {
      0%, 23.2% { transform: translateY(var(--does-o1)); }
      26.8%, 100% { transform: translateY(0); }
    }
    @keyframes does-shift-3 {
      0%, 23.2% { transform: translateY(var(--does-o1)); }
      26.8%, 47% { transform: translateY(var(--does-o2)); }
      50%, 100% { transform: translateY(0); }
    }
    @keyframes does-shift-4 {
      0%, 23.2% { transform: translateY(var(--does-o1)); }
      26.8%, 47% { transform: translateY(var(--does-o2)); }
      50%, 72% { transform: translateY(var(--does-o3)); }
      75%, 100% { transform: translateY(0); }
    }
    @keyframes does-shift-end {
      0%, 23.2% { transform: translateY(var(--does-o1)); }
      26.8%, 47% { transform: translateY(var(--does-o2)); }
      50%, 72% { transform: translateY(var(--does-o3)); }
      75%, 100% { transform: translateY(var(--does-o4)); }
    }
  }
}
