/* ---- Crossing between editions ----
   Each edition is its own page, so switching is a real navigation.
   It used to look like one, a flash, the board redrawn and the panel
   rebuilt. The three things that made it so are fixed where they
   live:

     The page was shown before its scripts had run, so the panel was
     built a moment after arrival. Both scripts now hold the first
     frame (wire-demo-panel.py puts them in the head).
     The panel replayed its opening fade on arrival, because the
     no-anim rule below never matched the root it was put on.
     The board's content rose in as on a first visit. On arrival from
     another edition those entrances are finished before the first
     frame (tour-panel.js, Start).

   So the new page's first frame is already the finished page, and
   the switch should be one clean swap with nothing in between. A
   browser does not promise that on its own: Chrome keeps the old
   page up until the new one paints, but other Chromium browsers,
   Dia among them, show a white page in between, and on every switch
   that was a white flash.

   A cross-document view transition is the part of the platform that
   does promise it: the old frame is held until the new page is ready
   to show. That is ALL it is used for here. Nothing animates, so the
   transition ends the moment it begins. It used to run a 260ms
   cross-fade, and while a transition plays the browser sends every
   click to the page's root element, whatever the stylesheet asks: a
   reader pressing Free, Pro and Studio in quick succession lost those
   presses, and the panel read them as a tap outside it and closed.
   With no animation there is no such window. */
@view-transition { navigation: auto; }

::view-transition-group(*),
::view-transition-image-pair(*),
::view-transition-old(*),
::view-transition-new(*) { animation: none; }

/* ============================================================
   THE TOUR PANEL, on the real demo pages.

   Lifted from the approved mockup at
   prototypes/tiers-2026-08-25/1f.html. Everything a visitor can
   see is that file's: the same glass, the same morph, the same
   named timings, the pricing card's rows, the same switcher geometry,
   the same reserved badge row. Nothing here is a redesign.

   WHAT THE MOCKUP DID NOT HAVE TO SOLVE, AND THIS DOES

   1. The mockup was a fake shell it owned outright. Here the
      panel is a layer over a real, responsive product, so every
      class is scoped inside .dtp and the only rules reaching
      outside it are the ones that make the room: .main's margin,
      and the sidebar's height while the band is down.

   2. The mockup switched edition in place. Here each edition is
      a separate page and the switcher is a set of links, so the
      panel has no in-page tier animation to drive: the ladder
      arrives already at this page's edition, and the wave of
      rows coming to life is replayed on arrival by the script.

   3. The mockup's shell scrolled as a page when it went narrow,
      and the band lay in that flow with its head sticky. The real
      page does not scroll at all: .app is one viewport tall and
      .main-scroll scrolls inside it. So the band is pinned to the
      bottom of the viewport, capped, and is its own scroller. Its
      head stays sticky and opaque, for the reason the mockup
      gives, and at every width with room for two columns the band
      fits and nothing scrolls at all.

   4. The drawer and the submission modal each own the screen
      while they are up, so the object steps back behind them
      rather than floating over them, and comes back as it was.
   ============================================================ */

:root {
  /* The unfold, and its quicker reverse. Exits are always faster
     than entrances: the reader has already decided. */
  --open-dur: 340ms;

  /* The morph's timing, split per property so the axes need not
     move in lockstep. The pane travels and grows at once, and
     each axis gets the time its own distance deserves. Height
     runs longest. The corner resolves early, so the shape reads
     as a panel before it has finished becoming one. The content
     waits for the pane to be nearly still. And the height starts
     30ms after the travel, which is below the threshold of being
     seen as a pause and above the threshold of being felt as
     weight. Carried across verbatim. */
  --ease-geo: cubic-bezier(0.22, 0.68, 0, 1);
  --dur-pos: 460ms;
  --dur-w: 440ms;
  --dur-h: 560ms;
  --dur-h-delay: 30ms;
  --dur-r: 320ms;
  --dur-light: 340ms;
  --delay-light: 120ms;
  --stagger-1: 210ms;
  --stagger-2: 290ms;
  --stagger-3: 350ms;
  --dur-content: 260ms;
  --dur-content-move: 380ms;
  --close-dur: 230ms;
}

/* ---- The pill's ground, taken verbatim from the tour pill ---- */
[data-theme="light"] {
  --o-glass: rgba(20, 24, 29, 0.86);
  --o-rim: rgba(226, 235, 255, 0.10);
  --o-blur: blur(14px) saturate(1.4);
  --o-cast:
    0 2px 8px rgba(0, 0, 0, 0.22),
    0 14px 38px -10px rgba(0, 0, 0, 0.50),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);

  /* ---- The panel's ground: light frosted glass ----
     Three things carry "this is a layer on top of the software",
     and on a light app they have to do it without the help of a
     dark fill. The blur, so the board is faintly legible through
     it. The rim, a lit inner edge plus a hairline outer one, so
     the pane has a thickness. And a deep cast, so the app is
     underneath rather than beside. */
  --p-glass: rgba(255, 255, 255, 0.60);
  --p-rim: rgba(255, 255, 255, 0.95);
  --p-blur: blur(26px) saturate(1.8);
  /* Four terms, and each one is doing a job the others cannot.
     A 2px contact shadow, so the pane touches the page. A 22px
     ambient one, so it has bulk. A 90px pool, so the software is
     a long way underneath. And a 1px outer hairline, because
     where the pane crosses empty canvas the shadows alone leave
     no edge at all, and an edge is what a sheet of glass has. */
  --p-cast:
    0 2px 4px rgba(21, 27, 40, 0.07),
    0 8px 22px rgba(21, 27, 40, 0.09),
    0 44px 90px -24px rgba(21, 27, 40, 0.50),
    0 0 0 1px rgba(20, 24, 33, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 1);
  /* The same glass already composited over the page canvas, for the
     sticky head in the lying-down layout, which has to be opaque and
     must not show a seam. */
  --p-solid: #fcfcfd;

  --p-ink: #191c22;
  --p-ink-2: #575d68;
  --p-ink-3: #7c828d;
  /* Greyed, but still above 4:1 against the glass. This list is
     what buying gets you, so the half of it you have not bought
     yet has to be readable, not decorative. */
  --p-dim: #767c85;
  --p-accent: #44586b;
  --p-on-accent: #ffffff;
  --p-tint: rgba(68, 88, 107, 0.13);
  --p-track-in: rgba(20, 24, 33, 0.06);
  --p-thumb-cast: 0 1px 2px rgba(20, 24, 33, 0.10);
  --p-hover: rgba(20, 24, 33, 0.05);
  --p-thumb: #ffffff;
  --p-thumb-ink: #14181d;
  /* The ghost button's outline and ground. The pricing card's own
     ghost is a hairline on the page canvas; on glass a hairline
     alone leaves the button with no body at all, so it gets a
     faint fill as well as the outline. */
  --p-line: rgba(20, 24, 33, 0.14);
  --p-btn: rgba(255, 255, 255, 0.72);
  --p-btn-hover: rgba(255, 255, 255, 0.92);

  --p-tag: #7d8694;
}

