/*
 * Our CSS layer over Influex's design: mobile refinement, and the changes Ted
 * has asked for by name.
 *
 * It was mobile-only when it was written, and the header below still describes
 * that part accurately. Section 6 at the end is different in kind: deliberate
 * design changes Ted requested in his 2026-08-26 walkthrough, at every width.
 * They live here because this file is already loaded last on all nine routes,
 * and because their own sections.css is regenerated from baseline/ by
 * port-site.mjs — anything written there is erased the next time it runs.
 *
 * Their site was authored on a fixed 1920 stage, so small screens were never a
 * layout problem for it — every size is an absolute px value chosen for that
 * canvas. On a 390px phone the consequences are measurable: /home's wordmark is
 * 658px wide at 72px, /contact's hero is 726px at 100px, and the document
 * scrolls sideways by up to 370px. A page wider than the phone is also why the
 * header appears to float: it is correctly 390px and anchored, and the page
 * slides underneath it.
 *
 * EVERYTHING HERE IS INSIDE A max-width MEDIA QUERY, deliberately. Desktop is
 * proven pixel-identical to the frozen original and tablet measures zero
 * overflow, so neither may move. Rules that do not exist above 780px cannot
 * disturb them — the guarantee is structural rather than a promise to be
 * careful.
 *
 * Influex owns the design. This makes their composition fit a phone; it does
 * not restyle it. Type scale, colour, imagery and rhythm are theirs, and the
 * small-screen result is for their team to refine.
 */

