/* ============================================================== the page
   Three rows, measured rather than assumed: the header is as tall as it renders, the
   feed takes what is left, the controls sit under it. Nothing overlaps, so no padding
   constant has to track a header that reflows at narrow widths.

   svh, not vh: on a phone vh is the viewport with the browser chrome retracted, so a
   100vh page is taller than what you can see until you scroll.

   Every track is sized from the viewport downward. The first attempt at this grid left
   the column implicit, so it sized to the clip's own max-content: at a 320px viewport
   the header and feed rendered 365px wide and only `overflow:hidden` hid it. An
   explicit minmax(0,1fr) column cannot be widened by its contents. */
.reelpage { --tall:760px;   /* the tallest the clip may be on a wide screen */

  /* The reel view is dark in BOTH themes, like any video player: the clip should be the
     only lit thing on the screen. The tokens are redefined here rather than the
     background alone, so everything inside - headings, buttons, rules - re-resolves
     against this surface. With only `background:var(--bg)` the page rendered a white
     feed between a dark header and dark controls, with the site's dark body text on it.
     `color` is restated because body's own `color:var(--ink)` was resolved against the
     site tokens and would otherwise be inherited straight through. */
  --bg:#070d0b; --band:#0f1714; --ink:#eef3f1; --soft:#afbbb5; --faint:#93a6a0;
  --rule:#2a3630; --acc:#69d9bd; --acc-ink:#07241c; color-scheme:dark;

  height:100svh; display:grid; grid-template-rows:auto minmax(0,1fr) auto;
  grid-template-columns:minmax(0,1fr); overflow:hidden;
  background:var(--bg); color:var(--ink); }
@supports not (height: 100svh) { .reelpage { height:100vh; } }

/* nav — a row, not a fixed overlay. Explicit colours: inherited dark-on-dark made the
   logo and the back link almost unreadable against the clip behind them. */
