/* ==========================================================================
   Postprod Tools - "Rack"
   --------------------------------------------------------------------------
   A dark catalogue, not a brochure. The page is a shelf: a bright strip of
   type at the top, then rows of item tiles with the thing itself playing
   inside them. Everything is dense, everything is edged with a hairline, and
   exactly one colour is allowed to shout: amber #fbbf24 on the dark skin,
   deep blue #1e3a8a on the light one. Two hues, one job - each is the one
   that carries enough contrast against the ground it sits on. Both clear AA
   twice over, in both directions (amber 12.0:1, blue 10.4:1), because the
   accent is a button background as often as it is text.

   Dark is the default here; light is a second skin, not the other way round.
   ========================================================================== */

:root {
  --bg:      #08080a;
  --raise:   #0f0f13;
  --card:    #141419;
  --card-2:  #1c1c23;
  --line:    #26262f;
  --line-2:  #34343f;

  --tx:      #f6f6f8;
  --dim:     #a2a2b1;
  --mut:     #6e6e7c;

  --acc:     #fbbf24;          /* the only loud colour on the site */
  --acc-tx:  #0a0a0c;
  --acc-2:   #e0a013;          /* pressed and hover: the same amber, turned down */
  --acc-wash: rgba(251,191,36,.12);
  --dot:     rgba(255,255,255,.05);
  --warn:      #ff6b6b;
  --warn-wash: rgba(255,107,107,.10);
  --warn-line: rgba(255,107,107,.34);

  --sh:      0 18px 40px rgba(0,0,0,.45);
  --sh-sm:   0 6px 18px rgba(0,0,0,.32);

  --max: 1220px;
  --gut: 24px;
  --gap: 16px;
  --r-xs: 8px;
  --r-sm: 10px;
  --r:    14px;
  --r-lg: 18px;
  --pill: 999px;

  --font: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --display: var(--font);
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  color-scheme: dark;
}

[data-theme="light"] {
  --bg:      #ffffff;
  --raise:   #f2f2f4;
  --card:    #ffffff;
  --card-2:  #f7f7f9;
  --line:    #e4e4e9;
  --line-2:  #d4d4dc;

  --tx:      #0a0a0c;
  --dim:     #4d4d59;
  --mut:     #7b7b89;

  --acc:     #1e3a8a;
  --acc-tx:  #ffffff;
  --acc-2:   #16295f;          /* pressed and hover: the same blue, deeper */
  --acc-wash: rgba(30,58,138,.09);
  --dot:     rgba(0,0,0,.06);
  --warn:      #c0261f;
  --warn-wash: rgba(192,38,31,.06);
  --warn-line: rgba(192,38,31,.28);

  --sh:      0 14px 34px rgba(15,15,30,.10);
  --sh-sm:   0 4px 14px rgba(15,15,30,.07);

  color-scheme: light;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--tx);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  font-variation-settings: "wdth" 100;
  -webkit-font-smoothing: antialiased;
}
body.ready { transition: background .18s linear, color .18s linear; }

img, svg { max-width: 100%; }

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 var(--gut); }

section { padding: 74px 0; }
section + section { padding-top: 0; }

/* ---- type ---------------------------------------------------------------
   One family, worked hard: wide and heavy for headlines, plain for prose,
   and small caps with a lot of tracking for the labels that name a row.   */

h1, h2, h3 { margin: 0; font-family: var(--display); font-weight: 800; letter-spacing: -.035em; line-height: 1.02; }

h1 { font-size: clamp(42px, 7.2vw, 82px); font-variation-settings: "wdth" 108; text-transform: none; }
h2 { font-size: clamp(28px, 3.6vw, 42px); font-variation-settings: "wdth" 104; }
h3 { font-size: 17px; font-weight: 700; letter-spacing: -.015em; line-height: 1.3; }

p { margin: 0 0 16px; }

.eyebrow {
  margin: 0 0 14px; font-size: 11.5px; font-weight: 700; letter-spacing: .18em;
  text-transform: uppercase; color: var(--acc);
}
.lede { max-width: 58ch; font-size: clamp(16.5px, 1.5vw, 19px); line-height: 1.62; color: var(--dim); }
.sub  { max-width: 62ch; font-size: 16.5px; color: var(--dim); }
.small { font-size: 13.5px; color: var(--mut); }
.small a, .sub a, p a { color: var(--tx); text-decoration: none; box-shadow: 0 1px 0 var(--line-2); }
.small a:hover, .sub a:hover, p a:hover { color: var(--acc); box-shadow: 0 1px 0 var(--acc); }

.mute { color: var(--mut); }

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

.btn {
  display: inline-flex; align-items: center; gap: 9px;
  height: 48px; padding: 0 22px;
  border: 0; border-radius: var(--r-sm);
  background: var(--acc); color: var(--acc-tx);
  font: inherit; font-size: 15px; font-weight: 700; letter-spacing: -.01em;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: transform .12s ease, background .15s ease, box-shadow .15s ease;
}
.btn:hover { background: var(--acc-2); transform: translateY(-1px); }
.btn .ico { width: 16px; height: 16px; }

.btn-quiet {
  background: transparent; color: var(--tx);
  box-shadow: inset 0 0 0 1px var(--line-2);
}
.btn-quiet:hover { background: var(--card-2); box-shadow: inset 0 0 0 1px var(--tx); }

/* ---- small parts -------------------------------------------------------- */

.ico {
  width: 18px; height: 18px; flex: none; display: inline-block; vertical-align: -3px;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}

.badge {
  display: inline-flex; align-items: center; gap: 8px;
  height: 30px; padding: 0 13px 0 11px; margin: 0 0 22px;
  border-radius: var(--r-xs);
  background: var(--acc-wash); color: var(--acc);
  font-size: 11.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  text-decoration: none;
}
.badge .ico { width: 14px; height: 14px; }
.badge:hover { background: color-mix(in srgb, var(--acc) 22%, transparent); }

.tag {
  /* One line, always: the height is fixed, so a tag that wraps on a narrow
     shelf header spills out of its own box. */
  display: inline-flex; align-items: center; height: 24px; padding: 0 9px;
  flex: none; white-space: nowrap;
  border-radius: var(--r-xs); background: var(--card-2); color: var(--dim);
  font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}
.tag-live { background: var(--acc-wash); color: var(--acc); }

.itile {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: none;
  border-radius: var(--r-xs); background: var(--card-2); color: var(--acc);
  box-shadow: inset 0 0 0 1px var(--line);
}
.itile .ico { width: 18px; height: 18px; }
.pmark svg { width: 22px; height: 22px; }

/* ---- header -------------------------------------------------------------
   A flat catalogue bar, edge to edge, with a hairline under it. No floating
   capsule, no shadow: the shelf starts right under it.                     */

header {
  position: sticky; top: 0; z-index: 80;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid var(--line);
}
.bar {
  max-width: var(--max); margin: 0 auto; padding: 0 var(--gut);
  height: 62px; display: flex; align-items: center; gap: 26px;
}
.brand {
  display: inline-flex; align-items: center; gap: 10px;
  color: var(--tx); text-decoration: none;
  /* Set in caps, so the tight display tracking has to go the other way -
     capitals need air where lowercase needed none. */
  font-size: 14px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
  font-variation-settings: "wdth" 106;
}
.brand em { font-style: normal; color: var(--mut); }
.brand .mark { width: 22px; height: 22px; color: var(--tx); }