@media (max-width: 780px) {

  /* ---- 1. Display type becomes fluid --------------------------------------
   * Each clamp keeps their original size as its ceiling, so nothing shrinks
   * until the viewport genuinely cannot hold it. The vw middle value is what
   * makes a long wordmark fit instead of running off the screen.            */

  /* .wordmark is 13vw where the others are 11vw.
   *
   * 11vw was tuned to hold "Wielding Magic" on one line, which it never
   * managed. Now that it wraps to two words, the longer of them is "Wielding"
   * and the line is far shorter, so the same value leaves the headline filling
   * 67% of its box: measurably under-set for a hero wordmark. 13vw takes it to
   * about 80% and stops short of the edge, which is the margin a longer word
   * than Ted's current one needs before the last resort above is reached.
   * 72px, their original size, is still the ceiling. */
  h1.wordmark            { font-size: clamp(28px, 13vw, 72px)  !important; }
  span.ink-sans,
  h1.c-display           { font-size: clamp(30px, 13vw, 100px) !important; }
  span.nf-digit          { font-size: clamp(64px, 26vw, 150px) !important; }
  h1.pp-hero__title      { font-size: clamp(28px, 11vw, 72px)  !important; }
  h2.lp-banner__title,
  h2.c-display           { font-size: clamp(26px, 9.5vw, 64px) !important; }
  h2.lph-title           { font-size: clamp(24px, 8.5vw, 56px) !important; }
  h1.wk2-wordmark        { font-size: clamp(24px, 8vw, 52px)   !important; }
  h1.wgd-hero__title,
  h2.flib__title         { font-size: clamp(22px, 7.5vw, 46px) !important; }
  h2.cine-line,
  h2.ins-head,
  h2.fp-reels__title,
  h2.mp-head__title,
  h2.wk2-close__title    { font-size: clamp(22px, 7vw, 44px)   !important; }

  /* Headlines wrap at spaces, never inside a word.
   *
   * `word-break: break-word` split "WIELDING MAGIC" into "WIELDING MAG / IC",
   * chopping the word instead of using the space sitting right there. Only
   * `overflow-wrap: anywhere` is kept, and it is the last resort: it applies
   * to a single unbroken word too long for the screen, and never in preference
   * to an ordinary space.
   *
   * `hyphens` is off. Their display faces are set in caps for effect and an
   * inserted hyphen reads as a typo in that context. */
  h1, h2, .wordmark, .ink-sans, .c-display {
    overflow-wrap: anywhere;
    word-break: normal;
    hyphens: none;
  }

  /* Why `anywhere` above is now safe, having been the visible bug.
   *
   * It is a last resort: it breaks inside a word only when that word cannot
   * fit a line by itself. It was firing on every phone because the copy
   * carried a non-breaking space, which made "Wielding\u00a0Magic" a single
   * unbreakable token with no other break available — so the headline read
   * "WIELDING MAG / IC", and "WIELDING MA / GIC" at 360px.
   *
   * The hard space is now softened where copy enters the app (lib/queries.ts),
   * so the string has a real break opportunity between the two words and the
   * last resort stops being reached. Keeping `anywhere` rather than switching
   * to `normal` is deliberate: `normal` would guarantee no word is ever cut,
   * and would guarantee instead that a single word too long for the screen
   * runs off the side of it. A clipped headline is not an improvement on a
   * broken one, and Ted edits this copy himself. */

  /* ---- 2. Nothing may be wider than the phone ----------------------------
   * Their forms are 409px against a 390px viewport. Media keeps its aspect
   * ratio rather than being squashed.                                       */

  form, input, textarea, select, button,
  .c-form, .c-field, .c-formbox { max-width: 100% !important; }
  img, video, iframe, svg, canvas { max-width: 100%; height: auto; }

  /* ---- 3. Touch targets --------------------------------------------------
   * The audit found 11 to 39 controls under 44px per page. 44px is the size
   * a fingertip actually hits; below it, taps land on the wrong thing.
   * Padding rather than height, so their layout is not pushed around.       */

  a, button, [role="button"], input[type="submit"] { min-height: 44px; }
  nav a, .tk-nav a, footer a { padding-block: 10px; }

  /* ---- 4. The Tedshots carousel fits the phone ---------------------------
   * Influex already wrote this breakpoint themselves:
   *
   *   @media (max-width: 560px) { .bc-card { flex-basis: 100%; } }
   *
   * One card per view on a phone is their decision, and it is the right one.
   * It has never worked, and the reason is two lines apart in their own file.
   *
   * .ip-pin is `display: flex; flex-direction: column`, so .bc-rail is a flex
   * item and should stretch to the container's 390px. It does not, because of
   * `.bc-rail { margin: 42px auto 0 }`: a flex item with `auto` margins on the
   * cross axis is never stretched. Auto margins absorb the free space instead,
   * and the item falls back to being sized by its content. Its content is
   * .bc-track, a flex row of four cards, so the rail measures 845px inside a
   * 390px parent.
   *
   * From there `flex-basis: 100%` is 100% of 845px, which draws a 685px card,
   * and .ip-pin's `overflow: hidden` slices it. What reaches the phone is one
   * photograph wider than the screen, pushed 76px right and cut off.
   *
   * This is also why the responsive audit reported zero overflow and was right
   * to. The document really is 390px wide. The clipping happens inside a
   * container that is itself correctly sized, which no document-width check
   * can see: an element can be badly broken and perfectly contained.
   *
   * So the cross-axis margins go, which lets stretch apply, and min-width: 0
   * releases the automatic minimum on the flex chain beneath it so the track
   * and card can follow the rail down. Their own breakpoint then does exactly
   * what it says. Nothing here changes their layout, their card proportions or
   * their breakpoint; it removes what was stopping theirs from applying.
   *
   * The padding is the one number chosen here. 76px of gutter each side is a
   * 1920-canvas value; on a 390px screen it spends 39% of the width on empty
   * margin. 20px keeps their edge rhythm at a size the screen can afford. */

  .bc-rail, .bc-viewport, .bc-track, .bc-card { min-width: 0 !important; }
  .bc-rail { margin-inline: 0 !important; padding-inline: 20px !important; }
}

/* ---- Image fit, up to 1024px --------------------------------------------
 * Deliberately a wider breakpoint than the rest of this file.
 *
 * Everything above is scoped to 780px because it changes layout, and tablet at
 * 834px measured zero overflow and did not need it. But image proportions are
 * not layout: a logo squashed from 4.08:1 into a square is wrong at every size.
 * Scoping these to 780px left tablet rendering the unfixed images and matching
 * desktop's broken state exactly, which was an accident of a breakpoint chosen
 * for a different reason rather than a decision.
 *
 * 1024px stops short of desktop, where the same distortion exists in Influex's
 * live site and correcting it would change their design and break the pixel
 * proof against the frozen original. That remains theirs to call.
 */
