/* ============================================================================
   anunciazo.com  ·  css/materials.css  ·  FROZEN after founder approval
   ----------------------------------------------------------------------------
   Every material on the desk, with its hover, focus-visible and press states
   defined ONCE, here. Section CSS positions and sizes materials; it never
   redefines what a material looks like and never needs a texture path:
   every url() in the project lives in this file and is written relative to it.
   Extracted from headers/b-tablero/index.html (the shipped hero) and then
   generalized. Order of this file:
     0 reset · 1 desk · 2 board + pinned + pin · 3 paper family · 4 kraft
     family · 5 receipt · 6 photographic (print, polaroid, play, hook)
     7 fasteners (tape, clip, rope, string) · 8 marks (stamp, claim)
     9 controls (cta, btn, link, chev, index-tab) · 10 lightbox
     11 focus · 12 motion (entry choreography + scroll reveal)
   ========================================================================= */

/* Manager fix after the first strict merge run (2026-09-21): --rot, --lift and --press describe ONE object. As ordinary
   custom properties they inherit, so a lifting material or a revealed object inside a tilted parent picked up the
   parent's tilt and was rotated twice (s02's columns inside the tilted sheet, 3.46% reduced-vs-allowed difference at
   390). Registered as non-inheriting: every object reads only what is set on itself. */
@property --rot{syntax:'<angle>';inherits:false;initial-value:0deg}
@property --lift{syntax:'<length>';inherits:false;initial-value:0px}
@property --press{syntax:'<number>';inherits:false;initial-value:1}

/* ---------- 0. reset (from the hero, unchanged) -------------------------- */
*,*::before,*::after{box-sizing:border-box}
[hidden]{display:none !important}
html,body{margin:0;padding:0}
html{
  -webkit-text-size-adjust:100%;
  /* the root element renders nothing itself, but leaving it at the UA default
     means the document still has Times in it: form controls, a stray text node
     and qa/materials-check.mjs all see it. Archivo everywhere. */
  font-family:var(--sans);
}
body{
  color:var(--ink);
  font-family:var(--sans);
  font-weight:500;
  overflow-x:hidden;
}
img,video{display:block;max-width:100%}
button{font:inherit;color:inherit}

/* anchored sections clear the nav when the nav links jump to them */
[data-section][id],[data-anchor]{scroll-margin-top:var(--anchor-offset)}

/* ---------- 1. the desk: the page canvas -------------------------------- */
/* #3b2a1a is the hero's wall tone, kept. Cheap to paint on a 16000px page:
   one 420px oak tile plus a FLAT translucent wash (a two-stop gradient sized
   100% 100% would have to be rasterised the full height of the document, and a
   vertical ramp would also drift in lightness from the nav to the footer). No
   filter on the element either: a filter would become the containing block for
   the fixed sticky bar and the lightbox. */
.desk{
  background-color:var(--desk);
  background-image:
    linear-gradient(rgba(44,32,21,.74), rgba(44,32,21,.74)),
    url(../assets/board/wood.webp);
  background-repeat:repeat, repeat;
  background-size:420px 420px, 420px 420px;
  background-attachment:scroll, scroll;
}

/* Every material that carries a pseudo-element (a torn edge, a lifted corner,
   a flap, a stamp, a punched hole) needs its own positioning context, and
   every .pin inside an object needs a positioned parent. Declared BEFORE
   .pinned on purpose: a PINNED copy of the same material must still go
   absolute, so .pinned wins by source order without any specificity games. */
.sheet,.card,.card-tabbed,.tag,.strip,.index-tab,
.kraft,.kraft-tag,.folder,.envelope,.cardbox,.receipt,.polaroid,
.print,.cta,.btn,.stamp,.stamp-block,.tape,.clip,.rope,.taped{position:relative}

/* MANDATORY, not decoration: .stamp and .tape use mix-blend-mode, and a
   blended element blends against the nearest ANCESTOR STACKING CONTEXT. With
   no isolation anywhere, that backdrop group is the whole document, so every
   stamp on a 7000px page forces Chrome to rasterise the entire page as its own
   backdrop buffer: the page hangs and the tab crashes on a full-page capture
   (measured, 2026-09-21). Isolating each object confines the blend to the
   object it is printed on. The hero's .board already did this. */
.sheet,.card,.card-tabbed,.tag,.strip,.kraft,.receipt,.polaroid,
.folder,.envelope,.cardbox,.taped,[data-claim]{isolation:isolate}

/* One lift/press transform for every material that is NOT hanging on a pin,
   so the hover and press states below work on the desk as well as on felt. */
.print:not(.pinned),.cta:not(.pinned),.btn,.sheet:not(.pinned),.card:not(.pinned),
.card-tabbed,.polaroid,.folder,.envelope,.taped,.receipt,.index-tab{
  transform:translateY(var(--lift,0)) scale(var(--press,1)) rotate(var(--rot,0deg));
}

/* ---------- 2. the board: felt in an oak frame -------------------------- */
.board{
  position:relative;
  overflow:clip;
  isolation:isolate;          /* so .string can sit ON the felt, under the paper */

  background-color:var(--felt);
  /* vignette on top of the felt tile, 18% max at the corners (BRIEF-B S2) */
  background-image:
    radial-gradient(125% 115% at 50% 42%, rgba(0,0,0,0) 44%, rgba(0,0,0,.18) 100%),
    url(../assets/board/felt.webp);
  background-repeat:no-repeat, repeat;
  background-size:100% 100%, 320px 320px;

  border:18px solid var(--wood);
  border-image:url(../assets/board/wood.webp) 40 round;
  box-shadow:inset 0 0 44px rgba(0,0,0,.46), inset 0 0 0 1px rgba(0,0,0,.35);
}

/* every pinned object hangs from its pin(s); board.js rotates it around them */
.pinned{
  position:absolute;
  margin:0;
  transform:translateY(var(--lift,0)) scale(var(--press,1)) rotate(var(--rot,0deg));
  transform-origin:50% 0;
  z-index:var(--z-paper);
}

/* pins: rendered by the page at the data-pin coordinates, pulled up by half
   their height so the head sits through the top edge of the paper. */
.pin{
  position:absolute;
  left:var(--px,50%);top:var(--py,0%);
  width:var(--pin-size,clamp(16px,1.6vw,24px));
  aspect-ratio:1;
  transform:translate(-50%,-50%) rotate(var(--pr,0deg));
  background:url(../assets/board/pin.png) center/contain no-repeat;
  filter:drop-shadow(0 2px 2px rgba(0,0,0,.35));
  z-index:var(--z-pin);
  pointer-events:none;
}

