/* ==========================================================================
   MARKETPLACE - Swivl case study
   Rebuilt 2026-09-11 against neiden.framer.media.

   WHAT WAS TAKEN is the LAYOUT SYSTEM and the MOTION:
     - a three-column hairline grid that every section paints for itself
     - alternating paper / wash / void bands, the band owning its own inks
     - display type as the landmark, micro-caps as the connective tissue
     - bracketed eyebrows with a chevron ramp
     - scroll-linked text fill, blur-in reveals, sticky chapter columns
     - a numbered card grid where exactly one card is filled
     - full-bleed plates with the metadata sitting on a rule above them

   WHAT WAS DELIBERATELY NOT TAKEN is the COLOUR. The reference runs a signal
   red (#f02b42) on pure black and white. This page keeps the portfolio's
   maroon, the same call gpay.css records against its own reference: a case
   study wearing an unrelated brand colour reads as a template. Asked and
   answered before any of it was built.

   The face is Oswald + Montserrat, already on the page. Oswald is CONDENSED,
   so the reference's -8px display tracking is not transferable - that number
   only works on a wide geometric. Display tracking here is -.015em, tuned by
   eye against the reference's optical weight, not copied from it.
   ========================================================================== */

/* --------------------------------------------------------------------------
   01  TOKENS
   -------------------------------------------------------------------------- */
:root{
  /* The band palette. A band modifier re-declares these, which is the whole
     theming mechanism - nothing below reads a literal colour. */
  --ink:#0b0b0b;
  --ink-2:rgba(11,11,11,.68);
  /* .55, not .40. --ink-3 draws the 12px micro-caps that carry every label on
     the page, and at .40 they measure 2.70:1 on paper and 4.07:1 on the void -
     both under 4.5, and 12px bold is not WCAG "large text". Measured with the
     alpha composited off the painted background, which is the only way this
     reads correctly; modelling the cascade and dropping alpha reports these as
     passing. The floor lands at rgb(119) on paper, so the alpha has to be .56
     or more - .55 composites to rgb(121) and still measured 4.37:1. .60 is
     the value with headroom: 5.20:1 on paper, 6.97:1 on the void, 7.29:1 on
     the deep band, and it still reads recessed because the SIZE STEP is what
     makes it secondary here, not the tone. */
  --ink-3:rgba(11,11,11,.60);
  --line:rgba(11,11,11,.12);
  --face:#fff;

  /* THE FOUR FACES, named by their step rather than by their colour: in the
     dark theme --face-1 is not paper and --void is not a void. */
  --face-1:#fff;
  --face-2:#f2f2f2;
  --face-3:#191919;
  --face-4:#0b0b0b;

  --accent:#6d142f;          /* the fill. Never changes, in either theme. */
  --accent-ink:#6d142f;      /* the accent AS TEXT. Lifted on dark grounds. */
  --accent-on:#fff;          /* ink that sits on an accent fill */

  /* THE DARK-GROUND INK BUNDLE, as tokens rather than as literals inside
     .band--void. In the light theme only bands three and four are dark
     grounds; in the dark theme all four are, and they read the same bundle.
     Written out twice is how the two themes start disagreeing about what
     --ink-3 means. */
  --ink-dk:#fff;
  --ink-dk-2:rgba(255,255,255,.70);
  --ink-dk-3:rgba(255,255,255,.60);
  --line-dk:rgba(255,255,255,.13);
  --accent-dk:#e06880;

  --disp:'Oswald',system-ui,sans-serif;
  --sans:'Montserrat',system-ui,sans-serif;

  /* The gutter IS the nav's own edge, so the first grid line lands on the
     brand and the last on the CTA. Two different numbers here is how the
     grid stops looking like it belongs to the page. */
  --gut:clamp(20px,6vw,120px);
  --nav-h:72px;

  /* How far loose copy stands off a rule. BOXES (cards, panels, rows, steps,
     plates) are flush to the rules and inset their own content with their own
     padding - that flush edge is what makes them read as sitting ON the grid
     rather than near it. Only running type in a bare column pays this. */
  /* THE STANDOFF, FLAT 12PX. This is the one margin every box and every line
     of loose copy keeps from the hairline behind it, and it is deliberately
     not a clamp any more: at clamp(18,2.4vw,40) it grew with the viewport, so
     the phones in the screens fold sat 35px off their own column at 1440 and
     read as floating inside it rather than sitting in it. One value, so the
     cards and the headings that align with them cannot drift apart. */
  --inset:12px;

  --fs-display:clamp(52px,10.5vw,168px);
  --fs-chapter:clamp(38px,5.6vw,88px);
  --fs-big:clamp(26px,3.2vw,52px);
  --fs-body:clamp(15px,1.05vw,17px);
  --fs-micro:12px;

  --ease:cubic-bezier(.22,.61,.36,1);

  /* THE SHARED BAR, re-tinted through its own tokens. Never restyle .nav,
     .cta or .mnav - see nav.css. This page opens on a void band, so the bar
     keeps its dark face and only the accent and the two faces move. */
  --nav-bg:#0b0b0b;
  --nav-ink:#fff;
  --nav-muted:rgba(255,255,255,.56);
  --nav-faint:rgba(255,255,255,.38);
  --nav-line:rgba(255,255,255,.12);
  --nav-accent:#e06880;
  --nav-serif:var(--disp);
  --nav-sans:var(--sans);
  --nav-fill:rgba(255,255,255,.05);
  --nav-edge:rgba(255,255,255,.22);
  --nav-edge-hi:rgba(255,255,255,.5);
  --nav-hair:rgba(255,255,255,.08);
  --nav-press:rgba(255,255,255,.05);
  --nav-grid:rgba(255,255,255,.04);
  --nav-eye-ring:rgba(255,255,255,.26);
  --nav-x:var(--gut);
}