@media (max-width: 1024px) {
  /* Images keep their own proportions inside whatever box holds them.
   *
   * The audit found images drawn at an aspect ratio their file does not have:
   * reviews/r1.png is a 4.08:1 logo rendered at 1.00:1, squashed into a square,
   * and eight more like it per page. The cause is that these images carry no
   * object-fit rule at all, so they inherit the CSS default of `fill`, which
   * stretches rather than fits. Influex uses `cover` and `contain` elsewhere in
   * their own stylesheet, so this reads as an oversight rather than a choice.
   *
   * `contain` rather than `cover` for logos and marks: a squashed logo is wrong,
   * but a cropped one is also wrong, and letterboxing is the honest option when
   * the box and the artwork disagree. Photography keeps `cover`, where filling
   * the frame is the point and a small crop is expected.
   *
   * Mobile only, deliberately. The same distortion exists in their live desktop
   * site, and correcting it there would change their design and break the
   * pixel-proof against the frozen original. That one is theirs to call. */
  img[class*="logo"], img[class*="press"], img[class*="mark"],
  .rev img, [class*="review"] img {
    object-fit: contain !important;
  }
  [class*="hero"] img, [class*="poster"] img, [class*="thumb"] img,
  [class*="card"] img, [class*="gallery"] img {
    object-fit: cover;
  }

  /* One measured overflow: heroes/cinematographer-c.jpg is drawn 10px taller
   * than its 390x250 box, so the bottom of the frame is cut. */
  [class*="hero"] img, [class*="hero"] picture { max-height: 100%; }
  table { display: block; overflow-x: auto; }

  /* ---- 2b. Fixed-width stages become fluid --------------------------------
   * The deeper cause, and the one type sizing cannot reach. Their pages are
   * built inside fixed-width stage containers chosen for the 1920 canvas:
   * /contact's .v2-stage is 720px and simply does not shrink, so the page
   * stays wider than the phone no matter how small the headline gets.
   *
   * Width is released rather than overridden with a number, so the stage takes
   * whatever the phone actually offers instead of trading one fixed size for
   * another.                                                                */

  .v2-stage, .cpage, .v2-standalone > div,
  [class*="-stage"], [class*="__stage"] {
    width: auto !important;
    max-width: 100% !important;
    /* min-width is what actually pins these open. A stage carrying a min-width
     * sized for the 1920 canvas ignores max-width entirely, which is why the
     * contact page stayed 760px wide while its headline shrank obediently. */
    min-width: 0 !important;
  }

  /* A row of tabs should wrap onto the next line rather than run off-screen. */
  .c-ptabs { flex-wrap: wrap !important; max-width: 100% !important; }
  .c-ptab  { max-width: 100% !important; }
}



/* ---- 4. The last line of defence -----------------------------------------
 * If one element still escapes at some width we have not measured, the page
 * must not become horizontally scrollable: that is the bug that reads as a
 * drifting header. Deliberately outside the media query, because a page
 * should never scroll sideways at ANY width.
 *
 * `clip`, NOT `hidden`, and the difference is the whole section.
 *
 * `overflow-x: hidden` on html/body computes the other axis to `auto` and
 * makes the body a scroll container. Every `position: sticky` on the site
 * then has a scroll container that never scrolls, so nothing sticks — at
 * every width, desktop included. That is what took out .cine-pin and
 * .finv-pin on the homepage and .ab-compass__stage on /about: measured
 * moving 1:1 with the scroll, 2000px of scroll for 2000px of travel, when
 * each is supposed to hold for a screen.
 *
 * `clip` refuses the overflow without creating a scroll container, which is
 * exactly what this rule wanted and what `hidden` cannot give it. The page
 * still cannot scroll sideways. Sticky works again.                        */
html, body { max-width: 100%; overflow-x: clip; }

/* The contact and 404 pages put `min-width: 760px` on <body> itself. Every
 * descendant inherits that floor, which is why releasing the stage containers
 * changed nothing: they were being sized inside a body already pinned open.
 * min-width beats max-width in the cascade by design, so the floor has to be
 * removed rather than out-declared.
 *
 * Only below the breakpoint. Above it the 760px floor is theirs and is exactly
 * what their layout expects. */