[data-theme="dark"] {
  /* The pill lifts to a lighter slate on a dark page rather than
     going darker, so it still reads as laid ON the software. */
  --o-glass: rgba(48, 55, 66, 0.92);
  --o-rim: rgba(226, 235, 255, 0.10);
  --o-blur: blur(14px) saturate(1.4);
  --o-cast:
    0 2px 8px rgba(0, 0, 0, 0.30),
    0 14px 38px -10px rgba(0, 0, 0, 0.60),
    inset 0 1px 0 rgba(255, 255, 255, 0.07);

  /* Dark glass, and thinner than the pill's, because a pane this
     large at the pill's opacity stops being glass and becomes a
     slab. */
  --p-glass: rgba(33, 38, 47, 0.72);
  --p-rim: rgba(226, 235, 255, 0.14);
  --p-blur: blur(24px) saturate(1.6);
  --p-cast:
    0 2px 8px rgba(0, 0, 0, 0.34),
    0 36px 76px -26px rgba(0, 0, 0, 0.66),
    0 0 0 1px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
  --p-solid: #1d2129;

  --p-ink: #edeff3;
  --p-ink-2: #a7aeb9;
  --p-ink-3: #868d98;
  --p-dim: #858c96;
  --p-accent: #9eb5c8;
  --p-on-accent: #14181d;
  --p-tint: rgba(158, 181, 200, 0.16);
  --p-track-in: rgba(0, 0, 0, 0.30);
  --p-thumb-cast: 0 1px 2px rgba(0, 0, 0, 0.22);
  --p-hover: rgba(226, 235, 255, 0.06);
  --p-thumb: #e9edf2;
  --p-thumb-ink: #14181d;
  --p-line: rgba(226, 235, 255, 0.18);
  --p-btn: rgba(226, 235, 255, 0.07);
  --p-btn-hover: rgba(226, 235, 255, 0.13);

  --p-tag: #8b93a1;
}

/* ============ The object's own layer ============
   One fixed sheet across the viewport, transparent to the mouse,
   holding the three layers that belong to one thing: the glass
   that travels, grows and lightens, and the two contents that
   cross over on top of it. The veil under the resting pill is
   the page's own .dt-veil, which is already the right shape at
   every width, so it is borrowed rather than rebuilt.

   It sits above .app rather than inside it. .app opens a
   stacking context of its own, so anything inside it, the
   sidebar included, is sealed underneath. */
.dtp {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  opacity: 1;
  transition: opacity 200ms var(--ease-out);
}

/* The page's own reset already covers all of this, and every one of
   these lines is a belt to its braces rather than a change. They are
   written through :where() so they carry no specificity at all: a
   type selector that outranks a class is how a local reset silently
   eats the design's own margins and padding, and this one is aimed
   at exactly the elements the panel is built from. */
.dtp, .dtp *, .dtp *::before, .dtp *::after { box-sizing: border-box; }
:where(.dtp) :where(h3, p, ul, li, div, span, nav, section, aside, b, i, s, em) { margin: 0; padding: 0; }
:where(.dtp) :where(ul) { list-style: none; }
:where(.dtp) :where(a) { color: inherit; text-decoration: none; }
:where(.dtp) :where(button) { font-family: inherit; font-size: inherit; cursor: pointer; border: none; background: none; color: inherit; }

.dtp .sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Used for one frame at a time, whenever a measurement has to be
   taken at the target layout without the reader ever seeing it.

   The script puts the class on the root, the .dtp element itself,
   far more often than on anything inside it, and until 2026-09-25
   only the descendant form was here, so every one of those calls
   did nothing. On a fast connection that was invisible. On a slow
   one the panel arriving from another edition played its whole
   opening fade from blank, which is most of why switching edition
   felt like a reload. */
.dtp.no-anim, .dtp.no-anim *, .dtp.no-anim *::before, .dtp.no-anim *::after,
.dtp .no-anim, .dtp .no-anim *, .dtp .no-anim *::before, .dtp .no-anim *::after {
  transition: none !important;
  animation: none !important;
}

/* The script is what makes the object real, so it is the script
   that retires the plain pill. Without it the page keeps the
   chrome it shipped with and the tour still works. */
html.dtp-on .dt-pill { display: none; }

/* ---- The fade, which comes back ----
   At rest something floats over the reading area, so the page's
   fade stays. Open, the panel is the thing on top and the fade
   has nothing left to do. */
.dt-veil {
  opacity: 1;
  transition: opacity 220ms var(--ease-out) 60ms;
}
body[data-dtp-panel="open"] .dt-veil {
  opacity: 0;
  transition: opacity 140ms var(--ease-out);
}

/* ============ THE GLASS ============
   One childless box. It holds the ground, the blur, the rim and
   the casts, and nothing else, so it can change shape without
   dragging any content through the change with it. JavaScript
   measures whichever content layer is active and gives the glass
   that box. Everything else here is content laid on top of it. */

.o-ground {
  position: absolute;
  z-index: 1;
  left: 0; top: 0; width: 0; height: 0;
  border-radius: 999px;
  pointer-events: none;
  background: var(--o-glass);
  -webkit-backdrop-filter: var(--o-blur);
  backdrop-filter: var(--o-blur);
  border: 1px solid var(--o-rim);
  box-shadow: var(--o-cast);
  transition:
    left var(--dur-pos) var(--ease-geo),
    top var(--dur-pos) var(--ease-geo),
    width var(--dur-w) var(--ease-geo),
    height var(--dur-h) var(--ease-geo) var(--dur-h-delay),
    border-radius var(--dur-r) var(--ease-geo),
    /* The lighting change runs behind the geometry, not with it.
       Delayed 120ms it is still dark while the pill's own chips
       are fading out over the first 110ms, and it has arrived at
       the panel's glass by the time the manifest has faded in. So
       the pane never shows a colour the content on it was not
       drawn for. */
    background-color var(--dur-light) var(--ease-out) var(--delay-light),
    border-color var(--dur-light) var(--ease-out) var(--delay-light),
    box-shadow calc(var(--dur-light) + 40ms) var(--ease-out) 40ms,
    -webkit-backdrop-filter var(--dur-light) var(--ease-out) var(--delay-light),
    backdrop-filter var(--dur-light) var(--ease-out) var(--delay-light);
}