*,*::before,*::after{box-sizing:border-box}

html{-webkit-text-size-adjust:100%}

body{
  margin:0;
  background:var(--face-4);
  color:var(--ink);
  font-family:var(--sans);
  font-size:var(--fs-body);
  line-height:1.62;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  /* CLIP, NOT HIDDEN, AND THE DIFFERENCE IS THE WHOLE STICKY COLUMN.
     `overflow-x:hidden` forces the computed `overflow-y` to `auto`, which makes
     BODY a scroll container - and a position:sticky descendant then sticks
     inside body rather than inside the viewport. Body does not scroll, the
     viewport does, so every .stick on the page silently never engaged.
     Measured before the change: twelve sticky columns, travel 0 on all twelve.
     `clip` does the same job for the bleeding plates without creating a scroll
     container, so sticky keeps the viewport as its scrollport. */
  overflow-x:clip;
}

img{max-width:100%}

a{color:inherit}

::selection{background:var(--accent);color:var(--accent-on)}

/* --------------------------------------------------------------------------
   02  THE BAND, AND THE GRID IT PAINTS

   Every section is a band. The band owns its inks, so a single modifier
   flips the whole section - copy, rules, chevrons, chips and all - and
   nothing downstream needs to know which band it is sitting on.

   The three-column hairline grid is painted PER BAND rather than as one
   fixed overlay. A fixed overlay cannot know what is behind it, so it draws
   one line colour over both a white section and a black one and is wrong on
   at least one of them.
   -------------------------------------------------------------------------- */
.band{position:relative;background:var(--face);color:var(--ink);isolation:isolate}

.band--paper{--face:var(--face-1)}
.band--wash {--face:var(--face-2)}

/* #6d142f is ~1.4:1 on #191919, so --accent-ink lifts here. As a FILL the
   maroon does not move - a maroon card is maroon in both themes. */
.band--void,
.band--deep{
  --ink:var(--ink-dk);
  --ink-2:var(--ink-dk-2);
  --ink-3:var(--ink-dk-3);
  --line:var(--line-dk);
  --accent-ink:var(--accent-dk);
}
.band--void{--face:var(--face-3)}
.band--deep{--face:var(--face-4)}

/* --------------------------------------------------------------------------
   THE DARK THEME

   Conventional dark: every band goes dark and the section rhythm survives as
   a tonal step rather than as a hard white-to-black flip. The four faces keep
   ALTERNATING - adjacent bands still differ - which is the part that carries
   the page; what they lose is the contrast range, which is the part that
   makes a dark mode a dark mode.

   THE CLASSLESS STATE IS LIGHT, and that is not a preference. It is the only
   state that survives the resolver script being blocked or failing, so it has
   to be the intended default. `html.dark` is the site's existing convention,
   set by index.html's switch since 2026-08-03 and shared through one storage
   key - a second convention here is how the homepage and a case study end up
   disagreeing about what theme the reader picked.

   Note the light bands need the ink bundle here and the dark ones do not:
   .band--void and .band--deep already read it, because they are dark grounds
   in BOTH themes. Only paper and wash change ground.
   -------------------------------------------------------------------------- */
html.dark{
  --face-1:#0b0b0b;
  --face-2:#141414;
  --face-3:#1f1f1f;
  --face-4:#000;
}
html.dark .band--paper,
html.dark .band--wash{
  --ink:var(--ink-dk);
  --ink-2:var(--ink-dk-2);
  --ink-3:var(--ink-dk-3);
  --line:var(--line-dk);
  --accent-ink:var(--accent-dk);
  color:var(--ink);                /* --ink alone is never enough - see .card--on */
}

/* The rules. Four lines bounding three columns, inset to the gutter so they
   land on the nav's brand and CTA. Absolutely positioned and aria-hidden -
   they are furniture, not content. */
.rules{
  position:absolute;top:0;bottom:0;left:var(--gut);right:var(--gut);
  pointer-events:none;z-index:0;
}
.rules::before{
  content:'';position:absolute;inset:0;
  background-image:
    linear-gradient(var(--line),var(--line)),
    linear-gradient(var(--line),var(--line)),
    linear-gradient(var(--line),var(--line)),
    linear-gradient(var(--line),var(--line));
  background-size:1px 100%;
  background-repeat:no-repeat;
  background-position:0 0, 33.3333% 0, 66.6667% 0, 100% 0;
}
/* 960, NOT 860 - the same width .split folds to one column at. The interior
   pair describes a three-column grid, so it has to go the moment the grid
   stops being one; at 860 it outlived the structure by a hundred pixels and
   drew two hairlines straight through running type. The outer pair is the
   margin and stays. */
@media (max-width:960px){
  .rules::before{background-position:0 0, 100% 0;background-image:
    linear-gradient(var(--line),var(--line)),
    linear-gradient(var(--line),var(--line))}
}

.wrap{position:relative;z-index:1;padding-inline:var(--gut)}