@media (max-width: 780px) {
  html, body, body.v2-standalone { min-width: 0 !important; }
}

/* ---- 5. Touch targets on every touch device, any screen width ------------
 * Section 3 fixed touch targets for narrow screens by testing viewport
 * width, which is a proxy: it assumes small screens are touched and large
 * ones are moused. A touchscreen laptop breaks that assumption. `pointer:
 * coarse` asks about the actual input device instead, so a touchscreen
 * laptop at 1920 gets the larger targets and a mouse-driven tablet does not.
 *
 * Desktop at 1920 measures 19 controls under 44px, 14 header nav links at
 * 79x25 and 4 tab buttons at 106x38, all failing on height only. Padding
 * those 14px taller would visibly inflate the header on the one surface
 * proven pixel-identical to the original, so this never grows a real box.
 * It grows an invisible one instead: a `::after` sized to at least 44px and
 * centred on the control, positioned absolutely so it sits outside the
 * padding/border/height box the control is actually laid out and painted
 * with. `max(44px, 100%)` means a control already 44px or larger gets an
 * overlay the same size as itself, i.e. no functional change, so this is
 * safe to point at every control on the page rather than an enumerated
 * few — it only does something where the control is genuinely undersized.
 *
 * One more variable this site has that most don't: pages are authored on a
 * fixed 1920 stage and `useStageScale` (src/App.jsx) scales that whole stage
 * down with `transform: scale()` to fit anything narrower, down to a 768px
 * floor below which it switches to true responsive layout instead. A tablet
 * at 834px is rendered at roughly 0.43 scale, so a 60px-tall button can
 * still land under 44 screen px even though its authored height is already
 * generous. The fix is their own idiom for this exact problem — nav.css:109
 * already counter-scales the mega-menu with `scale(calc(1 / var(--stage-
 * scale)))` so it renders 1:1 inside a scaled stage. Dividing the 44px
 * target by that same variable makes the overlay always resolve to a true
 * 44 screen px, at any width the stage scales to, rather than 44 of the
 * pre-scale design pixels it is actually declared in. */
@media (pointer: coarse) {
  a, button, [role="button"], input[type="submit"], input[type="button"] {
    position: relative;
  }
  a::after, button::after, [role="button"]::after,
  input[type="submit"]::after, input[type="button"]::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: max(calc(44px / var(--stage-scale, 1)), 100%);
    height: max(calc(44px / var(--stage-scale, 1)), 100%);
    transform: translate(-50%, -50%);
  }

  /* Step-indicator dots sit 8-10px apart. Growing every dot to 44px would
   * merge the whole row into one hit area and make individual dots
   * untappable, defeating the point of having separate ones. A smaller,
   * non-overlapping overlay still roughly doubles each dot's target without
   * erasing the gaps between them. Same reasoning for the inline play glyph
   * that substitutes for a letter mid-headline (about-origin.css): it sits
   * between two other characters with no room to spare. */
  .mp-carousel__dot::after, .wk-quad__dots i::after, .wk-slider__dots i::after,
  .ab-compass__dots i::after, .ao-play-o::after {
    width: max(calc(17px / var(--stage-scale, 1)), 100%);
    height: max(calc(17px / var(--stage-scale, 1)), 100%);
  }

  /* .tk-btn--bronze and .tk-btn--gold-outline (the two hero CTAs) already
   * use ::after for a shimmer sweep and set overflow: hidden to contain its
   * animation, which would also clip an overlay meant to extend past their
   * box. Both are 60px tall natively, comfortably over 44 at every width
   * this site actually ships (only dipping under it briefly around the
   * tablet-scale midpoint), so this is a known, narrow, non-visual gap
   * rather than a fix forced through by touching their animation. */
}