.reelpage .nav { position:static; z-index:auto; display:flex; align-items:center;
  gap:18px; flex-wrap:nowrap; min-height:0;
  padding:10px max(14px,env(safe-area-inset-left,0px))
          10px max(14px,env(safe-area-inset-right,0px));
  padding-top:calc(10px + env(safe-area-inset-top,0px));
  background:#0a1512; border-bottom:1px solid rgba(255,255,255,.1); }
.reelpage .nav .shell { display:contents; }   /* the shared wrapper must not re-wrap */
.mark { font-weight:800; font-size:17px; letter-spacing:-.04em; color:#f2f7f5; }
.mark i { font-style:normal; color:var(--acc); }
.reelpage .nav nav { display:flex; gap:16px; font-size:13.5px; }
.reelpage .nav nav a { color:#b8c7c1; }
.reelpage .nav nav a.on { color:#f2f7f5; }
.reelpage .nav .sp { flex:1 1 auto; }
.reelpage .nav .btn { font:500 12.5px var(--f); border:1px solid rgba(255,255,255,.26);
  border-radius:999px; padding:7px 14px; color:#f2f7f5; min-height:0; white-space:nowrap; }
.reelpage .nav .pols { display:flex; gap:14px; }
.reelpage .nav .pol { font:11px var(--f-mono); color:#93a6a0; letter-spacing:.04em;
  white-space:nowrap; }
.reelpage .nav .pol:hover { color:#f2f7f5; }

/* Two rows on a narrow phone. The header is ALLOWED to reflow now - that used to be the
   bug, because a fixed header that grew covered the clip, and it is simply a grid row
   today: a second line costs the clip a few pixels of height and hides nothing. In one
   line the Library button ran 45px past the header at 320px and 85px at 280px, clipped
   out of sight by .reelpage's overflow:hidden. */
@media (max-width:560px) {
  .reelpage .nav { display:grid; grid-template-columns:minmax(0,1fr) auto;
    gap:6px 12px; align-items:center; }
  .reelpage .nav nav, .reelpage .nav .sp { display:none; }
  .reelpage .nav .mark { grid-area:1/1; }
  .reelpage .nav .btn { grid-area:1/2; justify-self:end; }
  .reelpage .nav .pols { grid-area:2/1/auto/3; }
}
@media (max-width:420px) { .reelpage .nav .pol { font-size:10px; } }

/* feed
   Heights use svh, not vh. On a phone vh is the viewport with the browser chrome
   RETRACTED, so a 100vh slide is taller than what you can actually see until you
   scroll - which is why the title and buttons sat below the fold and the fixed header
   appeared to cover the clip. svh is the smallest viewport, so the layout fits from
   the first paint. dvh/vh are kept as fallbacks for browsers without svh. */
/* The feed is the middle row, so it has a definite height and scrolls inside itself.
   Previously `height:100%` had no sized parent, so it fell back to content height -
   about 17,500px - and the DOCUMENT scrolled instead. Scroll-snap then had nothing to
   snap within, and an end card could share a screen with the reel before it. */
.feed { height:100%; min-height:0; overflow-y:auto; scroll-snap-type:y mandatory;
  scroll-behavior:smooth; scrollbar-width:none; overscroll-behavior-y:contain; }
.feed::-webkit-scrollbar { display:none; }
/* Both tracks are explicit and bounded, so the row the clip lives in is a definite
   height rather than one that grew to fit the clip. A percentage height only resolves
   against a definite containing block; against an auto row that sized itself to its
   content it resolves against the content it was supposed to limit, which is why
   `max-height:100%` let the clip run 220px past the bottom of the feed. */
/* container-type:size makes the slide's own content box measurable in CSS, which is how
   the phone layout bounds the clip by width without hard-coding the padding. It is legal
   here precisely because the slide's size owes nothing to its contents: its height is
   100% of a definite feed row and its width is the feed's content width. */
.slide { height:100%; scroll-snap-align:start; scroll-snap-stop:always;
  display:grid; grid-template-rows:minmax(0,1fr); grid-template-columns:minmax(0,1fr);
  place-items:center; padding:16px 20px; container-type:size; }

/* Side by side. minmax(0,360px) lets the text column give way first when the window is
   narrow, which is what keeps the clip's own track intact. */
.stage { height:100%; max-width:100%;
  display:grid; grid-template-columns:auto minmax(0,360px);
  grid-template-rows:minmax(0,1fr); gap:28px; align-items:center; }

/* The clip's track is computed from the height the clip will take, so it is exactly as
   wide as the clip needs and the ratio can never be squeezed by the text beside it.
   `auto` was not enough: the track measured the clip's intrinsic width, the text column
   then claimed its 360px, and max-width:100% squeezed the clip back to 402x760 - 0.53,
   not 0.56 - on an 830px-wide window. */
@supports (container-type: size) {
  .stage { grid-template-columns:calc(min(100cqh, var(--tall)) * 9 / 16) minmax(0,360px); }
}

/* The height is a share of a definite row, not of the viewport: 78vh ignored the header
   and the controls and overflowed a short window. */
.frame { position:relative; height:min(100%,var(--tall)); max-width:100%; align-self:center;
  aspect-ratio:9/16; border-radius:20px; overflow:hidden;
  background:#050b09; border:1px solid var(--rule); box-shadow:0 24px 70px rgba(0,0,0,.6); }
.frame video,.frame img { width:100%; height:100%; object-fit:cover; }
.frame .tap { position:absolute; inset:0; border:0; background:transparent; cursor:pointer; }
.frame .play { position:absolute; inset:0; margin:auto; width:66px; height:66px; border-radius:50%;
  background:rgba(6,14,11,.5); border:1.5px solid rgba(255,255,255,.85); display:grid; place-items:center; pointer-events:none; transition:opacity .18s; }
.frame .play::after { content:""; width:0; height:0; margin-left:5px; border-left:18px solid #fff; border-top:12px solid transparent; border-bottom:12px solid transparent; }
.frame.playing .play { opacity:0; }
.frame .pending { position:absolute; left:0; right:0; bottom:0; padding:18px 16px;
  background:linear-gradient(to top,rgba(5,11,9,.95),transparent); font:11px/1.6 var(--f-mono); color:var(--faint); }
.frame .prog { position:absolute; left:14px; right:14px; top:14px; display:flex; gap:4px; }
.frame .prog i { flex:1; height:3px; border-radius:3px; background:rgba(255,255,255,.26); overflow:hidden; }
.frame .prog i b { display:block; height:100%; width:0; background:var(--acc); }

/* the panel beside the clip */
.meta { max-width:min(100%,360px); padding-bottom:10px; }
.meta .kick { display:block; font:500 11px/1.5 var(--f-mono); letter-spacing:.12em; text-transform:uppercase; color:var(--acc); }
.meta h2 { margin:14px 0 0; font-size:clamp(20px,2.2vw,27px); line-height:1.2; font-weight:600; letter-spacing:-.03em; text-wrap:balance; }
.meta .line { margin-top:12px; font-size:14.5px; color:var(--soft); }
.meta .facts { margin-top:14px; font:11px/1.8 var(--f-mono); color:var(--faint); }
.meta .tags { display:flex; gap:7px; flex-wrap:wrap; margin-top:14px; }
.meta .tag { border:1px solid var(--rule); border-radius:999px; padding:5px 12px; font:11px var(--f-mono); color:var(--soft); }
.meta .acts { display:flex; gap:10px; flex-wrap:wrap; margin-top:22px; }
.btn { display:inline-flex; align-items:center; justify-content:center; min-height:46px; padding:11px 20px;
  border:1px solid var(--rule); border-radius:999px; background:transparent; color:var(--ink);
  font:500 14px var(--f); cursor:pointer; transition:background .18s,border-color .18s; }
.btn:hover { border-color:var(--soft); background:var(--surf); }
.btn.pri { background:var(--acc); border-color:var(--acc); color:var(--acc-ink); }
.btn.pri:hover { filter:brightness(.93); }

/* rail — the third grid row, on every width. */
.rail { position:static; display:flex; align-items:center; justify-content:center;
  gap:16px; padding:10px 16px calc(10px + env(safe-area-inset-bottom,0px));
  border-top:1px solid rgba(255,255,255,.08); background:#0a1512; }
.rail button { width:44px; height:44px; border-radius:50%;
  border:1px solid rgba(255,255,255,.26); background:rgba(255,255,255,.07);
  color:#f2f7f5; cursor:pointer; display:grid; place-items:center; }
.rail button:hover { border-color:var(--acc); color:var(--acc); }
.rail button[disabled] { opacity:.32; cursor:default; }
.rail .count { font:11px var(--f-mono); color:var(--faint); }
.rail svg { width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:2; }
.hint { position:fixed; left:50%; bottom:18px; transform:translateX(-50%); z-index:15;
  font:11px var(--f-mono); color:var(--faint); letter-spacing:.06em; }
.hint .k { border:1px solid var(--rule); border-radius:4px; padding:1px 5px; }

/* the card at the end of a page of reels */
/* 100%, not 92vw: vw is the viewport including the slide's own padding, so at 320px the
   card was wider than the space it sat in. max-height keeps a long card inside the feed
   row on a short screen rather than pushing the controls off the bottom. */
.endcard { max-width:min(100%,460px); max-height:100%; overflow-y:auto;
  text-align:center; }
.endcard .kick { display:block; font:500 11px/1.5 var(--f-mono); letter-spacing:.12em;
  text-transform:uppercase; color:var(--acc); }
.endcard h2 { margin:14px 0 0; font-size:clamp(24px,3vw,32px); line-height:1.15;
  font-weight:600; letter-spacing:-.03em; }
.endcard p { margin-top:12px; color:var(--soft); font-size:15px; line-height:1.6; }
.endcard .acts { display:flex; gap:10px; flex-wrap:wrap; justify-content:center; margin-top:24px; }

/* ---------------------------------------------------------------- phones
   The grid already reserves the header and the controls, so these rules only tune
   sizes. No offset constants: that is what went wrong before. */
@media (max-width:820px) {
  .slide { padding:12px 14px; }
  .stage { grid-template-columns:minmax(0,1fr); gap:10px; width:100%;
    justify-items:center; grid-template-rows:minmax(0,1fr) auto; }

  /* Fitting a fixed ratio inside a box takes arithmetic on ONE axis, not a cap on each.
     Capping the width at 340px while the height filled the row gave a 340x738 clip on a
     tablet - 0.46, not 0.56 - and object-fit:cover cropped the video to match. So the
     height is the largest 9:16 height that fits the row, the slide's own width, and the
     340px cap, whichever is smallest; the width follows from the ratio.
     `height:auto` with `max-height:100%` did not work at all: the percentage had nothing
     definite to resolve against and the clip rendered 599px tall inside a 461px feed. */
  .frame { height:min(100%, calc(340px * 16 / 9)); width:auto; max-width:100%;
    min-height:0; border-radius:14px; }
  /* Guarded: an unparsed height would fall back to auto, which is the bug above. Without
     container queries the clip still fits the row, it just may not fill it. */
  @supports (container-type: size) {
    .frame { height:min(100%, calc(100cqw * 16 / 9), calc(340px * 16 / 9)); }
  }

  .meta { max-width:min(100%,420px); padding-bottom:0; text-align:center; }
  .meta h2 { font-size:clamp(15px,4.2vw,19px); margin-top:8px;
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
    overflow:hidden; }
  .meta .kick { font-size:10px; }
  .meta .facts { display:none; }
  .meta .acts { margin-top:10px; justify-content:center; }
  .meta .acts .btn { min-height:40px; padding:8px 15px; font-size:13px; }
  .hint { display:none; }
}

/* Short screens: the title gives way before the clip does. */
@media (max-height:620px) {
  .meta h2 { -webkit-line-clamp:1; font-size:15px; }
  .meta .kick { display:none; }
  .rail { padding-top:6px; padding-bottom:calc(6px + env(safe-area-inset-bottom,0px)); }
  .rail button { width:38px; height:38px; }
}