/* --------------------------------------------------------------------------
   NOTHING TEXTUAL STARTS OR ENDS ON A RULE

   The rules sit at the gutter so they line up with the navbar, and .wrap is
   padded to the same gutter - which put the first character of every
   left-column element exactly ON rule one. The chevron ramp overlapped it
   outright, because a 9px triangle at x=0 has nowhere else to go.

   The fix is NOT to move .wrap: the three-column maths is measured off that
   padding, and widening it walks rules two and three off every box on the
   page. The standoff belongs on the CONTENT instead.

   BOXES ARE EXEMPT AND MUST STAY EXEMPT. A card, panel, row, plate or bar
   opens ON its rule and insets its own text with its own padding - that flush
   edge is the whole reason the grid reads as a grid rather than as four
   stripes behind some paragraphs.
   -------------------------------------------------------------------------- */
.wrap > .eyebrow,
.wrap > .display,
.wrap > .chapter,
.wrap > .big,
.wrap > .quote,
.wrap > .lede,
.wrap > .body{padding-inline:var(--inset)}

/* Cells of a metadata bar each open on their own rule, so each pays it too. */
.hero-rail .cell,
.plate-bar .cell,
.foot-meta > *{padding-inline:var(--inset) 16px}

@media (max-width:860px){
  .hero-rail .cell,
  .plate-bar .cell,
  .foot-meta > *{padding-inline:var(--inset) 0}
}

/* The band's own vertical rhythm. One-sided, like gpay.css: a section pays a
   TOP pad only, so two adjacent sections do not buy the same empty stretch
   twice. Bands have backgrounds here, so the LAST child of a band pays the
   bottom - that is what .band-end is for. */
.sec{padding-top:clamp(72px,9vw,164px)}
.band-end{padding-bottom:clamp(72px,9vw,164px)}
/* A band that holds one short statement does not need the full rhythm.
   Same numbers as the film fold's top pad, on both sides. */
.band-compact .sec{padding-top:clamp(44px,5.6vw,96px)}
.band-compact.band-end{padding-bottom:clamp(44px,5.6vw,96px)}

/* A full-width hairline, the reference's section divider.

   IT NEEDS BOTH MARGINS. With only the top one the next block opened flush on
   the line - "The three roots" sat ON the rule that was supposed to separate
   it from the section above. A divider that touches its own content is not a
   divider. The bottom is smaller than the top on purpose: a rule belongs to
   what follows it, so it sits closer to it. */
.hr{
  height:1px;background:var(--line);
  margin-top:clamp(56px,7vw,120px);
  margin-bottom:clamp(34px,4.2vw,72px);
}

/* --------------------------------------------------------------------------
   03  TYPE
   -------------------------------------------------------------------------- */
.display{
  font-family:var(--disp);
  font-weight:600;
  font-size:var(--fs-display);
  line-height:.86;
  letter-spacing:-.015em;           /* tuned for Oswald. NOT the reference's -8px. */
  margin:0;
  text-wrap:balance;
}
.chapter{
  font-family:var(--disp);
  font-weight:600;
  font-size:var(--fs-chapter);
  line-height:.92;
  letter-spacing:-.012em;
  margin:0;
  text-wrap:balance;
}
/* On a phone text-wrap:balance breaks the chapters where they fall, and at
   .92 a descender on one line closed on the ascender below it ("app, or /
   beside it?", "adding / anything" on GPAY). */
@media (max-width:620px){.chapter{line-height:1.04}}
.big{
  font-family:var(--disp);
  font-weight:500;
  font-size:var(--fs-big);
  line-height:1.1;
  letter-spacing:-.005em;
  margin:0;
}

/* THE PULL QUOTE IS NOT A CHAPTER TITLE, AND IT CANNOT BORROW THAT SIZE.
   `.chapter` is tuned for two or three words on a line - "Never gate / the
   browse" - where a .92 line-height reads as deliberate tight setting. Run a
   28-word sentence through the same rule and both halves turn against it: at
   5.6vw it came to seven lines that filled a laptop viewport on their own, and
   at .92 the descenders of one line closed on the ascenders of the next, so it
   read as collapsed as well as oversized. A long quote wants a smaller size
   AND more leading, not one or the other. */
.quote{
  font-family:var(--disp);
  font-weight:500;
  font-size:clamp(22px,2.75vw,44px);
  line-height:1.18;
  letter-spacing:-.004em;
  margin:0;
  text-wrap:pretty;
}
.body{margin:0;color:var(--ink-2);max-width:62ch}
.body + .body{margin-top:1.1em}
/* A SECTION'S LEAD SENTENCE, at full ink. The one piece of body-size copy on
   the page that is not recessed, because it IS the section's argument rather
   than support for it - and because at --ink-2 under a display-size chapter it
   read as a caption that had lost its picture. */
.body--lead{color:var(--ink);font-size:clamp(16px,1.15vw,18.5px);line-height:1.55}
.body b{color:var(--ink);font-weight:600}
.lede{margin:0;color:var(--ink-2);font-size:clamp(16px,1.35vw,21px);line-height:1.55;max-width:56ch}

/* The micro-caps that hold the whole system together. */
.micro{
  font-family:var(--sans);
  font-size:var(--fs-micro);
  font-weight:700;
  letter-spacing:.085em;
  text-transform:uppercase;
  line-height:1.25;                 /* caps have no descenders; 1.6 is phantom space */
  color:var(--ink-3);
  margin:0;
}
.micro b,.micro strong{color:var(--ink);font-weight:700}
.micro--accent{color:var(--accent-ink)}

/* --------------------------------------------------------------------------
   04  THE EYEBROW

   The reference's is a chevron ramp, then a bracketed label with a second
   script after a slash. The ramp is four triangles on an opacity ladder -
   drawn, not a glyph, so it tints with the band.
   -------------------------------------------------------------------------- */