header nav { margin-left: auto; display: flex; align-items: center; gap: 6px; }
header nav a {
  padding: 8px 12px; border-radius: var(--r-xs);
  color: var(--dim); text-decoration: none;
  font-size: 13.5px; font-weight: 600; letter-spacing: -.005em;
}
header nav a:hover { color: var(--tx); background: var(--card-2); }
header nav a.on { color: var(--tx); }
header nav .btn { height: 38px; padding: 0 16px; font-size: 13.5px; margin-left: 6px; }

.theme {
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--r-xs); background: transparent; color: var(--mut);
  font-size: 15px; cursor: pointer;
}
/* The lit background is for a mouse only.
 *
 * A phone has no hover, so it fakes one: after a tap the :hover state sticks
 * until you touch somewhere else, and the button was left sitting in a grey
 * box. Safari adds its own grey flash on top of that. Both are switched off
 * here, so a tap on a phone changes the theme and nothing else. */
@media (hover: hover) and (pointer: fine) {
  .theme:hover { color: var(--tx); background: var(--card-2); }
}
.theme { -webkit-tap-highlight-color: transparent; }
.theme:focus { outline: none; }
/* Kept for the keyboard. :focus-visible fires for tab and not for a tap or a
   click, so this is the one ring that has to stay - without it the button is
   unreachable for anyone not using a mouse. */
.theme:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }

/* The icon is drawn rather than typed - see theme.js - so it takes the
   button's colour and never the font's. 17px sits it level with the nav. */
.theme .ico { width: 17px; height: 17px; vertical-align: 0; }

/* ---- hero ---------------------------------------------------------------
   Left aligned and wide. The old one was a centred column; this one starts
   at the gutter and runs, the way a catalogue front page does.            */

.hero { padding: 60px var(--gut) 34px; }
main > section:first-of-type { padding-top: 46px; }
.hero h1 { max-width: 15ch; }
.hero .lede { margin-top: 22px; }
.cta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 26px; }

.trust {
  display: flex; flex-wrap: wrap; gap: 8px 26px; margin: 24px 0 0;
  font-size: 13px; color: var(--mut);
}
.trust span { display: inline-flex; align-items: center; gap: 7px; }
.trust .ico { width: 14px; height: 14px; color: var(--acc); }

/* ---- the rail -----------------------------------------------------------
   The eleven tools, running past once like a shelf you scroll. Two copies of
   the same list, translated by exactly half, so the loop has no seam.      */

.rail { position: relative; overflow: hidden; padding: 6px 0 4px;
        border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
        background: var(--raise); }
.rail::before, .rail::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 90px; z-index: 2; pointer-events: none;
}
.rail::before { left: 0;  background: linear-gradient(90deg, var(--raise), transparent); }
.rail::after  { right: 0; background: linear-gradient(270deg, var(--raise), transparent); }

.railt { display: flex; gap: 10px; width: max-content; padding: 14px 0;
         animation: railRun 46s linear infinite; }
.rail:hover .railt { animation-play-state: paused; }
@keyframes railRun { to { transform: translateX(-50%); } }

.rtile {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 16px 11px 12px; border-radius: var(--r-sm);
  background: var(--card); box-shadow: inset 0 0 0 1px var(--line);
  color: var(--tx); text-decoration: none; white-space: nowrap;
}
.rtile:hover { box-shadow: inset 0 0 0 1px var(--acc); }
.rtile b { font-size: 14px; font-weight: 700; letter-spacing: -.015em; }
.rtile span { display: block; font-size: 12px; font-weight: 500; color: var(--mut); letter-spacing: 0; }
.rtile .itile { width: 32px; height: 32px; }
.rtile .itile .ico { width: 16px; height: 16px; }

/* ---- stat strip --------------------------------------------------------- */

.stats {
  display: grid; grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
  background: var(--card);
}
.stat { padding: 22px 24px; border-right: 1px solid var(--line); }
.stat:last-child { border-right: 0; }
.stat b {
  display: block; font-family: var(--display); font-size: 30px; font-weight: 800;
  letter-spacing: -.04em; font-variation-settings: "wdth" 106; line-height: 1;
}
.stat span { display: block; margin-top: 7px; font-size: 12.5px; color: var(--mut); }

/* ---- section heads ------------------------------------------------------ */

.grid-head { margin: 0 0 26px; display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; }
.grid-head > div { max-width: 64ch; }
/* 64ch is measured on the body font, which cuts a 42px h2 in half - so a
   heading meant to hold one line asks for the whole column. */
.grid-head.wide > div { max-width: none; }
.grid-head h2 { margin: 0; }
.grid-head .sub { margin: 12px 0 0; }
.grid-head .btn-quiet { flex: none; height: 40px; font-size: 13.5px; }

/* The contact heads: heading on one line, note on one line under it. The 64ch
   cap is measured on the body font, so it halves a 42px heading - dropped
   here, and the note goes below rather than beside so the heading has the
   whole column to sit on. Only from 1000px: below that a nowrap heading would
   run off the screen, so it wraps like any other. */
.chead { display: block; }
.chead > div { max-width: none; }
@media (min-width: 1000px) {
  .chead h2 { font-size: 40px; white-space: nowrap; }
  .chead .sub { max-width: none; white-space: nowrap; }
}

/* ---- plain card grid ---------------------------------------------------- */

.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); }
.grid.two { grid-template-columns: repeat(2, 1fr); }
.grid > * {
  background: var(--card); border-radius: var(--r); padding: 24px 22px;
  box-shadow: inset 0 0 0 1px var(--line);
}
.grid h3 { margin: 0; }
.grid p { margin: 10px 0 0; font-size: 14.5px; color: var(--dim); }
.cell-h { display: flex; align-items: center; gap: 12px; }

/* ---- item tiles ---------------------------------------------------------
   The catalogue itself. Media on top at a fixed ratio, a caption bar under
   it, and the copy below that - the shape of a stock item, because that is
   exactly what these are: eleven things you are buying.                    */

.items { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap); }

.itemc {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--card); border-radius: var(--r);
  box-shadow: inset 0 0 0 1px var(--line);
  transition: box-shadow .16s ease, transform .16s ease;
  scroll-margin-top: 84px;
}
.itemc:hover { box-shadow: inset 0 0 0 1px var(--line-2); transform: translateY(-2px); }

.itemc .demo { position: relative; }
.itemc .stage { border-radius: 0; box-shadow: none; border-bottom: 1px solid var(--line); }
/* Out of the picture and onto its own strip under it: a line describing the
   demo should not be sitting on top of the demo. */