/* Open, the pane is the panel's glass: lighter, more blurred, and
   sitting further off the page, because a sheet that tall has to
   cast further than a pill does. */
body[data-dtp-panel="open"] .o-ground {
  background: var(--p-glass);
  border-color: var(--p-rim);
  box-shadow: var(--p-cast);
  -webkit-backdrop-filter: var(--p-blur);
  backdrop-filter: var(--p-blur);
}

body[data-dtp-panel="closed"] .o-ground {
  transition-duration: var(--close-dur), var(--close-dur), var(--close-dur), var(--close-dur), var(--close-dur), 180ms, 180ms, 200ms, 180ms, 180ms;
  transition-delay: 0s;
}

/* ============ THE PILL, AT REST ============
   The tour's own pill, reproduced: same gaps, same padding, same
   chips, same slate Pricing button. It keeps its dark glass in
   both themes. A pill this small is the only thing on screen
   claiming to be a separate object, and on a white board a light
   one would read as a piece of the app's own chrome. The panel
   can afford to be light because it is large enough to prove
   itself with blur, rim and cast. The pill is not. */

.o-pill {
  position: absolute;
  z-index: 3;
  pointer-events: auto;
  /* Centred by auto margins across the full width rather than by
     left:50% and a translate, which would cap the pill at half
     the viewport and wrap it into ragged rows. */
  left: 0;
  right: 0;
  bottom: 48px;
  width: fit-content;
  margin-inline: auto;
  max-width: calc(100% - 32px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 4px;
  /* Asymmetric on purpose. Every chip sits 5px inside the surface,
     which is right along the straight run and wrong at the ends: a
     pill's cap curves away, so 5px of clearance measured radially
     reads as none at all where the curve is tightest. The left end
     gets away with it because DEMO is bare text and its own 12px of
     inner padding does the work. The right end is a filled block, so
     it needs the room in the padding instead, or Pricing reads as
     falling off the edge of the surface it is sitting on. */
  padding: 5px 12px 5px 5px;
  border-radius: 999px;
  opacity: 1;
  scale: 1;
  filter: blur(0px);
  visibility: visible;
  transition:
    opacity 170ms var(--ease-out) 90ms,
    scale 170ms var(--ease-out) 90ms,
    filter 170ms var(--ease-out) 90ms,
    visibility 0s;
}

/* Out fast, and barely moving. The reader's eye should be on the
   glass travelling, not on five chips performing. */
body[data-dtp-panel="open"] .o-pill {
  opacity: 0;
  scale: 0.97;
  filter: blur(3px);
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity 110ms var(--ease-out),
    scale 110ms var(--ease-out),
    filter 110ms var(--ease-out),
    visibility 0s linear 110ms;
}

.o-pill :focus-visible { outline-color: rgba(244, 246, 249, 0.85); }

/* Every page of the tour is invented: invented companies,
   invented people, invented testimonials. A visitor has to be
   able to see that, and this is the only place it is said. */
.p-tag {
  flex: none;
  padding: 0 10px 0 12px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--p-tag);
  white-space: nowrap;
}