.eyebrow{display:flex;align-items:center;gap:14px;margin:0 0 clamp(20px,2.4vw,34px)}
.eyebrow .ramp{display:inline-flex;gap:3px;flex:none}
.eyebrow .ramp i{
  width:9px;height:11px;background:var(--accent-ink);
  clip-path:polygon(0 0,100% 50%,0 100%);
}
.eyebrow .ramp i:nth-child(1){opacity:.32}
.eyebrow .ramp i:nth-child(2){opacity:.54}
.eyebrow .ramp i:nth-child(3){opacity:.76}
.eyebrow .ramp i:nth-child(4){opacity:1}
.eyebrow .lab{
  font-size:var(--fs-micro);font-weight:700;letter-spacing:.085em;
  text-transform:uppercase;color:var(--ink);line-height:1.25;
}
.eyebrow .lab em{font-style:normal;color:var(--ink-3)}

/* --------------------------------------------------------------------------
   05  THE SCROLL FILL

   The reference's copy arrives grey and fills to full ink word by word as the
   block crosses the viewport. Implemented on OPACITY, not on a colour mix:
   the ink is already the band's own, so one mechanism works on paper, wash
   and void with no per-band maths.

   Words are wrapped by marketplace.js. Without it the text is simply at full
   strength, which is the correct failure.
   -------------------------------------------------------------------------- */
/* THE .js RULE HAS TO READ --l, NOT RE-STATE THE RESTING VALUE.
   Written as a literal `.22` this outranked `.fill .w{opacity:var(--l,1)}` on
   specificity - (0,3,0) against (0,2,0) - so once html.js was set, which is
   always, every word was pinned at 22% and the --l that marketplace.js writes
   on scroll could never win. Measured: scrolled a .fill right through the
   viewport and the words never moved off 0.22, so all five filled headings on
   gpay were permanently near-invisible. Same fallback, read through the
   variable, and the scroll drives it again. */
.fill .w{opacity:var(--l,1)}
.js .fill .w{opacity:var(--l,.22)}

/* --------------------------------------------------------------------------
   06  REVEALS

   .js is set by an inline head script and REMOVED by an inline failsafe if
   marketplace.js has not checked in. Without that, a 404 on the script leaves
   every .rv at opacity 0 - a blank page under a working navbar. That exact
   failure is written up in CLAUDE.md; this is the guard for it.
   -------------------------------------------------------------------------- */
.js .rv{
  opacity:0;
  transform:translateY(26px);
  filter:blur(7px);
  transition:opacity .78s var(--ease),transform .78s var(--ease),filter .78s var(--ease);
}
.js .rv.in{opacity:1;transform:none;filter:none}

/* Media is heavy to blur. It gets the lift and the fade only. */
.js .rv--plain{filter:none;transition:opacity .8s var(--ease),transform .8s var(--ease)}

@media (prefers-reduced-motion:reduce){
  .js .rv{opacity:1;transform:none;filter:none;transition:none}
  .js .fill .w{opacity:1}
  html{scroll-behavior:auto}
}

/* --------------------------------------------------------------------------
   07  HERO
   -------------------------------------------------------------------------- */
.hero{padding-top:calc(var(--nav-h) + clamp(46px,7vw,104px))}
.hero .display{margin-bottom:clamp(22px,2.6vw,40px)}

/* The reference sets its three service labels on a rule above the wordmark,
   one per grid column. Here they are the study's own coordinates. */
.hero-rail{
  display:grid;grid-template-columns:repeat(3,1fr);
  border-top:1px solid var(--line);
  padding-top:14px;margin-bottom:clamp(30px,4vw,58px);
}
.hero-rail .cell{display:flex;gap:10px;align-items:baseline}
.hero-rail .n{color:var(--ink-3)}
@media (max-width:860px){
  .hero-rail{grid-template-columns:1fr;gap:8px}
}

/* On the page grid: the lede takes column one, the marks take two and three,
   so the marks' own left border lands exactly on the first rule. */
.hero-foot{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;
  align-items:end;margin-top:clamp(30px,4vw,56px);
}
.hero-foot .lede{grid-column:1;padding-inline:var(--inset)}
.hero-foot .marks{grid-column:2/4}
@media (max-width:860px){
  .hero-foot{grid-template-columns:1fr;gap:clamp(24px,4vw,40px)}
  .hero-foot .lede{grid-column:auto;padding-right:0}
  .hero-foot .marks{grid-column:auto}
}

/* The meta marks - role, timeline, platform, team. */
.marks{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:var(--line);border:1px solid var(--line)}
.marks .m{background:var(--face);padding:14px 16px}
.marks .k{display:block;color:var(--ink-3);margin-bottom:5px}
.marks .v{display:block;color:var(--ink);font-weight:600;letter-spacing:.02em;font-size:13px;text-transform:uppercase}
@media (max-width:560px){.marks{grid-template-columns:1fr}}

/* --------------------------------------------------------------------------
   08  PLATES  -  the full-bleed artwork rows

   THE IMAGES ARE NEVER CROPPED AND NEVER STRETCHED. The reference crops its
   stock photography into fixed-height bands; these are product screens, and a
   screen with its bottom third cut off is not evidence of anything. So a
   plate is width-driven: the <img> carries its real pixel dimensions in the
   markup, the box takes the ratio from them, and the height is whatever that
   comes to. No object-fit, no fixed height, nothing that can letterbox.

   The metadata sits on a rule ABOVE the plate rather than over the picture,
   which is the one place this departs from the reference and it departs for
   the same reason: over a photograph that text is decoration, over a UI
   screenshot it reads as part of the UI.
   -------------------------------------------------------------------------- */