/* ---------- 3. the paper family ----------------------------------------- */
/* one paper material, five sizes. .paper is the base the hero already uses;
   .sheet .card .tag .strip inherit the same surface so a builder needs one
   class, not two. */
.paper,.sheet,.card,.card-tabbed,.tag,.strip,.index-tab{
  background-color:var(--paper);
  background-image:url(../assets/board/paper.webp);
  background-size:300px 300px;
  background-blend-mode:multiply;
  border-radius:var(--r-paper);
  box-shadow:var(--shadow-paper), var(--shadow-inset-paper);
  color:var(--ink);
}

/* ---------- 6. photographic: the print ---------------------------------- */
.print{
  width:var(--pw,100%);
  aspect-ratio:var(--print-ar,9/16);
  border:6px solid #fff;
  border-radius:var(--r-print);
  background:#0D100E;
  box-shadow:var(--shadow-paper);
  container-type:inline-size;          /* hooks and play buttons scale to the print */
  cursor:pointer;
}
.print img,.print video{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
  object-position:var(--print-focus,50% 38%); /* faces sit in the upper half */
  border-radius:var(--r-print-in);
  z-index:0;
}
.print video{opacity:0}                /* the engine fades it in when it plays */
.print video[src]{opacity:1}

/* gloss: a soft bottom scrim so the hook clears WCAG over any still, plus the
   inner shadow of a photographic print inside its white border */
.print::before{
  content:"";
  position:absolute;inset:0;z-index:2;
  border-radius:var(--r-print-in);
  background:linear-gradient(to top,
    rgba(0,0,0,.50) 0%, rgba(0,0,0,.16) 27%, rgba(0,0,0,0) 54%);
  box-shadow:inset 0 0 14px rgba(0,0,0,.30);
  pointer-events:none;
}

.hook{
  position:absolute;z-index:3;
  left:4%;right:4%;bottom:var(--hook-y,30%);
  text-align:center;
  font-family:var(--display);
  font-weight:400;
  font-size:clamp(11px,9.2cqw,30px);
  line-height:1.08;
  letter-spacing:-.01em;
  text-transform:uppercase;
  color:#fff;
  text-shadow:0 2px 0 rgba(0,0,0,.72), 0 0 10px rgba(0,0,0,.45);
  pointer-events:none;
}
.hook b{display:block;font-weight:inherit}

.play{
  position:absolute;z-index:4;
  left:50%;top:var(--play-y,84%);
  --d:clamp(34px,26cqw,72px);
  width:var(--d);height:var(--d);
  transform:translate(-50%,-50%) scale(var(--play-s,1));
  display:grid;place-items:center;
  border:3px solid #fff;
  border-radius:50%;
  background:rgba(0,0,0,.30);
  box-shadow:0 2px 10px rgba(0,0,0,.4);
  cursor:pointer;
  padding:0;
}
.play::before{                          /* CSS triangle, no icon font, no SVG */
  content:"";
  width:0;height:0;
  margin-left:12%;
  border-left:calc(var(--d) * .26) solid #fff;
  border-top:calc(var(--d) * .155) solid transparent;
  border-bottom:calc(var(--d) * .155) solid transparent;
}

/* ---------- 7. the red string ------------------------------------------- */
/* z-index 0 against .pinned's z-index 1 inside .board's own stacking context:
   the string lies on the felt and every piece of paper covers it.
   No viewBox in the markup: one SVG user unit = one CSS pixel, so board.js can
   write the polyline points straight from getBoundingClientRect. */
.string{
  position:absolute;inset:0;z-index:var(--z-felt);
  width:100%;height:100%;
  overflow:visible;
  pointer-events:none;
}
.string polyline{
  fill:none;
  stroke:var(--string);
  stroke-width:2;
  stroke-linecap:round;
  stroke-linejoin:round;
  stroke-dasharray:var(--len,1600);
  /* --draw defaults to 1, so with JS off, with reduced motion, or on an
     entry-drawn string the offset is 0 and the string is complete. site.js
     writes --draw 0 -> 1 on a [data-draw="scroll"] string. */
  stroke-dashoffset:calc(var(--len,1600) * (1 - var(--draw,1)));
}

/* ---------- 9. the coral CTA card --------------------------------------- */
.cta{
  --cta-corner:clamp(12px,1.4vw,22px);
  display:block;
  padding:clamp(14px,1.7vw,26px) clamp(16px,2vw,30px);
  font-size:clamp(1.05rem,1.5vw,1.5rem);
  text-align:center;
  text-decoration:none;
  font-family:var(--display);
  font-weight:400;
  line-height:1.16;
  letter-spacing:-.01em;
  color:#fff;
  background:var(--coral);
  border-radius:var(--r-btn);
  box-shadow:var(--shadow-paper), inset 0 1px 0 rgba(255,255,255,.22);
}
/* the lifted top corner of a card pinned over felt */
.cta::after{
  content:"";
  position:absolute;top:0;right:0;
  width:var(--cta-corner);aspect-ratio:1;
  border-top-right-radius:var(--r-btn);
  background:linear-gradient(218deg,
    rgba(255,255,255,.22),
    rgba(255,255,255,.05) 44%,
    rgba(0,0,0,.11) 76%,
    rgba(0,0,0,0));
  pointer-events:none;
}

/* ---------- 3b. the paper family, member by member ---------------------- */
/* .sheet: the wide object you READ on. Max width comes from --content-max in
   tokens; a section only sets its own width and tilt (+-0.6deg maximum). */
.sheet{
  padding:clamp(20px,2.6vw,40px) clamp(18px,2.4vw,44px);
  box-shadow:var(--shadow-desk), var(--shadow-inset-paper);
}
/* .card: the index card. Steps, FAQ questions, resources, testimonials. */
.card{
  padding:clamp(14px,1.5vw,22px) clamp(14px,1.6vw,24px);
  box-shadow:var(--shadow-desk), var(--shadow-inset-paper);
}
/* .card-tabbed: a card whose top edge carries its own paper tab (s08's box,
   s12's deck). The tab is a child, not a pseudo-element, because it holds text. */