.p-opt {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 8px 15px;
  border-radius: 999px;
  white-space: nowrap;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: #b9c0cc;
  transition: background-color 180ms var(--ease-out), color 180ms var(--ease-out);
}
[data-theme="dark"] .p-opt { color: #c4cbd6; }
.p-opt:hover { background: rgba(226, 235, 255, 0.10); color: #fff; }
.p-opt[aria-current="true"] { background: #f4f6f9; color: #14181d; }

/* The views a Studio buyer gets, which is the tier's whole
   argument. They appear inside the same pill only while Studio is
   the edition being shown, so the pill carries them through
   unchanged rather than the panel deciding they exist. */
.p-view {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 8px 13px;
  border-radius: 999px;
  white-space: nowrap;
  font-size: 12.5px;
  font-weight: 550;
  color: #97a0ad;
  transition: background-color 180ms var(--ease-out), color 180ms var(--ease-out);
}
[data-theme="dark"] .p-view { color: #a7b0bd; }
.p-view:hover { background: rgba(226, 235, 255, 0.10); color: #fff; }
.p-view[aria-current="true"] { background: rgba(226, 235, 255, 0.14); color: #fff; }

/* 3px each side was set when a run of view chips followed the divider.
   With Pricing straight after it, and the selected chip straight
   before it on the Studio board, three solid blocks ran together. */
.p-sep { width: 1px; align-self: stretch; margin: 4px 8px; background: rgba(226, 235, 255, 0.16); }

.p-buy {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 8px 15px;
  border-radius: 999px;
  white-space: nowrap;
  font-size: 13px;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: #fff;
  background: #44586b;
  transition: filter 180ms var(--ease-out);
}
.p-buy:hover { filter: brightness(1.18); }

/* ---- The control that opens it ----
   It sits after the pill's existing divider, with Pricing, not
   with the three editions. On that side of the line the pill is
   about finding out and buying, which is what this does. It
   carries a word, not just a chevron, because someone who lands
   on one edition and never touches the switcher has only this to
   go on. */
.o-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 36px;
  padding: 8px 13px;
  border-radius: 999px;
  white-space: nowrap;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: #cbd2dd;
  background: rgba(226, 235, 255, 0.10);
  transition: background-color 180ms var(--ease-out), color 180ms var(--ease-out), scale 140ms var(--ease-out);
}
.o-toggle:hover { background: rgba(226, 235, 255, 0.18); color: #fff; }
.o-toggle:active { scale: 0.96; }
.o-toggle svg { width: 10px; height: 6px; flex: none; opacity: 0.8; }
.o-toggle .short { display: none; }

/* ============ THE PANEL ============
   Full height down the right, so the app below it is a whole app
   and not a two-column layout with a marketing column in it.
   Everything interactive inside is a 999px pill, because that is
   the radius the tour already owns and the app never uses. */

.overlay {
  position: absolute;
  z-index: 2;
  pointer-events: auto;
  top: 22px;
  right: 22px;
  bottom: 22px;
  width: var(--fit-w, 316px);
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 15px 15px 13px;
  border-radius: 24px;
  color: var(--p-ink);
  visibility: hidden;
  transition: visibility 0s linear 130ms;
}

body[data-dtp-panel="open"] .overlay {
  visibility: visible;
  transition-delay: 0s;
}

/* Split and staggered, never one block sliding in. The head
   arrives first because it is what the pill just was, then the
   list it was hiding, then the thing to do about it. */
.overlay > * {
  opacity: 0;
  translate: 0 10px;
  transition:
    opacity 120ms var(--ease-out),
    translate 120ms var(--ease-out);
}

body[data-dtp-panel="open"] .overlay > * {
  opacity: 1;
  translate: 0 0;
  transition:
    opacity var(--dur-content) var(--ease-out),
    translate var(--dur-content-move) var(--ease-geo);
}

body[data-dtp-panel="open"] .o-head { transition-delay: var(--stagger-1); }
body[data-dtp-panel="open"] .o-body { transition-delay: var(--stagger-2); }
body[data-dtp-panel="open"] .o-foot { transition-delay: var(--stagger-3); }

.overlay :focus-visible {
  outline: 2px solid color-mix(in srgb, var(--p-accent) 75%, transparent);
  outline-offset: 2px;
  border-radius: 6px;
}

.o-head { position: relative; flex: none; }

/* ---- The switcher, which is now the object's head ----
   Shape and style follow the closed pill exactly, because they
   are the same control: 5px of padding, 4px between the options,
   no sunken track. The pill carries its options straight on the
   glass, so this one does too, or the morph lands on a different
   object from the one it set off as.

   On the real site each option is a link to that edition's own
   pages, which is the one thing the mockup could only mime. */
/* One recessed track across the full width rather than three chips
   sized to their own words. Everything up to and including the
   chosen edition fills, so the control reads as a meter pushed
   rightward: the ascent from Free to Studio is the amount of the bar
   that is lit, which is the thing the panel is there to say. The old
   form left 117px of its 310 empty and read as three loose words. */
.o-switch {
  position: relative;
  display: flex;
  margin-top: 0;
  padding: 3px;
  border-radius: 999px;
  /* The brand navy, so the one control the reader keeps coming back
     to is the one thing at the foot that is unmistakably ours. */
  background: #44586B;
}

/* A thumb that slides rather than three chips that repaint. The
   selected edition is the one thing in the object that moves, so
   the control reads as a control at a glance. It settles with the
   rest rather than ahead of it, and without an overshoot: a
   bounce inside a pane that is itself still easing reads as two
   competing movements. */
/* The lit part of the track, from the left edge to the trailing
   edge of the chosen edition. It runs slower than the thumb so the
   fill reads as being dragged along behind it rather than the two
   moving as one block. */
.o-fill {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.13);
  transition: width 480ms var(--ease-geo);
}

.o-thumb {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: 0;
  border-radius: 999px;
  background: var(--p-thumb);
  box-shadow: var(--p-thumb-cast);
  translate: 0;
  transition: translate 400ms var(--ease-geo), width 400ms var(--ease-geo);
}
[data-theme="dark"] .o-thumb { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.22); }

/* Nothing should slide while nobody can see it. Closed, the thumb
   is simply placed. */
body[data-dtp-panel="closed"] .o-thumb { transition: none; }

/* Equal thirds, so the three editions are visibly one scale rather
   than three labels of whatever width their words happen to be. */
.o-opt {
  position: relative;
  z-index: 1;
  flex: 1 1 0;
  min-width: 0;
  min-height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: rgba(255, 255, 255, 0.72);
  transition: color 220ms var(--ease-out);
}
.o-opt:hover { color: #FFFFFF; }
.o-opt[aria-current="true"] { color: var(--p-thumb-ink); }
.o-opt[aria-current="true"]:hover { color: var(--p-thumb-ink); }

/* ---- What edition you are looking at ----
   The pricing card's own head, in the card's own order: the name
   with its badge, the one line about who it is for, then the
   price. Nothing here is invented; the type sizes, the badge, the
   struck comparison and its label are the card's, carried across
   rather than re-approximated. */

.o-ident { margin-bottom: calc(12px * var(--fit-h, 1)); padding-left: 3px; }

/* The money leads on bare glass: the price, and what it is paid for. */
.o-amt {
  display: flex;
  align-items: baseline;
  gap: 8px;
  line-height: 1;
}
.o-amt b {
  font-size: calc(38px * var(--fit-h, 1));
  line-height: 1;
  font-weight: 800;
  color: var(--p-ink);
  letter-spacing: -0.055em;
  font-variant-numeric: tabular-nums;
}
.o-amt i { font-style: normal; font-size: 14px; color: var(--p-ink-3); }


/* The plate holds the one piece that is context rather than claim.
   Pro's takes the accent tint, so the recommended edition is marked
   by the surface it sits on rather than by a second decoration. */
.o-plate {
  margin-top: calc(13px * var(--fit-h, 1));
  padding: calc(11px * var(--fit-h, 1)) 13px;
  border-radius: 14px;
  background: var(--p-track-in);
}
body[data-dtp-tier="pro"] .o-plate {
  background: var(--p-tint);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--p-accent) 16%, transparent);
}
.o-one {
  font-size: calc(12.8px * var(--fit-h, 1));
  line-height: 1.45;
  color: var(--p-ink-2);
  text-wrap: pretty;
  /* Two lines exactly, on all three editions. Reserved in ems so it
     holds at every scale: a block 3px shorter on one edition resizes
     the whole ladder above it the moment the reader switches. */
  min-height: 2.9em;
}

/* Run in at the head of the sentence rather than floated above it,
   so it reads as the opening of the thought instead of a label
   hovering over one. */
.o-pop {
  margin-right: 7px;
  font-size: 9.5px;
  letter-spacing: 0.12em;
  font-weight: 800;
  text-transform: uppercase;
  color: var(--p-accent);
}

/* ============ THE MANIFEST ============
   The landing page's pricing card, with all three cards' lists
   stacked into one. Same three headings, same names, same accent
   on the two accumulating headings, same 16px tick, same faint
   dash. Every row is here in every edition: the edition changes
   each row's state, never the list. */

.o-body {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin-top: 16px;
  padding-right: 4px;
  overflow-x: hidden;
  scrollbar-width: none;
  /* The fade says a list runs past the panel. Now that the ladder
     is scaled to fit, it almost never does, and a fade over a last
     row that is fully present is a hint about something untrue: it
     dims the row an agency is paying for. So the script puts the
     fade on only when the list really does overflow. */
}

/* The product's own scrollbar rule paints a wide accent thumb on
   every scroller on the page. Inside the glass that is the app's
   colour arriving on a surface that is not the app. */
.o-body::-webkit-scrollbar { width: 0; height: 0; }

/* Only when there is genuinely more below. */
.o-body.is-cut {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 8px, #000 calc(100% - 22px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 8px, #000 calc(100% - 22px), transparent 100%);
}

.grp { break-inside: avoid; }
.grp + .grp { margin-top: calc(22px * var(--fit-s, 1)); }

/* The pricing card's own group heading, verbatim, including the
   card's rule that the two accumulating ones take the accent and
   the first does not. Free's heading names what you have; the
   other two are a claim about everything above them, which is why
   the card colours them and not the first. */
.grp-label {
  font-size: calc(11px * var(--fit-s, 1));
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--p-ink-3);
  margin-bottom: calc(10px * var(--fit-s, 1));
}
.grp-label.plus { color: var(--p-accent); }

/* The card's own list: a flex column with a 12px gap, each row a
   line of mark then label. Space is the separator, which is why no
   stripe and no rule sits under it. */
.grp-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: calc(12px * var(--fit-s, 1));
}

.row {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: calc(11px * var(--fit-s, 1));
  align-items: start;
  /* No horizontal bleed. It bought a hover background a few pixels
     wider than the text and cost twelve pixels of reported overflow
     on a box whose whole promise is that there is nothing to
     scroll. */
  padding: calc(2px * var(--fit-s, 1)) 0;
  margin: calc(2px * var(--fit-s, 1) * -1) 0;
  border-radius: 8px;
  transition: background-color 150ms var(--ease-out);
}

.grp-list .row:hover { background: var(--p-hover); }

/* The wash that passes through a row as it wakes, on its own layer
   so nothing underneath is touched. */
.row::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 9px;
  background: var(--p-tint);
  opacity: 0;
  pointer-events: none;
}