.itemc .demo-cap {
  display: block; margin: 0; padding: 12px 22px 0;
  font-size: 12.5px; line-height: 1.5; color: var(--mut); text-align: left;
}
.itemc-b { padding: 20px 22px 24px; }
.itemc-h { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.itemc-h h3 { font-size: 20px; font-weight: 800; letter-spacing: -.03em; }
.fnum { margin-left: auto; font-family: var(--mono); font-size: 12px; color: var(--mut); }
.itemc p { margin: 0; font-size: 14.5px; color: var(--dim); }

.ticks { list-style: none; display: flex; flex-wrap: wrap; gap: 7px; margin: 16px 0 0; padding: 0; }
.ticks li {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: var(--pill);
  background: var(--card-2); color: var(--dim); font-size: 12.5px;
}
.ticks .ico { width: 12px; height: 12px; color: var(--acc); }

/* One tile carries a caveat rather than a feature: unnest cannot put a
   transition's own settings back. No box - the red says what a border would.
   The icon runs in the text rather than beside it, so every line starts on the
   same left edge as the pills above and the paragraph above them. */
/* .itemc p sets margin and size for every paragraph in a tile and is the more
   specific selector, so a bare .warn rule never applied - hence the caveat
   sitting on the pills at full body size. Named through .itemc to win. */
.itemc p.warn {
  display: block; margin: 15px 0 0;
  font-size: 11.5px; line-height: 1.75; color: var(--mut);
}
.warn .ico {
  width: 14px; height: 14px; margin-right: 7px;
  vertical-align: -2px; color: var(--warn);
}
/* The sentence that matters holds its size; the reasons behind it sit a step
   smaller and a step quieter. */
.itemc p.warn b { color: var(--warn); font-weight: 700; font-size: 13px; }

/* ---- chip index (the eleven, as a filter row) --------------------------- */

/* Eleven chips that wrap, at the size of a caption. No box around them, no
   numbers, no tiles behind the icons: the tiles below carry all of that, and
   this row only has to be a way in. */
.toc { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.tocl {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 12px; border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--line);
  text-decoration: none; color: var(--tx);
  font-size: 13px; font-weight: 600; letter-spacing: -.01em; line-height: 1;
  transition: background .14s, color .14s, box-shadow .14s;
}
.tocl .ico { width: 14px; height: 14px; color: var(--acc); }
.tocl:hover { background: var(--card); color: var(--acc); box-shadow: inset 0 0 0 1px var(--acc); }

/* ---- the app cards (home) ----------------------------------------------- */

/* Two panels, two cards, one row. Stacked full width they read as two
   announcements one after the other; side by side they read as the choice
   they are. auto-fit means a third panel would wrap rather than squeeze, and
   on a phone they go back to one under the other. */
.apps2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); gap: var(--gap); }
/* Equal height cards, so the two price lines sit level even though one blurb
   runs longer than the other. */
.apps2 .app { display: flex; flex-direction: column; }
.apps2 .app p { margin-bottom: 20px; }
.apps2 .app-foot { margin-top: auto; padding-top: 18px; }


.app {
  display: block; text-decoration: none; color: inherit;
  background: var(--card); border-radius: var(--r); padding: 24px;
  box-shadow: inset 0 0 0 1px var(--line);
}
.app:hover { box-shadow: inset 0 0 0 1px var(--acc); }
.app-top { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.app-top .left { display: flex; align-items: center; gap: 12px; }
.app-top h3 { font-size: 22px; font-weight: 800; }
.app p { margin: 16px 0 0; font-size: 15px; color: var(--dim); max-width: 70ch; }
.app-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line);
  font-size: 14px; color: var(--mut);
}
.app-price b { font-family: var(--display); font-size: 20px; font-weight: 800; letter-spacing: -.03em; color: var(--tx); }
.app-price s { color: var(--mut); margin-right: 4px; }
.app-go { display: inline-flex; align-items: center; gap: 7px; color: var(--acc); font-weight: 700; }

/* The door to the catalogue: one row, logos first so it is obvious what is
   behind it without reading a word. */
.hband { display: flex; align-items: center; gap: 20px; padding: 22px 26px;
         background: var(--card); border-radius: var(--r); text-decoration: none;
         box-shadow: inset 0 0 0 1px var(--line); transition: box-shadow .18s; }
.hband:hover { box-shadow: inset 0 0 0 1px var(--acc); }
.hband-logos { display: flex; gap: 8px; flex: none; }
.hband-logos .alogo { width: 38px; height: 38px; }
.hband-t { flex: 1; min-width: 0; }
.hband-t b { display: block; font-size: 18px; font-weight: 800; letter-spacing: -.02em; color: var(--tx); }
.hband-t span { display: block; margin-top: 4px; font-size: 14.5px; color: var(--dim); }
.hband .app-go { flex: none; }

/* ---- rows (apps, contact) ----------------------------------------------- */