.card-tabbed{
  padding:clamp(14px,1.5vw,22px) clamp(14px,1.6vw,24px);
  box-shadow:var(--shadow-desk), var(--shadow-inset-paper);
}
.card-tabbed > .card-tab{
  position:absolute;left:var(--tab-x,18px);top:calc(-1 * var(--tab-h,24px));
  height:var(--tab-h,24px);
  display:inline-flex;align-items:center;
  padding:0 clamp(10px,1.1vw,16px);
  border-radius:4px 4px 0 0;
  background-color:var(--paper);
  background-image:url(../assets/board/paper.webp);
  background-size:300px 300px;
  background-blend-mode:multiply;
  box-shadow:0 -5px 9px rgba(0,0,0,.18), inset 0 1px 0 rgba(255,255,255,.5);
  font-weight:700;
  font-size:clamp(.62rem,.74vw,.78rem);
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--ink-soft);
  white-space:nowrap;
  z-index:var(--z-tab);
}
/* .tag: the small paper label. One pin, or hanging from a string. */
.tag{
  padding:clamp(7px,.8vw,11px) clamp(9px,1vw,15px);
  text-align:center;
  font-size:clamp(.72rem,.86vw,.9rem);
  line-height:1.4;
  color:var(--ink-soft);
}
/* .strip: the long thin paper strip. A pin at each end, one line of type. */
.strip{
  padding:clamp(9px,1.1vw,15px) clamp(20px,2.6vw,44px);
  text-align:center;
  font-size:clamp(.8rem,1vw,1.1rem);
  line-height:1.35;
  color:var(--ink);
}
/* .index-tab: the paper tab, as a <button role="tab"> (s03's board tabs) or a
   plain label. It sits 3px lower when it is not the chosen one, so the chosen
   tab reads as pulled forward without any colour change. */
.index-tab{
  --lift:0px;
  --tab-y:3px;
  display:inline-flex;align-items:center;
  padding:clamp(6px,.7vw,9px) clamp(11px,1.3vw,20px) clamp(7px,.8vw,10px);
  border:0;
  border-radius:4px 4px 0 0;
  box-shadow:0 -6px 10px rgba(0,0,0,.20), inset 0 1px 0 rgba(255,255,255,.45);
  transform:translateY(var(--tab-y)) scale(var(--press,1));
  font-weight:700;
  font-size:clamp(.66rem,.78vw,.82rem);
  line-height:1.2;
  letter-spacing:.09em;
  text-transform:uppercase;
  color:var(--ink-soft);
  white-space:nowrap;
  cursor:pointer;
}
.index-tab:hover,.index-tab.is-hover{--tab-y:1px;color:var(--ink)}
.index-tab[aria-selected="true"],.index-tab.is-active{
  --tab-y:0px;
  font-weight:900;
  color:var(--ink);
  box-shadow:0 -9px 14px rgba(0,0,0,.26), inset 0 1px 0 rgba(255,255,255,.6);
  z-index:var(--z-tab);
}
.index-tab:active,.index-tab.is-pressed{--tab-y:2px;--press:.99}

/* ---------- 4. the kraft family ----------------------------------------- */
/* Kraft is never a flat tan: a warm face, two crossed fibre gradients at a few
   degrees off-axis, the paper grain multiplied on top, a 1px darker cut edge
   all round and a darker bottom edge where the board thickness shows. */
.kraft,.kraft-tag,.folder,.folder-tab,.envelope,.envelope-flap,
.cardbox,.cardbox-lid,.cardbox-front{
  background-color:var(--kraft);
  background-image:
    linear-gradient(176deg, rgba(255,255,255,.10), rgba(0,0,0,.05) 42%, rgba(0,0,0,.19)),
    repeating-linear-gradient(94deg, rgba(122,90,45,.13) 0 1px, rgba(255,255,255,0) 1px 3px),
    repeating-linear-gradient(7deg, rgba(92,68,33,.09) 0 1px, rgba(255,255,255,0) 1px 4px),
    url(../assets/board/paper.webp);
  background-repeat:no-repeat, repeat, repeat, repeat;
  background-size:100% 100%, 7px 7px, 9px 9px, 220px 220px;
  background-blend-mode:normal, normal, normal, multiply;
  border-radius:2px;
  color:var(--kraft-ink);
}
.kraft{
  padding:clamp(16px,2vw,28px) clamp(16px,2.1vw,30px);
  box-shadow:inset 0 0 0 1.5px var(--kraft-edge), inset 0 -3px 0 rgba(0,0,0,.17), var(--shadow-desk);
}
/* .kraft-tag: the small kraft label. .is-punched adds the tag's own eyelet, for
   a tag that hangs from a string (s10's price tags, s04's caption tags). */
.kraft-tag{
  display:inline-block;
  padding:clamp(4px,.5vw,7px) clamp(9px,1vw,14px);
  box-shadow:inset 0 0 0 1px var(--kraft-edge), inset 0 -1px 0 rgba(0,0,0,.12), var(--shadow-desk);
  font-weight:700;
  font-size:clamp(.64rem,.76vw,.8rem);
  line-height:1.3;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--kraft-ink);
}
.kraft-tag.is-punched{padding-left:clamp(20px,2vw,28px)}
.kraft-tag.is-punched::before{
  content:"";
  position:absolute;left:clamp(7px,.8vw,11px);top:50%;
  width:7px;height:7px;
  transform:translateY(-50%);
  border-radius:50%;
  background:rgba(0,0,0,.44);
  box-shadow:inset 0 1px 1px rgba(0,0,0,.5), 0 1px 0 rgba(255,255,255,.28);
}

/* .folder: the kraft dossier. Default state is OPEN (you read what is inside).
   .is-closed puts the front flap back over the contents, and the flap lifts
   12deg on hover, focus-within or .is-open. The tab is a real element. */
.folder{
  --flap-open:0deg;
  padding:clamp(18px,2.2vw,32px) clamp(18px,2.2vw,32px) clamp(22px,2.6vw,38px);
  perspective:900px;
  box-shadow:inset 0 0 0 1px var(--kraft-edge), inset 0 3px 8px rgba(0,0,0,.14), var(--shadow-object);
}
.folder > .folder-tab{
  position:absolute;left:var(--tab-x,clamp(18px,2.4vw,40px));top:calc(-1 * var(--tab-h,28px));
  height:var(--tab-h,28px);
  display:inline-flex;align-items:center;
  padding:0 clamp(12px,1.4vw,20px);
  border-radius:4px 4px 0 0;
  box-shadow:inset 0 0 0 1px var(--kraft-edge), 0 -6px 10px rgba(0,0,0,.24);
  font-weight:700;
  font-size:clamp(.64rem,.76vw,.8rem);
  letter-spacing:.1em;
  text-transform:uppercase;
  white-space:nowrap;
  z-index:var(--z-tab);
}
.folder > .folder-inner{position:relative;z-index:1;transform:translateY(var(--inner-lift,0))}
.folder > .folder-flap{display:none}
.folder.is-closed > .folder-flap{
  display:block;
  position:absolute;left:0;right:0;bottom:0;
  height:var(--flap-h,62%);
  transform-origin:50% 100%;
  transform:rotateX(var(--flap-open));
  border-radius:0 0 2px 2px;
  /* the top edge of the cover, and the shadow it throws back on itself: both
     get stronger when the flap lifts, which is what makes 12deg legible. */
  box-shadow:
    inset 0 0 0 1px var(--kraft-edge),
    inset 0 2px 0 rgba(255,255,255,var(--flap-edge,.14)),
    inset 0 5px 9px rgba(255,255,255,.10),
    0 calc(-1 * var(--flap-cast,6px)) var(--flap-blur,14px) rgba(0,0,0,var(--flap-cast-a,.28));
  z-index:2;
}
/* the gap under the lifted cover: a shadow cast into the folder, sitting right
   on the flap's top edge. Opacity only. */