/* ---- 5. The sticky bar keeps its menu on a phone ---------------------------
 *
 * sticky-nav.js clones the header into a bar appended to <body>, because the
 * real header sits inside a transform:scale() stage and cannot be fixed to the
 * viewport from in there. The clone escapes the stage, and with it every rule
 * written as `.tk-stage.is-mobile .tk-menu-toggle`, which is how the hamburger
 * is shown. Outside the stage those selectors never match, so the clone falls
 * back to `.tk-menu-toggle { display: none }`.
 *
 * Measured at 390px, scrolled: the bar appears carrying the logo and nothing
 * else. No links, no menu button. On a phone, scrolling past the header leaves
 * a visitor with no navigation at all until they scroll back to the top.
 *
 * Their original does exactly the same — verified against baseline/ at the
 * same width, same result — so this is inherited rather than a port
 * regression. It is still a site you cannot navigate on a phone.
 *
 * 768px is their own MOBILE_BP, read from App.jsx rather than chosen here, so
 * the clone flips to mobile on the same width the stage does. sticky-nav.js
 * already forwards a click on the clone's button to the live one, so showing
 * it is all that was missing.                                               */
@media (max-width: 767.98px) {
  .tk-stickynav .tk-menu-toggle { display: inline-flex !important; }
  .tk-stickynav .tk-nav,
  .tk-stickynav .tk-btn { display: none !important; }
}

/* ─────────────────────────────────────────────────────────────────────────────
 * 6. Changes Ted asked for. `source: ted · recorded: 2026-08-26`
 *
 * Not responsive work. These apply at every width and each one is a request in
 * his walkthrough, quoted where the wording matters.
 * ────────────────────────────────────────────────────────────────────────── */

/* "This logo scroller is a little bit fast. I would slow it down a tiny bit."
 *
 * 40s to 56s. The distance is fixed (translateX(-50%) over a duplicated
 * track), so the duration is the only control and 40% longer is the "tiny bit"
 * he is describing — still moving, no longer hurrying. */
.tk-logobar__track { animation-duration: 56s !important; }

/* "This font should be changed to match the font of the scroll section that
 * follows it."
 *
 * "The fragility and resilience of humanity is beautiful" is .cine-line in
 * CinematicManifesto, set inline in 'Caveat', cursive — a handwriting face.
 * The section immediately after it in App.jsx is CompassOrrery, which is set
 * in var(--font-display). That is the match he is asking for.
 *
 * !important because the value is an inline style on the element, which no
 * ordinary rule can outrank. Size, weight and line-height are deliberately
 * left alone: he asked about the typeface, and Caveat at 700 is a handwriting
 * weight that the display serif carries differently, so its own normal weight
 * is what "matching" means here.
 *
 * This moves the desktop pixel diff for / on purpose. */
.cine-line {
  font-family: var(--font-display) !important;
  font-weight: 600 !important;
}

/* The library's View More button.
 *
 * Styled as one of their own filter chips rather than as a new control: it
 * sits directly beneath the grid those chips filter, and inventing a second
 * button language for "show me more of what I just filtered" would read as
 * two different systems on one page. `source: ted · recorded: 2026-08-26` */
.flib__more { display: flex; justify-content: center; padding: 34px 0 8px; }
.flib__more-btn { cursor: pointer; }

/* ============================================================
   §7  TEDFLIX plays the films
   ------------------------------------------------------------
   Influex's progress bar was an 8-second CSS sweep because the
   player was an 8-second simulation. Now that a real file plays,
   the bar is set from currentTime/duration on every timeupdate,
   and an animation racing that would fight it for the width.

   The sweep stays for a film with no playable file, which still
   holds the screen on its poster for a few seconds — there the
   bar is the only thing telling you the playlist is still moving.
   ============================================================ */
.tplayer__progress--live i { animation: none; transition: width .25s linear; }

/* The video sits over their poster frame and under the chrome:
   count, close and the control bar all carry z-index 4. */
.tplayer__video { z-index: 2; }

/* ============================================================
   §8  Brands Built for Others, on the AI/Web/Graphic page
   ------------------------------------------------------------
   "Add the brands-built-for-others cards at the bottom."
   `source: ted · recorded: 2026-08-26`

   The cards come from work.css unchanged. This is only the band
   they sit in: the same horizontal padding as .wgd-cats above
   them so the grid lines up with the categories, and a seam so
   the section reads as its own beat rather than a fifth category.

   .wk-subtitle carries a large top margin of its own for the
   Work with Ted layout, so the band's top padding is deliberately
   small — the two together are the gap.
   ============================================================ */