.appnav { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.atab {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 7px 16px 7px 8px; border: 0; border-radius: var(--pill);
  background: var(--card); box-shadow: inset 0 0 0 1px var(--line);
  color: var(--dim); font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer;
}
.atab:hover { color: var(--tx); }
/* The chosen chip is highlighted, not filled: a flooded pill fights the
   application marks sitting inside it. */
.atab.on { background: var(--card); color: var(--tx); font-weight: 700;
           box-shadow: inset 0 0 0 1.5px var(--tx); }
.atab .alogo { width: 24px; height: 24px; }
.alogo { width: 44px; height: 44px; border-radius: 9px; display: block; flex: none; }
.alogo-pad { transform: scale(1.22); }
.alogo-all { display: inline-flex; align-items: center; justify-content: center; color: currentColor; }
.atab .alogo-all { width: 24px; height: 24px; }
.alogo-all .ico { width: 16px; height: 16px; }

/* ---- shelves ------------------------------------------------------------
   One card per application, always open. The head names the host and the
   state it is in; underneath sits either something you can buy or one honest
   line about why there is nothing there yet.                              */

/* Two shelves to a row: a shelf is a card about an application, not a banner,
   and at full width three of them turned the page into a stack of bars. */
.applist { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; align-items: start; }
.shelf { background: var(--card); border-radius: var(--r); box-shadow: inset 0 0 0 1px var(--line); overflow: hidden; }
.shelf[hidden] { display: none; }
.shelf-h { display: flex; align-items: center; gap: 14px; padding: 18px 22px; }
.shelf-n { min-width: 0; }
.shelf-n b { display: block; font-size: 16px; font-weight: 700; letter-spacing: -.02em; }
.shelf-n span { display: block; font-size: 13px; color: var(--mut); }
.shelf-h .tag { margin-left: auto; }
/* The caret only means anything while the shelves are drawers, which is the
   "Everything" chip and nothing else - on a single application there is one
   shelf and it stands open, so pointing at it would be a lie. */
.shelf-cv { display: none; }
.applist.acc .shelf-h { cursor: pointer; }
.applist.acc .shelf-cv {
  display: block; width: 8px; height: 8px; margin-left: 4px; flex: 0 0 auto;
  border-right: 2px solid var(--mut); border-bottom: 2px solid var(--mut);
  transform: rotate(45deg); transform-origin: 60% 60%;
  transition: transform .18s ease;
}
.applist.acc .shelf.open .shelf-cv { transform: rotate(-135deg); }
.applist.acc .shelf:not(.open) .shelf-b { display: none; }

.ar {
  display: flex; align-items: center; gap: 14px;
  padding: 18px 22px; border-top: 1px solid var(--line);
  color: inherit; text-decoration: none;
}
.ar:hover { background: var(--card-2); }
.ar-b { flex: 1 1 0; min-width: 0; }
.ar-b b { display: block; font-size: 16px; font-weight: 700; letter-spacing: -.02em; }
.ar-b span { display: block; font-size: 13.5px; color: var(--mut); }
.ar-r { margin-left: auto; display: inline-flex; align-items: center; gap: 8px;
        font-size: 13.5px; font-weight: 600; color: var(--dim); white-space: nowrap; }
.ar-r b { font-family: var(--display); font-size: 19px; font-weight: 800; letter-spacing: -.03em; color: var(--tx); }
.ar-r s { color: var(--mut); font-weight: 400; }
.ar:hover .ar-r { color: var(--acc); }
.ar .ico { width: 15px; height: 15px; }

/* A title that exists but is not out yet. The same box, with every affordance
   of a link taken back off it: no pointer, no hover lift, no arrow. It reads
   as a statement rather than a door, which is the honest thing for a row that
   has nowhere to go today. */
.ar-soon { cursor: default; }
.ar-soon .itile, .ar-soon .ar-b b { opacity: .82; }
.ar-soon .ar-b span { opacity: .72; }
.ar-soon:hover { background: transparent; }
.ar-soon .ar-r .tag { font-size: 12px; }

/* The contact lanes. On the apps page a row sits inside a shelf and a top
   border is the line between two of them; standing on their own in a two
   column grid those borders read as five broken table rules, and the odd
   fifth row leaves a hole. As cards they are five of a kind, and the empty
   sixth cell is simply the end of the list. */
.applist.cards { align-items: stretch; gap: var(--gap); }
.applist.cards .ar {
  border-top: 0; border-radius: var(--r);
  background: var(--card); box-shadow: inset 0 0 0 1px var(--line);
  align-items: flex-start;
}
.applist.cards .ar:hover { box-shadow: inset 0 0 0 1px var(--line-2); }
.applist.cards .ar-b { padding-right: 8px; }
.applist.cards .ar-r { align-self: center; }

.ar-empty {
  display: flex; align-items: center; gap: 20px;
  padding: 18px 22px; border-top: 1px solid var(--line);
  background: var(--card-2);
}
.ar-empty p { margin: 0; font-size: 14px; color: var(--mut); }
.ar-empty .btn { align-self: flex-start; height: 40px; font-size: 13.5px; }

/* Inside a half-width card the row stacks: name and blurb, then the price on
   its own line, so nothing is squeezed into a column two words wide. */
.shelf .ar { flex-wrap: wrap; }
.shelf .ar-r { flex-basis: 100%; margin: 12px 0 0 52px; }
.ar-empty { flex-direction: column; align-items: stretch; gap: 14px; }

/* One application chosen, one shelf on screen: the second column has nothing
   to hold, so the shelf takes the whole width instead of leaving half the page
   blank. Set by apps.js when it counts the visible shelves.

   With the room back the titles sit two to a row, one left one right, rather
   than one wide bar each - two products on a shelf read as a choice, and a
   full width bar reads as a heading. auto-fit is doing the work: a shelf with
   one thing on it, or one honest line about having nothing, collapses the
   empty track and takes the whole width back. */
.applist.solo { grid-template-columns: 1fr; }
/* Two across, always - not auto-fit. With auto-fit a shelf carrying three
   titles put all three in a row on a wide screen, so the Resolve shelf did not
   match the Premiere one beside it and the cards came out narrower the more we
   shipped. Fixed at two, an odd last title takes the full width rather than
   leaving a hole, and the shelves agree with each other whatever is on them. */
.applist.solo .shelf-b { display: grid; grid-template-columns: repeat(2, 1fr); }
.applist.solo .shelf-b > .ar:nth-child(even) { border-left: 1px solid var(--line); }
.applist.solo .shelf-b > .ar:last-child:nth-child(odd) { grid-column: 1 / -1; border-left: 0; }
@media (max-width: 720px) {
  .applist.solo .shelf-b { grid-template-columns: 1fr; }
  .applist.solo .shelf-b > .ar { border-left: 0; }
}

/* Shaped like the two cards on the home page, because they are the same two
   products and a visitor arriving from there should recognise them: mark and
   name on top, the blurb, then a rule with the price on the left and the way
   in on the right. The rows are the same height and the two price lines sit
   level, whichever blurb runs longer. */
.applist.solo .shelf-b > .ar {
  display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto 1fr;
  align-items: start; column-gap: 14px;
}
.applist.solo .shelf-b > .ar > .ar-r {
  grid-column: 1 / -1; align-self: end; width: 100%;
  margin: 18px 0 0; padding-top: 18px; border-top: 1px solid var(--line);
}
.applist.solo .shelf-b > .ar > .ar-r .ico { margin-left: auto; }

/* ---- buy ---------------------------------------------------------------- */

.buy { display: grid; grid-template-columns: 380px 1fr; gap: 20px; align-items: start; }
.price {
  padding: 28px 26px; border-radius: var(--r);
  background: var(--card); box-shadow: inset 0 0 0 1px var(--acc);
}
.price .tag { background: var(--acc); color: var(--acc-tx); margin-bottom: 16px; }
.amount {
  display: flex; align-items: baseline; gap: 10px;
  font-family: var(--display); font-size: 54px; font-weight: 800;
  letter-spacing: -.05em; font-variation-settings: "wdth" 108; line-height: 1;
}
.amount s { font-size: 22px; font-weight: 600; letter-spacing: -.02em; color: var(--mut); }
.amount small { font-size: 14px; font-weight: 600; letter-spacing: 0; color: var(--dim); }
.price ul { list-style: none; margin: 22px 0 24px; padding: 0; }
.price li { position: relative; padding: 7px 0 7px 22px; font-size: 14.5px; color: var(--dim);
            border-top: 1px solid var(--line); }
.price li:first-child { border-top: 0; }
.price li::before { content: ""; position: absolute; left: 4px; top: 16px; width: 6px; height: 6px;
                    border-radius: 2px; background: var(--acc); }
.price .btn { width: 100%; justify-content: center; }

.how { padding: 28px 26px; border-radius: var(--r); background: var(--card-2); }
.steps { list-style: none; counter-reset: s; margin: 20px 0 0; padding: 0; }
.steps li {
  counter-increment: s; position: relative; padding: 0 0 18px 46px; font-size: 15px; color: var(--dim);
}
.steps li:last-child { padding-bottom: 0; }
.steps li::before {
  content: counter(s, decimal-leading-zero); position: absolute; left: 0; top: -1px;
  font-family: var(--mono); font-size: 12px; color: var(--acc);
  width: 30px; height: 24px; display: flex; align-items: center;
}
.steps b { color: var(--tx); font-weight: 700; }

/* ---- pricing plans ------------------------------------------------------
   The same card as PILE's own buy box, three across, with the application it
   belongs to named at the top of each one.                                */

.plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start; }
.plans .price { padding: 26px 24px; }
.price.soon { box-shadow: inset 0 0 0 1px var(--line); }
.price.soon .amount { font-size: 30px; color: var(--mut); }
.price.soon li::before { background: var(--line-2); }
.plan-h { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.plan-h .alogo { width: 38px; height: 38px; }
.plan-n b { display: block; font-size: 17px; font-weight: 800; letter-spacing: -.025em; }
.plan-n span { display: block; font-size: 12.5px; color: var(--mut); }
.plans .btn { width: 100%; justify-content: center; }
/* One line, so the page name reads as a headline and not a paragraph. */
.phero h1 { max-width: none; font-size: min(6.2vw, 74px); }
.phero .lede { max-width: none; }
/* One line on a desktop width: the sentence is short enough for the 1220px
   wrap, and only the 58ch cap was breaking it. It still wraps on narrow
   screens, where nothing else would fit. */
.ahero .lede { max-width: none; }
.hhero h1 { max-width: 20ch; }
/* Same sentence, two lines instead of three: it is the 58ch cap doing it. */
.hhero .lede { max-width: 88ch; }
/* A break we choose, so the second sentence starts the second line. It is
   dropped on narrow screens, where the text has to wrap where it can. */
.wbr { display: none; }
@media (min-width: 861px) { .wbr { display: inline; } }

/* ---- questions ---------------------------------------------------------- */

.qas { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap); }
.qa { padding: 22px 24px; border-radius: var(--r); background: var(--card);
      box-shadow: inset 0 0 0 1px var(--line); }