.plate{margin-top:clamp(28px,3.4vw,52px)}
/* Each cell opens ON its own rule, left aligned - the reference sets its
   `01. / YEAR / CLIENT` bar exactly this way. Centring the middle cell and
   right-aligning the last one is what put the last word against the margin. */
.plate-bar{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;
  border-top:1px solid var(--line);padding:14px 0 16px;
}
.plate-bar .cell{display:flex;gap:10px;align-items:baseline;min-width:0}
.plate-bar .k{color:var(--ink-3);flex:none}
.plate-bar .v{color:var(--ink);font-weight:700}
@media (max-width:860px){
  .plate-bar{grid-template-columns:1fr;gap:7px}
}

.plate-img{
  display:block;width:100%;height:auto;   /* the two halves of "not stretched" */
  border:1px solid var(--line);
  background:var(--face-2);
}

/* THE FILM FOLD (01B, both case studies). A plate like any other - bar on a
   rule, then the picture flush to it - but the picture is a player, so it is
   a fixed 16:9 box rather than width-driven artwork. The player's insides
   are assets/css/film.css; only the frame is set here. Tighter than a full
   .sec on both sides: it follows the hero's own divider, and the next band
   opens with its full top pad anyway. */
.film-band .sec{padding-top:clamp(44px,5.6vw,96px)}
.film-band.band-end{padding-bottom:clamp(56px,7vw,120px)}
.film-band .plate{margin-top:0}
.film-plate .film{border:1px solid var(--line)}
/* Marketplace carries its film INSIDE the hero, in place of the cover plate,
   so there is no film band there and the hero pays its own, compact bottom
   pad instead of the old trailing .hr (whose margins stacked 180px of air
   between the headline block and the next band). */
.hero-band{padding-bottom:clamp(44px,5.6vw,96px)}
/* SHORT SCREENS: cap by height, not width. The page's fixed readout and CTA
   sit at the bottom corners, so a 16:9 frame taller than the viewport left
   under the nav and the bar puts them straight over the player's controls.
   180 = nav + plate bar + the readout's own band. At 1440x900 this does not
   bind (the cap is wider than the column); on a short laptop it narrows and
   centres the whole plate rather than cropping the film. */
.film-plate{max-width:max(320px,calc((100vh - 180px) * 16 / 9));margin-inline:auto}
@supports (height:100svh){
  .film-plate{max-width:max(320px,calc((100svh - 180px) * 16 / 9))}
}

.legend{
  list-style:none;margin:18px 0 0;padding:0;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:1px;background:var(--line);border:1px solid var(--line);
}
.legend li{
  background:var(--face);padding:13px 15px;display:flex;gap:10px;align-items:baseline;
  font-size:var(--fs-micro);font-weight:700;letter-spacing:.085em;text-transform:uppercase;
  color:var(--ink);line-height:1.25;
}
.legend .i{color:var(--accent-ink);flex:none}

/* The two complete pages, side by side. These are the Figma frames end to
   end - hero through footer - so they run at their own full height beside
   each other rather than being cropped to match. */
/* --------------------------------------------------------------------------
   09  THE STICKY CHAPTER

   Left column pins, right column scrolls past it. The sticky element is a
   CHILD of the grid item, never the grid item itself: a sticky grid item is
   clamped to the grid CONTAINER rather than to its own area in engines other
   than Chrome, which is written up in CLAUDE.md against gpay's old pillars.
   -------------------------------------------------------------------------- */
/* ON THE PAGE GRID, NOT NEAR IT. A 1fr/2fr split with a gap puts the divide
   ~30px off the rule behind it, and the whole look of the reference is that
   nothing is ever 30px off a rule. The chapter takes column one; the content
   takes two and three and sits FLUSH, so every box inside it opens on a rule. */
.split{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;align-items:start}
.split > .col{min-width:0}
/* ...AND THE SECOND CAUSE. `align-items:start` leaves each column at content
   height, so the sticky child's own parent is exactly as tall as the child and
   there is nothing to travel through - sticky with zero range is static.
   The first column, and only it, is stretched back to the row height; the
   right column must stay `start` or short flows grow a tall empty box. */
.split > .col:first-child{align-self:stretch}
.split > .col:first-child{grid-column:1;padding-inline:var(--inset)}
.split > .col + .col{grid-column:2/4}

/* Loose copy is the one thing that stands off the rule. Boxes never do. */
.split > .col + .col > .body,
.split > .col + .col > .big,
.split > .col + .col > .lede,
.split > .col + .col > .quote,
.split > .col + .col > .chapter{padding-left:var(--inset)}

/* RUNNING COPY NEVER HAS A RULE THROUGH IT. The wide column spans two page
   columns, so a 62ch paragraph dropped into it runs straight over rule three.
   A hairline crossing 16px type is just noise in the middle of a sentence.
   Capped to one page column (less the standoff) it starts and ends clear of
   both rules that bound it.

   DISPLAY TYPE IS DELIBERATELY NOT CAPPED. At 40px and up the rule reads as a
   layer behind the letterforms rather than as a line through a word - it is
   what the reference does with its own big statements, and capping .big and
   .quote to one column is what made the pull quote seven lines tall. */
.split > .col + .col > .body,
.foot-grid > :last-child > .body{max-width:calc(50% - var(--inset))}
/* THE EXCEPTION, .body--wide: the screens, ask and reframe descriptions fill BOTH
   boxes of the wide column (owner's call, 2026-09-24), so rule three runs
   behind them. It pays
   the inset on both sides so it still starts and ends clear of the outer two. */