.wgd-brands {
  position: relative;
  padding: 0 clamp(24px, 5vw, 90px) clamp(70px, 11vh, 140px);
  border-top: 1px solid rgba(209, 157, 99, 0.16);
}
.wgd-brands__inner { max-width: 1720px; margin: 0 auto; }

/* Four across is right beside the category grids on a wide screen and
   unreadable on a narrow one. work.css sets the four; these two steps
   are for the phone and tablet this page is also read on. */
@media (max-width: 1100px) {
  .wgd-brands .wk-cardgrid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .wgd-brands .wk-cardgrid { grid-template-columns: 1fr; }
}

/* ============================================================
   §9  The 3D hero
   ------------------------------------------------------------
   Ted's single-take persona video, alpha-matted into one living
   loop, over five room plates that crossfade on the take's own
   beats. From `hero3d` at INFINITSTUDIOS/tedverse-dashboard
   (9812c72), reproduced here rather than in public/src/site.css
   because that file is regenerated from baseline/ by
   scripts/port-site.mjs and hand edits to it are lost.

   --mx / --my are smoothed -1..1 cursor coordinates published on
   the section by Hero.jsx. Every layer multiplies them by its own
   depth, so the room, Ted, the glow and the type each move on a
   separate plane. Doing it in CSS rather than in the component is
   what keeps one rAF loop driving eight moving things.
   ============================================================ */
.tk-hero__scene {
  position: absolute; inset: 0;
  z-index: 0;
  overflow: hidden;
  background: #0a0705;
  perspective: 1400px;
}

/* Room plates — shown as shot, crossfading on the video's persona beats,
   drifting gently against the cursor */
.h3d__bg {
  position: absolute; left: -8%; top: -4%;
  width: 116%; height: 106%;
  object-fit: cover; object-position: center 30%;
  transform: translate3d(calc(var(--mx, 0) * -2.2%), calc(var(--my, 0) * -1.4%), 0);
  opacity: 0; transition: opacity 1300ms ease;
}
.h3d__bg.is-on { opacity: 1; }

/* Ted plate — sharp, larger travel, slight tilt toward the cursor */
.h3d__cut {
  position: absolute; left: -5%; top: -3%;
  width: 110%; height: 105%;
  object-fit: cover; object-position: center 30%;
  transform:
    translate3d(calc(var(--mx, 0) * 3.8%), calc(var(--my, 0) * 2.4%), 0)
    rotateY(calc(var(--mx, 0) * 6deg))
    rotateX(calc(var(--my, 0) * -3deg));
  filter: drop-shadow(0 40px 60px rgba(0, 0, 0, 0.55));
}
.h3d__still { animation: h3dCondense 800ms ease both; }

/* Living take: hidden until its first 'playing' event, then it takes
   over from the still (which fades so edges don't double). */
.h3d__vid { opacity: 0; transition: opacity 500ms ease; object-fit: cover; }
.tk-hero__scene.has-live .h3d__vid   { opacity: 1; }
.tk-hero__scene.has-live .h3d__still { opacity: 0; transition: opacity 500ms ease; animation: none; }
/*
 * The entrance keeps its bloom and loses its opacity ramp, because opacity was
 * gating the page's Largest Contentful Paint.
 *
 * This image IS the LCP element. Influex animated it `from { opacity: 0 }` with
 * `both` fill, so it was fully transparent until the animation ran — and Chrome
 * does not count a transparent element as painted. LCP therefore waited out the
 * entrance: 1,602ms of "element render delay" against a completely idle main
 * thread, which is the signature of content waiting for permission to paint
 * rather than for work to finish.
 *
 * `filter` does not gate LCP. Only opacity, visibility and display do. So the
 * blur and the over-brightness stay exactly as they were and the fade is
 * dropped: Ted condenses out of a bloom instead of fading up from nothing. It
 * is the same beat — arguably a truer one, since a matte emerging from light
 * reads better than one emerging from black — and the browser can paint it on
 * the first frame.
 */