.qa h3 { margin: 0 0 10px; font-size: 16.5px; }
.qa p { margin: 0; font-size: 14.5px; color: var(--dim); }
.qa a { color: var(--acc); text-decoration: none; }

/* ---- product page ------------------------------------------------------- */
/* The price used to sit at the bottom, after eleven demos. It belongs beside
   the first paragraph: what it is on the left, what it costs on the right. */
.hsplit { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, .82fr);
          gap: 40px; align-items: start; }
.pile-hero h1 { max-width: 16ch; }
.pile-hero .lede { max-width: 52ch; }
.pile-hero .trust { margin-top: 26px; }
.hsplit .price { margin: 6px 0 0; padding: 26px 24px; }
.hsplit .price .btn { width: 100%; justify-content: center; }
.price-note { margin: 14px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--mut); }

/* The closing band: the three steps on the left, one more way to buy on the
   right, so nobody has to scroll back up the page to find the button. */
.buy-end { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 34px;
           align-items: center; background: var(--card); border-radius: var(--r);
           padding: 30px 32px; box-shadow: inset 0 0 0 1px var(--line); }
.buy-end h2 { margin: 0 0 18px; font-size: 26px; }
.buy-end-a { display: grid; gap: 12px; justify-items: stretch; text-align: center;
             padding: 22px 20px; background: var(--card-2); border-radius: var(--r);
             box-shadow: inset 0 0 0 1px var(--line); }
.buy-end-a .amount { margin: 0; }
.buy-end-a .btn { width: 100%; justify-content: center; }
.buy-end-a .small { color: var(--mut); }

@media (max-width: 1000px) {
  .hsplit, .buy-end { grid-template-columns: 1fr; }
  .pile-hero h1, .pile-hero .lede { max-width: none; }
}

/* ---- legal -------------------------------------------------------------- */

/* .legal sits on a .wrap, and .wrap is what centres the page column. Narrowing
   .legal itself pulled the whole stack out of that column and hard against the
   window edge on a wide screen - so the width belongs on the stack inside it. */
.lstack { display: grid; gap: 10px; max-width: 860px; margin-inline: auto; }
.lhero { display: flex; flex-direction: column; align-items: flex-start;
         max-width: 860px; }
.lcard { background: var(--card); border-radius: var(--r); padding: 26px 28px;
         box-shadow: inset 0 0 0 1px var(--line); }
.lcard h2 { margin: 0 0 14px; font-size: 21px; letter-spacing: -.025em; }
.legal p, .legal li { font-size: 15.5px; line-height: 1.7; color: var(--dim); margin: 0 0 14px; }
.lcard > :last-child { margin-bottom: 0; }
.legal p b, .legal li b { color: var(--tx); font-weight: 700; }
.legal ul { margin: 0 0 14px; padding-left: 20px; }
.legal li { margin: 0 0 9px; }
.legal a { color: var(--acc); text-decoration: none; }
.legal a:hover { box-shadow: 0 1px 0 var(--acc); }
.legal .upd { max-width: 860px; margin-inline: auto; font-size: 13px; color: var(--mut); margin-top: 22px; }

/* ---- footer -------------------------------------------------------------
   Four columns, the way a catalogue signs off.                            */

footer { margin-top: 74px; border-top: 1px solid var(--line); background: var(--raise); }
footer .wrap { padding-top: 46px; padding-bottom: 34px; }
/* Four columns on one baseline: the brand lockup and the three column heads
   are all given the same 22px line box, so the first link in each column and
   the first line of the blurb start at exactly the same height. */
.fcols { display: grid; grid-template-columns: 1.5fr 3fr; gap: 30px; align-items: start; }
/* The three link columns are one object, so they can stay three columns on a
   phone - they are short enough to read side by side, and stacking them turned
   the footer into a screen of its own. */
.fstrip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px; align-items: start; }
.fcols .brand { line-height: 22px; }
.fcv { display: none; }
.fcol h4 { margin: 0 0 16px; height: 22px; display: flex; align-items: center;
           font-size: 11.5px; font-weight: 700; letter-spacing: .16em;
           text-transform: uppercase; color: var(--mut); }
/* :not(.brand) matters - the lockup is an <a> too, and a plain link rule was
   flattening it into a grey 14px block with its gap collapsed. */
.fcol a:not(.brand) { display: block; padding: 5px 0; color: var(--dim); text-decoration: none; font-size: 14px; line-height: 1.5; }
.fcol a:not(.brand):hover { color: var(--acc); }
.fcol p { margin: 16px 0 0; padding: 5px 0 0; font-size: 13.5px; line-height: 1.6; color: var(--mut); max-width: 34ch; }
.fbot {
  display: flex; flex-wrap: wrap; gap: 10px 24px; justify-content: space-between;
  margin-top: 34px; padding-top: 20px; border-top: 1px solid var(--line);
  font-size: 12.5px; color: var(--mut);
}
.foot-links { display: flex; flex-wrap: wrap; gap: 8px 18px; }
.fbot a { color: var(--mut); text-decoration: none; }
.fbot a:hover { color: var(--acc); }

/* ---- the demo stage ----------------------------------------------------- */

.stage {
  /* The demos borrow the accent for clips and the playhead. The site's
     shouting colour is not an editing application's, so inside a stage it is
     swapped for the blue-violet a timeline actually looks like. Left alone
     when the palette changed: this one is imitating Premiere, not branding. */
  --acc: #6e73e8;
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  background: var(--raise);
}
.stage::before {
  content: ""; position: absolute; inset: 0;
  background-image: radial-gradient(var(--dot) 1px, transparent 1px);
  background-size: 16px 16px;
}
.demo-cap { display: block; margin-top: 12px; font-size: 13px; color: var(--mut); }

/* ---- back to top -------------------------------------------------------- */
/* Every page is long enough to want one. It stays out of the way until there
   is something to go back up to, and it never covers the footer links. */
.totop {
  position: fixed; right: 22px; bottom: 22px; z-index: 90;
  width: 42px; height: 42px; display: grid; place-items: center;
  border: 0; border-radius: var(--pill); cursor: pointer;
  background: var(--card-2); color: var(--tx);
  box-shadow: inset 0 0 0 1px var(--line), 0 6px 20px rgba(0, 0, 0, .28);
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity .2s, transform .2s, visibility .2s, background .16s, color .16s;
}
.totop.on { opacity: 1; visibility: visible; transform: none; }
.totop:hover { background: var(--acc); color: var(--acc-tx); }
.totop .ico { width: 18px; height: 18px; }
@media (max-width: 560px) { .totop { right: 14px; bottom: 14px; } }

/* ---- reveal ------------------------------------------------------------- */

.js [data-reveal] { opacity: 0; transform: translateY(10px); }
.js [data-reveal].in {
  opacity: 1; transform: none;
  transition: opacity .5s ease var(--d, 0s), transform .5s cubic-bezier(.2,.7,.3,1) var(--d, 0s);
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important;
      transition-duration: .001ms !important; scroll-behavior: auto !important; }
  .js [data-reveal] { opacity: 1; transform: none; }
}

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

