/* ══════════════════════════════════════════════════════════════════════════
   css/sell-media-motion.css — MOTION AND THE DYNAMIC PARTS
   service.tnaado.ca · /brand-video-toronto · /product-photography-toronto
                     · /content-retainer

   A SEPARATE FILE, DELIBERATELY. css/sell-media.css is the paint for these
   three pages and it is left untouched; ten agents are in this repo at once
   and an edit there is an edit to somebody else's surface. This sheet only
   adds, and it reads css/sell-media.css's tokens rather than redeclaring them,
   so the two cannot drift on colour.

   IT ALSO OVERRULES ONE RULE WRITTEN INTO THAT FILE. Its header says "ONE
   THING MOVES PER PAGE ... on the other two, nothing moves at all. Motion is
   not decoration on a page that takes paid traffic." The first half is right
   and the conclusion is wrong: the motion here is not decoration, it is the
   argument. A stacked bar that draws 5h30 of a 12h day says what a paragraph
   claiming "five and a half hours of it rolling" had to assert. A total that
   recomputes when you switch the crew tier proves the add-it-up-yourself
   promise the old copy spent three sentences insisting on. Every animation
   below replaced words; none was added on top of them.

   DEGRADATION, THE SAME CONTRACT AS THE SHEET IT SITS BESIDE
   Everything is authored in its FINISHED state. Motion runs backwards out of
   `from` keyframes, or forwards off a class an observer adds once and never
   removes. A blocked, 404ing or throwing script costs the animation and
   nothing else: every price, every line and every figure is in the HTML.

   prefers-reduced-motion is honoured at the foot, and the quote builder keeps
   working under it — the total simply snaps instead of counting.
   ══════════════════════════════════════════════════════════════════════════ */

/* ══ 1. THE LIFT ═══════════════════════════════════════════════════════════
   One reveal, shared. `[data-lift]` is authored visible; js/sell-media.js adds
   `.is-armed` to the html element on boot, which is what arms the pre-state.
   So if the script never runs, nothing is ever hidden — the failure mode is a
   page with no reveal, never a page with no content. Same reason the storm
   pages gate on .sc-live.
   --i staggers a group; it is a plain integer on the element.  */
.is-armed [data-lift] {
  opacity: 0;
  transform: translateY(14px);
}
.is-armed [data-lift].is-in {
  opacity: 1;
  transform: none;
  transition:
    opacity .55s var(--ease) calc(var(--i, 0) * 70ms),
    transform .62s var(--ease) calc(var(--i, 0) * 70ms);
}
/* A photograph is never put behind a reveal that could hold it back on a slow
   network: images fade only, no transform, so layout is settled before paint. */
.is-armed figure[data-lift] { transform: none; }

/* ══ 2. THE FOLD — every disclosure on these three pages ═══════════════════
   <details>/<summary>. No JS, keyboard-operable, and Chromium expands it for
   find-in-page on its own.

   NO `hidden="until-found"` INSIDE THESE. The brief asked for it; inside a
   <details> it is a bug. Firefox and Safari do not implement it, and to those
   engines `hidden="until-found"` is just `hidden` — the body would stay
   invisible with the fold OPEN. <details> already delivers all three
   properties the attribute was wanted for.

   The open transition uses grid-template-rows 0fr -> 1fr, which is the only
   way to animate to intrinsic height without measuring in script. */
.folds {
  margin-top: clamp(1.4rem, 3.5vw, 2rem);
  border-top: 1px solid var(--hair-ink);
}
.band--paper .folds { border-top-color: var(--hair-bone); }

.fold {
  border-bottom: 1px solid var(--hair-ink);
}
.band--paper .fold { border-bottom-color: var(--hair-bone); }

.fold__s {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: baseline;
  gap: .5rem .9rem;
  padding: .95rem 0;
  cursor: pointer;
  list-style: none;
}
.fold__s::-webkit-details-marker { display: none; }
.fold__s:focus-visible {
  outline: 2px solid var(--red-ink);
  outline-offset: 3px;
}
.band--paper .fold__s:focus-visible { outline-color: var(--red); }

.fold__n {
  font-family: var(--mono);
  font-size: .6875rem;
  font-style: normal;
  letter-spacing: .1em;
  color: var(--red-ink);
}
.band--paper .fold__n { color: var(--red-bone); }