@keyframes rowWash {
  0% { opacity: 0; }
  16% { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes rowRise {
  from { translate: 0 5px; }
  to { translate: 0 0; }
}

/* A longer and later stagger than the rest of the object, so the
   growth reads as a wave crossing the list rather than a block of
   rows all changing at once. */
.row.is-waking::before { animation: rowWash 700ms var(--ease-out) var(--d, 0ms) both; }
.row.is-waking .r-name { animation: rowRise 340ms var(--ease-out) var(--d, 0ms) both; }
.row.is-waking .r-state { animation: rowRise 380ms var(--ease-out) var(--d, 0ms) both; }

.row .r-name { font-size: calc(12.8px * var(--fit-s, 1)); }

.r-name {
  position: relative;
  z-index: 1;
  font-size: 12.8px;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.34;
  color: var(--p-ink);
  text-wrap: pretty;
  transition: color 280ms var(--ease-out) var(--d, 0ms);
}

/* Locked. Greyed in place, never moved, never struck through, and
   still readable: this is the list of what buying gets you, so it
   has to be legible before it is bought. */
.row.is-locked .r-name { color: var(--p-dim); font-weight: 500; }

.r-state {
  position: relative;
  z-index: 1;
  flex: none;
  width: calc(16px * var(--fit-s, 1));
  height: calc(16px * var(--fit-s, 1));
  margin-top: calc(2px * var(--fit-s, 1));
  display: grid;
  place-items: center;
}
.r-state > * { grid-area: 1 / 1; }

.yes {
  width: calc(16px * var(--fit-s, 1));
  height: calc(16px * var(--fit-s, 1));
  color: var(--p-accent);
  opacity: 1;
  scale: 1;
  transition:
    opacity 220ms var(--ease-out) var(--d, 0ms),
    scale 300ms var(--ease-out) var(--d, 0ms);
}

/* A dash says only that something is absent. A lock says it is
   there, it is real, and it is on the other side of a decision the
   reader can make, which is the whole argument of the panel. Grey
   and quiet, in a soft pill so it reads as a state rather than as a
   warning: this is a row worth wanting, not a row that failed. */
.no {
  display: grid;
  place-items: center;
  width: calc(16px * var(--fit-s, 1));
  height: calc(16px * var(--fit-s, 1));
  border-radius: 999px;
  background: color-mix(in srgb, var(--p-ink-3) 15%, transparent);
  color: color-mix(in srgb, var(--p-ink-3) 78%, transparent);
  opacity: 0;
  transition: opacity 170ms var(--ease-out);
}
.no svg {
  width: calc(11px * var(--fit-s, 1));
  height: calc(11px * var(--fit-s, 1));
  display: block;
}

.row.is-locked .yes { opacity: 0; scale: 0.72; }
.row.is-locked .no { opacity: 1; }

/* ---- The two rows that run the other way ----
   Manual Replies and the Docket Badge are the free card's own two
   dashes. They are not rungs on the ladder: nothing unlocks them,
   because they are true only while you have not paid. So they are
   not greyed and then woken, they are simply what Free carries and
   what the paid editions leave behind, and they leave by folding
   away rather than by vanishing between frames.

   40px is a hair over a one-line row's real height, which is what
   both of these are, so the fold reads as a fold rather than as a
   pause and then a collapse. */
.row.is-only .no { opacity: 1; }
.row.is-only .r-name { color: var(--p-dim); font-weight: 500; }

.row[data-only] {
  max-height: 40px;
  overflow: hidden;
  transition:
    max-height 240ms var(--ease-out),
    opacity 180ms var(--ease-out),
    padding-top 240ms var(--ease-out),
    padding-bottom 240ms var(--ease-out);
}
/* While the fit is being measured every row is present, including
   the two that only Free carries. They are 43px, and measuring
   without them meant Free was solved against a list 43px shorter
   than the one it actually shows: every other edition fitted and
   Free ran off the bottom. */
.overlay.is-measuring .row[data-only] {
  max-height: 40px !important;
  padding-top: calc(2px * var(--fit-s, 1)) !important;
  padding-bottom: calc(2px * var(--fit-s, 1)) !important;
  opacity: 1 !important;
  transition: none !important;
}

body:not([data-dtp-rows="free"]) .row[data-only="free"] {
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  opacity: 0;
  pointer-events: none;
}

/* ---- The foot ----
   The card's own button, with the card's own rule about which
   edition fills it: Pro's is, the other two are outlined. */

.o-foot { flex: none; margin-top: 12px; }

.o-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 100%;
  min-height: 44px;
  padding: 0 18px;
  border-radius: 999px;
  border: 1.5px solid var(--p-line);
  background: var(--p-btn);
  color: var(--p-ink);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -0.018em;
  text-align: center;
  transition:
    background-color 180ms var(--ease-out),
    border-color 180ms var(--ease-out),
    color 180ms var(--ease-out),
    box-shadow 220ms var(--ease-out),
    filter 160ms var(--ease-out),
    scale 140ms var(--ease-out);
}
.o-btn:hover { background: var(--p-btn-hover); border-color: color-mix(in srgb, var(--p-ink) 45%, transparent); }
.o-btn:active { scale: 0.98; }

/* The brand navy, filled, white on it, on every edition rather than
   Pro alone. It is the only filled shape in the panel, which is what
   makes it read as the thing to do about the list, and the button
   that closes a sale should not be quieter on two editions out of
   three. The navy is the site's own #44586B, held in both themes:
   the dark palette lightens the accent for text legibility, which is
   right for text and wrong for a brand-coloured button. */
.o-btn-buy {
  background: #44586B;
  border-color: transparent;
  color: #FFFFFF;
  box-shadow: 0 1px 2px rgba(20, 24, 33, 0.2), 0 8px 18px -8px rgba(20, 24, 33, 0.4);
}
.o-btn-buy:hover { filter: brightness(1.12); background: #44586B; }

/* Pricing wears the same chip as the three editions, because a
   filled shape sitting against the filled thumb put two solid
   objects in one track and they fought. It is still not a fourth
   edition though: it leaves the page, so it takes the brand navy as
   its TEXT and carries a small arrow, and the fill stays the thumb's
   alone. Colour and a glyph mark the action; shape marks the set. */
.o-opt-buy {
  margin-left: 6px;
  gap: 5px;
  color: #FFFFFF;
  font-weight: 700;
}
.o-opt-buy:hover { color: #FFFFFF; }
.o-opt-buy svg {
  width: 11px;
  height: 11px;
  flex: none;
  translate: 0 0.5px;
  transition: translate 200ms var(--ease-geo);
}
.o-opt-buy:hover svg { translate: 2px 0.5px; }

/* Quiet at rest so it never competes with the title beside it, and
   plainly there when the pointer is anywhere on the panel. It takes
   no vertical room: it sits on the title's own line, in the corner
   that line leaves empty. */
.o-close {
  position: absolute;
  top: 13px;
  right: 13px;
  z-index: 4;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  padding: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--p-ink-3);
  cursor: pointer;
  opacity: 0.55;
  transition:
    opacity 160ms var(--ease-out),
    background-color 160ms var(--ease-out),
    color 160ms var(--ease-out);
}
.overlay:hover .o-close { opacity: 1; }
.o-close:hover {
  background: var(--p-track-in);
  color: var(--p-ink);
}
.o-close:active { scale: 0.94; }
.o-close svg { width: 14px; height: 14px; }

/* The title stops short of it, so a long edition name and the
   control can never share the same pixels. */
.o-title { padding-right: 30px; }

/* The panel names itself. A fixed line height on the title, so the
   three names cannot change the panel's height between them, which
   would resize the ladder every time the reader switched. */
.o-title {
  flex: none;
  padding-left: 3px;
  font-size: 16.5px;
  line-height: 22px;
  height: 22px;
  font-weight: 800;
  letter-spacing: -0.032em;
  color: var(--p-ink);
}
.o-frame {
  flex: none;
  margin-top: 2px;
  padding-left: 3px;
  font-size: 11px;
  line-height: 1.38;
  font-weight: 500;
  color: var(--p-ink-3);
  text-wrap: pretty;
}
/* The gap under the subtitle. It scales with the fit like everything
   else in the ladder, so a short window still closes it up rather
   than pushing the last rows out of sight. */
.o-body { margin-top: calc(24px * var(--fit-s, 1)); }

/* The title and the line under it are the first group's heading now,
   so the whisper they replace does not stay behind underneath. */
.grp:first-child .grp-label { display: none; }

/* ---- Tier text, inline only ---- */
body[data-dtp-tier="free"] .dtp .t-free,
body[data-dtp-tier="pro"] .dtp .t-pro,
body[data-dtp-tier="studio"] .dtp .t-studio { display: revert; }
.dtp .t-free, .dtp .t-pro, .dtp .t-studio { display: none; }

/* ============ THE ROOM IT MAKES ============
   The one rule in this file that reaches outside the object.

   Open, the reserve on the right is 336px against a panel whose
   own edge starts 362px in, so the app runs 26px underneath the
   glass. That is more than the panel's 24px corner radius, which
   means the corner disappears beneath the app and the app's own
   shadow passes under the pane. It is the cheapest proof of depth
   there is, and on a light panel over a light app it is also the
   most necessary.

   Closed, the reserve is gone and the software has the width
   back. The two are one transition, so the room is made in the
   same motion that fills it.

   The product's own transition on .main is restated rather than
   replaced: dropping it would cost the panel its theme cross-fade
   and nobody would connect the two. */
.main {
  transition:
    background-color 280ms var(--ease-out),
    border-color 280ms var(--ease-out),
    margin-right var(--dur-pos) var(--ease-geo),
    margin-bottom var(--dur-pos) var(--ease-geo);
}
/* The board does not move at all. It reserved 336px at first, then
   176, and both are the same mistake at different sizes: the panel is
   a sheet lying ON the software, not a column beside it, and every
   pixel it takes is a pixel of product the reader is judging. At 336
   the overview's six cards came out narrow enough to break "Duplicate
   Footer" and "Export Signup Source" over three lines each, and the
   product read as cramped, which is the opposite of the thing the
   panel is there to sell.

   So there is no rule here. The board keeps the 12px margin it has
   when the panel is shut, at every width, and the panel covers its
   right end. Some cards sit partly under the glass; that is the
   trade, and it is the better one. The 1100 to 1279 band used to say
   this on its own and no longer needs to. */
body[data-dtp-panel="closed"] .main { transition-duration: 280ms, 280ms, var(--close-dur), var(--close-dur); }

/* ============ THE WIDTH ANSWER ============
   Below 1280 a 340px sheet cannot float over the software without
   covering it, so the object lies down: same glass, same head,
   same cast, now a band beneath the demo, pulled up 18px so it
   still tucks under the demo panel's bottom edge and the app's
   shadow still falls on it.

   The band is capped and the list inside it scrolls, so the head
   and the two ends of the object are on screen at every width.
   --dtp-reserve is written by the script from the band's real
   height, so the demo above gives up exactly the room the band
   takes and not a pixel more. */

/* It lies down only where a right-hand column genuinely will not
   fit. Below 1100 the software's own sidebar has already become a
   slide-over drawer, so there is no room for a second column beside
   it. Above that there is, and the object stays where the reader
   expects to find it: a panel that moves to the foot of the window
   on a merely narrower desktop reads as a different component
   rather than the same one. */
@media (max-width: 1099px) {
  body[data-dtp-panel="open"] .main { margin-right: 12px; margin-bottom: var(--dtp-reserve, 12px); }

  /* The band is the scroller, not the list inside it. A multi-column
     box with a height put on it overflows sideways rather than
     downwards, so the list is left to be as tall as its columns make
     it and the band scrolls past it. Which also means that wherever
     the band fits, as it does at every width with room for two
     columns, nothing scrolls at all and this is the mockup's own
     band exactly. */
  .overlay {
    display: block;
    top: auto;
    left: 12px;
    right: 12px;
    bottom: 12px;
    width: auto;
    max-height: min(64vh, 640px);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, var(--p-ink-3) 32%, transparent) transparent;
    padding: 0 18px 18px;
    border-radius: 22px;
  }
  .overlay::-webkit-scrollbar { width: 6px; }
  .overlay::-webkit-scrollbar-thumb {
    border: none;
    border-radius: 999px;
    background: color-mix(in srgb, var(--p-ink-3) 32%, transparent);
  }

  /* The head goes sticky in this layout, and that is not decoration.
     Floating at the bottom of the page is how the switcher
     guarantees it is always reachable, and lying down would
     otherwise cost the object that guarantee. Sticky buys it back.
     It needs an opaque ground to do that, which is what --p-solid
     is: the panel's own glass already composited over the canvas, so
     there is no seam where the two meet. */
  .o-head {
    position: sticky;
    top: 0;
    z-index: 3;
    margin: 0 -18px;
    padding: 18px 18px 15px;
    border-radius: 21px 21px 0 0;
    background: var(--p-solid);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px 30px;
  }

  .o-ctl { flex: 0 0 auto; width: 300px; }

/* In the foot, the switcher sits directly on the button. */
.o-foot .o-ctl { width: auto; margin-bottom: 10px; }
  .o-switch { margin-top: 0; }

  /* The band runs the whole width, so the sidebar stops stretching
     to the foot of the window while it is there. Left alone, its
     Powered by Docket badge would ride down behind the band. The
     mockup shortens its own sidebar for the same reason and by the
     same amount, and the sidebar gives the room back in the same
     motion the band takes it.

     Shortened, a board with ten products has more sidebar than
     there is column left, so the column scrolls, exactly as it
     already does when it becomes a drawer. Without it the nav and
     the badge would simply run out through the bottom. */
  body[data-dtp-panel="open"] .sidebar {
    bottom: calc(var(--dtp-reserve, 12px) + 18px);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, var(--ink-3) 34%, transparent) transparent;
  }
  .sidebar { transition: bottom var(--dur-pos) var(--ease-geo); }
  body[data-dtp-panel="closed"] .sidebar { transition-duration: var(--close-dur); }

  body[data-dtp-panel="open"] .sidebar::-webkit-scrollbar { width: 6px; }
  body[data-dtp-panel="open"] .sidebar::-webkit-scrollbar-thumb {
    border: none;
    border-radius: 999px;
    background: color-mix(in srgb, var(--ink-3) 34%, transparent);
  }

  /* Lying down there is width, so the card's head lies down with
     it: name, who it is for and price on one baseline instead of
     stacked. Every type size is the one measured upright,
     including the struck figure's -12px offset, because
     re-deriving a measured number for a second layout is how the
     two drift apart. */
  .o-ident {
    flex: 1 1 300px;
    margin-top: 0;
    padding-left: 0;
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 22px;
  }
  /* Lying down, the block runs along the head rather than down it:
     the money, then its footline, then the plate beside them. */
  .o-ident { display: flex; align-items: baseline; gap: 4px 22px; flex-wrap: wrap; margin-top: 0; }
  .o-amt { flex: 0 0 auto; }
  .o-plate { flex: 1 1 240px; margin-top: 0; }
  .o-one { min-height: 0; }

  /* Lying down there is width instead of height, so the list takes
     it and the scroll problem mostly goes away. Columns rather than
     a grid of the three groups, because columns balance: a grid
     leaves the short group's row half empty and pushes the third
     out of sight, and columns simply fill. */
  .o-body {
    flex: none;
    margin-top: 20px;
    overflow: visible;
    padding-right: 0;
    -webkit-mask-image: none;
    mask-image: none;
    columns: 340px auto;
    column-gap: 34px;
  }

  .grp-list { max-width: 46ch; }
  .grp { break-inside: avoid-column; margin-bottom: 22px; }
  .grp + .grp { margin-top: 0; }

  /* The pair sits at the end of the band rather than under a
     column, and the filled one is the last thing on the line. */
  .o-foot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 6px;
  }
  .o-btn { width: auto; min-width: 210px; }
}

