/* COPY of design/v2/containers.css — that file is the source; change it there and re-copy.
   The docroot must be self-contained: design/ sits above site/public and is unreachable. */
/* ============================================================================
   losses — CONTAINERS
   ----------------------------------------------------------------------------
   The middle of three layers. Read them in this order, because each one is only
   allowed to answer its own question:

     tokens.css      what things look like   (colour, type, space, gradients)
     containers.css  WHERE THINGS GO         (this file)
     the page CSS    what things are         (beats, ledgers, tables, controls)

   The rule that keeps the layering honest: **this file may not name a single
   component.** No `.beat`, no `.wrap`, no `.fignum`. If a rule here mentions
   something the reader could point at on the page, it is in the wrong file.
   In return, the component layer may not set a width, a page-level margin or a
   grid column — it asks for a track by name instead.

   Governs every losses surface (D20). Only the session report is ported onto it
   so far; the landing pages still carry their own layout and are expected to
   move here when one of them is actually built rather than sketched.
   ============================================================================ */

:root{
  --gutter:clamp(20px,4vw,56px);   /* page edge to first content            */
  --spine:clamp(18rem,23vw,25rem); /* the claim column                      */
  --split:clamp(36px,4.5vw,76px);  /* the channel between the two halves    */
  --masthead-h:63px;               /* sticky chrome; every sticky top offsets from it */

  /* The measure is a MEASURE, not the leftovers. Every block that sits in it is
     a name-at-the-left / value-at-the-right pairing — ledger rows, disclosure
     summaries, table cells — and past about 47rem the value is far enough from
     its name to cost a head-turn per row. So it is capped and a wide screen's
     surplus is banked as margin around the whole split rather than spent
     stretching rows. This is the finding that killed the earlier four-track
     system, which widened blocks that were worse for being wide (D20). */
  --measure:47rem;
}

/* ---------------------------------------------------------------- the grid --
   One grid, five named areas. A block opts into a track by name and never
   carries a max-width of its own — that is the whole contract.

     full     edge to edge, including the gutters
     spine    the claim column
     measure  the working column
     body     spine + channel + measure, i.e. everything but the gutters
     (the channel itself is deliberately unnamed: nothing may sit in it)

   `body` is the important one. It is what a section rule spans, and it exists
   because the previous system drew rules on a wider track than the content
   beneath them, so every divider ran ~270px past the last thing under it. A
   rule now spans exactly the columns that sit under it, by construction.
   -------------------------------------------------------------------------- */
.page{
  display:grid;
  grid-template-columns:
    [full-start] minmax(var(--gutter),1fr)
    [spine-start body-start] minmax(0,var(--spine)) [spine-end]
    var(--split)
    [measure-start] minmax(0,var(--measure)) [measure-end body-end]
    minmax(var(--gutter),1fr) [full-end];
}
.page > *{grid-column:body}
.t-body{grid-column:body}
.t-full{grid-column:full}
.t-spine{grid-column:spine}
.t-measure{grid-column:measure}

/* ------------------------------------------------------------- the split --
   The page's one compositional primitive: a claim and the working that backs
   it up, side by side, with the claim held still while the working scrolls
   past. On the report the claim is a section's label and the sentence it is
   making; on a landing hero it is the headline and the working is the artwork.
   Same shape, different content, so it lives here and not in either page's CSS.

   Rows are STATED, not inferred. Auto-placement puts the two halves on separate
   rows as soon as anything else (a divider pseudo-element) has taken row 1 —
   and a sticky element whose grid area is only as tall as itself has nothing to
   stick within, so the claim silently stops pinning. This is the single most
   fragile thing in the file; do not delete the grid-row declarations.
   -------------------------------------------------------------------------- */
.split > .claim,
.split > .working{grid-row:2}
.claim{
  grid-column:spine;
  position:sticky;align-self:start;
  top:calc(var(--masthead-h) + var(--stack-l));
  padding-bottom:var(--stack-l);
}
.working{grid-column:measure;min-width:0}
/* Nothing in the working column opens with its own top margin — the column's
   top edge is already set by whatever contains it. */
.working > :first-child{margin-top:0}

/* ------------------------------------------------ the instrument variant --
   A split whose claim column is a control surface rather than a sentence. The
   claim does not pin as a whole: its opening copy is read once and scrolls
   away, and the component inside it pins instead — which is the component's
   own business, so only the un-pinning lives here.

   `align-self:stretch` is the load-bearing half. A sticky child resolves
   against its containing block, and `align-self:start` collapses the claim's
   box to its own content, leaving the child nothing to stick within. Same trap
   as the grid-row declarations above, one level down.
   -------------------------------------------------------------------------- */
.split.instrument > .claim{position:static;align-self:stretch}

/* A sticky claim resolves against its nearest SCROLL CONTAINER, so any ancestor
   with `overflow:hidden` silently un-pins it. Use `clip`, which clips without
   creating one. Kept here as a rule rather than a comment because it is a trap
   that has already been fallen into once. */
.clip{overflow:clip}