.fold__t {
  font-size: .9375rem;
  font-weight: 600;
  color: var(--bone);
}
.band--paper .fold__t { color: var(--ink); }

.fold__d {
  font-family: var(--mono);
  font-size: .75rem;
  font-variant-numeric: tabular-nums;
  color: var(--dim-ink);
  white-space: nowrap;
}
.band--paper .fold__d { color: var(--ink-mute); }

/* The chevron. Rotation only — a transform, so it composites. */
.fold__c {
  width: .55rem;
  height: .55rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
  transition: transform .28s var(--ease);
  color: var(--dim-ink);
}
.band--paper .fold__c { color: var(--ink-mute); }
.fold[open] .fold__c { transform: rotate(225deg) translateY(-2px); }

.fold__body > * {
  max-width: 58ch;
  padding-bottom: 1.05rem;
  font-size: .875rem;
  line-height: 1.6;
  color: var(--cap-ink);
}
.band--paper .fold__body > * { color: var(--ink-mute); }

/* The animated close/open, only where the browser can do it without breaking
   the no-JS case: ::details-content is the modern hook and degrades to an
   instant toggle everywhere else. */
@supports selector(:open) {
  .fold::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size .34s var(--ease), content-visibility .34s allow-discrete;
    transition-behavior: allow-discrete;
  }
  .fold[open]::details-content { block-size: auto; }
}

/* ══ 3. THE DAY ARC — /brand-video-toronto ═════════════════════════════════
   2h30 build, 4h00 light, 5h30 rolling, of a 12h call sheet. The widths are
   authored as percentages in the markup because they ARE the fact; the
   animation only reveals them left to right. */
.arc { margin-top: clamp(1.4rem, 3.5vw, 1.9rem); }
.arc__bar {
  display: flex;
  height: 2.25rem;
  overflow: hidden;
  border-radius: 2px;
  background: rgba(245, 241, 232, .06);
}
.arc__seg {
  display: block;
  transform: scaleX(0);
  transform-origin: left center;
}
/* The transition is declared on the ARMED state, not on the .is-on state, so
   it is already in force when the observer flips the class. Declaring it in
   the same rule as the value change is the classic no-animation bug. */
.is-armed .arc__seg { transition: transform .8s var(--ease) calc(var(--i, 0) * 160ms); }
.is-armed .arc.is-on .arc__seg,
html:not(.is-armed) .arc__seg { transform: none; }
.arc__seg--build   { background: rgba(245, 241, 232, .22); }
.arc__seg--light   { background: var(--dim-ink); }
.arc__seg--rolling { background: var(--red); }

.arc__key {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem 1.4rem;
  margin-top: .85rem;
}
.arc__k {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .8125rem;
  color: var(--mute-ink);
}
.arc__sw { width: .8rem; height: .8rem; border-radius: 1px; }
.arc__k b {
  font-family: var(--mono);
  font-size: .75rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--bone);
}

/* ══ 4. THE QUOTE BUILDER ══════════════════════════════════════════════════
   The dynamic system. Real form controls, so it is keyboard-native and every
   published rate is on the page with the script blocked. The <output> ships
   with the default day's total already in it as text.

   It replaced, on two pages: a static worked-example table, a rate card of
   twenty non-interactive rows, and four sentences insisting the reader could
   rebuild the figure themselves. Now they can.  */
.qb {
  margin-top: clamp(1.6rem, 4vw, 2.25rem);
  border-top: 2px solid var(--red);
}
.qb__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .8rem 1.5rem;
  padding-top: 1.1rem;
}
.qb__h {
  font-family: var(--display);
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1.2;
  color: var(--ink);
}
.qb__h em { font-style: italic; color: var(--red-bone); }
.qb__presets { display: flex; flex-wrap: wrap; gap: .4rem; }
.qb__preset {
  padding: .42rem .8rem;
  font-family: var(--mono);
  font-size: .75rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--hair-bone);
  border-radius: 2px;
  cursor: pointer;
  transition: border-color .18s var(--ease), background .18s var(--ease);
}
.qb__preset:hover { border-color: var(--red); }
.qb__preset[aria-pressed="true"] {
  background: var(--red);
  color: #fff;
  border-color: var(--red);
}
.qb__preset:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