@media (max-width: 1099px) {
  body[data-dtp-panel="open"] .main { margin-right: 8px; margin-bottom: var(--dtp-reserve, 8px); }

  /* Below 1100 the sidebar is a slide-over drawer, so there is no
     column standing beside the band and nothing to shorten. */
  body[data-dtp-panel="open"] .sidebar { bottom: 0; }
  .sidebar { transition: transform 340ms var(--ease-out), visibility 0s linear 340ms; }

  .overlay {
    left: 8px;
    right: 8px;
    bottom: calc(8px + env(safe-area-inset-bottom, 0px));
    max-height: min(62vh, 580px);
    padding: 0 16px 16px;
    border-radius: 18px;
  }
  .o-head { margin: 0 -16px; padding: 16px 16px 13px; border-radius: 17px 17px 0 0; }
}

@media (max-width: 720px) {
  .o-body { columns: auto; }
  .o-ctl { width: 100%; }

  /* One column, and the head is a third of the band's height once it
     stacks. Pinned, it would leave a phone about two hundred pixels
     of list. So on a phone the band simply scrolls as one thing,
     head and all, which is also what gives the head its glass back:
     nothing is sitting still on top of anything, so nothing needs an
     opaque ground to do it on. */
  .overlay { padding: 16px; }
  .o-head {
    position: static;
    margin: 0;
    padding: 0;
    background: none;
    border-radius: 0;
  }

  /* One column again, so the head goes back to the stacked
     arrangement it was measured in rather than trying to hold a
     row that has no width left to hold it. Both reserves come
     back with it, or the head changes height as the switcher
     moves and the list under it jumps. */
  .o-ident { display: block; margin-top: 14px; }
  .o-amt { margin: 0; }
  .o-plate { margin-top: 13px; }
  .o-one { min-height: 37px; }

  .o-foot { display: block; margin-top: 12px; }
  .o-btn { width: 100%; min-width: 0; }

  .o-pill { bottom: 16px; padding: 4px 10px 4px 4px; gap: 3px; }
  .p-tag { padding: 0 6px 0 8px; font-size: 9.5px; }
  .p-opt, .p-buy { padding: 7px 11px; font-size: 12px; }
  .p-view { padding: 7px 10px; font-size: 11.5px; }
  .o-toggle { padding: 7px 11px; font-size: 11.5px; }
  /* The divider goes at this size, so the gap it was providing has to
     come from somewhere: without it the selected chip sits straight
     onto Pricing, which on the Studio board is a white block against a
     filled one with 3px between them. */
  .p-sep { display: none; }
  .p-buy { margin-left: 7px; }
}