.folder.is-closed::after{
  content:"";
  position:absolute;left:0;right:0;
  bottom:var(--flap-h,62%);
  height:16px;
  background:linear-gradient(rgba(0,0,0,0), rgba(0,0,0,.46));
  opacity:var(--flap-gap,0);
  z-index:3;
  pointer-events:none;
}
.folder.is-closed:hover,.folder.is-closed:focus-within,
.folder.is-closed.is-hover,.folder.is-closed.is-open{
  --flap-open:-12deg;--lift:-3px;--inner-lift:-4px;
  --flap-gap:1;--flap-edge:.46;--flap-cast:13px;--flap-blur:22px;--flap-cast-a:.46;
}
.folder:active,.folder.is-pressed{--press:.995;--lift:0px}

/* .envelope: the kraft envelope of s05. The flap is a CSS triangle over the
   TOP of the envelope only, so the text inside is legible whether the flap is
   open or closed: with reduced motion or JS off nothing is hidden.
   OPEN has to be unmistakable in a STILL screenshot, so three things move at
   once: the flap stands up past the top edge and shows its lighter inner face,
   a fold shadow appears across the mouth, and the sheet inside rises 46px
   clear of the top edge. That means an open envelope paints ABOVE its own box:
   leave it ~ (--env-flap + 60px) of clearance in the section layout.
   --env-flap is a length, not a percentage, so the lift can be computed. */
.envelope{
  --env-open:0deg;
  --env-flap:clamp(52px,6.4vw,84px);
  --env-lift:0px;
  padding:calc(var(--env-flap) + clamp(10px,1.2vw,18px)) clamp(14px,1.6vw,24px) clamp(14px,1.6vw,24px);
  perspective:800px;
  box-shadow:inset 0 0 0 1px var(--kraft-edge), inset 0 -2px 0 rgba(0,0,0,.12), var(--shadow-desk);
}
.envelope > .envelope-flap{
  position:absolute;left:0;right:0;top:0;
  height:var(--env-flap);
  clip-path:polygon(0 0, 100% 0, 50% 100%);
  transform-origin:50% 0;
  transform:rotateX(var(--env-open));
  box-shadow:inset 0 0 0 1px var(--kraft-edge);
  filter:brightness(var(--env-face,.94));
  z-index:3;
}
/* the fold: the shadow the standing flap throws across the mouth. Opacity only,
   so it costs nothing when the envelope is closed. */
.envelope::before{
  content:"";
  position:absolute;left:0;right:0;top:0;
  height:13px;
  background:linear-gradient(rgba(0,0,0,.42), rgba(0,0,0,0));
  opacity:var(--env-fold,0);
  z-index:6;
  pointer-events:none;
}
/* an envelope holds a sheet of PAPER: white, so "open" reads at a glance. It
   sits above the flap so the standing flap never covers the words. */
.envelope > .envelope-sheet{
  position:relative;z-index:5;
  padding:clamp(10px,1.2vw,16px) clamp(12px,1.4vw,18px);
  background-color:var(--paper);
  background-image:url(../assets/board/paper.webp);
  background-size:300px 300px;
  background-blend-mode:multiply;
  border-radius:var(--r-paper);
  box-shadow:var(--shadow-desk), var(--shadow-inset-paper);
  color:var(--ink);
  transform:translateY(var(--env-lift));
}
.envelope:hover,.envelope:focus-within,.envelope.is-hover,.envelope.is-open{
  --env-open:-158deg;
  --env-face:1.18;
  --env-fold:1;
  --env-lift:calc(-1 * (var(--env-flap) + 46px));
  --lift:-2px;
}
.envelope:active,.envelope.is-pressed{--press:.995}

/* .cardbox: the kraft index-card box with its lid open behind it (s08).
   Three planes, no image: the lid laid back, the well the cards stand in, and
   the front panel that hides their bottom half. */
.cardbox{
  --box-front:32%;
  --lid-h:30%;
  padding:calc(var(--lid-h) + clamp(8px,1vw,14px)) clamp(14px,1.6vw,24px) clamp(12px,1.4vw,20px);
  perspective:1000px;
  box-shadow:inset 0 0 0 1px var(--kraft-edge), inset 0 8px 16px rgba(0,0,0,.20), var(--shadow-object);
}
.cardbox > .cardbox-lid{
  position:absolute;left:2.5%;right:2.5%;top:0;
  height:var(--lid-h);
  transform-origin:50% 100%;
  transform:rotateX(62deg);
  border-radius:2px 2px 0 0;
  box-shadow:inset 0 0 0 1px var(--kraft-edge), 0 8px 14px rgba(0,0,0,.30);
  filter:brightness(.88);
  z-index:0;
}
.cardbox > .cardbox-cards{position:relative;z-index:1;display:flex;align-items:flex-end}
.cardbox > .cardbox-front{
  position:absolute;left:0;right:0;bottom:0;
  height:var(--box-front);
  border-radius:0 0 3px 3px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.40), inset 0 0 0 1px var(--kraft-edge), 0 -8px 16px rgba(0,0,0,.26);
  z-index:var(--z-tab);
}
/* a card standing in the box comes up when you point at it and stays up when
   you press it (.is-active), which is the whole interaction of s08. */
.cardbox .card-tabbed{--pull:0px;transform:translateY(var(--pull)) rotate(var(--rot,0deg))}
.cardbox .card-tabbed:hover,.cardbox .card-tabbed:focus-within,
.cardbox .card-tabbed.is-hover{--pull:-24px;z-index:var(--z-lift)}
.cardbox .card-tabbed.is-active{--pull:-38px;z-index:var(--z-float)}
.cardbox .card-tabbed:active,.cardbox .card-tabbed.is-pressed{--pull:-20px}

/* ---------- 5. the thermal receipt -------------------------------------- */
/* Thermal paper: colder and lighter than index-card paper, a faint roller
   banding, grey-black ink, tabular figures, and both short edges torn. The
   tear is a mask, so the shadow has to be a filter (a mask clips box-shadow). */