.split > .col + .col > .body--wide{max-width:none;padding-right:var(--inset)}

/* A CHAPTER IN THE PINNED COLUMN GETS A THIRD OF THE PAGE, AND ITS AUTHORED
   BREAKS ASSUME IT. --fs-chapter is sized for a heading that spans the wide
   column; in the one-column slot it overflowed the <br> breaks the copy is
   written around - "not drawn at" wrapped and orphaned "at", turning a
   four-line heading into six, and the same at every width from 1024 to 1440.
   Sized to the column it is actually given. */
.split > .col:first-child .chapter{font-size:clamp(26px,4vw,58px)}

.split .stick{position:sticky;top:calc(var(--nav-h) + 34px)}
@media (max-width:960px){
  .split{grid-template-columns:1fr;gap:clamp(22px,4vw,40px)}
  .split > .col:first-child,
  .split > .col + .col{grid-column:auto;padding-right:0}
  /* The inset is KEPT - see the note above. The outer rules are still on the
     page at this width and loose copy may not sit on them. */
  .split > .col + .col > .body,
  .split > .col + .col > .big,
  .split > .col + .col > .lede,
  .split > .col + .col > .quote,
  .split > .col + .col > .chapter{padding-left:var(--inset)}
  /* One column IS the page here, so a 50% cap would halve the measure. */
  /* 62ch measured past the right margin rule once the left standoff is paid.
     The cap has to leave room for BOTH insets, not just the one it starts at. */
  .split > .col + .col > .body,
  .foot-grid > :last-child > .body{max-width:min(62ch, 100% - var(--inset))}
  .split > .col + .col > .body--wide{max-width:min(62ch, 100% - var(--inset));padding-right:0}
  .split .stick{position:static}
}

.chap-note{margin-top:clamp(18px,2vw,30px)}

/* The pinned column's attributed line, the reference's quote block. */
.said{margin-top:clamp(30px,4vw,64px);max-width:34ch}
/* Decorative and aria-hidden, so it is exempt from the contrast floor - but at
   --line it measured 1.30:1 and simply could not be seen at all. */
.said .mark{font-family:var(--disp);font-size:56px;line-height:.6;color:var(--ink-3);opacity:.5;display:block;margin-bottom:10px}
.said p{margin:0;color:var(--ink);font-weight:500;line-height:1.45}
.said .by{margin-top:14px}

/* --------------------------------------------------------------------------
   10  THE NUMBERED CARD GRID

   The reference's "From brief to launch": a chip with the step number, a
   right-aligned label, then the claim and the copy, with EXACTLY ONE card
   filled. One filled card is a focal point; two is a pattern and reads as a
   state nobody chose.
   -------------------------------------------------------------------------- */
.cards{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--line);border:1px solid var(--line)}
.card{
  background:var(--face);
  padding:clamp(20px,2.2vw,32px);
  min-height:clamp(220px,22vw,300px);
  display:flex;flex-direction:column;
}
.card-top{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:auto}
.chip{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:46px;height:46px;padding:0 12px;border-radius:14px;
  background:var(--line);color:var(--ink);
  font-size:var(--fs-micro);font-weight:700;letter-spacing:.06em;
}
.card h3{margin:26px 0 10px;font-family:var(--disp);font-weight:500;font-size:clamp(19px,1.6vw,25px);line-height:1.12;letter-spacing:-.005em}
.card p{margin:0;color:var(--ink-2);font-size:14.5px;line-height:1.55}

/* ANYTHING THAT REDECLARES --ink MUST REDECLARE `color` WITH IT.
   `color` inherits as a COMPUTED value, resolved back at .band - so a panel
   that flips the token alone keeps whatever ink the band computed and every
   heading inside it that has no colour rule of its own stays the old one.
   Measured before this line existed: this card's h3 was #0b0b0b on #6d142f at
   1.68:1, and .panel--yes's h4 was #0b0b0b on #0b0b0b at 1.00:1 - an invisible
   heading on a panel whose body copy looked perfectly fine, because .card p
   and .panel p DO name a token and so did switch. */
/* THE FILLED FACE, DECLARED ONCE. What switches it on differs by pointer:
   hover where there is one, .card--on where there is not. */