/* On a phone the pill already fought this battle once: it carries
   up to seven chips, and at 375px it used to wrap into three
   ragged rows. So the DEMO tag goes, exactly as it does on the
   live tour, and the control shortens to one word. */
@media (max-width: 480px) {
  .p-tag { display: none; }
  .o-pill { bottom: 12px; gap: 2px; max-width: calc(100% - 20px); }
  .p-opt, .p-buy, .o-toggle, .p-view { min-height: 36px; }
  .p-opt, .p-buy, .o-toggle { padding: 6px 10px; font-size: 11.5px; }
  .p-view { padding: 6px 9px; font-size: 11px; }
  .o-toggle .long { display: none; }
  .o-toggle .short { display: inline; }

  /* The page already reserves the bottom of its scroller so nothing
     a page ends with can be stranded under the pill. That reserve
     was measured against a pill one row tall, and the agency board
     carries enough chips to need two. --dtp-pill is the pill's real
     height, written by the script, so the reserve is the pill's own
     rather than a number that was true when it was written. */
  .main-scroll { padding-bottom: calc(var(--dtp-pill, 37px) + 41px); }
}

/* ============ Out of the way ============
   The drawer and the submission modal are each a single task the
   visitor has started, and both of them own the screen while they
   are up. The object steps back rather than floating over them,
   and comes back exactly as it was. */