@keyframes h3dCondense {
  from { filter: blur(16px) brightness(1.6) drop-shadow(0 40px 60px rgba(0,0,0,0.55)); }
  to   { filter: blur(0)    brightness(1)   drop-shadow(0 40px 60px rgba(0,0,0,0.55)); }
}

/* Gold spotlight that follows the cursor across the scene */
.h3d__glow {
  position: absolute; inset: -10%;
  pointer-events: none;
  background: radial-gradient(
    640px circle at calc(50% + var(--mx, 0) * 40%) calc(42% + var(--my, 0) * 40%),
    rgba(209, 157, 99, 0.17), rgba(209, 157, 99, 0.05) 45%, transparent 68%);
  mix-blend-mode: screen;
}

/* The headline stack counter-drifts a touch for depth against Ted */
.tk-hero__center {
  transform: translate3d(calc(var(--mx, 0) * -18px), calc(var(--my, 0) * -12px), 0);
}

@media (prefers-reduced-motion: reduce) {
  .h3d__bg, .h3d__cut, .tk-hero__center { transform: none !important; }
  .h3d__still { animation: none; }
}

/* The measuring twin and the width glide, from the same source. The box
   glides to the incoming word's measured width during the dissolve, so the
   centred "THROUGH <word>" line drifts instead of jumping. */
.tk-hero .through .film-text {
  display: inline-block;
  min-width: 1ch;
  transition: width 600ms cubic-bezier(0.4, 0, 0.2, 1);
}
.tk-hero .through .film-measure {
  position: absolute;
  visibility: hidden;
  white-space: nowrap;
  transition: none;
}

/* The old full-bleed hero video element is gone from the markup; this keeps
   any cached stylesheet from reserving space for it. */
.tk-hero__video { display: none; }

/* ============================================================
   §10  The featured reel opens without a mark on it
   ------------------------------------------------------------
   "Remove the play button and just have it playing."
   `source: dan · recorded: 2026-08-26`

   The reel is a full-bleed backdrop behind the awards and the
   floating comment cards, and it autoplays. A play triangle over
   it read as a poster waiting to be started.

   The way into the film is now the stage itself. A real <button>
   rather than a click handler on the div, so it keeps a name, a
   tab stop and a focus ring — invisible is not the same as
   inaccessible, and a bare div would have been both.
   ============================================================ */