.qb__g { margin-top: 1.5rem; padding: 0; border: 0; }
.qb__lg {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .3rem .6rem;
  width: 100%;
  padding: 0 0 .5rem;
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--red-bone);
  border-bottom: 1px solid var(--hair-bone);
}
.qb__lg span {
  font-size: .75rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-cap);
}

.qb__o {
  display: grid;
  grid-template-columns: 1.1rem 1fr auto;
  align-items: baseline;
  gap: .3rem .8rem;
  padding: .72rem 0;
  border-bottom: 1px solid var(--hair-bone);
  cursor: pointer;
}
.qb__o:hover .qb__ol { color: var(--ink); }
.qb__o input { margin: 0; accent-color: var(--red); justify-self: center; }
.qb__o input:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }
.qb__ol {
  font-size: .9375rem;
  color: var(--ink-body);
  transition: color .18s var(--ease);
}
.qb__ol em {
  display: block;
  margin-top: .2rem;
  font-style: normal;
  font-size: .8125rem;
  color: var(--ink-cap);
}
.qb__ov {
  font-family: var(--mono);
  font-size: .875rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  white-space: nowrap;
}
.qb__ov--soft { color: var(--ink-mute); }

/* A quantity row: same grid, a number field where the radio would be. */
.qb__o--qty { grid-template-columns: 3.4rem 1fr auto; }
.qb__qty {
  width: 3.4rem;
  padding: .3rem .4rem;
  font-family: var(--mono);
  font-size: .8125rem;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--hair-bone);
  border-radius: 2px;
}
.qb__qty:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

/* The running total. Sticks to the bottom of the viewport, because the five
   groups are taller than a phone screen and a total you cannot see while you
   are changing it is not a demonstration of anything.

   bottom is 2.9rem, NOT 0. css/chrome.css parks .footpull — the fixed "site
   directory" pull — across the bottom of every page at .85rem padding either
   side of an 11px label, about 2.6rem. At bottom:0 the two overlapped at 390px
   and the total read "STARTING TOTAL · CAD, BEFORE" with the rest under the
   footer bar. Measured, not guessed.

   The fade runs the full height of the bar rather than the top fifth, so a
   rate row passing behind it dissolves instead of being cut through the
   middle of its own text. */
.qb__out {
  position: sticky;
  bottom: 2.9rem;
  z-index: 3;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: .5rem 1.2rem;
  margin-top: 1.6rem;
  padding: .95rem 0 1rem;
  background: var(--paper);
  border-top: 2px solid var(--red);
}
/* THE FADE IS A SIBLING BAND ABOVE THE BAR, NOT THE BAR'S OWN BACKGROUND.
   As a gradient on the bar itself, the label sat in the semi-transparent half
   and "STARTING TOTAL · CAD, BEFORE TAX" printed on top of a live rate row at
   390px. Solid paper behind the bar, a 2.4rem fade above it, and nothing
   overlaps text at any width. */
.qb__out::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 2.4rem;
  background: linear-gradient(to top, var(--paper), rgba(242, 237, 225, 0));
  pointer-events: none;
}
.qb__out-k {
  font-family: var(--mono);
  font-size: .6875rem;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--red-bone);
}
.qb__total {
  display: block;
  margin-top: .25rem;
  font-family: var(--display);
  font-size: clamp(2rem, 6vw, 2.75rem);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--ink);
}
.qb__reset {
  padding: .4rem .75rem;
  font-family: var(--mono);
  font-size: .75rem;
  color: var(--ink-mute);
  background: transparent;
  border: 1px solid var(--hair-bone);
  border-radius: 2px;
  cursor: pointer;
}
.qb__reset:hover { color: var(--ink); border-color: var(--red); }
.qb__reset:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