.receipt{
  --tear-w:12px;
  --tear-d:6px;
  width:min(var(--receipt-w,420px),100%);
  padding:calc(var(--tear-d) + clamp(14px,1.6vw,22px)) clamp(16px,1.8vw,26px);
  background-color:var(--receipt);
  background-image:
    linear-gradient(180deg, rgba(0,0,0,.05), rgba(0,0,0,0) 12%, rgba(0,0,0,0) 88%, rgba(0,0,0,.05)),
    repeating-linear-gradient(180deg, rgba(0,0,0,.022) 0 1px, rgba(0,0,0,0) 1px 4px),
    linear-gradient(90deg, rgba(0,0,0,.05), rgba(0,0,0,0) 9%, rgba(0,0,0,0) 91%, rgba(0,0,0,.05));
  background-size:100% 100%, 100% 4px, 100% 100%;
  color:var(--receipt-ink);
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1;
  -webkit-mask:
    conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg)
      50% 0 / var(--tear-w) var(--tear-d) repeat-x,
    conic-gradient(from 135deg at top, #0000, #000 1deg 89deg, #0000 90deg)
      50% 100% / var(--tear-w) var(--tear-d) repeat-x,
    linear-gradient(#000 0 0) 50% 50% / 100% calc(100% - 2 * var(--tear-d)) no-repeat;
  mask:
    conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg)
      50% 0 / var(--tear-w) var(--tear-d) repeat-x,
    conic-gradient(from 135deg at top, #0000, #000 1deg 89deg, #0000 90deg)
      50% 100% / var(--tear-w) var(--tear-d) repeat-x,
    linear-gradient(#000 0 0) 50% 50% / 100% calc(100% - 2 * var(--tear-d)) no-repeat;
  filter:drop-shadow(0 2px 2px rgba(0,0,0,.26)) drop-shadow(0 14px 22px rgba(0,0,0,.34));
}
.receipt-rule{
  height:0;margin:clamp(10px,1.2vw,16px) 0;
  border:0;border-top:1px dashed rgba(37,36,31,.40);
}
.receipt-line{
  display:flex;align-items:baseline;gap:var(--sp-3);
  justify-content:space-between;
}
.receipt-line > :last-child{font-variant-numeric:tabular-nums;white-space:nowrap}
.receipt-dim{color:var(--receipt-dim)}

/* ---------- 6b. the polaroid -------------------------------------------- */
/* A photo print with its own margin to write in. The wide bottom margin is
   what makes it a polaroid and not a .print, so it is not a variant. */
.polaroid{
  --polaroid-pad:clamp(9px,1.05vw,14px);
  padding:var(--polaroid-pad) var(--polaroid-pad) clamp(14px,1.6vw,22px);
  background:var(--polaroid);
  border-radius:2px;
  box-shadow:var(--shadow-desk), inset 0 0 0 1px rgba(0,0,0,.06);
}
.polaroid > img,.polaroid > .polaroid-window{
  width:100%;
  aspect-ratio:var(--polaroid-ar,1);
  object-fit:cover;
  background:#0D100E;
  border-radius:1px;
  box-shadow:inset 0 0 10px rgba(0,0,0,.35);
}
/* the wide bottom margin IS the polaroid, so the note lives in it rather than
   on top of the photograph: min-height keeps the margin even for one line. A
   .polaroid should always carry a .polaroid-note, even an empty one. */
.polaroid > .polaroid-note{
  margin-top:clamp(10px,1.2vw,16px);
  min-height:clamp(24px,2.6vw,34px);
  font-size:clamp(.72rem,.84vw,.88rem);
  line-height:1.35;
  letter-spacing:.012em;
  color:var(--ink-soft);
}
.polaroid:hover,.polaroid:focus-within,.polaroid.is-hover{
  --lift:-6px;--rot:0deg;
  box-shadow:var(--shadow-desk-hi), inset 0 0 0 1px rgba(0,0,0,.06);
}
.polaroid:active,.polaroid.is-pressed{--lift:-2px;--press:.99}

/* ---------- 7b. fasteners: tape, clothespin, rope ----------------------- */
/* Masking tape: translucent so the paper under it shows through, multiplied
   into it, both ends torn. .taped is the object the tape holds. */
.tape{
  --tape-w:clamp(64px,7vw,104px);
  --tape-h:clamp(20px,2.1vw,28px);
  --tear-w:9px;
  --tear-d:4px;
  width:var(--tape-w);height:var(--tape-h);
  background:
    linear-gradient(176deg, rgba(255,255,255,.34), rgba(0,0,0,.05) 40%, rgba(0,0,0,.12)),
    repeating-linear-gradient(91deg, rgba(120,104,72,.10) 0 1px, rgba(255,255,255,0) 1px 5px),
    linear-gradient(var(--tape), var(--tape));
  background-size:100% 100%, 6px 6px, 100% 100%;
  opacity:var(--tape-a);
  mix-blend-mode:multiply;
  transform:rotate(var(--tape-rot,-4deg)) translate(var(--tape-peel-x,0),var(--tape-peel,0));
  -webkit-mask:
    conic-gradient(from 45deg at right, #0000, #000 1deg 89deg, #0000 90deg)
      0 50% / var(--tear-d) var(--tear-w) repeat-y,
    conic-gradient(from -135deg at left, #0000, #000 1deg 89deg, #0000 90deg)
      100% 50% / var(--tear-d) var(--tear-w) repeat-y,
    linear-gradient(#000 0 0) 50% 50% / calc(100% - 2 * var(--tear-d)) 100% no-repeat;
  mask:
    conic-gradient(from 45deg at right, #0000, #000 1deg 89deg, #0000 90deg)
      0 50% / var(--tear-d) var(--tear-w) repeat-y,
    conic-gradient(from -135deg at left, #0000, #000 1deg 89deg, #0000 90deg)
      100% 50% / var(--tear-d) var(--tear-w) repeat-y,
    linear-gradient(#000 0 0) 50% 50% / calc(100% - 2 * var(--tear-d)) 100% no-repeat;
  z-index:var(--z-pin);
  pointer-events:none;
}
.taped:hover,.taped:focus-within,.taped.is-hover{--lift:-3px}
.taped:hover > .tape,.taped:focus-within > .tape,.taped.is-hover > .tape{
  --tape-peel:-2px;--tape-rot:-6deg;
}
.taped:active,.taped.is-pressed{--lift:0px;--press:.995}

/* Wooden clothespin, drawn in CSS: two legs with a dark seam, the brass spring
   across the middle, the pinch gap at the bottom. Set --clip-img to an asset
   URL and add .is-image to swap the drawing for a photograph without touching
   any markup. */
.clip{
  --clip-w:15px;
  --clip-h:46px;
  --clip-img:linear-gradient(90deg,
    var(--clip-wood-lo) 0%, var(--clip-wood-hi) 17%, #CFA96F 46%,
    #8E6B3C 50%, #C8A268 54%, var(--clip-wood-hi) 82%, #B48F58 100%);
  width:var(--clip-w);height:var(--clip-h);
  background-image:var(--clip-img);
  background-size:cover;
  background-position:center;
  border-radius:3px 3px 2px 2px;
  box-shadow:
    0 2px 4px rgba(0,0,0,.42),
    inset 0 -7px 9px rgba(0,0,0,.16),
    inset 0 1px 0 rgba(255,255,255,.38);
  transform:rotate(var(--clip-rot,0deg));
  z-index:var(--z-pin);
  pointer-events:none;
}
.clip::before{
  content:"";
  position:absolute;left:-2px;right:-2px;top:54%;
  height:4px;
  background:linear-gradient(#F0DEAB, var(--brass) 45%, #7A5F22);
  border-radius:2px;
  box-shadow:0 1px 1px rgba(0,0,0,.45);
}
.clip::after{
  content:"";
  position:absolute;left:47%;right:47%;top:63%;bottom:0;
  background:rgba(0,0,0,.34);
}
.clip.is-image{box-shadow:none;border-radius:0}
.clip.is-image::before,.clip.is-image::after{display:none}
/* Manager addition at gate 1 (founder approved the props pack, 2026-09-21): the photographed clothespin is the
   default image for .clip.is-image, so s04 only writes <i class="clip is-image"></i>. 297x700 source. */
.clip.is-image{
  --clip-img:url(../assets/props/clothespin.webp);
  --clip-w:22px;--clip-h:52px;
  background-size:contain;background-repeat:no-repeat;
  filter:drop-shadow(0 2px 2px rgba(0,0,0,.42));
}
@media (max-width: 899px){ .clip.is-image{--clip-w:18px;--clip-h:42px} }

/* Desk props: photographic cut-outs lying on the desk at the edges of a section. Decorative only:
   <img class="prop" src="assets/props/tape.webp" alt="" aria-hidden="true" width="700" height="672" loading="lazy">
   Section CSS positions them (left/right/top/bottom, width, --rot). They never sit over text, never take pointer
   events, and they are removed below 900px, where the desk margin is only 12px. */
.prop{
  position:absolute;z-index:0;
  width:var(--prop-w,180px);height:auto;
  transform:rotate(var(--rot,0deg));
  filter:drop-shadow(0 10px 12px rgba(0,0,0,.45));
  pointer-events:none;user-select:none;
}
@media (max-width: 899px){ .prop{display:none} }

/* Rope: a twisted cord. Straight as an element, or as an SVG path when it has
   to sag (s04 draws a catenary and styles it with .rope-svg). */
.rope{
  height:var(--rope-h,6px);
  border-radius:3px;
  background:
    repeating-linear-gradient(64deg,
      rgba(0,0,0,.18) 0 2px, rgba(255,255,255,.16) 2px 4px, rgba(0,0,0,0) 4px 7px),
    linear-gradient(180deg, #DCCBA9, var(--rope) 45%, var(--rope-dark));
  box-shadow:0 2px 3px rgba(0,0,0,.34);
}
.rope-svg{overflow:visible;pointer-events:none}
.rope-svg path{
  fill:none;
  stroke:var(--rope);
  stroke-width:5;
  stroke-linecap:round;
  filter:drop-shadow(0 2px 2px rgba(0,0,0,.35));
}
.rope-svg path.rope-shade{stroke:var(--rope-dark);stroke-width:6;filter:none}

/* ---------- 8. marks: the rubber stamp and the claim container ---------- */
/* Coral outline and coral text, 2deg off true, multiplied into the paper so it
   reads as ink and not as a badge; the paper grain is screened back over it so
   the ink breaks up where the paper is raised. Stamps belong ON PAPER OR KRAFT:
   on the bare desk use .stamp.is-plain, which drops the blend mode. */
.stamp,.stamp-block{
  display:inline-block;
  color:var(--coral);
  border:2px solid var(--coral);
  border-radius:3px;
  font-family:var(--sans);
  font-weight:900;
  letter-spacing:.14em;
  text-transform:uppercase;
  white-space:nowrap;
  mix-blend-mode:multiply;
  transform:rotate(var(--stamp-rot,-2deg));
}
.stamp::after,.stamp-block::after{
  content:"";
  position:absolute;inset:-2px;
  background:url(../assets/board/paper.webp) 0 0/110px 110px;
  mix-blend-mode:screen;
  opacity:.30;
  border-radius:inherit;
  pointer-events:none;
}
/* the small inline variant build.mjs injects inside [data-claim] */
.stamp{
  margin-left:.45em;
  padding:.1em .34em;
  font-size:.62em;
  line-height:1.5;
  vertical-align:.1em;
  border-width:1.5px;
}
/* the large block variant: SIN SUSCRIPCION across a sheet, INCLUIDO on a flap */
.stamp-block{
  padding:clamp(7px,.85vw,12px) clamp(12px,1.5vw,22px);
  font-size:clamp(.78rem,1.05vw,1.15rem);
  line-height:1.15;
  border-width:2.5px;
  box-shadow:inset 0 0 0 1px rgba(216,56,47,.35);
}
/* Manager fix after Phase 4 (2026-09-21): coral multiplied onto kraft measures about 2:1, so the small EJEMPLO stamp all
   but vanished on kraft tags, folders and envelopes, which defeats the stamp. On kraft the small stamp sits on its own
   sliver of paper (coral on paper is 4.6:1) and is not blended. The large .stamp-block keeps its inked look. */
:is(.kraft,.kraft-tag,.folder,.envelope,.cardbox) .stamp:not(.stamp-block){
  background-color:var(--paper);
  mix-blend-mode:normal;
  box-shadow:0 1px 1px rgba(0,0,0,.18);
}
.stamp.is-plain,.stamp-block.is-plain{mix-blend-mode:normal}
.stamp.is-plain::after,.stamp-block.is-plain::after{display:none}

/* [data-claim] is written by build.mjs around every number that is still the
   competitor's. Inline it is a <span>; the {{#claim}} block form is any other
   tag and parks its stamp in the top-right corner. */
span[data-claim]{position:relative}
[data-claim]:not(span){position:relative}
[data-claim]:not(span) > .stamp:last-child{
  position:absolute;top:clamp(8px,1vw,14px);right:clamp(8px,1vw,14px);
  margin-left:0;
  font-size:.7rem;
  z-index:var(--z-lift);
}

/* ---------- 9b. buttons, links and the chevron -------------------------- */
/* .btn is the coral BUTTON (the nav and inline CTAs); .cta is the coral CARD.
   Same coral, same radius, one intent label across the page. */
.btn{
  display:inline-block;
  padding:clamp(11px,1.15vw,15px) clamp(16px,1.9vw,26px);
  border:0;
  border-radius:var(--r-btn);
  background:var(--coral);
  color:#fff;
  font-family:var(--sans);
  font-weight:700;
  font-size:clamp(.88rem,1vw,1.02rem);
  line-height:1.2;
  text-align:center;
  text-decoration:none;
  white-space:nowrap;
  cursor:pointer;
  box-shadow:0 1px 0 rgba(0,0,0,.14), 0 4px 10px rgba(0,0,0,.20);
}
.btn:hover,.btn.is-hover{background:var(--coral-press);--lift:-2px}
.btn:active,.btn.is-pressed{--lift:1px;--press:.99;background:var(--coral-press)}
/* the quiet paper variant: a secondary action on a sheet, never next to a .cta */
.btn-quiet{
  background-color:var(--paper);
  background-image:url(../assets/board/paper.webp);
  background-size:300px 300px;
  background-blend-mode:multiply;
  color:var(--ink);
  box-shadow:inset 0 0 0 1px var(--paper-edge), 0 2px 5px rgba(0,0,0,.20);
}
.btn-quiet:hover,.btn-quiet.is-hover{
  background-color:#FDFCFA;color:var(--ink);
  box-shadow:inset 0 0 0 1px #CFCAC0, 0 5px 12px rgba(0,0,0,.24);
}
.btn-quiet:active,.btn-quiet.is-pressed{background-color:#EFECE5;--lift:1px}

/* .link: a text link read on paper. The coral underline is the only accent a
   link gets; the text itself stays ink until you point at it. */
.link{
  color:var(--ink);
  text-decoration:underline;
  text-decoration-thickness:1.5px;
  text-underline-offset:3px;
  text-decoration-color:var(--coral);
  cursor:pointer;
}
.link:hover,.link.is-hover{color:var(--coral)}
.link:active,.link.is-pressed{color:var(--coral-press);text-decoration-color:var(--coral-press)}
.link-kraft{color:var(--kraft-ink);text-decoration-color:var(--kraft-ink-soft)}
.link-kraft:hover,.link-kraft.is-hover{color:#100D06;text-decoration-color:var(--kraft-ink)}

/* .chev: the accordion chevron. Two borders on a square, rotated; no icon set. */
.chev{
  display:inline-block;
  width:9px;height:9px;
  flex:0 0 auto;
  border-right:2px solid currentColor;
  border-bottom:2px solid currentColor;
  transform:rotate(var(--chev-rot,45deg)) translateY(var(--chev-y,-2px));
  opacity:.72;
}
[aria-expanded="true"] .chev,[open] .chev,.chev.is-open{--chev-rot:-135deg;--chev-y:2px}
summary:hover .chev,[aria-expanded] :hover > .chev,.chev.is-hover{opacity:1}
summary{
  cursor:pointer;list-style:none;
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
}
summary::-webkit-details-marker{display:none}

/* ---------- 12b. the scroll-entry reveal -------------------------------- */
/* The hidden start state is gated behind html.reveal-ready, which ONLY site.js
   adds, and only on the same frame it starts observing. With JS off, with
   site.js absent, or with reduced motion, every [data-reveal] renders complete
   and static in its final state. 12px settle, 70ms stagger through --i. */
@media (prefers-reduced-motion: no-preference){
  html.reveal-ready [data-reveal]:not(.pinned){opacity:0;transform:translateY(12px)}
  /* Manager fix after Phase 3 (found by builders A and C): the end state used to be `transform:none`, which erased a
     revealed object's own --rot tilt and its hover lift for the rest of the page's life. It now resolves to the same
     composition every lifting material uses, which is the identity when none of the three properties is set. */
  html.reveal-ready [data-reveal]:not(.pinned).is-in{
    opacity:1;transform:translateY(var(--lift,0)) scale(var(--press,1)) rotate(var(--rot,0deg));
    transition:
      opacity 520ms cubic-bezier(.2,.8,.25,1) calc(var(--i,0) * 70ms),
      transform 520ms cubic-bezier(.2,.8,.25,1) calc(var(--i,0) * 70ms);
  }
  /* a pinned object reveals the way the hero settles: from above, around its pin */
  html.reveal-ready .pinned[data-reveal]{
    opacity:0;transform:translateY(-12px) scale(1.03) rotate(var(--rot,0deg));
  }
  html.reveal-ready .pinned[data-reveal].is-in{
    opacity:1;transform:rotate(var(--rot,0deg));
    transition:
      opacity 520ms cubic-bezier(.2,.8,.25,1) calc(var(--i,0) * 70ms),
      transform 520ms cubic-bezier(.2,.8,.25,1) calc(var(--i,0) * 70ms);
  }
  html.reveal-ready [data-reveal] .pin{animation:none}
  html.reveal-ready [data-reveal].is-in .pin{
    animation:pop 260ms cubic-bezier(.34,1.56,.64,1) backwards;
    animation-delay:calc(var(--i,0) * 70ms + 80ms);
  }

  /* the states materials owns, so a section never writes a transition */
  .index-tab,.polaroid,.folder,.folder > .folder-inner,.envelope,
  .envelope > .envelope-flap,.envelope > .envelope-sheet,.folder > .folder-flap,
  .cardbox .card-tabbed,.taped,.taped > .tape,.btn,.card:not(.pinned),
  .sheet:not(.pinned),.receipt{
    transition:transform .24s cubic-bezier(.2,.8,.25,1), box-shadow .24s ease, background-color .18s ease;
  }
  .envelope::before,.folder.is-closed::after{transition:opacity .22s ease}
  .chev{transition:transform .2s cubic-bezier(.2,.8,.25,1), opacity .15s ease}
  .link{transition:color .15s ease, text-decoration-color .15s ease}
}

/* ---------- 10. the one shared lightbox --------------------------------- */
.lightbox{
  position:fixed;inset:0;z-index:var(--z-lightbox);
  display:grid;place-items:center;
  padding:clamp(16px,4vw,48px);
  background:rgba(8,14,11,.88);
}
.lightbox video{
  width:auto;height:100%;
  aspect-ratio:9/16;
  max-width:100%;max-height:100%;
  border-radius:var(--r-print);
  background:#000;
  box-shadow:0 30px 70px rgba(0,0,0,.6);
}
.lightbox .close{
  position:absolute;top:clamp(12px,2vw,26px);right:clamp(12px,2vw,26px);
  width:44px;height:44px;
  border:1px solid rgba(255,255,255,.4);
  border-radius:50%;
  background:rgba(255,255,255,.1);
  cursor:pointer;
}
.lightbox .close::before,.lightbox .close::after{
  content:"";position:absolute;left:13px;top:21px;
  width:18px;height:2px;background:#fff;
}
.lightbox .close::before{transform:rotate(45deg)}
.lightbox .close::after{transform:rotate(-45deg)}
.lightbox .close:hover{background:rgba(255,255,255,.2)}

/* ---------- 11. focus --------------------------------------------------- */
:focus-visible{outline:3px solid #fff;outline-offset:3px}
/* Manager fix after Phase 3 (found by builder A): the white ring is right on felt, oak and coral, and invisible on
   paper and kraft. Anything focusable INSIDE a light surface gets an ink ring; the surface itself, when it is the
   focusable object lying on the dark desk, keeps the white one. */
:is(.paper,.sheet,.card,.tag,.strip,.receipt,.polaroid,.index-tab) :focus-visible{outline-color:var(--ink)}
:is(.kraft,.kraft-tag,.folder,.envelope,.cardbox) :focus-visible{outline-color:var(--kraft-ink)}
:is(.paper,.sheet,.card,.tag,.strip,.receipt,.kraft,.folder) :is(.cta,.btn:not(.btn-quiet)):focus-visible{outline-color:var(--ink);outline-offset:4px}

/* ---------- hover / press (materials own these; swing, drag and video are
   board.js) ------------------------------------------------------------- */
.print:hover,.print:focus-visible{--lift:-4px;box-shadow:var(--shadow-paper-hi)}
.print:hover .play{--play-s:1.08}
.play:hover{--play-s:1.12;background:rgba(0,0,0,.45)}
.cta:hover{--lift:-3px;box-shadow:var(--shadow-paper-hi)}
.cta:active{--lift:0px;--press:.985}

/* ---------- 12. motion -------------------------------------------------- */
@media (prefers-reduced-motion: no-preference){
  html{scroll-behavior:smooth}
  .print,.cta{transition:transform .22s cubic-bezier(.2,.8,.25,1), box-shadow .22s ease}
  .play{transition:transform .2s cubic-bezier(.2,.8,.25,1), background-color .2s ease}

  /* ---------- entry choreography (the hero, on load) -------------------
     Reading order, 70ms apart: the story is "here is the promise, here is
     the proof", so the copy column lands before the prints and the string
     that ties them together draws last.  Opacity and transform only, and
     the fill is `backwards` so the engine keeps ownership of .style.transform
     the moment each element has settled. */
  .pinned:not([data-reveal]){
    animation:settle 520ms cubic-bezier(.2,.8,.25,1) backwards;
    animation-delay:calc(var(--i,0) * 70ms);
  }
  .pin{
    animation:pop 260ms cubic-bezier(.34,1.56,.64,1) backwards;
    animation-delay:calc(var(--i,0) * 70ms + 80ms);
  }
  .string:not([data-draw="scroll"]) polyline{
    animation:draw 700ms cubic-bezier(.4,0,.2,1) backwards;
    animation-delay:840ms;
  }
}
@keyframes settle{
  from{opacity:0;transform:translateY(-10px) scale(1.04) rotate(var(--rot,0deg))}
  to  {opacity:1;transform:rotate(var(--rot,0deg))}
}
@keyframes pop{
  from{opacity:0;transform:translate(-50%,-50%) rotate(var(--pr,0deg)) scale(.4)}
  to  {opacity:1;transform:translate(-50%,-50%) rotate(var(--pr,0deg)) scale(1)}
}
/* board.js sets --len (and stroke-dasharray) to the measured polyline length;
   1600 is a safe default, longer than the string at any viewport. */
@keyframes draw{
  from{stroke-dashoffset:var(--len,1600)}
  to  {stroke-dashoffset:0}
}

/* ============================================================================
   MOBILE < 900px, material level only (a section's own collapse lives in its
   own file). A board stops being a stage: it keeps its felt and its frame but
   takes its height from its content.
   ========================================================================= */
@media (max-width: 899px){
  .board{
    height:auto;min-height:0;
    container-type:normal;
    border-width:10px;
    border-image:url(../assets/board/wood.webp) 40 round;
    background-size:100% 100%, 240px 240px;
  }
  /* opt-in: a pinned composition becomes a single column of the same objects,
     each still pinned and still slightly crooked (--rot-m).
     The class is doubled on purpose: section CSS loads AFTER this file, so a
     plain `.board-stack .pinned` (0,2,0) would lose to a section's own
     `.s-NN .card` (0,2,0) desktop rule and the column would keep its absolute
     positions. (0,3,0) wins. A section that really needs to override writes
     `.s-NN .board-stack .pinned` (0,3,0) and wins on order. */
  .board-stack{
    display:flex;flex-direction:column;align-items:stretch;
    gap:var(--gap-row);   /* objects in the column need air; a section may override */
  }
  .board-stack.board-stack .pinned{
    position:relative;
    left:auto;right:auto;top:auto;width:auto;height:auto;
    transform:rotate(var(--rot-m,0deg));
  }
  .lightbox video{height:auto;width:100%;max-height:78dvh}

  /* the objects on the desk go full width inside the desk margin; a section
     still owns its own stacking order and its own tilt. */
  .receipt{--receipt-w:100%}
  .sheet{padding:clamp(16px,4.4vw,24px) clamp(14px,4vw,20px)}
  .folder{padding:18px 16px 22px}
  .envelope{--env-flap:46px}
  .cardbox{--box-front:38%;--lid-h:26%}
  .cardbox > .cardbox-cards{flex-direction:column;align-items:stretch}
  .cardbox .card-tabbed:hover,.cardbox .card-tabbed.is-hover,
  .cardbox .card-tabbed.is-active{--pull:0px}
  .index-tab{padding:6px 11px 7px;font-size:.68rem;letter-spacing:.07em}
  .stamp-block{font-size:.8rem;padding:7px 12px}
  .polaroid{padding:8px 8px 34px}
  .clip{--clip-w:13px;--clip-h:38px}
  .tape{--tape-w:64px;--tape-h:20px}
}

/* ---------- oak strip (manager addition after Phase 4, verifier finding on s14) ----------
   The front edge of the desk: the same photographed oak as the board frame, lighter than the desk surface, with a lit
   chamfer on top and a wash that keeps paper-coloured text above 4.5:1. Used by the footer. */
.oak-strip{
  background-color:var(--wood);
  background-image:
    linear-gradient(rgba(20,12,6,.34), rgba(20,12,6,.52)),
    url(../assets/board/wood.webp);
  background-repeat:no-repeat, repeat;
  background-size:100% 100%, 560px 560px;
  box-shadow:
    inset 0 3px 0 rgba(255,255,255,.16),
    inset 0 1px 0 rgba(0,0,0,.55),
    0 -12px 26px rgba(0,0,0,.38);
  color:var(--paper);
}