.fwork-stage .fwork-open {
  position: absolute;
  inset: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  /* No appearance of its own, but still focusable and still announced. */
  appearance: none;
}
.fwork-stage .fwork-open:focus-visible {
  outline: 2px solid var(--lp-peach, #E9A968);
  outline-offset: -6px;
}

/* The comment cards drift above it and are decorative, so they must not
   swallow the click that reaches the film. */
.fwork-stage .fwork-reviews { pointer-events: none; }

/* Their play button is gone from the markup; this stops a cached stylesheet
   reserving space or painting a triangle for an element that no longer exists. */
.fwork-stage .fwork-play { display: none; }

/* ─────────────────────────────────────────────────────────────────────────────
 * 7. Phone bugs Ted reported. `source: ted · recorded: 2026-08-31`
 *
 * Mobile-only again, so the same structural guarantee as sections 1-5 holds:
 * rules that do not exist above 780px cannot move the desktop composition that
 * is proven pixel-identical to the frozen original.
 * ────────────────────────────────────────────────────────────────────────── */

@media (max-width: 780px) {

  /* ---- 7a. "The Burning Man video is not showing up on mobile." -----------
   *
   * It was not a video problem. `.fwork-stage` asks for `width: 100vw`, but on
   * a phone its parent `.tk-fscroll.fwork` is a flex container, and every one
   * of the stage's children (the video, the fade, the reviews, the open
   * button) is absolutely positioned. That gives the stage a min-content width
   * of zero, so flex-shrink is free to crush it to 0px wide — which it does.
   *
   * A zero-area box never satisfies an IntersectionObserver. The reel's src is
   * deliberately withheld until the section is scrolled into view, to keep a
   * 19.69 MB download off first paint, so `isIntersecting` never firing means
   * the src is never set and the element stays empty forever. The performance
   * win quietly turned into a permanently black rectangle, and only on phones,
   * because only there is the parent a flex container.
   *
   * The parent is `flex-direction: column` with `align-items: center`, and in a
   * column flex container align-items sizes the CROSS axis, which is the width.
   * `center` means the item is sized to fit its content, and since every child
   * is absolutely positioned there is no in-flow content to measure, so the
   * stage resolves to 0px wide. Its own `width: 100vw` does not save it: an
   * inline `width: 100vw` was measured here and still produced 0.
   *
   * `align-self: stretch` opts this one item back into filling the cross axis,
   * which is what restores the width, which re-arms the observer, which sets
   * the src. Measured, not reasoned: stretch gives 390px where an inline width
   * gives 0. The lazy-load behaviour itself is untouched.                    */
  .fwork-stage {
    align-self: stretch;
  }

  /* ---- 7b. "There is a slight overlay on this image in mobile." -----------
   *
   * Two defects stacked on the director portrait.
   *
   * The vignette is a `::after` with `inset: 0`, which should make it span the
   * figure. Their mobile rule then adds `height: 42%`. Height and a 0/0
   * top/bottom pair are over-constrained, so height wins and bottom is
   * dropped: the black gradient covers the top 176px of a 420px figure and
   * then stops dead, leaving the hard horizontal edge Ted can see. At desktop
   * the same declaration overshoots to 1240px against a 930px figure and is
   * harmlessly clipped, which is why it only reads as a bug on a phone.
   *
   * Underneath it, `object-fit: contain` fits a square 1633x1633 source into a
   * 390x420 box, so the portrait is letterboxed with a band above and below.
   * Two horizontal seams on one image is what reads as "an overlay".         */
  .tk-stage.is-mobile .tk-figure::after { height: 100%; }
  .tk-stage.is-mobile .tk-figure img { object-fit: cover; }

  /* ---- 7c. "The header title overflows and the word is cut off." ----------
   *
   * `.pathways-title` carries `white-space: nowrap` with a font-size that
   * clamps at a 34px floor. "There is a path here for you." needs 565px at
   * that size and the column is 342px, so it was clipped by 223px and the last
   * word was simply gone. Ted asked for it to wrap to the next line instead,
   * which is what removing the nowrap does; `text-wrap: balance` keeps the two
   * resulting lines close in length rather than leaving one orphan word.     */
  .pathways-title {
    white-space: normal;
    text-wrap: balance;
  }

  /* ---- 7d. "The titles are super squished. TedPlots, TedThoughts on mobile,
   *          it's unreadable." ---------------------------------------------
   *
   * The category wordmark (word-tedplots.png and friends, 858x235) sits in
   * `.tk-tedivider`, a flex row between two gold rules. That row carries
   * `padding: 0 200px` from the desktop composition. On a 390px screen that is
   * 400px of padding inside a 400px box, so the content width is exactly ZERO
   * and the image is drawn 0px wide at its full 81px height. A 3.65:1 wordmark
   * squeezed to nothing is the unreadable smear Ted is looking at.
   *
   * Their own mobile rule for this divider is scoped to `.tk-stage.is-mobile`,
   * a class this section's ancestor does not carry, so the padding was never
   * reduced here. Measured: at `padding: 0 16px` the image resolves to 294px,
   * which is exactly its natural ratio at the height it is given. Forcing
   * `flex: none` on the image alone changes nothing, because the problem is
   * the container having no room to give.                                    */
  .tk-tedivider {
    padding-left: 16px;
    padding-right: 16px;
  }

  /* With room to exist, the image is then sized by the space the flex line has
   * left rather than by its own ratio, and `object-fit` defaults to `fill`, so
   * the four wordmarks are different shapes stretched into one identical box.
   * Measured across the tabs: TedPlots landed at 0.99 of its true ratio but
   * TedDrops at 0.83 and TedThoughts at 0.68, visibly squashed. `contain` makes
   * distortion impossible whatever width the row hands it, and the transparent
   * PNG means the letterboxing it introduces is invisible. The tighter gap
   * gives the wordmark back the width the two rules were holding.            */
  .tk-tedivider--logo { gap: 16px; }
  .tk-tedivider--logo .tk-tedflix { object-fit: contain; }

}