.qb__lines {
  grid-column: 1 / -1;
  display: grid;
  gap: .25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.qb__line {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .8rem;
  font-family: var(--mono);
  font-size: .75rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}
.qb__line b { font-weight: 500; color: var(--ink); }

@media (min-width: 46rem) {
  .qb__body {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 clamp(1.5rem, 4vw, 3rem);
  }
}

/* ══ 5. THE TWO-WAY COMPARISON — /content-retainer ═════════════════════════
   $9,300 bought one at a time against $5,400 on retainer. The bars are the
   arithmetic PRICES.md requires published, drawn rather than argued. Widths
   are authored: 5400/9300 = 58.06%.  */
.vs { margin-top: clamp(1.6rem, 4vw, 2.25rem); }
.vs__r { padding: 1rem 0; border-top: 1px solid var(--hair-bone); }
.vs__r:last-of-type { border-bottom: 1px solid var(--hair-bone); }
.vs__h {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .3rem 1rem;
}
.vs__l { font-size: .9375rem; color: var(--ink-body); }
.vs__l span { display: block; font-size: .8125rem; color: var(--ink-cap); }
.vs__v {
  font-family: var(--mono);
  font-size: 1.25rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.vs__track {
  margin-top: .7rem;
  height: 1.35rem;
  overflow: hidden;
  border-radius: 2px;
  background: rgba(10, 10, 10, .07);
}
.vs__fill {
  display: block;
  height: 100%;
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left center;
}
.is-armed .vs__fill { transition: transform .95s var(--ease) calc(var(--i, 0) * 190ms); }
html:not(.is-armed) .vs__fill,
.is-armed .vs.is-on .vs__fill { transform: none; }
.vs__fill--one { background: var(--ink-mute); }
.vs__fill--ret { background: var(--red); }

.vs__save {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .5rem .9rem;
  margin-top: 1.1rem;
}
.vs__save-n {
  font-family: var(--display);
  font-size: clamp(1.75rem, 5vw, 2.25rem);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--red-bone);
}
.vs__save-t { font-size: .9375rem; color: var(--ink-body); }

/* ══ 5b. ONE FRAME AT ITS NATIVE RATIO ════════════════════════════════════
   `.solo img` in css/sell-media.css crops to 16:9. On a 3:2 original that is
   15.6% of the height gone, and on /product-photography-toronto's lead frame
   the product is at the bottom edge and the city is at the top, so the crop
   removes both halves of the argument. One extra class rather than a new
   component. */
.solo--native img { aspect-ratio: 3 / 2; }

/* ══ 5c. THE HERO LADDER, MADE LIVE ═══════════════════════════════════════
   The price rungs on the first screen are anchors carrying a preset for the
   quote builder further down. They must not look like buttons — the plate is
   a price plate, not a toolbar — so the affordance is a rule that lights and a
   short line that arrives on hover and on focus. The line is authored, never
   inserted, so it is in the accessible name at all times and a screen reader
   never announces a bare figure.

   `.rung__v` and `.rung__l` are block <p> rules in css/sell-media.css, which is
   not this pass's file to edit; inside an anchor they are <span>, so the two
   declarations below restore the block flow rather than duplicating the paint.
   Nothing here changes a colour, a size or a spacing value that sheet sets. */
a.rung__i,
a.plate__jump {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color .2s var(--ease);
}
a.rung__i .rung__v,
a.rung__i .rung__l { display: block; }
a.rung__i:hover,
a.rung__i:focus-visible { border-top-color: var(--red); }
a.rung__i:focus-visible,
a.plate__jump:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }

.rung__go {
  display: block;
  margin-top: .45rem;
  font-family: var(--mono);
  font-size: .625rem;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--red-ink);
  opacity: 0;
  transform: translateY(-3px);
  transition: opacity .22s var(--ease), transform .22s var(--ease);
}
a.rung__i:hover .rung__go,
a.rung__i:focus-visible .rung__go { opacity: 1; transform: none; }
/* On a touch screen there is no hover, so the invitation is simply always on
   and quieter. Without this the rungs are inert-looking on every phone. */
@media (hover: none) {
  .rung__go { opacity: .75; transform: none; }
}

/* THE PLATE VARIANT KEEPS ITS INVITATION ON. A lone figure that is secretly a
   link is worse than no link: on the rung ladder three bordered cells read as a
   group, but here there is one number and nothing to say it does anything. The
   line it carries is the same text the old always-visible kicker carried
   ("built line by line below"), so nothing has been hidden behind a hover —
   only the arrow moves. */
a.plate__jump { border-top: 1px solid transparent; }
a.plate__jump .rung__go { opacity: 1; transform: none; }
a.plate__jump .rung__go::after {
  content: ' \2192';
  display: inline-block;
  transition: transform .22s var(--ease);
}
a.plate__jump:hover .rung__go::after,
a.plate__jump:focus-visible .rung__go::after { transform: translateX(4px); }
/* The hover moves the arrow and rules the invitation. It deliberately does NOT
   recolour the figure: turning a 3.75rem hero price red under the pointer is
   the loudest thing on the first screen and it fights the one red element that
   should own it, which is the call to action directly beneath. */