@media (max-width: 1000px) {
  .buy { grid-template-columns: 1fr; }
  .plans { grid-template-columns: 1fr; }
  .grid { grid-template-columns: repeat(2, 1fr); }
  .stats { grid-template-columns: repeat(2, 1fr); }
  .stat:nth-child(2) { border-right: 0; }
  .stat:nth-child(-n+2) { border-bottom: 1px solid var(--line); }
}

@media (max-width: 860px) {
  .items { grid-template-columns: 1fr; }
  .applist { grid-template-columns: 1fr; }
  .qas { grid-template-columns: 1fr; }
  .fcols { grid-template-columns: 1fr; gap: 26px; }
  /* On a phone the three columns become drawers, the same as the shelves on
     the apps page: three headings you can read at a glance, and one open at a
     time. Without scripts nothing carries the "acc" class and every link is
     simply there, stacked. */
  .fstrip { display: block; }
  .fstrip.acc .fcol { box-shadow: inset 0 -1px 0 var(--line); }
  .fstrip.acc .fcol h4 { margin: 0; padding: 15px 0; height: auto; cursor: pointer;
                         display: flex; align-items: center; }
  .fstrip.acc .fcol:not(.open) a { display: none; }
  .fstrip.acc .fcol.open h4 { padding-bottom: 6px; }
  .fstrip.acc .fcol.open a:last-child { padding-bottom: 14px; }
  .fstrip.acc .fcv {
    display: block; width: 7px; height: 7px; margin-left: auto;
    border-right: 2px solid var(--mut); border-bottom: 2px solid var(--mut);
    transform: rotate(45deg); transform-origin: 60% 60%; transition: transform .18s ease;
  }
  .fstrip.acc .fcol.open .fcv { transform: rotate(-135deg); }
}

@media (max-width: 980px) {
}

@media (max-width: 700px) {
  .hband { flex-wrap: wrap; gap: 14px; }
  .hband-t { flex-basis: 100%; }
  :root { --gut: 18px; }
  section { padding: 54px 0; }
  .hero { padding: 44px var(--gut) 34px; }
  .grid { grid-template-columns: 1fr; }
  .grid-head { flex-direction: column; align-items: flex-start; }
  header nav a { padding: 8px 8px; }
  footer { margin-top: 54px; }
}

@media (max-width: 560px) {
  header nav a:nth-child(3) { display: none; }
  .ar { flex-wrap: wrap; }
  .ar-r { flex-basis: 100%; margin-left: 0; padding-left: 52px; }
  /* A shelf header on a phone: the name wraps to two lines, so the tag hangs
     level with the name instead of floating in the middle of the block, and
     the icon sits at the top of the text rather than beside its centre. The
     price does the same - left with the words it belongs to, not adrift in
     the middle of the card. */
  .shelf-h { align-items: flex-start; }
  .shelf-h .tile, .shelf-h .itile { margin-top: 1px; }
  .shelf-h .tag { margin-top: 2px; }
  .shelf .ar { align-items: flex-start; }
  .shelf .ar-r { margin: 12px 0 0; padding-left: 52px; justify-content: flex-start; }
  .btn { height: 46px; }
  .cta .btn { flex: 1 1 auto; justify-content: center; }

}

@media (max-width: 460px) {
  /* The nav sheds a link at a time rather than all at once: PILE and the price
     are the two things anyone came for. */
  header nav a:nth-child(2) { display: none; }
  .amount { font-size: 44px; }
  .app-foot { flex-wrap: wrap; }
}

/* ---- the demos ---------------------------------------------------------- */
/* A lane is a track: the faint bed a clip sits in. */
.lane { position: absolute; left: 6%; right: 6%; height: 10%; border-radius: 3px;
        background: color-mix(in srgb, var(--tx) 5%, transparent); }