.card:hover,
.card--on{
  background:var(--accent);
  --ink:#fff;--ink-2:rgba(255,255,255,.82);--ink-3:rgba(255,255,255,.66);
  color:var(--ink);
}
.card:hover .chip,
.card--on .chip{background:rgba(255,255,255,.16);color:#fff}
.card:hover .micro,
.card--on .micro{color:rgba(255,255,255,.72)}

/* THE FILL IS THE ONLY THING HOVER DOES. No lift, no shadow, no border move -
   the grid's whole structure is that the cards are flush tiles on the rules,
   and anything that displaces one breaks that on every pointer-in. */
.card,.card *{transition:background-color .32s var(--ease),color .32s var(--ease)}

@media (hover:hover) and (pointer:fine){
  /* Hover can earn the fill here, so 01 must NOT open already lit: otherwise
     pointing at a second card lights two, and "exactly one card is filled" -
     the thing that makes the grid have a focal point at all - is gone.
     Reset via `inherit` rather than by restating the band's values, so this
     stays correct on whichever band the grid is dropped onto. */
  .card--on:not(:hover){
    background:var(--face);
    --ink:inherit;--ink-2:inherit;--ink-3:inherit;
    color:inherit;
  }
  .card--on:not(:hover) .chip{background:var(--line);color:var(--ink)}
  .card--on:not(:hover) .micro{color:var(--ink-3)}
}

@media (max-width:760px){.cards{grid-template-columns:1fr}}

/* --------------------------------------------------------------------------
   11  THE NUMBERED ROWS  -  the reference's services list
   -------------------------------------------------------------------------- */
.rows{border-top:1px solid var(--line)}
/* Two halves of the wide column, so the divide lands on the middle rule. The
   row's border runs flush edge to edge and its CELLS carry the inset - the
   same contract every other box on the page keeps. */
.row{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0;
  border-bottom:1px solid var(--line);
  padding:clamp(22px,2.6vw,38px) 0;
  align-items:baseline;
}
.row > *{padding-left:var(--inset);padding-right:20px}
.row .n{color:var(--accent-ink)}
.row h3{margin:8px 0 0;font-family:var(--disp);font-weight:500;font-size:clamp(22px,2.4vw,38px);line-height:1.04;letter-spacing:-.01em}
.row p{margin:0;color:var(--ink-2);max-width:56ch}
@media (max-width:760px){.row{grid-template-columns:1fr;gap:10px}}

/* --------------------------------------------------------------------------
   12  THE TWO-PANEL DECISION
   -------------------------------------------------------------------------- */
.panels{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--line);border:1px solid var(--line)}
.panel{background:var(--face);padding:clamp(22px,2.6vw,38px);display:flex;flex-direction:column}
.panel h4{margin:22px 0 12px;font-family:var(--disp);font-weight:500;font-size:clamp(20px,1.9vw,29px);line-height:1.1;letter-spacing:-.005em}
.panel p{margin:0;color:var(--ink-2);font-size:15px}
.panel--no .tag{color:var(--ink-3)}
.panel--yes{
  background:var(--face-4);
  --ink:#fff;--ink-2:rgba(255,255,255,.72);--ink-3:rgba(255,255,255,.55);
  --accent-ink:#e06880;         /* #6d142f is 1.68:1 on #0b0b0b. It has to lift. */
  color:var(--ink);             /* see .card--on above */
}
.panel--yes .tag{color:var(--accent-ink)}
.panel .tag{display:flex;align-items:center;gap:9px}
.panel .tag .dot{width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}
@media (max-width:760px){.panels{grid-template-columns:1fr}}

/* --------------------------------------------------------------------------
   13  THE INSTALL
   -------------------------------------------------------------------------- */
.steps{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-top:clamp(22px,2.6vw,36px)}
.step{background:var(--face);padding:clamp(18px,2vw,28px);display:grid;grid-template-columns:46px 1fr;gap:18px;align-items:start}
.step .b{
  width:46px;height:46px;border-radius:14px;background:var(--accent);color:var(--accent-on);
  display:flex;align-items:center;justify-content:center;font-family:var(--disp);font-size:20px;font-weight:600;
}
.step h4{margin:0 0 6px;font-family:var(--disp);font-weight:500;font-size:clamp(18px,1.5vw,23px);line-height:1.15}
.step p{margin:0;color:var(--ink-2);font-size:14.5px}

.steps + .code{margin-top:clamp(22px,2.6vw,36px)}

