/* Variant A, "The Deck": each app is a sheet of its own color that pins as you scroll.
   Covered sheets stay as rims (an accent line plus the app's name over its ground), so by the last app the top of the screen holds the spectrum.
   Every sheet holds for --hold of scroll before the next one arrives, and the finished stack holds again before the shelf rises. */

/* The cover: a heading, then one paragraph that names every app and jumps to it */
.cover { padding: clamp(5rem, 12vh, 9rem) var(--gx) clamp(3rem, 8vh, 6rem); max-width: 96rem; margin: 0 auto; }

.cover-h { font-family: var(--font-display); font-weight: 800; letter-spacing: -0.03em; font-size: clamp(2.2rem, 5vw, 4rem); line-height: 0.95; margin-bottom: 1.5rem; text-wrap: balance; }
.cover-lead {
  font-family: var(--font-body); font-weight: 400;
  font-size: clamp(1.2rem, 1.75vw, 1.5rem); line-height: 1.5;
  max-width: 58rem; text-wrap: pretty; color: rgba(255,255,255,0.86);
}
.cover-lead a { color: var(--c, #fff); font-family: var(--font-display); font-weight: 700; font-size: 1.05em; text-decoration: none; border-bottom: 2px solid color-mix(in srgb, var(--c, #fff) 35%, transparent); transition: border-color 0.2s; }
.cover-lead a:hover { border-bottom-color: var(--c, #fff); }
/* The names are 34px tall in a 36px line. The pseudo-element gives them a full line box of target (37px at 1440,
   30px on a phone) rather than padding, which would have pushed the underline away from the word. A 44px box was
   tried and reverted: it is taller than the line, so a name stole taps meant for the line above or below. 30px
   clears the 24px that WCAG asks of a link inside a sentence. */
.cover-lead a { position: relative; }
.cover-lead a::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1.42em; transform: translateY(-50%); }   /* one line box, so a name never takes a tap meant for the line above or below */
.cover-lead a.soon { color: rgba(255,255,255,0.55); font-weight: 700; border-bottom-color: rgba(255,255,255,0.2); }
.cover-lead .nw { white-space: nowrap; }
.cover-index {
  display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; margin-top: clamp(1.5rem, 3vh, 2.25rem);
  font-family: var(--font-display); font-weight: 800; font-size: clamp(1.6rem, 3vw, 2.8rem); line-height: 1.1; letter-spacing: -0.025em;
}
.cover-index a { color: var(--c, #fff); white-space: nowrap; padding-bottom: 0.1em; border-bottom: 3px solid color-mix(in srgb, var(--c, #fff) 30%, transparent); transition: border-color 0.2s, transform 0.25s var(--ease); }
.cover-index a:hover { border-bottom-color: var(--c, #fff); transform: translateY(-2px); }
.cover-index a.soon { color: rgba(255,255,255,0.45); border-bottom-color: rgba(255,255,255,0.15); }
.cover-index small { font-size: 0.4em; font-weight: 700; letter-spacing: 0.06em; vertical-align: middle; margin-left: 0.2em; }

/* The stack */
.sheets {
  --rim: 14px;       /* what stays visible of a covered sheet: its accent line and a sliver of tinted ground */
  --tab-h: 30px;     /* the folder tab that rises above each sheet's line */
  --navh: calc(4.6rem + var(--tab-h) + 12px);   /* the fixed nav plus room for the first tab and its 44px hit area */   /* the fixed nav (0.9rem padding around a 44px row) plus room for the first tab */
  --hold: 12svh;     /* how long a sheet stays alone before the next one starts to arrive */
  position: relative; z-index: 1;
}
/* HOW FAR THE DECK SLIDES AT THE END OF THE PAGE IS EXACTLY THE FOOTER'S HEIGHT. Measured: the last sheet unpins
   when its container's bottom reaches it, so at maximum scroll the deck sits (document height - sheets bottom)
   above its pin, and that difference is the footer, nothing else. Adding room inside .sheets cannot change it,
   because that moves the unpin point and the document end by the same amount. The last folder's top therefore
   lands at (nav + five rims - footer), whatever the window is.

   THIS USED TO BE PAID FOR WITH A NEGATIVE TOP MARGIN ON THE FOOTER, AND THAT WAS THE WRONG CURRENCY. Pulling the
   footer up by 6.2rem did hold the closing heading clear of the nav, but a negative margin does not make the slide
   smaller, it only parks the footer on top of the folder: the footer paints above the sheet, so it covered 13 to
   60px of the filmstrip at seven of the nine sizes measured, and a little of the folder tab rail was still hanging
   under the nav on a wide screen. Cutting something off to stop something else being cut off is not a fix.

   So the slide is left alone and the two things competing for the window are made to fit in it instead. At the
   tightest size measured (1024x740) the closing folder wanted 459px, the nav takes 74 and the footer took 250:
   783px of a 740px window, which is why one end or the other had to go missing. The footer under the deck is
   tighter now, and the filmstrip gives up a little of its own padding, which brings the three back under the window
   at every size the deck pins at. Both are changes those two elements can carry on their own.
   Scoped to the deck: the app pages load this stylesheet too and have no deck to end. */
@media (min-width: 56rem) and (min-height: 46.01rem) {
  .lab-deck .footer { padding-block: 1.5rem 0.85rem; }
  .lab-deck .footer-inner { row-gap: 0.75rem; }
  .lab-deck .footer-nav { row-gap: 0.5rem; }   /* only the gap BETWEEN wrapped rows of links, which is the 67px the narrow window cannot afford */
  .lab-deck .reel-wrap { padding-block: 0.75rem; }   /* bottom aligned, so this IS the strip's gap to the folder floor; at 0.3rem it measured 3.3px clear at 1536x740, which is one type change away from through it */
  /* The sprockets give the height up instead, continuously, the way everything else in this file that has to fit a
     short window does. 24px at 740 tall, nothing at all by 1190. */
  .lab-deck .reel-edge { margin-block: clamp(4px, calc(4px + (100svh - 46rem) * 0.022), 10px); }
  /* The filmstrip sits at the foot of its folder rather than in the middle of it. Every pixel of slack a centred
     strip leaves above itself is a pixel the closing heading does not have when the folder rises, and it rises by
     the height of the footer. Bottom-aligned, the whole of that slack goes where it is needed. */
  .lab-deck .sheet-more .sheet-inner-wide { align-items: end; }
}
/* the finished deck rests here before it scrolls away (an element, not padding: sticky only holds inside the content box) */
.deck-rest { height: max(12svh, 7rem); }   /* at least as tall as the largest pull the footer takes above, or the pull eats the last folder's own bottom edge */
.sheet {
  --i: 0;
  position: sticky; top: calc(var(--navh) + var(--i) * var(--rim));
  height: calc(100svh - var(--navh) - var(--i) * var(--rim));
  background: var(--bg);
  border-top: 3px solid var(--accent);
  box-shadow: 0 -30px 60px -30px rgba(0,0,0,0.75);
  overflow: visible; clip-path: inset(-48px 0 0 0);   /* clip the sides and bottom, let the tab rise above the edge */
  isolation: isolate;
  transform-origin: 50% 0%;
  z-index: calc(var(--i) + 1);
}
/* the hold between sheets is an element too: Chrome counts a sticky element's margins against its container */
.deck-gap { height: var(--hold); }
/* the rim band: the part of a covered sheet that stays visible, tinted with its accent so the stack reads as color, not hairlines */
.sheet::after { content: ""; position: absolute; left: 0; right: 0; top: 0; z-index: 1; height: calc(var(--rim) - 3px); pointer-events: none; background: color-mix(in srgb, var(--accent) 30%, var(--bg)); }
/* the name in the rim: a tab you can click to come back. Tabs pack side by side across the top (JS measures them; the 11rem step is the no-JS fallback) */
.sheet-tab {
  /* THE RAIL SITS ON THE SAME GRID AS THE FOLDER'S CONTENT. The offset was the page gutter measured from the
     sheet, which is full width, while .sheet-inner is capped at 96rem and centred: aligned at 1440, but by 2560
     the six tabs were 551px to the left of the copy they label, floating against the window edge while the app's
     name started half a screen away. max(0px, (100% - 96rem) / 2) is the inner's own left edge. */
  position: absolute; top: calc(-1 * var(--tab-h) - 3px); z-index: 2;
  left: calc(max(0px, (100% - 96rem) / 2) + var(--gx) + var(--tab-x, var(--i) * 11rem));
  display: inline-flex; align-items: center; height: var(--tab-h); padding: 0 0.75rem; margin-left: -0.75rem;
  font-family: var(--font-display); font-weight: 700; font-size: 0.92rem; letter-spacing: 0;
  color: var(--accent); background: var(--bg); white-space: nowrap;
  border: 2px solid var(--accent); border-bottom: 0; border-radius: 8px 8px 0 0;
  box-shadow: 0 -8px 20px -10px rgba(0,0,0,0.7);
  transition: color 0.2s, background-color 0.2s;
}
.sheet-tab::after { content: ""; position: absolute; left: 0; right: 0; top: -9px; bottom: -9px; }   /* 44px hit area */
/* A TAB TAKES A CLICK ONLY ONCE ITS FOLDER IS PARKED IN THE RAIL. A tab sits above its own sheet and rides up with
   it, so while a folder is arriving its tab travels across the folder you are still reading, painting above it at
   z-index 2 and swallowing the pointer: 35 scroll positions were measured where the tab was topmost over a link or
   a button, including 111x21px of "Get Song Reviewer" at a position you can simply stop scrolling at.
   Parked, with its folder covered and inert, the tab is the control that brings that folder back, and it stays
   clickable. Arriving, it is decoration and gets out of the way. The 44px hit area goes with it, which is why this
   is on the anchor and not only on the ::after. */
.sheet-tab { pointer-events: none; }
.sheet:has(> .sheet-inner[data-covered="1"]) > .sheet-tab { pointer-events: auto; }
.sheet-tab:focus-visible { pointer-events: auto; }   /* the keyboard reaches it whatever the pointer may do */
.sheet-tab:hover { background: color-mix(in srgb, var(--accent) 18%, var(--bg)); }
.sheet-tab[aria-current] { background: var(--accent); color: var(--bg); }   /* the folder you are reading */
/* A waiting tab is invisible to the eye and to the mouse, but stays in the keyboard's path: hiding it with
   visibility would drop it out of the tab order, and by the time it appeared the tab sequence had passed it,
   so a keyboard user could reach only the first of the six. Focusing one reveals it and brings its folder up. */
.sheet-tab.is-waiting { opacity: 0; pointer-events: none; transition: background-color 0.2s, opacity 0.25s; }
.sheet-tab.is-waiting:focus-visible { opacity: 1; pointer-events: auto; }
.sheet.is-waiting .demo-badge { opacity: 0; visibility: hidden; }   /* and so does its sticker */
.sheet-inner[data-covered="1"] .demo-badge { opacity: 0; visibility: hidden; }   /* a covered folder's sticker must not float in the rim band */

/* THE RECEDE (do not move: it has been lost twice to edits that replaced the section blocks below).
   A covered sheet's content scales down, lifts and dims while the next one slides over it; the rim and tab stay full-bleed.
   Ranges are lengths along the stack's exit-crossing timeline: sheet i recedes from the moment sheet i+1 enters
   the bottom of the screen until it pins. t = natural top of sheet i+1 inside the stack. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .sheets { view-timeline-name: --deck; view-timeline-axis: block; }
    .sheet { --t: calc((var(--i) + 1) * (100svh - var(--navh) + var(--hold)) - var(--rim) * var(--i) * (var(--i) + 1) / 2); }
    .sheet-inner {
      animation: recede linear both;
      animation-timeline: --deck;
      animation-range: exit-crossing calc(var(--t) - 100svh) exit-crossing calc(var(--t) - var(--navh) - (var(--i) + 1) * var(--rim));
    }
    .sheet:last-of-type .sheet-inner { animation: none; }
    @keyframes recede { to { transform: scale(0.96) translateY(-2vh); opacity: 0.55; } }
  }
}
.sheet-shelf .sheet-inner, .sheet-more .sheet-inner { padding-top: clamp(1.25rem, 3vh, 2.5rem); }   /* their stickers sit inside the cards, so no extra room needed */
.sheet-tab { outline: 3px solid transparent; transition: background-color 0.2s, opacity 0.25s, visibility 0s, outline-color 0.5s; }
.sheet-tab:focus-visible { outline-color: #fff; outline-offset: 2px; }
.sheet.is-kbd:focus-visible .sheet-tab { outline-color: #fff; outline-offset: 3px; }   /* the folder you landed on rings its tab, for a trip that started at the keyboard; a mouse click already has the filled tab */
.sheet-inner {
  height: 100%; max-width: 96rem; margin: 0 auto;
  padding: clamp(5.5rem, 14vh, 8.5rem) var(--gx) clamp(1.5rem, 4vh, 3rem);   /* a clear band above the copy and the demo, so the stickers hang in space */
  transform-origin: 50% 0%;
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); grid-template-areas: "copy obj"; grid-template-rows: minmax(0, 1fr); gap: clamp(2rem, 4vw, 5rem);
  align-items: start; align-content: start;   /* the demo's top lines up with the app's icon; anchored to the top so a growing demo never shifts the copy */
}
.sheet:focus { outline: none; }   /* the folder itself takes focus after a tab jump; its ring is the filled tab */
.sheet .sec-copy { grid-area: copy; }
.sheet .obj { grid-area: obj; }
/* every other sheet swaps sides, so the eye lands somewhere new each time */
.sheet-alt .sheet-inner { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); grid-template-areas: "obj copy"; }
/* the last two folders are one wide column */
.sheet-inner-wide { display: block; padding-top: clamp(1.5rem, 3vh, 2.5rem); padding-bottom: clamp(1.5rem, 3vh, 2.5rem); }

.sheet .sec-copy { align-self: start; }
.sheet .obj { max-height: 100%; align-self: start; }
.sheet-broller .obj, .sheet-access .obj { margin-top: 3.5rem; }   /* these two demos sit lower than their copy */
/* 01 B-Roller Studio: highlight words, get soundbites */
.sheet-broller .sec-fig { width: clamp(5rem, 7vw, 6.5rem); margin-bottom: 0; }
.sheet-broller .obj .demo { width: min(100%, 42rem); }
.sheet-broller .obj .demo-transcript { font-size: clamp(1.15rem, 1.6vw, 1.5rem); line-height: 1.6; }
.sheet-broller .obj .clip { font-size: 1.05rem; padding: 0.8rem 1rem; }
.sheet-broller .obj .clip i { width: 8rem; height: 26px; }
/* 02 AudioDesc: the slide on the left, copy on the right */
.sheet-access .obj .demo { width: min(100%, 42rem); }
/* 03 Bubble Reel: a tall phone that starts high and runs off the bottom */
.sheet-bubble .obj .demo-chat { --chat-phone-col: 19rem; width: 100%; margin: 0; display: grid; grid-template-columns: minmax(min-content, 1fr) minmax(0, var(--chat-phone-col)); gap: 1.25rem; align-items: start; padding: 0; background: none; box-shadow: none; border-radius: 0; }
.sheet-bubble .obj .demo-chat .chat-panel { order: 1; }
.sheet-bubble .obj .demo-chat .phone { order: 2; }   /* settings on the left, phone in the middle, copy on the right */
.sheet-bubble .obj .demo-chat .phone { border-radius: 30px; box-shadow: 0 40px 80px -30px rgba(0,0,0,0.85), 0 0 0 10px #15171f, 0 0 0 11px rgba(255,255,255,0.12); margin: 11px; }
.sheet-bubble .obj { justify-items: stretch; }
/* ONE HEIGHT BUDGET PER BAND, AND THE RATIO DRIVES THE WIDTH FROM IT. The size buttons used to change height only,
   so "1:1" rendered 0.73 and "16:9" rendered 0.98 and the receipt reported a resolution the picture contradicted.
   Each band sets --phone-h; the three rules below turn that budget into a real shape, capped at the column so a wide
   phone cannot overflow and a tall one cannot outgrow its folder. Set --phone-h, never the width or the height. */
.sheet-bubble .obj .demo-chat { --phone-h: min(32rem, calc(100svh - var(--navh) - 2 * var(--rim) - 10rem)); }
.sheet-bubble .obj .phone { transition: width 0.4s var(--ease), height 0.4s var(--ease); }
/* THE RATIO BELONGS TO THE VIDEO, NOT TO THE DEVICE AROUND IT. These buttons choose the shape of the video Bubble
   Reel exports, and the ratio used to be applied to the whole phone: contact header, conversation and composer
   together. At 16:9 in a 304px column that made the entire phone 171px tall, of which 124px is chrome, so fit() hid
   every bubble and the demo showing "a text thread becomes a video" showed an empty box after one ordinary click.
   The conversation now carries the ratio and the phone is as tall as its chrome plus that. The width cap spends the
   height budget the same way, so no shape can exceed the room the folder has. */
/* The shape itself is set in common.css, because this demo is on the Bubble Reel page too. All a context does
   here is say how tall the phone may be. */
/* A 16:9 phone is 288px, and the receipt that mounts inside it is around 190: the thread collapsed to nothing and
   the compose bar ran out through the phone's own bottom radius. It takes the height it needs while a receipt is
   up, still shorter than the 9:16 phone the folder already fits. */
/* A SHORT PHONE CANNOT HOLD A HEADER, A THREAD, A RECEIPT AND A REPLY BOX AT ONCE. It used to give up its shape to
   make room, which put a portrait frame under a receipt reading "Exported 1920 by 1080", and at 1:1 below 1000px
   wide it pushed the reply box 77px clear of the phone's bottom, still focusable and impossible to scroll to inside
   a pinned folder. The thread stands aside instead: the receipt takes its place, the shape stays true, and the
   thread comes back the moment the receipt retires. */
/* AT EVERY SIZE, NOT TWO OF THREE. At 9:16 the thread stayed and the receipt mounted inside it, so fit() clipped
   the bubbles one by one until the phone held nothing but a "Read just now" floating under the contact header
   with a band of empty black above it: the conversation the visitor had just built vanished at the moment it was
   said to be exported. The receipt is the whole phone now, whichever shape is selected. */
.demo-chat:has(.demo-done.is-shown) .msgs,
.demo-chat:has(.demo-done.is-shown) .compose,
.demo-chat:has(.demo-done.is-shown) .phone-top { display: none; }
/* At 16:9 the frame is 134 to 171px tall and the receipt is 184: it cannot hold a chat UI and a receipt at once,
   whatever stands aside. So at these two shapes the phone simply becomes the card it just exported, and the
   receipt is the compact version of itself. Everything comes back when the receipt retires. */
.demo-chat.size-wide:has(.demo-done.is-shown) .demo-done,
.demo-chat.size-square:has(.demo-done.is-shown) .demo-done { margin: auto 0; }
.demo-chat.size-wide:has(.demo-done.is-shown) .tick,
.demo-chat.size-square:has(.demo-done.is-shown) .tick,
.demo-chat.size-wide:has(.demo-done.is-shown) .done-cta,
.demo-chat.size-square:has(.demo-done.is-shown) .done-cta,
.demo-chat.size-wide:has(.demo-done.is-shown) .done-detail,
.demo-chat.size-square:has(.demo-done.is-shown) .done-detail { display: none; }
/* the smallest wide frame is 134px tall and the buttons were taking a row of their own under a two-line title,
   which put the receipt 20px through the phone's floor: "Start over" sits beside the title instead, and the
   receipt ends on Start over alone, the same trade the two shelf receipts already make */
.demo-chat.size-wide:has(.demo-done.is-shown) .done-actions,
.demo-chat.size-square:has(.demo-done.is-shown) .done-actions { flex-basis: auto; margin-left: auto; }
/* At the narrowest pinned window the 16:9 frame is 134px and the compacted receipt is still 136. The choice there
   is a receipt cut off by the frame or a frame that is not quite 16:9 while a receipt is covering it anyway, and a
   clipped control is the worse of the two. The shape is exact everywhere else, and exact here the moment the
   receipt retires. */
@media (min-width: 56rem) and (max-width: 60rem) {
  .demo-chat.size-wide:has(.demo-done.is-shown) .phone { aspect-ratio: auto; height: 11rem; }
}
/* the app's right-hand pane: look, size, options, export */
.chat-panel { display: grid; gap: 1rem; align-content: start; padding: 1.1rem 1.2rem; border-radius: 16px; background: rgba(11,13,18,0.5); border: 1px solid rgba(255,255,255,0.1); }
.cp-group { display: grid; gap: 0.5rem; }
.cp-label { font-family: var(--font-display); font-weight: 700; font-size: 0.8rem; color: rgba(255,255,255,0.7); }
.cp-hint { font-size: 0.85rem; color: rgba(255,255,255,0.5); }
.cp-swatches { display: flex; gap: 0.6rem; }
.cp-swatch { width: 44px; height: 44px; border-radius: 50%; border: 3px solid transparent; background: var(--sw); cursor: pointer; box-shadow: 0 6px 14px -6px rgba(0,0,0,0.7); transition: transform 0.2s var(--ease), border-color 0.2s; }
.cp-swatch.is-on { border-color: #fff; transform: scale(1.1); }
.cp-swatch:hover { transform: scale(1.08); }
.cp-seg { display: inline-flex; gap: 4px; padding: 4px; border-radius: 999px; background: rgba(255,255,255,0.08); width: fit-content; }
.cp-seg button { border: 0; background: none; color: rgba(255,255,255,0.75); font-family: var(--font-display); font-weight: 700; font-size: 0.85rem; padding: 0.45rem 0.9rem; min-height: 44px; min-width: 44px; border-radius: 999px; cursor: pointer; }
.cp-seg button.is-on { background: var(--accent); color: var(--bg); }
.cp-toggles { gap: 0.6rem; }
.cp-toggle { display: flex; align-items: center; gap: 0.7rem; font-family: var(--font-display); font-weight: 600; font-size: 0.9rem; cursor: pointer; min-height: 44px; position: relative; }
.cp-toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cp-switch { width: 40px; height: 24px; border-radius: 999px; background: rgba(255,255,255,0.45); position: relative; flex: 0 0 auto; transition: background-color 0.2s; }
.cp-switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: #fff; transition: transform 0.2s var(--ease); }
.cp-toggle input:checked + .cp-switch { background: var(--accent); }
.cp-toggle input:checked + .cp-switch::after { transform: translateX(16px); }
.cp-toggle input:focus-visible + .cp-switch { outline: 3px solid #fff; outline-offset: 3px; }
/* NO STICKER IS POSITIONED OUTSIDE ITS OWN DEMO. This one was pushed 10rem past the demo's right edge, which put
   it across the copy column and over "Get Bubble Reel": measured covering 81 to 120px of that button at five of the
   six desktop sizes tried, in the resting state every first-time visitor sees, and under reduced motion nothing
   ever moves it off. An offset is a guess about what is next door.
   It is placed by the grid instead, in its own demo's first cell, bottom and right: the control panel is 349px tall
   in a cell as tall as the phone, so there are 105 to 147px of clear ground under it, and the sticker lands there
   beside the compose bar it points at. No number here depends on what the neighbouring column happens to contain.
   (.demo-badge-next already uses this trick two rules down, for the same reason.) */
/* THE STICKER HAS TO REACH THE THING IT NAMES. This offset was measured to the phone's grid COLUMN plus a 0.9rem
   clearance, which left the tail of "Try it: type a reply" pointing at 44px of empty background beside the phone
   while every other folder's sticker overlaps the control it refers to. Pulled in so the tail lands on the phone's
   own edge, beside the compose bar it is talking about. */
.demo-chat .demo-badge { top: auto; bottom: 0.45rem; left: auto; right: calc(var(--chat-phone-col) + 1.25rem - 2.6rem); }
.demo-chat .demo-badge-next { top: -1.5rem; right: -1rem; bottom: auto; left: auto; }   /* the export sticker sits over the phone's top corner, pointing at the header button */   /* off the phone's corner; on tall desktop windows the copy's button sits just above the phone's bottom, so this is as high as it can go there */   /* hangs off the phone's bottom-right corner, pointing left at the compose bar */
.demo-chat .demo-badge::after { bottom: auto; top: 50%; left: auto; right: -5px; margin-top: -7px; }   /* the tail points right, at the phone beside it */
.demo-chat .demo-go { margin: 0; justify-self: start; }
.demo-chat .demo-done { margin: 0; }
/* 04 Ruff Cutter: the transcript on the left runs tall, copy on the right */
.sheet-ruff .obj { align-self: start; }   /* top-aligned like every other folder */
.sheet-ruff .obj .demo { width: min(100%, 46rem); }
.sheet-ruff .obj .lines { font-size: clamp(1rem, 1.3vw, 1.2rem); gap: 0.5rem; }
.sheet-ruff .obj .lines li { padding: 0.7rem 1rem; min-height: 44px; }   /* the row is the target everywhere, not only on the short laptop where this used to live; a phone had 30px rows while every other control beside them held 44 */
.sheet-ruff .obj .timeline { height: 40px; }
/* on tall screens the transcript and the lines are a size larger (after the per-folder rules so nothing above overrides them),
   and the export button stands in the resting state, quiet and inert, so the demo column has the same floor
   before and after you play with it, and it says what the demo makes before you touch it */
@media (min-width: 56rem) and (min-height: 54.01rem) {
  .obj .demo-transcript { font-size: clamp(1.3rem, 1.7vw, 1.62rem); line-height: 1.65; }
  .sheet-broller .obj .demo { gap: 1.2rem; }
  .sheet-broller .obj .clip { padding: 1rem 1.1rem; font-size: 1rem; }
  .sheet-broller .obj .clip i { height: 34px; }
}
/* Full-size band, 865 to about 930px tall: the AudioDesc receipt ran 5 to 10px past the folder floor, and the standing
   button would run 25px past. The demo keeps its 16:9 shape and gives up width instead of height. */
@media (min-width: 56rem) and (min-height: 54.01rem) {
  .sheet-access .obj .demo { width: min(100%, 42rem, calc((100svh - 34.375rem) * 16 / 9)); }
}
@media (min-width: 56rem) and (min-height: 46.01rem) {
  .sheet-access .obj .slide { height: 50%; }   /* a slightly shorter slide gives the room under it back to the presenter, who was a head with no shoulders under the two-line caption */
  .sheet-access .obj .desc-room { top: 57%; }
}
/* The AudioDesc folder has 109px of empty floor at rest between 46 and 48rem tall where the others have 15, so its
   button stands from the moment the deck pins; the receipt that replaces it needs less. */
@media (min-width: 56rem) and (min-height: 46.01rem) and (max-height: 47.99rem) {
  .sheet-access .obj .demo-go { display: inline-flex; }
  .sheet-access .obj .demo-hint:not([hidden]) { display: block; margin-bottom: -0.15rem; }
  .sheet-access .obj .demo-go[aria-disabled="true"] { background: transparent; color: rgba(255,255,255,0.62); box-shadow: inset 0 0 0 1px rgba(255,255,255,0.22); cursor: default; }
  .sheet-access .obj .demo-go[aria-disabled="true"]:hover { transform: none; filter: none; }
  .obj:has(.demo-done.is-shown) .demo-go { display: none; }
}
/* The standing export button and its hint. These used to sit in the 54rem block with the type-size changes, so on a
   768 or 800px laptop the demo column simply stopped after the timeline and left 178px of empty folder, and nothing
   said what the demo makes. They only need room for a button, not the taller type, so they start at 48rem. */
@media (min-width: 56rem) and (min-height: 48rem) {
  .sheet-broller .obj .demo-go, .sheet-ruff .obj .demo-go, .sheet-access .obj .demo-go { display: inline-flex; }
  .sheet-broller .obj .demo-hint:not([hidden]), .sheet-ruff .obj .demo-hint:not([hidden]), .sheet-access .obj .demo-hint:not([hidden]) { display: block; margin-bottom: -0.15rem; }   /* :not([hidden]) or this rule would outrank the hidden attribute and keep the space */
  .sheet-broller .obj .demo-go[aria-disabled="true"], .sheet-ruff .obj .demo-go[aria-disabled="true"], .sheet-access .obj .demo-go[aria-disabled="true"] {
    background: transparent; color: rgba(255,255,255,0.62); box-shadow: inset 0 0 0 1px rgba(255,255,255,0.22); cursor: default;
  }
  .sheet-broller .obj .demo-go[aria-disabled="true"]:hover, .sheet-ruff .obj .demo-go[aria-disabled="true"]:hover, .sheet-access .obj .demo-go[aria-disabled="true"]:hover { transform: none; filter: none; }
  .obj:has(> .demo > .demo-done.is-shown) .demo-go, .obj:has(.demo-done.is-shown) .demo-go { display: none; }   /* the receipt takes its place */
}

/* 05 Two more: the three cards inside a folder */
.sheet-shelf .sheet-inner-wide { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-areas: none; grid-template-rows: minmax(0, 1fr); align-items: stretch; }   /* the shelf fills the folder exactly; its own rows size the cards */
.lab-deck .sheet .shelf { padding: 0; margin: 0; width: 100%; display: grid; grid-template-rows: auto minmax(0, 1fr); min-height: 0; }
.lab-deck .sheet .shelf-grid { align-items: stretch; min-height: 0; }
.lab-deck .sheet .shelf-card .mini { flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; gap: 1.25rem; min-height: 3rem; margin-top: clamp(0.6rem, calc(0.6rem + (100svh - 46rem) * 0.08), 1.9rem); }
.lab-deck .sheet .shelf-card .mini-songs .song { min-height: 44px; margin: -2px 0; }   /* 44px targets sharing 2px with their neighbors, so the card keeps its floor */   /* the take rows set the card height; this keeps a floor under the cards at 900px; the margin above the mini is where the sticker sits */
.lab-deck .shelf-card .demo-badge { z-index: 6; }   /* above the perched figure (z 5) so it is never clipped */   /* the demo takes the card's spare height, so there is no dead band above the chips */
.lab-deck .sheet .shelf-card .mini-kinora .photos { margin-bottom: 0; }
.lab-deck .sheet .shelf-card .mini-kinora .beats { height: auto; flex: 1 1 auto; min-height: 3rem; max-height: 13rem; align-self: stretch; }   /* the beat track takes the room the photos do not: the tiles are sized by the column width, so this well used to sit under a third full while its two neighbours ran 60 to 88 */
.lab-deck .sheet .shelf-card .mini-kinora .photos i { max-height: 8rem; cursor: pointer; transition: transform 0.25s var(--ease), box-shadow 0.25s, outline-color 0.2s; outline: 3px solid transparent; outline-offset: 2px; }
.lab-deck .sheet .shelf-card .mini-kinora .photos i.is-cut { outline-color: var(--accent); transform: translateY(-4px) rotate(0deg) !important; box-shadow: 0 16px 30px -10px rgba(0,0,0,0.9); }
.lab-deck .sheet .shelf-card .mini-kinora .photos i:focus-visible { outline-color: #fff; }
/* A STICKER MUST NOT COVER THE THING IT POINTS AT. "Try it: tap a photo" was positioned by a negative top, which
   is a guess about how tall the sticker is, and the guess was wrong in the shelf: measured covering 33% of three
   photo thumbnails at 900px and still 24% at 1200x800. Anchored to the demo's top edge instead of guessing, it
   cannot land inside the demo at any size; sized down so it still fits the room the card leaves above it. */
.mini-kinora .demo-badge, .mini-songs .demo-badge {
  top: auto; bottom: 100%; margin-bottom: 0.3rem; right: 0.5rem;
  font-size: 0.78rem; padding: 0.42rem 0.62rem 0.42rem 0.52rem; gap: 0.35rem;
}
.mini-kinora .demo-badge svg, .mini-songs .demo-badge svg { width: 15px; height: 15px; }
.lab-deck .sheet .shelf-card .mini-songs { gap: 0.9rem; }
.lab-deck .sheet .shelf-card .mini-songs { gap: 0.6rem; }
.lab-deck .sheet .shelf-card .song { font-size: 0.875rem; }   /* the takes were at the 12px floor */
.lab-deck .sheet .shelf-card .mini-cg { justify-content: center; flex: 1 1 auto; gap: 0.75rem; }   /* the monitor and its preset strip sit together, centred like the other two wells; space-between opened a 198px hole once the well had height to spare */
.cg-presets { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.cg-presets span { font-family: var(--font-display); font-weight: 700; font-size: 0.78rem; letter-spacing: 0.02em; color: rgba(255,255,255,0.8); background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.18); border-radius: 999px; padding: 0.3rem 0.7rem; }   /* a control bar under the monitor, inside the dark well */
.cg-presets span.is-on { background: #fff; color: #0b0d12; border-color: #fff; }
.lab-deck .sheet .shelf-card .cg-frame { flex: 0 0 auto; aspect-ratio: 16 / 9; min-height: 0; width: 100%; }   /* a real 16:9 monitor, not a stretched void */
.lab-deck .sheet .shelf-card .mini-cg { max-height: none; }   /* the lower-third frame takes all the room, so the cream card has no void */
.lab-deck .sheet .shelf-head { margin-bottom: 0.5rem; }
.lab-deck .sheet .shelf-head h3 { font-family: var(--font-display); font-weight: 800; letter-spacing: -0.03em; line-height: 0.95; font-size: clamp(2rem, 3.6vw, 3rem); text-wrap: balance; }
.lab-deck .sheet .shelf-grid { padding-top: 1rem; }
/* THE SHELF CARD'S VERTICAL PADDING IS ONE CONTINUOUS FUNCTION OF THE FOLDER'S HEIGHT, NOT A STACK OF BANDS.
   It used to be set by fourteen separate media blocks, each carved out for one viewport somebody had measured, and
   every new measurement found the gap between two of them. A card is a stretched grid item, so its box IS the
   folder's height: the padding has to shrink as the folder does, continuously, or the content runs past the floor.
   Short folders (736 to 864px) climb from 0.4rem to 1.24rem; taller ones climb from there to the full 2.1rem.
   Change these two lines, not a band. */
.lab-deck .sheet .shelf-card { padding: 2.1rem 1.5rem; }
/* THE ONE THAT MATTERED. A grid item defaults to min-height: auto, so a card's min-content height became a floor the
   1fr row could not go below, and the row grew past its own container: at 976x740 the grid box was 506px and its row
   529.7px, so the cards hung 14px through the folder floor no matter what the padding did. That is why the padding was
   patched fourteen times and never held - shrinking padding was the only way to shrink min-content. With min-height: 0
   the row is honest, the card is compressed to the track, and the compression lands on .mini, the one child built to
   absorb it (flex: 1 1 auto). Do not remove this to fix a spacing problem. */
.lab-deck .sheet .shelf-card { min-height: 0; container-type: inline-size; }
/* EVERY ALLOWANCE INSIDE A 100svh CALC IS IN rem, NOT px, AND THAT IS DELIBERATE. A reader who raises the browser's
   default font size grows every rem on the page, but the window stays the size it is. When these subtrahends were
   fixed pixels they did not grow with the text, so the room reserved for copy stopped being enough: at an 18px
   default on a 1440x900 window the AudioDesc demo ran 19.1px through its folder floor. In rem they scale with the
   reader, and because each value is its own exact equivalent at 16px (27.5rem = 440px, 34.375rem = 550px,
   46rem = 736px, 54rem = 864px) nothing moves at the default. Measured both ways. */
/* A NARROW CARD IS A NARROW CARD AT EVERY WINDOW SIZE. These trims used to live in a `max-width: 60rem` window
   rectangle, so at 961px the card was still 284px wide but the trims had stopped: the chips wrapped, the name broke
   and it ran back into the perched art. Asking the CARD how wide it is holds at every viewport and at any future
   column count. Card widths measured: 264px at a 896 window, 284 at 960, 298 at 1008, 303 at 1024. */
@container (max-width: 19rem) {
  /* Four class names, not three: a container query adds no specificity, so these have to outrank the base shelf
     rules that come later in the file. At three they lost and the name kept its 6rem. */
  .lab-deck .sheet .shelf-card .fig-card { width: 4.5rem; }
  .lab-deck .sheet .shelf-card .shelf-desc::before { content: none; }
  .lab-deck .sheet .shelf-card .shelf-desc { padding-right: 4.7rem; }   /* the clearance runs the whole paragraph, so the lines rag evenly instead of stair-stepping past the figure */
  .lab-deck .sheet .shelf-card .shelf-name { padding-right: 6.5rem; }   /* the art is rotated, so its painted edge sits about 14px inside its own box, which is why the base 6rem read as a kiss. The art is 4.5rem here, so 6.5rem buys a 37 to 84px painted gap AND leaves Lower Key on one line; 7rem cleared just as well but wrapped it. */
  /* 11.5px was the only type on the page under 12px, and it was under 12px only to fit. The room comes from the
     well's own padding and the chips' gaps instead, so the four of them still hold one row at 12.5px. These are the
     mock character generator's preset buttons, not controls, so the smaller inset costs nobody a touch target. */
  .lab-deck .sheet .shelf-card .cg-presets span { padding: 0.28rem 0.3rem; font-size: 0.78rem; white-space: nowrap; }
  .lab-deck .sheet .shelf-card .cg-presets { gap: 0.16rem; }
  .lab-deck .sheet .shelf-card .mini-cg { padding: 0.35rem; }
}
/* The card is compressed onto .mini, and the song rows hold 44px for the thumb, so the row GAP is what gives. */
.lab-deck .sheet .shelf-card .mini.mini-songs { gap: clamp(0.15rem, calc(0.15rem + (100svh - 46rem) * 0.045), 0.6rem); }
@media (min-width: 56rem) and (min-height: 46.01rem) and (max-height: 54rem) {
  .lab-deck .sheet .shelf-card { padding-block: calc(0.4rem + (100svh - 46rem) * 0.105); }
}
@media (min-width: 56rem) and (min-height: 54.01rem) {
  .lab-deck .sheet .shelf-card { padding-block: clamp(1.25rem, calc(1.25rem + (100svh - 54rem) * 0.24), 2.1rem); }
}   /* the name sat as close to the top edge as to the sides, which read as cramped under the perched art; the shorter bands below keep their tighter padding, where the cards have no room to give */
.lab-deck .shelf-card .fig-card { z-index: 5; padding: 0.5rem; width: clamp(7rem, 9vw, 9rem); }
.lab-deck .shelf-card .shelf-desc { padding-right: 8.25rem; text-wrap: pretty; }   /* room for the perched figure (which sits at the card edge, not the content edge) */
.lab-deck .shelf-card .shelf-name { padding-right: 6rem; }
.lab-deck .top-sub .soon, .lab-deck .mobile-menu .mobile-soon, .lab-deck .mobile-menu .mobile-group { color: rgba(255,255,255,0.62); }
.lab-deck .sheet .shelf-card .panel-actions { margin-top: 0.25rem; }
.lab-deck .sheet .shelf-card .panel-meta { margin-top: auto; padding-top: 0.5rem; }
/* 06 Everything else: the page closes the way it opened, with a filmstrip. Four frames, one destination each, mascots peeking in */
.sheet-more .sheet-inner-wide { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-areas: none; grid-template-rows: minmax(0, 1fr); align-items: center; }
.reel-wrap { display: grid; grid-template-rows: auto minmax(0, 1fr); min-height: 0; max-height: 100%; }

.reel-h { font-family: var(--font-display); font-weight: 800; letter-spacing: -0.03em; font-size: clamp(2rem, 3.6vw, 3rem); line-height: 0.95; margin-bottom: clamp(1rem, 2.5vh, 1.5rem); }
.reel { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; min-height: 0; background: #2b2e37; border-radius: 10px; padding: 0 1.25rem; box-shadow: 0 30px 60px -30px rgba(0,0,0,0.9), inset 0 1px 0 rgba(255,255,255,0.1), inset 0 -1px 0 rgba(0,0,0,0.5); transform: rotate(-1.6deg); }
.reel-wrap { padding: 1.25rem 0; }
.reel-edge { height: 14px; margin: 10px 0; background: repeating-linear-gradient(90deg, #8b909e 0 16px, transparent 16px 40px); border-radius: 3px; opacity: 0.9; }
.frames { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.9rem; min-height: 0; }
.frames li { display: grid; min-height: 23rem; max-height: 27rem; }   /* tall enough for copy, the cut-out and the link line */
.frame {
  position: relative; display: flex; flex-direction: column; gap: 0.6rem; min-height: 0; overflow: hidden;
  padding: clamp(1.75rem, 4vh, 2.5rem) 1.5rem 1.5rem; border-radius: 6px;
  background: var(--c); color: var(--ink);
  transition: transform 0.3s var(--ease), filter 0.3s;
}
.frame:hover { transform: translateY(-3px); filter: brightness(1.06); }
.frame:focus-visible { outline: 3px solid var(--ink); outline-offset: -6px; box-shadow: none; }   /* the ink color clears 3:1 on every card, white did not on teal and amber */
.frame h4 { font-family: var(--font-display); font-weight: 800; font-size: clamp(1.5rem, 2.2vw, 1.95rem); line-height: 1; letter-spacing: -0.025em; }
.frame p { font-size: 1rem; line-height: 1.45; max-width: 26ch; padding-right: 1rem; }
.frame em { order: 2; margin-top: 0; font-style: normal; font-family: var(--font-display); font-weight: 700; font-size: 1rem; border-bottom: 2px solid currentColor; align-self: flex-start; padding-bottom: 2px; position: relative; z-index: 1; }
.frame .frame-email { font-size: clamp(0.95rem, 1.15vw, 1.1rem); overflow-wrap: normal; border-bottom: 0; padding-bottom: 0; text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 0.2em; }   /* the underline follows the text, so a two-line email is underlined on both lines */   /* it may break before the @ (a <wbr> from site.js), never mid-word */
.frame .frame-email { max-width: 100%; }
/* the cut-outs: flat paper figures, all the same height, standing on the link line so the four sit level */
.frame-fig { position: relative; order: 1; margin-top: auto; align-self: flex-end; margin-right: -0.25rem; margin-bottom: 0.4rem; height: clamp(6rem, 8vw, 7.5rem); width: auto;   /* the art carries real ornament now, so it is drawn big enough to see it */ filter: drop-shadow(0 10px 14px rgba(0,0,0,0.35)); transform: rotate(4deg); transition: transform 0.35s var(--ease); pointer-events: none; }
.frame:hover .frame-fig { transform: rotate(0deg) translateY(-4px); }
.frames li:nth-child(even) .frame-fig { transform: rotate(-4deg); }
.frames li:nth-child(even) .frame:hover .frame-fig { transform: rotate(0deg) translateY(-4px); }   /* every figure nudges on hover */
.frame em { position: relative; z-index: 1; }
.frame h4, .frame p { position: relative; z-index: 1; }
.frame-fig { z-index: 0; }

/* the filmstrip's first pill sits whole beside the pause button instead of half faded */
.lab-deck .strip-viewport:focus-within { mask-image: none; -webkit-mask-image: none; }   /* nothing fades over a focused pill */
.lab-deck .strip-viewport { margin-left: calc(clamp(1rem, 3vw, 2rem) + 44px + 1.25rem); mask-image: linear-gradient(to right, transparent, #000 1rem, #000 92%, transparent); -webkit-mask-image: linear-gradient(to right, transparent, #000 1rem, #000 92%, transparent); }   /* pills fade in a clear 20px past the pause button, never under it */
/* THESE BARS READ AS SOLID ANYWAY. Both sit on a near-black page, so the 6 to 14 per cent passing through buys no
   sense of glass and instead lets whatever is underneath show as a smear: at 390x844 a yellow demo sticker was
   legible straight through the nav and the folder rail at the same time. Opaque. */
.lab-deck .top.is-scrolled { background: #0b0d12; }
.lab-deck .footer-copy { color: rgba(255,255,255,0.6); }


/* Lower Key, the one that is not out yet. It used to be a cream card, which read clearly as "different" but made
   the app nobody can download the brightest thing in the section, ahead of the two you can. It is a dark card like
   its neighbours now, a step cooler and quieter, and the chip carries the "coming soon" on its own. */
.lab-deck .shelf-card-soon { border: 0; }
.lab-deck .shelf-card-soon .cg-third { border-left-color: #c8871e; }
.lab-deck .shelf-card-soon .cg-third span { color: #e0b23a; }
.lab-deck .shelf-card-soon .fig-card.is-dim img { filter: drop-shadow(0 18px 24px rgba(0,0,0,0.35)); }

/* footer: links on their own row so nothing orphans */
.lab-deck .footer-inner { flex-wrap: wrap; row-gap: 1.25rem; }
.lab-deck .footer-nav { order: -1; width: 100%; }

/* Phones and short screens (laptops with browser chrome, zoomed windows): sheets flow, no pinning, one column */
@media (max-width: 55.99rem), (max-height: 46rem) {
  .deck-gap, .deck-rest { display: none; }
  .sheet { position: relative; top: auto; height: auto; }
  .sheet-inner { animation: none !important; transform: none; filter: none; }
  .sheet-broller .obj, .sheet-ruff .obj { align-self: auto; }
  .sheet-tab, .sheet::after { display: none; }
  .sheet { overflow: hidden; }
  .sheet-inner, .sheet-alt .sheet-inner { grid-template-columns: 1fr; grid-template-areas: "copy" "obj"; grid-template-rows: auto auto; padding: clamp(2.5rem, 7vh, 4rem) var(--gx); gap: 2.5rem; }   /* the alternating folders stack too */
  .sheet-bubble .obj { margin-bottom: 0; }
  .sheet-bubble .obj .demo-chat { --phone-h: 26rem; }
  .chat-panel { padding: 0.9rem 1rem; gap: 0.7rem; }
  .sheet-inner { align-items: stretch; }
  .sheet-ruff .obj { align-self: auto; }
  .sheet-ruff .obj .demo { height: auto; }
  .sheet .obj { justify-items: start; }
  .sheet-bubble .obj .demo-chat { grid-template-columns: 1fr; }
  .sheet-bubble .obj .demo-chat { --phone-h: 26rem; }
  .demo.is-playable { margin-top: 1.75rem; }
  /* the panel sits under the phone here, so the sticker joins the flow between them, tail up at the compose bar */
  .sheet-bubble .obj .demo-chat .phone { order: 1; }
  .sheet-bubble .obj .demo-chat .demo-badge { position: static; order: 2; justify-self: start; margin: -0.4rem 0 0.1rem 4.5rem; }
  .sheet-bubble .obj .demo-chat .chat-panel { order: 3; }
  .sheet-bubble .obj .demo-chat .demo-badge-next { position: absolute; order: 0; margin: 0; top: -2rem; right: 1rem; left: auto; bottom: auto; }
  .demo-chat .demo-badge::after { top: -7px; left: 1rem; margin-top: 0; }
  .demo-bites .demo-badge, .demo-cut .demo-badge { top: -2.7rem; }
  .lab-deck .sheet .shelf { padding: 0; }
  /* very narrow phones: nothing in a shelf card may set a floor wider than the screen */
  .lab-deck .sheet .shelf-grid, .lab-deck .sheet .shelf-card, .lab-deck .sheet .shelf-card > * { min-width: 0; }
  .lab-deck .sheet .shelf-card .song, .lab-deck .sheet .shelf-card .song * { min-width: 0; }
}
/* phones: one column, the app's name and description above its demo, and the Bubble Reel phone keeps to a screen */
@media (max-width: 55.99rem) {
  .sheet-broller .sheet-inner, .sheet-access .sheet-inner, .sheet-ruff .sheet-inner, .sheet-bubble .sheet-inner { grid-template-areas: "copy" "obj"; }   /* the name and what the app does come before the demo of it, and this is the order the markup is already in, so what a screen reader hears and what the eye sees finally agree */
  .sheet-broller .obj, .sheet-access .obj { margin-top: 0; }   /* that margin drops these two demos below their copy in the two-column layout; in one column the grid gap already spaces them */
  /* The thread is 224px of bubbles. At 21rem the message area was 191px, so the two bubbles that carry the loop
     when every animated one has faded out were pushed off the top, and the phone showed as empty for about three
     and a half seconds of every cycle. On a phone the folder is in flow and has no floor to hit, so the phone gets
     the height its own content needs. */
  .sheet-bubble .obj .demo-chat { --phone-h: 26.5rem; }   /* a fixed height, not a range: between min and max the phone grew and shrank as bubbles faded in and out, and everything below it in the flow moved with it once every cycle */
}
/* THE FILMSTRIP'S PHONE TREATMENT IS FOR PHONES. It used to live in the shared flow fallback, which also catches a
   wide, short laptop window: at 1920x700 the page's closing image stopped being a filmstrip and became four
   full-width bars with the sprockets down the sides. The deck is in flow at that size, so the folder grows to fit
   and there is no floor to run past; the strip simply keeps the four-up grid and its tilt, shorter. */
@media (max-width: 55.99rem) {
  .reel { transform: none; }
  .frames { grid-template-columns: 1fr; }
  .frames li { min-height: 0; }
  /* a tall strip reads better with the sprockets down its sides; the figure tucks beside the title so the copy runs full width */
  .reel { grid-template-rows: none; grid-template-columns: auto minmax(0, 1fr) auto; grid-auto-flow: column; padding: 1rem 0; }
  .reel-edge { width: 14px; height: auto; margin: 0 10px; background: repeating-linear-gradient(180deg, #8b909e 0 16px, transparent 16px 40px); }
  .frame { min-height: 0; padding: 1.4rem 1.25rem 1.25rem; display: block; }
  .frame h4 { margin-bottom: 0.5rem; }
  .frame p { max-width: none; padding-right: 0; margin-bottom: 0.9rem; }
  .frame em { display: inline-block; }
  .frame-fig { position: static; float: right; height: 4.5rem; margin: -0.2rem -0.2rem 0.5rem 0.9rem; }   /* the copy wraps around the figure */
  .frame em, .frame .frame-email { max-width: 100%; margin-right: 0; }
  .frame .frame-email { font-size: 0.95rem; }
}
/* a wide, short window: the folders lie in flow but there is room for two columns, so they keep them */
@media (min-width: 56rem) and (max-height: 46rem) {
  .frames li { min-height: 0; max-height: none; }   /* the four-up grid, sized by its copy rather than a 23rem floor */

  .sheet-inner { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); grid-template-areas: "copy obj"; }
  .sheet-alt .sheet-inner { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); grid-template-areas: "obj copy"; }
  .sheet .obj { justify-items: center; }
  /* The phone kept the phone-layout's full column width here, so a 9:16 mock was a 645 to 795px landscape panel with the
     settings stretched underneath it. The settings sit beside a phone-width phone, as in the pinned layout. */
  .sheet-bubble .obj .demo-chat { --chat-phone-col: 22rem; grid-template-columns: minmax(0, var(--chat-phone-col)); justify-content: center; }   /* under 1280 wide the column is too narrow for two, so the panel stacks under a phone-width phone */
  .sheet-bubble .obj .demo-chat { --phone-h: 28rem; }
  .sheet-bubble .obj .demo-chat .demo-badge-next { grid-area: 1 / 1 / 2 / 2; }   /* the nudge is absolutely positioned: with explicit end lines its containing block is the phone's cell, so it sits over the phone's corner; an auto end line would resolve to the container's edge and put it 540px from the button it points at */
  .sheet-bubble .obj .demo-chat .demo-badge:not(.demo-badge-next) {
    grid-area: 1 / 1 / 2 / 2; position: absolute; top: auto; bottom: 4.4rem; left: auto; right: 0.6rem; margin: 0; order: 0;
  }   /* it used to take its own row under the phone, which is under the fold in a 700px-tall window; over the thread with its tail on the reply box it is on screen at rest */
  @media (min-width: 80rem) {
    .sheet-bubble .obj .demo-chat { grid-template-columns: minmax(0, 22rem) minmax(0, 19rem); align-items: start; }
    .sheet-bubble .obj .demo-chat .phone { grid-column: 1; grid-row: 1; }
    .sheet-bubble .obj .demo-chat .chat-panel { grid-column: 2; grid-row: 1 / span 2; }
  }
}
/* Laptop heights (736 to 864px): the deck still pins, everything inside sits a size smaller */
@media (min-width: 56rem) and (min-height: 46.01rem) and (max-height: 54rem) {
  .sheets { --rim: 11px; }   /* laptop heights: the stack of six rims costs 55px instead of 70 */
  .sheet-inner { padding-top: 5rem; padding-bottom: clamp(1rem, 2.5vh, 1.5rem); gap: clamp(1.5rem, 3vw, 3rem); }   /* a clear band above the copy and the demo */
  .sheet-broller .obj, .sheet-access .obj { margin-top: 2.25rem; }
  .demo-chat .demo-badge { bottom: 4.2rem; }   /* under the copy's button; hanging it higher up the phone's side runs it into the description at 1366x768 and 1440x800 */
  .demo-chat .demo-badge-next { bottom: auto; }   /* laptop heights: the copy is shorter, so the sticker rides higher up the phone's side */
  .sheet-shelf .sheet-inner, .sheet-more .sheet-inner { padding-top: 1.75rem; }
  .mini-kinora .demo-badge, .mini-songs .demo-badge { margin-bottom: 0.15rem; }   /* tighter here, but still above the demo rather than inside it: the bob is what has to clear the copy, not the sticker's body */
  .sec-copy { gap: 0.7rem; }
  .sec-fig, .sheet-broller .sec-fig { width: 4.5rem; margin-bottom: 0; }
  .rungs li { padding: 0.4rem 0; }
  .rungs img { width: 32px; height: 32px; }
  .sheet-access .obj .demo { width: min(100%, 36rem); }
  .sheet-access .obj { margin-top: 0.25rem; }
  /* The 16:9 frame left ~150px of empty folder under the demo and cut the presenter down to a head: the room under the
     slide was 99px and the two-line caption covered all of the shoulders. A taller frame, capped so the receipt still
     clears the floor, and a slightly shorter slide give the room back to the figure. */
  .sheet-access .obj .desc-frame { aspect-ratio: 4 / 3; max-height: calc(100svh - 27.5rem); }
  .sheet-access .obj .cap { font-size: clamp(0.9rem, 1.1vw, 0.95rem); }   /* the frame is capped at 36rem here, so the caption's 1.1vw ran to three lines at 1536 wide and covered the figure again */
  .demo-desc .demo-done .done-detail, .demo-bites .demo-done .done-detail, .demo-chat .demo-done .done-detail, .demo-cut .demo-done .done-detail { display: none; }   /* at laptop height these receipts keep to one line so they stay inside the folder */
  .lab-deck .sheet .shelf-card .mini { min-height: 4rem; gap: 0.5rem; margin-top: 1.9rem; }
  .lab-deck .sheet .shelf-card .mini-songs .song { min-height: 44px; margin: -2px 0; }   /* 44px targets that share 2px with their neighbors, so four rows cost 40px each */   /* the take rows set the card height here; 40px keeps the sticker gap without pushing the cards off the folder */
  .lab-deck .sheet .shelf-grid { padding-top: 0.9rem; }
  .lab-deck .sheet .shelf-card .cg-frame { min-height: 5rem; }
  .sheet-ruff .obj .lines li { padding: 0.45rem 0.8rem; min-height: 44px; }   /* the row is the target, and it stays 44px on short windows */
  .lab-deck .sheet .shelf-head { margin-bottom: 0.6rem; }   /* the headline clears the cards at 1024x768 and 1366x768 */
  .lab-deck .sheet .shelf-grid { padding-top: 0.25rem; gap: 1rem; }   /* the sticker gap now lives inside the cards */
  .lab-deck .sheet .shelf-card { padding-inline: 1.1rem; gap: 0.6rem; }   /* the vertical padding is the continuous rule near the top of this file */
  .lab-deck .sheet .shelf-card .panel-actions { padding-top: 0; margin-top: 0; }
  .lab-deck .sheet .shelf-card .mini { padding: 0.4rem; }
  .lab-deck .sheet .mini { padding: 0.6rem; }
  .lab-deck .sheet .mini-kinora .photos i { border-width: 3px; }
  .lab-deck .sheet .mini-kinora .beats { height: 22px; }
  .lab-deck .sheet .mini-songs { gap: 0.2rem; }
  .lab-deck .sheet .shelf-card .btn { padding: 0.65rem 1.1rem; font-size: 0.9rem; }
  .reel-h { margin-bottom: 0.75rem; }
  .frame { padding: 1.4rem 1.2rem 1.2rem; gap: 0.45rem; }
  .frames li { min-height: 19rem; }
  .frame-fig { height: 4rem; }
  .lab-deck .sheet .shelf-card .fig-card { width: 5.5rem; top: -1.25rem; }   /* three classes, or the base .lab-deck .shelf-card .fig-card rule outranks this and the art never shrinks */
  .lab-deck .shelf-card .shelf-desc { padding-right: 7.75rem; }
}
/* Short laptop windows only (736 to 767px tall): the name and the one-liner step down a size, because at full size
   the Ruff Cutter receipt runs past the folder floor here. From 768px tall up the text stays full size. */
@media (min-width: 56rem) and (min-height: 46.01rem) and (max-height: 47.99rem) {
  .sec-name { font-size: clamp(2.2rem, 4.2vw, 3.6rem); }
  .sec-desc { font-size: 1.05rem; line-height: 1.45; }
  .obj .demo-transcript { font-size: 1.05rem; }
  .sheet-ruff .obj .lines { font-size: 1rem; gap: 0.3rem; }
  .lab-deck .sheet .cell p { font-size: 1rem; }
}
/* narrow pinned windows: a soundbite row is mostly waveform and phrase, so those give back the width first */
@media (min-width: 56rem) and (max-width: 64rem) {
  .sheet-broller .obj .clip i { width: 5rem; }
  .demo-bites .clip-q { display: none; }
}
/* narrow pinned windows: the transcript rows are shorter here, so the sticker hangs clear of the first line
   instead of sitting on its last words, and the receipt title keeps to one line */
@media (min-width: 56rem) and (max-width: 72rem) {
  .frame { padding-left: 0.7rem; padding-right: 0.7rem; }   /* the cards are narrow here, so they give their copy back 26px: at 896 the address needs 164.5px and this is what puts it on one line at 12.8px */
  .frame .frame-email { font-size: clamp(0.8rem, 1.28vw, 0.85rem); letter-spacing: -0.022em; }   /* 12.8px is the floor (the page has no smaller running text) and the tracking is what buys the last 3px of the one line */   /* sized to the card, so the address is one line instead of "brian" over "@soundsandcolors.com" */
  .demo-done { flex-wrap: wrap; }
  .demo-done .done-actions { flex-basis: 100%; margin-left: 0; justify-content: flex-start; }   /* a narrow receipt puts its buttons on their own row; the title used to run underneath them */
  /* a shelf card cannot afford that second row: it grows the card past the folder floor on short screens.
     Its receipt keeps one row and lets the title wrap instead, which costs a line rather than a button row. */
  .lab-deck .shelf-card .demo-done { flex-wrap: nowrap; padding: 0.5rem 0.7rem; gap: 0.5rem; }
  .lab-deck .shelf-card .demo-done .tick { width: 28px; height: 28px; border-radius: 9px; }
  .lab-deck .shelf-card .demo-done .tick svg { width: 15px; height: 15px; }
  .lab-deck .shelf-card .demo-done .done-title { white-space: normal; }
  .lab-deck .shelf-card .demo-done .done-actions { flex-basis: auto; margin-left: auto; justify-content: flex-end; }
}
/* Narrow pinned windows that are also SHORT. The clamp means this line no longer WRAPS here, but one line is still
   21.8px and a three-column-narrow folder that short has not got 21.8px to give: measured, letting it through put
   the B-Roller receipt 42.5px past the folder floor at 896x870 and 33.8px at 896x880. The ceiling matters as much
   as the floor: this used to have no top bound, so it withheld the sentence from a tall window in the same width
   band that had 70px of room going spare. */
@media (min-width: 56rem) and (max-width: 72rem) and (min-height: 46.01rem) and (max-height: 58rem) {
  .demo-desc .demo-done .done-detail, .demo-bites .demo-done .done-detail,
  .demo-chat .demo-done .done-detail, .demo-cut .demo-done .done-detail { display: none; }
}
@media (min-width: 56rem) and (max-width: 63.99rem) {
  .lab-deck .sheet .shelf-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }   /* a plain 1fr lets a card's min-content steal width from its neighbours */
  .sheet-tab { padding: 0 0.6rem; margin-left: -0.6rem; }   /* same type size, a touch less air so all six fit beside the pause pill */
}

/* narrow pinned windows above the laptop band (896 to 1152 wide, taller than 864): the three columns wrap the copy, so the shelf gets the laptop trims here too */
@media (min-width: 56rem) and (max-width: 72rem) and (min-height: 54.01rem) {
  .lab-deck .sheet .shelf-head { margin-bottom: 0.4rem; }
  .lab-deck .sheet .shelf-grid { padding-top: 0.5rem; gap: 1rem; }
  .lab-deck .sheet .shelf-card { padding-inline: 1.1rem; gap: 0.6rem; }
  .lab-deck .sheet .shelf-card .mini { min-height: 4rem; gap: 0.5rem; padding: 0.4rem; margin-top: 1.9rem; }
  .lab-deck .sheet .mini { padding: 0.6rem; }
  .lab-deck .sheet .shelf-card .mini-songs .song { min-height: 44px; margin: -2px 0; }
  .lab-deck .sheet .shelf-card .cg-frame { min-height: 5rem; }
  .lab-deck .sheet .shelf-card .panel-actions { padding-top: 0; margin-top: 0; }
  .sheet-shelf .sheet-inner { padding-top: 1.5rem; }
}
/* under 72rem wide the perched figure only needs room beside itself, not the whole paragraph; and the preset chips keep to one row */
@media (min-width: 56rem) and (max-width: 72rem) {
  .lab-deck .shelf-card .shelf-desc { padding-right: 0; }
  .lab-deck .shelf-card .shelf-desc::before { content: ""; float: right; width: 6.5rem; height: 3.4rem; }   /* the figure and its shadow, at laptop heights */
  @media (min-height: 54.01rem) { .lab-deck .shelf-card .shelf-desc::before { height: 3.8rem; } }   /* the figure perches lower above laptop heights */
  .cg-presets span { padding: 0.3rem 0.5rem; }
  .lab-deck .sheet .shelf-card .song em { font-size: 0.75rem; }
  .cp-swatches { flex-wrap: wrap; gap: 0.5rem; max-width: 6.5rem; }   /* the narrower settings panel holds the four 44px swatches as two rows of two */
  .lab-deck .sheet .shelf-card .mini-cg { flex: 0 0 auto; justify-content: flex-start; gap: 0.9rem; }   /* Lower Key's monitor is short here, so its well hugs it and the spare height sits in the cream; the other two wells are filled by their own content and keep stretching */
  /* Hugging left 69 to 265px of empty cream, and Lower Key is the page's one light surface, so the hole showed.
     Wherever the deck pins, the well stretches like its two neighbours do everywhere else. Card heights are set by
     the grid, so this only moves space inside the card: the fit against the folder floor is unchanged. */
  @media (min-height: 46.01rem) {
    .lab-deck .sheet .shelf-card .mini-cg { flex: 1 1 auto; justify-content: center; }
  }
}
/* The requirement names the architecture on every rung now, which is a longer chip, and at the narrowest width the
   deck pins at (896px) the three rungs took a third line each and pushed the copy column 10px past the folder's
   floor. The ladder is the most compressible thing in that column, so the ladder gives, rather than the fact being
   shortened back to something that answers half the question. (The name taking its own line is unconditional now
   and lives in common.css, so the three rows match at every width.) */
@media (max-width: 72rem) {
  .rungs li { padding: 0.4rem 0; }
}
/* wide screens: the shelf cards stop stretching to the folder floor once the tallest card is filled */
@media (min-width: 100rem) and (min-height: 54.01rem) {
  .sheet-shelf .sheet-inner-wide { grid-template-rows: auto; align-content: center; }   /* the shelf sits in the folder with air above and below, like the app folders' top band */
  .lab-deck .sheet .shelf { grid-template-rows: auto auto; align-content: start; }
}
/* narrower laptops (under 64rem wide, laptop height): the shelf cards give back the few pixels they run over */
@media (min-width: 56rem) and (max-width: 88rem) and (min-height: 46.01rem) and (max-height: 54rem) {   /* narrower than 1408px the cards wrap more, so they give back a few pixels */
  .lab-deck .sheet .shelf-card { padding-inline: 0.9rem; }
  .sheet-shelf .sheet-inner { padding-top: 1.25rem; }
  @media (max-width: 66rem) { .sheet-shelf .sheet-inner { padding-top: 1rem; } }   /* 1024x768: the receipt state sat exactly on the floor */
  .lab-deck .sheet .shelf-card .mini { margin-top: 1.6rem; }
}

/* the shortest narrow windows: the shelf cards give back the last pixel they run over after both receipts */
@media (min-width: 56rem) and (max-width: 72rem) and (min-height: 46.01rem) and (max-height: 54rem) {
  .lab-deck .sheet .shelf-card { padding-inline: 0.9rem; }
  .sheet-shelf .sheet-inner { padding-top: 1rem; }
  .lab-deck .sheet .shelf-grid { padding-top: 0.15rem; gap: 0.8rem; }
  .lab-deck .sheet .shelf-card .mini { min-height: 3.5rem; margin-top: 1.7rem; }
  .sheet-shelf .sheet-inner { padding-bottom: 0.75rem; }   /* the cards never sit flush on the folder floor */
  /* short and narrow: a five-line description plus a receipt pushes the cards off the folder, so the copy
     goes back to clearing only the figure beside it and keeps to four lines here */
  .lab-deck .shelf-card .shelf-desc { padding-right: 0; }
  .lab-deck .shelf-card .shelf-desc::before { content: ""; float: right; width: 7rem; height: 2.6rem; }   /* wide enough to clear the 5.5rem art plus its padding; any taller and the copy runs the card past the folder floor */
  @media (max-width: 60rem) { .lab-deck .shelf-card .shelf-desc::before { width: 5.7rem; } }   /* the art is 4.5rem here, so the copy keeps the width it does not need to give up */
}

/* Wherever the deck does not pin, a scrolling row of folder names sits under the nav instead, since the folder
   tabs only exist in the pinned layout. This condition must stay equal to the flow fallback at the top of the
   file: a wide but short window (a 1366x768 laptop with browser chrome) had neither tabs nor row and left the
   visitor nine screens of scroll with no way to skip an app. */
.deck-jump { display: none; }
@media (max-width: 55.99rem), (max-height: 46rem) {
  .deck-jump {
    /* The nav's height and this offset both grow with the reader's font, but not at the same rate: the bar is
       padding plus a 44px row, this was a flat 4.55rem, and by a 200% default font they were 30px apart. Both
       bars are opaque, so the difference read as a slot with a sliced line of body copy travelling through it.
       js/site.js measures the bar and writes --nav-h; the rem is the answer if it never runs. */
    display: block; position: sticky; top: var(--nav-h, 4.55rem); z-index: 8;
    margin: 0; padding: 0;   /* .sheets is already full width, so no negative margin (it pushed the page 16px wide) */
    background: #0b0d12; backdrop-filter: blur(12px) saturate(140%); -webkit-backdrop-filter: blur(12px) saturate(140%);
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  }
  .deck-jump ul {
    display: flex; gap: 0.45rem; list-style: none; margin: 0; padding: 0.5rem var(--gx);
    overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity; scrollbar-width: none; scroll-padding-inline: 1rem;
  }
  .deck-jump ul::-webkit-scrollbar { display: none; }
  .deck-jump a {
    scroll-snap-align: center; display: inline-flex; align-items: center; min-height: 44px; padding: 0 0.9rem;
    border-radius: 999px; white-space: nowrap; text-decoration: none;
    font-family: var(--font-display); font-weight: 700; font-size: 0.85rem; letter-spacing: 0;
    color: var(--c); border: 2px solid color-mix(in srgb, var(--c) 55%, transparent); background: transparent;
    transition: background-color 0.2s, color 0.2s, border-color 0.2s;
  }
  .deck-jump a[aria-current] { background: var(--c); border-color: var(--c); color: #0b0d12; }
  .lab-deck .sheets { scroll-padding-top: 9.5rem; }
  .lab-deck .sheet, .lab-deck .shelf-card, .lab-deck .frames li, .lab-deck .frame { scroll-margin-top: 9.5rem; }   /* a jump lands below the nav and the row, not under them, whether it names a folder or one card inside one */
}
/* The marquee is the "All apps" nav, and frozen it has to be a scroller with no fade at its ends. That rule lives
   in spectrum.css, which loads BEFORE this file, and `.lab-deck .strip-viewport` here is the same specificity, so
   source order quietly won and the last pill kept fading out. Cancelling a rule from another file at equal weight
   is a coin toss; the cancellation belongs next to the rule it cancels. */
@media (prefers-reduced-motion: reduce) {
  .lab-deck .strip-viewport { mask-image: none; -webkit-mask-image: none; overflow-x: auto; }
}
@media (max-width: 55.99rem) and (prefers-reduced-motion: reduce), (max-height: 46rem) and (prefers-reduced-motion: reduce) {
  .deck-jump a { transition: none; }
}

/* very wide and short: the shelf cards run a few pixels over the folder floor, receipt or not */
@media (min-width: 88rem) and (min-height: 46.01rem) and (max-height: 54rem) {
  .lab-deck .sheet .shelf-grid { padding-top: 0.4rem; }
}

/* the narrowest pinned window at the shortest height: the shelf gives back the last pixels a receipt needs */
@media (min-width: 56rem) and (max-width: 60rem) and (min-height: 46.01rem) and (max-height: 47.99rem) {
  .sheet-shelf .sheet-inner { padding-top: 0.6rem; padding-bottom: 0.4rem; }   /* the cards sit 2px off the folder floor here and their content 4px inside the card: the grid stretches them to the folder, so that 2px is a fixed inset, not a near miss */
  .lab-deck .sheet .shelf-grid { gap: 0.7rem; padding-top: 0.1rem; }
  /* Same trade one folder over: with three soundbites pulled, the B-Roller receipt and its buttons were cut by the
     folder floor here, up to 15px past it. The rows give the pixels back. */
  .sheet-broller .obj .demo { gap: 0.7rem; }
  .sheet-broller .obj .clip { padding: 0.45rem 0.8rem; }
  .sheet-broller .obj .clip i { height: 26px; }
}

/* phones: the Kinora tiles give up width rather than spill inside their card */
@media (max-width: 55.99rem) {
  .lab-deck .sheet .shelf-card .mini-kinora .photos { gap: 6px; }
  .lab-deck .sheet .shelf-card .mini-kinora .photos i { flex: 1 1 0; min-width: 0; }
}

/* Just above the laptop band (865 to 896 tall) the copy column gets its full size back before the folder has
   the room for it, and the AudioDesc pair of Get buttons runs a few pixels past the floor mid-scroll. */
@media (min-width: 56rem) and (min-height: 54.01rem) and (max-height: 56rem) {
  .sheet-access .sec-copy { gap: 0.85rem; }
  .sheet-access .sec-fig { width: 6rem; }
}

/* the narrowest phones: a filmstrip card is too narrow for its heading to sit beside the cut-out, so the
   heading takes its own full-width line and the paragraph wraps around the art instead */
@media (max-width: 24rem) {
  .frame h4 { clear: right; }
  .frame-fig { height: 3.75rem; }
}

/* A LINK IN A TIGHT DISPLAY HEADING MUST NOT TAKE A CLICK MEANT FOR THE LINE BELOW IT. These headings set 0.92
   leading, so a link's glyph box (the font's ascent plus descent, about 1.27em) is taller than the line it sits on,
   and two links on consecutive lines overlapped by a quarter of their height: a real click 92% of the way down
   "AudioDesc" opened PhotoDesc, measured at every width from 1024 to 1920.
   Making the link an inline-block fixes the box but not the hit test, because the text still paints outside it and
   stays clickable. So the text stops taking the pointer and a layer exactly the size of the box takes it instead.
   .cover-lead already does this for the same reason, one line box per name; this is that rule, applied to the other
   place on the page where display type holds links. */
.sec-name a { display: inline-block; position: relative; pointer-events: none; }
.sec-name a::after { content: ""; position: absolute; inset: 0; pointer-events: auto; }

/* ------------------------------------------------------------------ THE SHELF
   Ten VHS cases standing beside the paragraph that names them. See the long note over shelfFrom() in
   tools/build-home.mjs: that is where the covers, the colours, both inks and every title size are worked out.
   Here they are only drawn.
   Rejected and not to be tried again: varying the thickness (they are tapes, not books, and it read as a bar
   chart), the app icon squeezed into a band at the head (mush at 36px), thicker cases, and mixing the accents
   toward grey to shade them (it turned amber into brown and coral into mud; scale the channels instead). */
.cover-body { display: grid; gap: clamp(2.5rem, 5vh, 4rem); }
.cover-shelf { display: grid; justify-items: start; }
.shelf-row {
  position: relative; display: flex; align-items: flex-end; gap: 3px;
  padding: 30px 0 14px;
  perspective: 1150px; perspective-origin: 50% 34%;
}
/* THE NICHE. The cases used to stand against the page itself, which is near black, so ten bright rectangles hung
   there with a dark strip under them. A shelf is a box: a back wall a step lighter than the page, lit along its
   top edge, falling into the shadow the shelf above would throw. */
.shelf-row::before {
  content: ""; position: absolute; z-index: 0; left: -30px; right: -30px; top: 0; bottom: 0;
  border-radius: 3px 3px 0 0;
  background: linear-gradient(#1b202a 0, #13171f 34%, #0c1015 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.10), inset 0 22px 28px -16px rgba(0,0,0,0.95);
}
.shelf-board {   /* the front lip, drawn in front of the cases: their feet tuck behind it, which is what stops them floating */
  position: absolute; z-index: 3; left: -30px; right: -30px; bottom: 0; height: 16px; border-radius: 1px 1px 2px 2px;
  background: linear-gradient(#7a8393 0 1px, #4e5663 1px 4px, #323843 4px 62%, #1c2129);
  box-shadow: 0 1px 0 rgba(255,255,255,0.07) inset, 0 22px 32px -18px rgba(0,0,0,0.9);
}
.shelf-board::after { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: rgba(255,255,255,0.4); }

.spine {
  position: relative; z-index: 1; width: 36px; height: 254px; flex: 0 0 auto;
  display: grid; grid-template-rows: 24px var(--top) minmax(0, 1fr) 9px 5px;
  border-radius: 2px 2px 1px 1px; background-color: var(--c); color: var(--ink);
  text-decoration: none; overflow: hidden; transform-origin: 50% 92%;
  box-shadow: inset 1px 0 0 rgba(255,255,255,0.26), inset -1px 0 0 rgba(0,0,0,0.5),
              inset 0 1px 0 rgba(255,255,255,0.3), 0 12px 18px -9px rgba(0,0,0,0.95);
}
.spine::after {    /* the printed sleeve: one light across the whole row, the grain of the stock, and the shelf's own shadow gathering at the foot */
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    repeating-linear-gradient(90deg, rgba(255,255,255,0.05) 0 1px, transparent 1px 3px),
    linear-gradient(to top, rgba(0,0,0,0.22) 0, rgba(0,0,0,0.05) 26px, transparent 60px),
    linear-gradient(100deg, rgba(255,255,255,0.26) 0%, rgba(255,255,255,0.03) 22%,
                    rgba(0,0,0,0.04) 62%, rgba(0,0,0,0.34) 100%);
}
/* EVERY PART OF A CASE IS IN COLUMN ONE. The field and the title share row 3 (the title is printed ON the field),
   and without this grid auto-placement refuses to overlap them: it invents a second column, and both come out at
   16px on a 36px case with the ground showing down one side. */
.spine > * { grid-column: 1; }
/* the catalogue number, at the head, in the same place on all ten */
.spine b { grid-row: 1; position: relative; display: grid; place-items: center; color: var(--ink);
           font-family: var(--font-display); font-weight: 800; font-size: 0.68rem; letter-spacing: 0.07em;
           line-height: 1; font-variant-numeric: tabular-nums; }
/* the hairline under the number, on the three covers where the number is not already sitting on a colour break */
.sp-2 b::after, .sp-3 b::after, .sp-4 b::after {
  content: ""; position: absolute; left: 7px; right: 7px; bottom: 0; height: 1px;
  background: currentColor; opacity: 0.42; }
.spine-field { grid-row: 3; background: var(--d); }
.spine-name  { grid-row: 3; justify-self: center; align-self: center; position: relative;
               writing-mode: vertical-rl; white-space: nowrap; color: var(--dink);
               font-family: var(--font-display); font-weight: 700; font-size: var(--t);
               letter-spacing: var(--tr); line-height: 1; }
/* THE HOUSE MARK. One continuous run of the site's own gradient along the foot of the whole shelf, cut into ten by
   the cases standing on it: the gradient is sized to the full row and each case shows the slice at its own offset,
   so slate runs into teal runs into amber runs into coral from the first case to the last. Per case it was ten
   little rainbows, which read as noise; across the row it reads as one imprint and says these all came from the
   same place. Drawn over the sleeve's shading so it stays the one bright thing at the bottom of the shelf. */
.spine-mark { grid-row: 4; z-index: 3;   /* row 5 below it is bare stock, so the bar reads as printed on the sleeve rather than as the case's own bottom edge */
  background-image: linear-gradient(to right, #3d5a80, #2a7f6f, #c8871e, #d94f2a);
  background-size: 387px 100%; background-position: var(--mx) 0; background-repeat: no-repeat; }

/* sp-4 is the one with no field: the title is printed straight onto the stock, in caps, between two hairlines */
.sp-4 .spine-field { background: none; position: relative; }
.sp-4 .spine-field::before, .sp-4 .spine-field::after {
  content: ""; position: absolute; left: 7px; right: 7px; height: 1px; background: var(--ink); opacity: 0.42;
}
.sp-4 .spine-field::before { top: 0; }
.sp-4 .spine-field::after { bottom: 0; }
.sp-4 .spine-name { color: var(--ink); align-self: center; }
/* sp-3 prints the title low on the field, the way a long tape leaves its top third empty */
.sp-3 .spine-name { align-self: end; margin-bottom: 14px; }

/* the one that has not shipped is an unprinted case: grey stock, the hatch of a blank sleeve, no house colour */
.spine.is-soon { background-image: repeating-linear-gradient(135deg, rgba(255,255,255,0.05) 0 5px, transparent 5px 10px); }
.spine.is-soon .spine-mark { filter: saturate(0.2) brightness(0.7); }

/* PULLING A CASE OUT. The old version rose 26px and tipped a degree and a half, which read as a web hover rather
   than as picking something up. The row carries a perspective, so a case can come forward and turn its face toward
   you, and its shadow swings out to the left as it does. */
@media (prefers-reduced-motion: no-preference) {
  .spine { transition: transform 0.5s cubic-bezier(0.18, 0.92, 0.24, 1.06), box-shadow 0.42s, filter 0.42s; }
  .spine:hover, .spine:focus-visible, .spine.is-lit {
    transform: translateY(-44px) translateZ(46px) rotateY(-11deg) rotateX(5deg) scale(1.015);
  }
}
.spine:hover, .spine:focus-visible, .spine.is-lit {
  z-index: 5; filter: brightness(1.09) saturate(1.05);
  box-shadow: inset 1px 0 0 rgba(255,255,255,0.5), inset -1px 0 0 rgba(0,0,0,0.5),
              inset 0 1px 0 rgba(255,255,255,0.55), -14px 34px 46px -14px rgba(0,0,0,0.95);
}
.spine:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
.cover-lead a.is-lit { border-bottom-color: var(--c, #fff); background: color-mix(in srgb, var(--c, #fff) 16%, transparent); }

/* The shelf is a wide-screen object. Below 68rem it would stack under the paragraph and repeat what the paragraph
   already says, so it is not there at all: the prose is the lineup at that width. */
.cover-shelf { display: none; }
@media (min-width: 68rem) {
  .cover-shelf { display: grid; }
  .cover-body { grid-template-columns: minmax(0, 58rem) auto; align-items: center;
                justify-content: space-between; gap: clamp(2.5rem, 4vw, 5rem); }
}