.cl {
  position: absolute; height: 10%; border-radius: 3px;
  background: color-mix(in srgb, var(--acc) 16%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--acc) 38%, transparent);
  display: flex; align-items: center; justify-content: center;
  font-size: 9px; color: var(--dim); overflow: hidden; white-space: nowrap;
}
.cl.aud { background: color-mix(in srgb, #17a67a 15%, var(--bg));
          border-color: color-mix(in srgb, #17a67a 38%, transparent); }
.cl.adj { background: color-mix(in srgb, var(--tx) 10%, var(--bg));
          border-color: color-mix(in srgb, var(--tx) 26%, transparent); }
.cl.nest{ background: color-mix(in srgb, #22a06b 20%, var(--bg));
          border-color: color-mix(in srgb, #22a06b 44%, transparent); }
.cl.bad { background: repeating-linear-gradient(45deg,
            color-mix(in srgb, #d9534f 16%, var(--bg)) 0 5px,
            color-mix(in srgb, #d9534f 6%, var(--bg)) 5px 10px);
          border-color: color-mix(in srgb, #d9534f 40%, transparent); }

/* The playhead, and the in/out band behind it. */
.head { position: absolute; top: 6%; bottom: 6%; width: 1.5px; background: var(--acc); opacity: .8; }
.band { position: absolute; top: 4%; bottom: 4%; border-left: 1.5px solid var(--acc);
        border-right: 1.5px solid var(--acc);
        background: color-mix(in srgb, var(--acc) 7%, transparent); }

/* A sequence marker. */
.pin { position: absolute; width: 9px; height: 9px; border-radius: 2px 2px 6px 6px;
       background: #d9534f; }

/* A little chip, for effect names. */
/* Phase labels sit in the bottom-right corner, over whatever the demo is
   doing, so they need to stay on top of the clips. */
.chip { position: absolute; z-index: 3; font-size: 8.5px; padding: 2px 7px; border-radius: 999px;
        background: var(--bg); border: 1px solid var(--line); color: var(--dim); }

/* --- the loop ------------------------------------------------------------
   Every utility runs the same four seconds and ends where it started, so a
   stage with six elements on five different delays still repeats seamlessly.
   Animations stay paused until the stage is actually on screen.            */

/* The loop used to run in 4s, which is long enough to see a move and too short
   to read it. Every keyframe is written in percentages, so the whole set slows
   down together; the per-element stagger is scaled by the same factor so it
   keeps its place in the cycle. */
.stage [class*="a-"] { animation-duration: 7.5s; animation-iteration-count: infinite;
                       animation-fill-mode: both;
                       animation-delay: calc(var(--d, 0s) * 1.875);
                       animation-play-state: paused; }
.stage.play [class*="a-"] { animation-play-state: running; }

@keyframes aIn   { 0%,8%   { opacity:0; transform:translateY(7px) scale(.97) }
                   20%,86% { opacity:1; transform:none }
                   100%    { opacity:0; transform:translateY(7px) scale(.97) } }
@keyframes aDrop { 0%,8%   { opacity:0; transform:translateY(-26px) }
                   24%,86% { opacity:1; transform:none }
                   100%    { opacity:0; transform:translateY(-26px) } }
@keyframes aWide { 0%,8%   { opacity:0; transform:scaleX(.06) }
                   26%,86% { opacity:1; transform:none }
                   100%    { opacity:0; transform:scaleX(.06) } }
@keyframes aPop  { 0%,10%  { opacity:0; transform:scale(.2) }
                   22%     { opacity:1; transform:scale(1.25) }
                   30%,86% { opacity:1; transform:scale(1) }
                   100%    { opacity:0; transform:scale(.2) } }
@keyframes aGo   { 0%,10%  { opacity:1; transform:none }
                   40%,100%{ opacity:0; transform:translateY(-10px) scale(.96) } }
@keyframes aSlide{ 0%,10%  { transform:none }
                   40%,86% { transform:translate(var(--x,0), var(--y,0)) }
                   100%    { transform:none } }
@keyframes aSweep{ 0%      { transform:translateX(0) }
                   80%,100%{ transform:translateX(var(--x,120%)) } }
@keyframes aBlink{ 0%,45%  { opacity:1 } 50%,95% { opacity:.25 } 100% { opacity:1 } }

.a-in    { animation-name: aIn; }
.a-drop  { animation-name: aDrop; }
.a-wide  { animation-name: aWide; transform-origin: left center; }
.a-pop   { animation-name: aPop; }
.a-go    { animation-name: aGo; }
.a-slide { animation-name: aSlide; }
.a-sweep { animation-name: aSweep; }
.a-blink { animation-name: aBlink; }

/* The rename demo's name strip - see .roll below. */

/*
 * With motion turned off, a demo has to hold a single readable frame - not all
 * of them at once. Forcing every animated element visible was doing exactly
 * that: the old clip name and the new one printed on top of each other, and the
 * nested sequence printed over the three clips it replaced.
 *
 * So the still frame is the *result* of each operation. What the demo adds
 * stays; what it removes, and the "before" half of any swap, is hidden.
 */
@media (prefers-reduced-motion: reduce) {
  .stage [class*="a-"] { animation: none !important; transform: none !important; }
  .rollt { animation: none !important; }
  .stage .a-in, .stage .a-drop, .stage .a-wide, .stage .a-pop,
  .stage .a-slide, .stage .a-sweep, .stage .a-blink, .stage .a-is,
  .stage .a-p1, .stage .a-q1 { opacity: 1 !important; }
  .stage .a-go, .stage .a-was, .stage .a-p2, .stage .a-p3,
  .stage .a-q2, .stage .a-q3, .stage .a-q4, .stage .a-q34 { opacity: 0 !important; }
  .js [data-reveal] { opacity: 1; transform: none; }
  .mark path { transition: none; }
}

/* Two-state swap, for the demos that show a before and an after. */
@keyframes aWas { 0%,42% { opacity:1 } 46%,96% { opacity:0 } 100% { opacity:1 } }
@keyframes aIs  { 0%,50% { opacity:0 } 54%,96% { opacity:1 } 100% { opacity:0 } }
/*
 * Three phases inside the one four-second loop, for a group whose buttons are
 * best understood against each other. Each phase holds for about a second with
 * a beat of clear air between them, so nothing is ever mid-fade against its
 * neighbour.
 */
@keyframes ph1 { 0%      { opacity:0; transform:translateY(5px) }
                 5%,28%  { opacity:1; transform:none }
                 33%,100%{ opacity:0; transform:translateY(5px) } }
@keyframes ph2 { 0%,33%  { opacity:0; transform:translateY(5px) }
                 38%,61% { opacity:1; transform:none }
                 66%,100%{ opacity:0; transform:translateY(5px) } }
@keyframes ph3 { 0%,66%  { opacity:0; transform:translateY(5px) }
                 71%,94% { opacity:1; transform:none }
                 99%,100%{ opacity:0; transform:translateY(5px) } }
/* Four beats rather than three, for the one demo that has a whole workflow to
   get through. Same loop, quartered. */
@keyframes q1  { 0%      { opacity:0; transform:translateY(5px) }
                 4%,21%  { opacity:1; transform:none }
                 25%,100%{ opacity:0; transform:translateY(5px) } }
@keyframes q2  { 0%,25%  { opacity:0; transform:translateY(5px) }
                 29%,46% { opacity:1; transform:none }
                 50%,100%{ opacity:0; transform:translateY(5px) } }
@keyframes q3  { 0%,50%  { opacity:0; transform:translateY(5px) }
                 54%,71% { opacity:1; transform:none }
                 75%,100%{ opacity:0; transform:translateY(5px) } }
@keyframes q4  { 0%,75%  { opacity:0; transform:translateY(5px) }
                 79%,96% { opacity:1; transform:none }
                 100%    { opacity:0; transform:translateY(5px) } }
/* The picture does not move again after Restack, so it holds across both. */
@keyframes q34 { 0%,50%  { opacity:0; transform:translateY(5px) }
                 54%,96% { opacity:1; transform:none }
                 100%    { opacity:0; transform:translateY(5px) } }
.a-q1 { animation-name: q1; } .a-q2 { animation-name: q2; }
.a-q3 { animation-name: q3; } .a-q4 { animation-name: q4; }
.a-q34 { animation-name: q34; }

/* Nine lanes in the same box as three: shorter rows, tighter gaps. Only the
   Sync demo needs it - it is the one showing a whole workflow. */
.stage.nine .lane, .stage.nine .cl { height: 7%; }
.stage.nine .cl { font-size: 8px; }

/* Switched off by Visibility: still in the timeline, plainly not playing. */
.cl.off { background: transparent; border-style: dashed;
          border-color: var(--line); color: var(--mut); opacity: .5; }

/* Muted audio: still there, plainly switched off. */
.cl.mute { background: color-mix(in srgb, var(--tx) 5%, var(--bg));
           border-color: var(--line); color: var(--mut); opacity: .75;
           text-decoration: line-through; }
.a-p1 { animation-name: ph1; }
.a-p2 { animation-name: ph2; }
.a-p3 { animation-name: ph3; }

.a-was { animation-name: aWas; }
.a-is  { animation-name: aIs; }

/* --- pieces the demos and the feature list need --------------------------- */

/* A colour swatch laid over a clip, for the Labels demo. */
.ov { position: absolute; inset: 0; border-radius: 2px; }

/* The Source Monitor stand-in, drawn dashed so it reads as "elsewhere". */
.srcbox { position: absolute; border: 1px dashed var(--line); border-radius: 8px;
          display: flex; align-items: center; justify-content: center;
          font-size: 8.5px; color: var(--mut); background: var(--bg); }

/*
 * Two names in one clip, shown one at a time.
 *
 * The clip is the window; the strip inside it is twice as tall and holds both
 * names stacked. Sliding it up by exactly one row swaps them. Because the
 * window clips, the second name is unreachable rather than merely transparent -
 * so a paused animation, a reduced-motion override or a stylesheet that has not
 * arrived yet all show one name, never two on top of each other.
 */
.roll { position: absolute; inset: 0; overflow: hidden; }
.rollt {
  position: absolute; left: 0; right: 0; top: 0; height: 200%;
  display: grid; grid-template-rows: 50% 50%;
  animation: rollUp 7.5s infinite both; animation-play-state: paused;
}
.stage.play .rollt { animation-play-state: running; }
.rollt b { display: flex; align-items: center; justify-content: center; font-weight: inherit; }

@keyframes rollUp { 0%,42% { transform: translateY(0) }
                    50%,96% { transform: translateY(-50%) }
                    100% { transform: translateY(0) } }

/* ---------------------------------------------------------------- checkout --
   The payment form sits in a card of its own, centred and narrow. Paddle's
   iframe brings its own padding and its own typography, so this draws only the
   frame around it: anything more and the two paddings stack into a card with a
   lot of air and a small form floating in the middle.

   min-height matches Paddle's frameInitialHeight, so the card is the right
   size before the form has loaded and the page does not jump when it does. */
/* Literal colours, not tokens: this card holds Paddle's iframe, and the iframe
   is painted from Paddle's Checkout Settings, which hold one set of colours for
   both themes. The card has to agree with the frame rather than with the page,
   or a dark site produces a dark card around a light form. See checkout.js. */
.pay {
  max-width: 560px;
  margin: 0 auto;
  background: #ffffff;
  border: 1px solid #e4e4e9;
  border-radius: var(--r-lg);
  padding: 18px;
  box-shadow: var(--sh);
}

/* The fallback text sits inside the white card in both themes, so it cannot
   take the page's foreground colour either. */
.pay .pay-fall, .pay .pay-fall a { color: #4d4d59; }

.checkout-container { min-height: 450px; }
.checkout-container iframe { display: block; }

.pay-fall { margin: 0; padding: 8px 4px; text-align: center; }
.pay-fall[hidden] { display: none; }

@media (max-width: 620px) { .pay { padding: 12px; border-radius: var(--r); } }

/* ---- the free-download form on /retrack ----
   Hidden until getfree.js adds .on, so a browser that never ran the script
   never shows a field that cannot post anywhere. The plain download link
   underneath is what those readers get instead. */
.getfree { display: none; margin-top: 10px; }
.getfree.on { display: block; }
.getfree.on + .getfree-fallback { display: none; }
.getfree input {
  width: 100%; box-sizing: border-box; height: 44px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card-2); color: var(--tx);
  font: inherit; font-size: 14.5px;
}
.getfree input:focus { outline: none; border-color: var(--acc); }
.getfree .btn { width: 100%; margin-top: 8px; }
.getfree.sent input, .getfree.sent .btn { display: none; }
.getfree-msg { margin: 8px 0 0; font-size: 13px; line-height: 1.5; min-height: 1px; }
.getfree-msg.good { color: var(--dim); }
.getfree-msg.bad { color: var(--warn); }
/* A label a screen reader announces and nobody else sees. The field has a
   placeholder, and a placeholder is not a label - it disappears the moment
   somebody starts typing. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- the film frame -------------------------------------------------------
   A placeholder that holds the exact space the demo will take, so the page
   does not jump on the day the video lands. 16:9 through aspect-ratio, with a
   padding-top fallback for anything that does not have it.

   Deliberately quiet: it is an empty rectangle on a page that already sells,
   so it announces itself once, in one small amber word, and otherwise stays
   out of the way. A big play button on a video that does not exist yet is a
   promise the page cannot keep. */
.filmwrap { max-width: 880px; margin: 0 auto; }

.filmframe {
  position: relative;
  aspect-ratio: 16 / 9;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  text-align: center;
  padding: 32px 28px;
  border: 1px dashed var(--line-2);
  border-radius: var(--r);
  background: var(--card);
  background-image: radial-gradient(var(--dot) 1px, transparent 1px);
  background-size: 22px 22px;
}

/* aspect-ratio is well supported now; this only catches an old browser, and
   costs one rule to stop the frame collapsing to nothing there. */
@supports not (aspect-ratio: 16 / 9) {
  .filmframe { height: 0; padding-top: 56.25%; }
}

.filmbadge {
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--acc);
  background: var(--acc-wash);
  border-radius: var(--pill);
  padding: 5px 12px;
}

.filmtitle {
  margin: 6px 0 0;
  font-size: clamp(20px, 3.4vw, 28px);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--tx);
  text-wrap: balance;
}

.filmnote {
  margin: 0;
  max-width: 46ch;
  font-size: 15px;
  line-height: 1.6;
  color: var(--mut);
  text-wrap: pretty;
}

@media (max-width: 640px) {
  .filmframe { padding: 22px 18px; gap: 8px; }
  .filmnote { font-size: 14px; }
}

/* ---- shots ----------------------------------------------------------------
   The palette card is the one product on this site whose output is a picture,
   so it is the one page that carries photographs. Every file under /shots is
   real output from the .dctl, rendered by SOURCE/TOOLS/make_shots.py in the
   DCTL repo - not a mock-up, and not a screenshot of a good day. A drawn
   approximation of a colour tool would be a picture of a claim, and the first
   customer to install it would find out.

   The frame is the same hairline card as everything else on the site; the
   caption sits under a rule, in the muted grey, because the picture is the
   argument and the caption is only the label on it. */
.shot {
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--card);
}
.shot img {
  display: block;
  width: 100%;
  height: auto;
  /* The renders are flat colour with hard edges; a browser smoothing them on
     the way down softens exactly the boundaries the picture is about. */
  image-rendering: -webkit-optimize-contrast;
}
.shot figcaption {
  padding: 12px 16px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.55;
  color: var(--mut);
}
.shot figcaption b { color: var(--dim); font-weight: 600; }
.shot figcaption code {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--acc);
}

/* Three cards abreast: the same shot cut for three deliveries. */
.shotrow { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); }
/* Two abreast: a pair being compared, where three would be a set. The cards
   are not forced to a common height - the shaped cards are different shapes,
   and stretching them to match would undo the thing the row is showing. */
.shotrow.two { grid-template-columns: repeat(2, 1fr); }
.shotrow { align-items: start; }

/* The three shapes are three different shapes, which is the point of the row -
   and it is also what breaks it. Equal-width thirds plus width:100% makes the
   1:1 three times taller than the 16:9, so the pictures stagger and no two
   captions start at the same line. Size these by HEIGHT instead: one scale for
   all three, each centred in its own third. The swatch rows then draw the same
   size on screen as they do on the card, which is the claim the section makes. */
.shotrow.shapes { align-items: stretch; }
.shotrow.shapes .shot { display: flex; flex-direction: column; }
.shotrow.shapes .shot img {
  height: 260px;
  width: auto;
  max-width: 100%;
  margin: 0 auto;
  object-fit: contain;
}
/* Pushes every caption to the bottom, so a two-line one does not drag its
   picture up out of line with the other two. */
.shotrow.shapes .shot figcaption { margin-top: auto; }

@media (max-width: 700px) {
  /* One per row: the height cap has nothing left to equalise and only makes
     the picture smaller than the column it is sitting in. */
  .shotrow.shapes .shot img { height: auto; width: 100%; }
}

/* One feature: the picture on one side, what it is for on the other. .flip
   puts the picture on the right, so a run of them alternates down the page
   instead of marching. Below 900px the two columns become one and .flip has
   nothing left to say - the picture goes first every time, which is the order
   that reads. */
.frow {
  display: grid;
  grid-template-columns: minmax(0, 1.04fr) minmax(0, 1fr);
  gap: 34px;
  align-items: center;
  margin: 0 0 42px;
}
.frow:last-child { margin-bottom: 0; }
.frow.flip > .shot { order: 2; }
.frow-t h3 {
  margin: 0;
  font-size: clamp(21px, 2.6vw, 26px);
  font-weight: 800;
  letter-spacing: -.03em;
}
.frow-t .eyebrow { margin: 0 0 10px; }
.frow-t p { margin: 12px 0 0; font-size: 15px; line-height: 1.65; color: var(--dim); }
.frow-t .ticks { margin-top: 18px; }

@media (max-width: 900px) {
  .frow { grid-template-columns: 1fr; gap: 20px; margin-bottom: 34px; }
  .frow.flip > .shot { order: 0; }
}
@media (max-width: 700px) {
  .shotrow, .shotrow.two { grid-template-columns: 1fr; }
}