body.drawer-open .dtp,
body.modal-open .dtp {
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms var(--ease-out);
}

/* Opacity alone leaves the chips tabbable behind an open modal, and
   the pill and the panel each set their own visibility, so each has
   to be told. These sit last so they win the tie on source order. */
body.drawer-open .o-pill, body.modal-open .o-pill,
body.drawer-open .overlay, body.modal-open .overlay {
  visibility: hidden;
  transition: visibility 0s linear 160ms;
}

/* ============ REDUCED MOTION ============
   No travel, no growth, no stagger, no blur, no wash. The glass is
   simply in the other place and a row that comes to life simply
   changes colour. Everything still reads: closed is the pill, open
   is the panel, greyed is locked, and all of it is legible on
   arrival. */
@media (prefers-reduced-motion: reduce) {
  .o-ground, .main, .o-thumb { transition: none !important; }

  .overlay > *, .o-pill { translate: none !important; scale: none !important; filter: none !important; }

  body[data-dtp-panel="open"] .o-head,
  body[data-dtp-panel="open"] .o-body,
  body[data-dtp-panel="open"] .o-foot,
  .o-pill,
  .dt-veil { transition-delay: 0s !important; }

  .overlay > *, .dt-veil { transition-duration: 120ms !important; }

  /* The state change stays, because it is the information. What
     goes is the travel, the stagger and the wash. */
  .row::before, .row .r-name, .row .r-state { animation: none !important; }

  .r-name, .yes, .no { transition-delay: 0s !important; transition-duration: 130ms !important; }
  .yes { scale: 1 !important; }

  /* The two free-only rows still leave, they just do not fold on
     the way out. */
  .row[data-only] { transition: none !important; }
}

/* ============ Touch ============
   Two things the close control assumed and a phone does not have.

   It sits at 55% until the pointer enters the panel, which is the
   right manners for a control that is only there to undo something.
   There is no pointer on a phone, so it would sit at 55% for ever,
   and it is the only way back out of the sheet from the top.

   And 26px is a pointer target. The expansion is a pseudo-element
   rather than a bigger box, because the title beside it is measured
   against the box: growing it would move the words. */
@media (hover: none) {
  .o-close { opacity: 1; }
}
@media (max-width: 720px) {
  .o-close::after { content: ""; position: absolute; inset: -9px; }

  /* The switcher's four options are 32px, which is a comfortable row
     under a mouse and two pixels short of a thumb. The track grows
     with them rather than the chips being padded inside it, so the
     four still fill it and the moving thumb still matches. */
  .o-opt { min-height: 40px; }
}

/* ---- Demo honesty notes (tools/wire-demo-legal.py) ----
   One line in the page footer saying this is a demo, with the Privacy and
   Terms links every page needs, and one line above each form's submit row
   saying nothing typed there is sent. Secondary ink (--ink-2), not --ink-3,
   which is below the contrast minimum on the light board. */
.demo-foot {
  margin: 0;
  padding: 18px 0 8px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2, #676d78);
}
.demo-foot a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  margin-left: 6px;
}
.demo-foot a:hover { color: var(--ink, currentColor); }
/* The tour pill floats over the bottom of the page. On a phone it sits across
   the last line, so the links need room under them to stay clickable. */
@media (max-width: 560px) {
  .demo-foot { padding-bottom: 96px; }
}
.demo-note {
  margin: 0 0 10px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-2, #676d78);
}