.code{
  background:var(--face-4);border:1px solid var(--line);
  border-radius:16px;overflow:hidden;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
}
.code .top{
  display:flex;align-items:center;gap:7px;
  padding:13px 16px;border-bottom:1px solid rgba(255,255,255,.1);
}
.code .top i{width:9px;height:9px;border-radius:50%;background:rgba(255,255,255,.2)}
.code .top .f{
  margin-left:8px;font-family:var(--sans);font-size:11px;font-weight:700;
  letter-spacing:.085em;text-transform:uppercase;color:rgba(255,255,255,.62);
}
.code pre{
  margin:0;padding:20px 16px 24px;overflow-x:auto;
  font-size:12.5px;line-height:1.75;color:rgba(255,255,255,.9);
}
.code .cm{color:rgba(255,255,255,.55)}   /* .4 measured 3.78:1 on #0b0b0b */
.code .tk{color:#e06880}
.code .at{color:#d8b4c2}
.code .st{color:#9fd2b0}

/* --------------------------------------------------------------------------
   14  FOOTER
   -------------------------------------------------------------------------- */
.foot{padding-top:clamp(64px,8vw,132px)}
.foot-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;align-items:start}
.foot-grid > :first-child{grid-column:1;padding-inline:var(--inset)}
.foot-grid > :last-child{grid-column:2/4;padding-left:var(--inset)}
@media (max-width:860px){
  .foot-grid{grid-template-columns:1fr;gap:clamp(24px,4vw,40px)}
  /* padding-inline, NOT padding:0 - the third place this file zeroed the
     standoff at a narrow width while the outer rules were still painted. The
     column stacks; the margin it has to clear does not go away. */
  .foot-grid > :first-child,
  .foot-grid > :last-child{grid-column:auto;padding-inline:var(--inset)}
}

/* THE NEXT PROJECT - the shape is assets/css/next.css, shared by all four
   case studies; this only tints it and places it. The footer gets a third
   column for it, so all three sit on the page's own rules: the line in column
   one, the reading and buttons in two, the next project in three. Top-aligned
   like every other row on this grid - the eyebrow, the paragraph and the
   card's label start on one line. */
.foot{
  --next-ink:var(--ink);
  --next-muted:var(--ink-2);
  --next-line:var(--line);
  --next-line-hi:var(--ink-3);
  --next-accent:var(--accent-ink);
  --next-name-font:var(--disp);
  --next-name-weight:500;
  --next-label-font:var(--sans);
  --next-r:0;
  --next-ease:var(--ease);
}
.foot-grid--next > :nth-child(2){grid-column:2;padding-inline:var(--inset)}
.foot-grid--next > .next-proj{grid-column:3;padding-inline:var(--inset);max-width:none}
/* Three columns only once each can carry its load. Below 1200 the card drops
   under the reading, across the same two columns, still on the rules. */
@media (max-width:1200px){
  .foot-grid--next > :nth-child(2),
  .foot-grid--next > .next-proj{grid-column:2/4}
  .foot-grid--next > .next-proj{max-width:calc(560px + var(--inset) * 2);margin-top:clamp(28px,3.4vw,44px)}
}
@media (max-width:860px){
  .foot-grid--next > :nth-child(2),
  .foot-grid--next > .next-proj{grid-column:auto}
  .foot-grid--next > .next-proj{max-width:calc(560px + var(--inset) * 2);margin-top:clamp(8px,2vw,16px)}
}

.foot h3{margin:0 0 16px;font-family:var(--disp);font-weight:500;font-size:clamp(24px,2.8vw,44px);line-height:1.08;letter-spacing:-.01em}

.btns{display:flex;flex-wrap:wrap;gap:12px;margin-top:clamp(22px,2.6vw,34px)}
.btn{
  display:inline-flex;align-items:center;gap:10px;
  padding:15px 22px;border-radius:999px;
  font-size:var(--fs-micro);font-weight:700;letter-spacing:.085em;text-transform:uppercase;
  text-decoration:none;border:1px solid var(--line);color:var(--ink);
  transition:background .3s var(--ease),border-color .3s var(--ease),color .3s var(--ease);
}
.btn svg{width:15px;height:15px;flex:none;transition:transform .3s var(--ease)}
.btn-primary{background:var(--accent);border-color:var(--accent);color:var(--accent-on)}
.btn:hover{border-color:var(--ink)}
.btn-primary:hover{background:#84182f;border-color:#84182f}
/* Position-based, never a class, so a back arrow moves the way it points. */
.btn:hover svg:first-child{transform:translateX(-3px)}
.btn:hover svg:last-child{transform:translateX(3px)}

/* The giant wordmark. Clipped to the gutter and allowed to run to the edges,
   which is what makes it read as a mark rather than a heading. */
.wordmark{
  margin-top:clamp(48px,7vw,120px);
  padding-inline:var(--inset);
  font-family:var(--disp);font-weight:600;
  font-size:clamp(64px,17.5vw,280px);
  line-height:.8;letter-spacing:-.02em;
  color:var(--ink);opacity:.14;
  user-select:none;white-space:nowrap;
}

.foot-meta{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 0;
  border-top:1px solid var(--line);margin-top:clamp(30px,4vw,56px);padding-top:20px;
}
@media (max-width:620px){.foot-meta{grid-template-columns:1fr}}

.nda{
  border-top:1px solid var(--line);
  margin-top:clamp(26px,3vw,44px);padding:20px var(--inset) clamp(30px,4vw,52px);
  display:flex;gap:11px;align-items:flex-start;color:var(--ink-3);
  max-width:calc(33.333% - var(--inset)*2);
  font-size:var(--fs-micro);font-weight:600;letter-spacing:.05em;text-transform:uppercase;line-height:1.5;
}
.nda svg{width:15px;height:15px;flex:none;margin-top:1px}
/* The third-width cap follows the three-column meta row above it. When that
   row stacks, so does this - capped, it wrapped one word per line. */
@media (max-width:620px){.nda{max-width:none}}

/* --------------------------------------------------------------------------
   15  THE FIXED CTA

   The reference parks a pill bottom-right for the length of the page. It sits
   ABOVE the nav's z-index band but below the drawer, and it hides itself once
   the footer's own buttons are on screen - two live calls to action in one
   view is the pill arguing with the thing it points at.
   -------------------------------------------------------------------------- */
.pill{
  position:fixed;right:clamp(12px,2vw,26px);bottom:clamp(12px,2vw,26px);z-index:45;
  display:inline-flex;align-items:center;gap:10px;
  padding:15px 21px;border-radius:999px;text-decoration:none;
  background:var(--accent);color:#fff;
  font-size:11.5px;font-weight:700;letter-spacing:.085em;text-transform:uppercase;
  box-shadow:0 10px 34px rgba(0,0,0,.34);
  transition:opacity .45s var(--ease),transform .45s var(--ease),background .3s var(--ease);
}
.pill:hover{background:#84182f}
.pill[hidden]{display:inline-flex!important}          /* [hidden] would kill the fade */
.pill.off{opacity:0;transform:translateY(14px) scale(.96);pointer-events:none}
@media (max-width:620px){.pill{display:none}}          /* the bar's CTA is already there */
/* ...and on a phone on its side, where a 50px pill is an eighth of the screen
   and sat on the title. */
@media (max-height:520px){.pill{display:none}}

/* --------------------------------------------------------------------------
   16  THE SCROLL READOUT  -  bottom-left, the reference's running counter
   -------------------------------------------------------------------------- */
.readout{
  position:fixed;left:var(--gut);bottom:clamp(12px,2vw,26px);z-index:44;
  display:flex;align-items:center;gap:10px;
  font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  color:#fff;mix-blend-mode:difference;pointer-events:none;
}
.readout .track{width:64px;height:2px;background:rgba(255,255,255,.3);position:relative}
.readout .track i{position:absolute;inset:0 auto 0 0;width:var(--p,0%);background:#fff}
@media (max-width:860px){.readout{display:none}}