/* ------------------------------------------------------------- the collapse --
   Below this width there is not enough room for two readable columns, so the
   claim un-sticks and stacks above its working. Reading order is identical
   either way, because the claim already precedes the working in the markup —
   which is the reason the split is two elements in source order and not a
   visual reordering.

   980px rather than a round 1024: a half-screen window on a large monitor and a
   tablet in landscape both keep the split, and at 980 the working column is
   still ~34rem, comfortably inside the measure.
   -------------------------------------------------------------------------- */
@media(max-width:979px){
  .page{
    grid-template-columns:
      [full-start] minmax(var(--gutter),1fr)
      [spine-start body-start measure-start] min(46rem,100% - var(--gutter)*2)
      [spine-end measure-end body-end] minmax(var(--gutter),1fr) [full-end];
  }
  .split > .claim{grid-row:2;position:static;padding-bottom:0;
    margin-bottom:var(--stack-l)}
  .split > .working{grid-row:3}
}

/* ---------------------------------------------------------------- chrome --
   The masthead sits OUTSIDE .page and carries an inner .page of its own: a
   sticky element is confined to its containing block, and a wrapper only as
   tall as the masthead would unstick it on the first pixel of scroll. The rule
   is full-bleed; the type inside it aligns to the grid.
   -------------------------------------------------------------------------- */
.masthead{position:sticky;top:0;z-index:20;background:var(--bg)}
.masthead > .page{align-items:baseline;padding:20px 0 17px}

/* The promo strip sits ABOVE the masthead and is pointedly not sticky: it is an
   arrival message, so it takes the top of the page once and then scrolls away,
   leaving the masthead to pin at 0. That is why --masthead-h is unaffected by it
   and no scroll offset has to account for it. */
.promo > .page{padding:9px 0}

/* The layer that covers the banner. It needs its own stacking context and an
   opaque ground, or the page would render over a photograph.

   `max-width` and `margin` are neutralised here on purpose: tokens.css still
   carries a legacy `.wrap{max-width:1140px;margin:0 auto}` from before the
   layers were separated, and it loads first. Left alone it silently clamps the
   whole grid — the symptom is the banner showing through in the gutters,
   because the covering sheet is narrower than the page. That rule wants
   deleting from tokens.css, but the design/v2 mockups still lean on it. */
.sheet{position:relative;z-index:1;background:var(--bg);max-width:none;margin:0}

/* ---------------------------------------------------------------- banner --
   A full-bleed image the page slides over. The mechanism is two rules and no
   script: the banner is sticky at the masthead's height and .sheet is an opaque
   layer above it, so the banner holds still and is covered rather than scrolled.
   The parallax is a consequence of the stacking, not an effect being animated —
   nothing to throttle, and it costs nothing on a phone.

   NOTE — the banner is the one place D11's white ground is knowingly broken.
   "No section backgrounds, no hero fills" forbids it outright and a headline
   over a photograph cannot be ink. The trade is fenced to this element: nothing
   below it gains a fill, a tint or a light-on-dark type colour. See D20.

   Two things vary PER IMAGE and are therefore variables, not constants: the
   focal point (so a crop does not behead the subject — a portrait viewport
   crops horizontally and will drop an off-centre figure off the edge) and the
   scrim weight (a pale photograph needs roughly twice the veil of a near-black
   one before white type is safe on it). Whatever supplies the image supplies
   all three.
   -------------------------------------------------------------------------- */
.banner{
  --hero:none;
  --hero-pos:50% 50%;
  --scrim:.66;
  position:sticky;top:var(--masthead-h);z-index:0;
  height:min(88vh,780px);
  display:grid;align-items:start;
  overflow:clip;
  background-color:#1A1A18;
  background-image:var(--hero),
    linear-gradient(158deg,#33322D 0%,#1F1E1B 52%,#100F0E 100%);
  background-size:cover;background-position:var(--hero-pos);
  background-repeat:no-repeat;
}
/* Top-weighted, because the copy is at the top. Scrimming the bottom would kill
   the transition into the page: a banner whose lower edge is light resolves
   INTO the white ground instead of cutting against it. */
.banner::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to bottom,
    rgba(0,0,0,var(--scrim)) 0%,
    rgba(0,0,0,calc(var(--scrim) * .52)) 28%,
    rgba(0,0,0,calc(var(--scrim) * .09)) 56%,
    rgba(0,0,0,0) 72%)}
.banner > .page{position:relative;z-index:1;padding-top:clamp(30px,4.5vw,60px)}
.banner-copy{grid-column:measure}

@media(max-width:979px){
  .banner{height:min(72vh,560px);
    background-position:var(--hero-pos-narrow,var(--hero-pos))}
  .banner-copy{grid-column:body}
}

/* The reveal is scroll-linked movement. Someone who has asked for less of that
   gets an ordinary banner that scrolls away with the page. */
@media(prefers-reduced-motion:reduce){.banner{position:static}}

/* Nothing should ever come to rest under the sticky masthead. */
[id],.scroll-target{scroll-margin-top:calc(var(--masthead-h) + var(--stack-l))}