a.plate__jump:hover .rung__go,
a.plate__jump:focus-visible .rung__go {
  box-shadow: inset 0 -1px 0 0 currentColor;
}

/* ══ 5d. THE PROOF FRAME ══════════════════════════════════════════════════
   `.proof__c img` in css/sell-media.css is a full-column 3:2 crop, written for
   a card that is mostly picture. The frame on /brand-video-toronto is a 1300px
   16:9 grab of an interview in progress: at full column width it renders past
   its own resolution, its grain becomes the subject, and it unbalances a
   two-up whose other column is a single figure.

   So: native ratio, no crop — the argument is the two people and the flagged
   mic, and 3:2 takes the sides off both — and capped so it reads as evidence
   beside the claim rather than as a second hero. Width, not height, because a
   height cap plus object-fit is a crop by another name. */
.proof__ev { max-width: 27rem; }
.proof__ev img { aspect-ratio: 16 / 9; }

/* ══ 5e. THE FREE OFFER, WITH THE THING IT HANDS OVER ═════════════════════
   Each of the three pages ends a band on a free offer — the Recut, the
   One-Frame Test, the Sample Week — and each was three folds and a great deal
   of empty ink. The offer is a FILE, at a known ratio, so the ratio is drawn
   beside the folds using `.msheet`, which css/sell-media-pass.css already
   defines for /content-retainer's month. Same plates, same reveal, same
   reduced-motion path, no new component and no new colour.

   It earns its place by deleting words, not by filling a hole: with the shape
   and the format drawn, fold 01 no longer has to spell out what a vertical is
   or what sizes a photograph is cropped to.

   Single column until 62rem. Below that the plates would be wider than the
   folds are tall and the band reads as a picture with a footnote. */
.give { display: grid; gap: clamp(1.4rem, 4vw, 2.5rem); }
@media (min-width: 62rem) {
  .give { grid-template-columns: 1.35fr .65fr; align-items: start; }
  .give > .msheet { margin-top: 0; }
}
/* A one-plate or three-plate set must not stretch to the full column the way a
   six-across row of verticals does — flex: 1 1 0 on two plates makes each one
   half a column wide and the ratio stops reading. Cap the row instead. */
.give .msheet__set { max-width: 22rem; }
.give .msheet__row { justify-content: flex-start; }
.give .msheet__p { flex: 0 1 auto; }
.give .msheet__p--vert { width: 7.5rem; }
.give .msheet__p--four { width: 6.4rem; }
.give .msheet__p--wide { width: 13rem; }
.give .msheet__p--sq { aspect-ratio: 1 / 1; width: 6.4rem; }
/* css/sell-media-pass.css gives the red to `--wide`, because on the month it is
   the long-form piece the other eleven are cut around. In a free offer there is
   no run to stand out from, so the red marks the ONE file being handed over and
   the greys are the other crops of it — which means `--wide` has to give the
   red back here, or a set whose lead is the 4:5 shows two reds and says the
   opposite. Same two values as that sheet, no third colour. */
.give .msheet__p--wide:not(.msheet__p--lead) {
  background: rgba(245, 241, 232, .13);
  border-color: rgba(245, 241, 232, .16);
}
.give .msheet__p--lead {
  background: rgba(200, 16, 46, .16);
  border-color: rgba(200, 16, 46, .42);
}

/* ══ 6. MOUNT POINTS ══════════════════════════════════════════════════════
   A component port is running in parallel. Where a demonstration belongs but
   its component is not written yet, the page carries an empty [data-mount]
   with an HTML comment naming what goes in it. Styled so an unfilled mount is
   invisible rather than a gap, and so a filled one needs no extra rule. */
[data-mount]:empty { display: none; }

/* ══ 7. REDUCED MOTION ════════════════════════════════════════════════════
   Nothing moves, nothing counts, everything is still there. The quote builder
   keeps recomputing; only the tween goes. */
@media (prefers-reduced-motion: reduce) {
  .is-armed [data-lift],
  .is-armed [data-lift].is-in,
  .arc__seg, .vs__fill {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .fold__c { transition: none !important; }
  .rung__go,
  a.plate__jump .rung__go::after { transition: none !important; transform: none !important; }
  @supports selector(:open) {
    .fold::details-content { transition: none !important; }
  }
}
