/* ===== Sample cards on an occasion page =====
 *
 * Draws the tiles js/samples.js mounts a real envelope into. Loaded only by the
 * per-occasion landing pages; nothing else on the site has a `.sample` on it.
 *
 *
 * ---- WHY THIS IS A SEPARATE FILE AND NOT AN APPENDIX TO styles.css ----
 *
 * _headers serves `/css/*` with `max-age=31536000` — one year — and the filenames
 * are not fingerprinted. So an edit to css/styles.css does not reach anybody who
 * has already visited the site until their cache expires or they hard-reload.
 *
 * For most edits that is a cosmetic lag. Here it would be a broken page: somebody
 * who read a guide page last week still has the old styles.css, and if these rules
 * lived in it they would arrive at the occasion page and get a `.sample-stage` with
 * no height, no `overflow: hidden` and no `position: relative` — which is not a
 * plainer tile, it is three or four envelope animations drawn on top of the text under
 * them. A NEW filename cannot be stale, because nobody has it yet.
 *
 * The consequence, stated plainly so the next person is not surprised by it: the
 * stage geometry below is a COPY of `.hero-demo`'s in css/styles.css rather than a
 * second selector on those rules, and copies drift. If css/styles.css ever gets a
 * fingerprint or a `?v=` on its 80-odd link tags, the right move is to fold these
 * rules in beside `.hero-demo` and `.mini-stage` and delete this file. Until then,
 * anybody retuning the demo panel's height or its shell width should read this file
 * too; the shared numbers are called out where they appear.
 *
 * teachers-day.html — the site's other occasion page — carries no stylesheet of its
 * own and says in its head that it needs none, because every class on it already
 * existed. That was true of a page with four links on it. It stops being true the
 * moment a page renders an envelope.
 */


/* ===== The list =====
   An <ol> for the same reason .msg-list is one: the order is meaningful (the
   sibling most people are writing to first), and the numbers themselves are noise
   beside a picture of a card, so they are not drawn at all. Unlike .msg-list there
   is no counter — a numbered envelope reads as step 1 of 3, which is a claim about
   a process rather than about a choice. */
.sample-list {
    list-style: none;
    margin: 0 0 8px;
}

.sample {
    margin-bottom: 34px;
    /* No border, no card-around-the-card. The tile's stage already has an edge and
       a tinted ground; a second frame outside it makes the envelope look like a
       thumbnail in a catalogue instead of an object on a table. */
}

.sample:last-child { margin-bottom: 8px; }

/* The line above each stage: which sibling, which paper. Two facts, one line,
   because the visitor's question at this point is "is one of these mine". */
.sample-cap {
    font-size: .78rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: 10px;
}


/* ===== The stage =====
 *
 * Geometry copied from `.hero-demo` in css/styles.css — see the header for why it
 * is a copy. That panel's own comment carries the measurements behind every number
 * and is the place to read before changing any of them; what follows records only
 * where this context DIFFERS and why.
 *
 * HEIGHT. .hero-demo is 560px, and 580px at 860px and below. This is 780px, and the
 * gap is not a preference — it is the card floor below, which lets the card grow to
 * 411px where the hero's is pinned at 250px. A taller card needs a taller arc, and
 * --rise derives from the floor, so THE TWO NUMBERS MOVE TOGETHER: change one and
 * re-measure the other. That is not advice, it is the thing that went wrong once
 * already — see the second paragraph below.
 *
 * The height is arrived at by measuring, not by adding up guesses. With the stage
 * temporarily at 800px the two gaps that matter came back as 47-49px at the peak of
 * the card's rise and 68px at the settled card's bottom, at 320, 390 and 1200 wide.
 * The stage centres the envelope, so shrinking it by D moves BOTH gaps in by D/2 —
 * which is the arithmetic to redo rather than re-derive: 800 - 2*(47-14) = 734, taken
 * up to 740, where the worst case across the three widths was 17px of clearance at the
 * peak and 38px under the settled card.
 *
 * 740px is not what is written below, and the reason is the paragraph above. Raising
 * the card floor from 400px to 420px — needed for the longest sample copy, see the
 * floor's own note — moved --rise with it and took the peak clearance at 320px wide
 * from 19px to 3px. Nothing was clipped, but 3px is not a margin. The same D/2
 * relationship gives the correction directly rather than by trial: +40px of stage buys
 * +20px at each end, so 780px. Re-measured across all seventeen occasion pages at 320,
 * 390 and 1200 wide, the worst case is 21px of clearance at the peak and 36px under
 * the settled card, and no card on any page is clipped or scrolled.
 *
 * The order to retake those in, if the sample copy is ever replaced with something
 * longer: message fit first (see the card floor below), because a longer note makes a
 * taller card, and only then the two clip gaps, because they are what the taller card
 * has to fit inside. Doing it the other way round is how the 3px above happened.
 *
 * One number and not two, at every width. The cards at 1200px wide come out at
 * 324-390px, so on a desktop this reserves 68-133px of tinted paper under a settled
 * card that will never use it. A second, shorter min-height for wide viewports would
 * reclaim that — and would immediately be wrong, because the rise the height exists
 * for is driven by the FLOOR, which does not change with viewport width: at 740px the
 * peak clearance at 1200 wide is 1px. The empty paper is the price of the arc.
 *
 * A FIXED height rather than one that grows when the envelope opens, and this is the
 * one place this file is deliberately wasteful: while the envelope is shut, most of
 * these 780px is empty tinted paper. The card leaves the envelope and everything it
 * does on the way out is a transform on an absolutely positioned element, so none of
 * it is in the flow and none of it can push the caption below out of the way. The
 * alternative — letting the tile size itself and grow on open — reflows the page
 * under the finger that just tapped it, and moves the two tiles below off the screen.
 * The room has to be reserved before it is needed.
 *
 * overflow: hidden is load-bearing and not tidiness: the rise overshoots, and the
 * clip is what makes the card look like it came out of the envelope rather than
 * flying in from above the tile. It is also why the height matters at all. */
.sample-stage {
    position: relative;
    min-height: 780px;
    /* Not the container's full 560px. A wider stage means a wider envelope, and the
       card's type and padding are absolute pixels — so a wide envelope beside a
       fixed-size card starts to read as a card resting on a mat rather than one
       standing out of an envelope. 380px is close to the width the hero panel gets
       at the narrow end, which is the case its height was measured against. */
    max-width: 380px;
    /* Not on this element: --card-floor-base. envelope.css declares it on .envelope
       itself, so a copy here is shadowed and silently does nothing — which is exactly
       what happened once during this file's tuning, and looked like the floor having
       no effect on card height. The rule that works is below, and it is why both
       .hero-demo and .mini-stage write `.<panel> .envelope { ... }` rather than
       putting the dial on their own container. */
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: 26px 18px 60px;
    /* --bg-1 and --bg-2 come from .card-theme.theme-<occasion> in css/envelope.css,
       and js/samples.js is what puts those two classes on the tile. love.html is the
       one page where they come from the bare .card-theme instead, and it is not a
       missing palette: love IS the default, which is why envelope.css's list of
       per-occasion overrides starts at .theme-sorry. `theme-love` is a class that
       matches no rule, and the page renders in the right pink because of it. So the
       ground is the OCCASION's colour on every tile of a page while the envelopes on
       them differ, which is not an oversight: applyCraft() never writes --bg-*, so
       preview.html's phone frame behaves the same way — the sender's paper choice
       changes the envelope, not the room it is sitting in. */
    background:
        radial-gradient(ellipse 80% 55% at 50% 0%, var(--bg-1), transparent 70%),
        linear-gradient(170deg, var(--bg-1), var(--bg-2));
}

/* 82%, from .hero-demo. Its comment explains the direction of the trade in full and
   it is worth restating because it is counter-intuitive: a BIGGER envelope produces
   a SHORTER card, because the message is set in absolute pixels and a narrow card
   needs more lines for the same words, while envelope.css caps the card as a
   fraction of the envelope's own height. Shrinking this to make the tile look
   dainty is how the card ends up running out of the bottom of the stage.

   That trade is not academic here — it is one of the two dials that fixed the narrow
   phone. See the 92% override in the media query at the foot of this file. */
.sample-stage .envelope-shell { width: 82%; }

/* The envelope moves down as the card travels up, which is what buys the rise room
   at the top of a stage that centres the envelope. 45%, again from .hero-demo: the
   largest drop that still keeps the envelope's bottom fold inside the frame. A
   percentage resolves against the envelope's own height, so it tracks every stage
   width without a second breakpoint. */
.sample-stage .envelope-shell.opened { transform: translateY(45%); }

/* ===== The one place this context does NOT copy .hero-demo, and why =====
 *
 * That panel and .mini-stage both override --card-floor-base DOWN to 250px from
 * envelope.css's 340px, because their card's type and padding are shrunk and --rise
 * derives from this floor: leave 340px in place there and the card is hauled further
 * up than it is tall, with the whole overshoot sliced off by the clip above.
 *
 * This context goes the other way, UP to 420px, because the three panels are showing
 * DIFFERENT THINGS. .hero-demo plays an 80-character stub, and .mini-stage's job is to
 * be a faithful small copy of a card whose real reader will hold it full-screen. A
 * sample's job is for somebody to READ the note, once, on this page, and the note is a
 * real suggestion line lifted verbatim from a guide page — 200 to 250 characters across
 * the seventeen occasion pages, and MAX_CHARS in bin/build-occasion-pages.js is the
 * generator-side half of this rule, which refuses to emit a sample longer than the
 * number this floor was measured against.
 *
 * The arithmetic, measured rather than assumed. The card's chrome — mark, greeting,
 * rule, signature, padding — is about 209px of fixed pixels even at the reduced type
 * below, and .letter-message is the only child of .letter that can shrink; everything
 * else is flex-shrink: 0. So the floor minus 209px IS the message's box:
 *
 *   250px (copied from .hero-demo)  ->  41px of box for 131px of words. Two lines of
 *                                       seven, the rest behind a scrollbar the height
 *                                       of two lines. Unusable, and the first version
 *                                       of this file shipped it.
 *   340px (envelope.css's default)  ->  125px of box. 0-25px over at 390 and 1200,
 *                                       43-81px over at 320.
 *   400px                           ->  zero overflow on rakhi.html's three samples at
 *                                       320, 390 and 1200. Not enough: it was measured
 *                                       against one page, and when the other fifteen
 *                                       were generated diwali.html's 249-character
 *                                       sample came back 2px over at 320 wide.
 *   420px (this)                    ->  zero overflow on all fifty-two samples across all
 *                                       seventeen pages, at 320, 360, 390 and 1200.
 *                                       Fifty-two and not fifty-one because
 *                                       teachers-day.html carries four tiles rather than
 *                                       three: that occasion has four recipients in
 *                                       js/data.js and the page shows all of them. It was
 *                                       forty-eight across sixteen until that page was
 *                                       generated with tiles instead of plain text.
 *
 * That 2px is worth being precise about, because it looked like a pass and was not.
 * FADE_EPSILON in js/craft.js is 2 and the test is `> 2`, so 2px of overflow draws no
 * fade cue and leaves tabindex null — the tile LOOKS correct and the last line of the
 * note is a hair out of reach. It is the failure mode this whole block exists to
 * prevent, arriving one pixel under the alarm. Hence the margin: 420px puts the worst
 * sample at zero rather than at the threshold.
 *
 * envelope.css records the same sum from the other end, at the min-height floor on
 * .letter-message: "44.7px of box showing 245px of message". That floor stops the box
 * going to zero; it was never enough to make a sample readable.
 *
 * 420px does NOT mean a 420px card, and this is the part worth understanding before
 * changing it. --card-maxh is a max(), so the floor is headroom rather than a height:
 * the cards actually come out at 324-411px, sized by their own content. The tile grows
 * only where the note is long — the 20px that took the floor from 400px to 420px bought
 * the worst card 2px of extra height and every other card none at all.
 *
 * What it does cost is stage height, because --rise derives from this floor. Going from
 * 400px to 420px took 16px off the peak clearance and needed 40px more stage to put
 * back; the HEIGHT note above has that arithmetic. Raising this again is cheap in card
 * height and expensive in stage height, which is the right way round but is not free.
 *
 * TRIED AND REJECTED, so nobody spends the afternoon on it again: dropping the type
 * overrides below so a sample renders at DELIVERED size — the argument being that a
 * sample should be the real thing. Measured, it is strictly worse. Full type means
 * bigger chrome, the box drops to 88-98px, and overflow goes to 32-105px. The sample
 * would then under-sell the product, implying a note does not fit when on the
 * recipient's full-screen card it does.
 *
 * The number to re-measure if the sample copy or the type scale ever changes is
 * `scrollHeight - clientHeight` on .letter-message with the envelope OPEN. Measuring
 * it shut is meaningless: the letter is at --card-enclosed-maxh then and everything
 * overflows. Anything above 2px counts — that is FADE_EPSILON in js/craft.js, so 3px
 * of overflow is enough to draw the fade cue and put the box in the tab order.
 *
 * The cost is paid in stage height, above, and it is the right way round: a taller
 * tile is a longer page, and a truncated sample is a page with nothing to show. */
.sample-stage .envelope { --card-floor-base: 420px; }


/* The letter, scaled to this stage. Matched to .hero-demo rather than to
   .mini-stage: this is a mock-up meant to look right in a panel, not the preview,
   which has the stricter job of showing the sender the size of card they are
   actually buying. */
.sample-stage .letter { padding: 18px 16px 16px; }
.sample-stage .letter-greeting { font-size: 1.02rem; margin-bottom: 8px; }
/* --msg-lh and never line-height. css/envelope.css multiplies this by
   --msg-lh-scale so a long message can close its lines up, and writing line-height
   here would quietly opt these tiles out of that — which matters more here than
   anywhere, because the sample messages are the longest text this component is
   asked to lay out on any page of the site. */
.sample-stage .letter-message { font-size: .86rem; --msg-lh: 1.58; margin-bottom: 12px; }
.sample-stage .letter-sign { font-size: .74rem; }
.sample-stage .letter-sign strong { font-size: .95rem; }
.sample-stage .letter-mark { width: 20px; height: 20px; margin-bottom: 9px; }
.sample-stage .seal { width: 44px; height: 44px; margin-left: -22px; margin-bottom: -22px; }
.sample-stage .seal svg { width: 19px; height: 19px; }

/* No .sample-stage .env-flip rule, and none is needed: the contexts js/samples.js
   builds are derived from ENVELOPE_IDS.demo, which names no `flip` id, so
   envelopeMarkup() emits no button here. A position override for an element that is
   never rendered reads as a working dial, which is the argument css/styles.css makes
   where .hero-demo's old override used to be. */


/* ===== The instruction, and how it leaves =====
   Same element and same treatment as .demo-label on the homepage. It is not shared
   with it: .demo-label's hide rule is written `.hero-demo:has(...)`, scoped to that
   panel, so a second context needs its own rule whichever file it lives in. */
.sample-hint {
    position: absolute;
    bottom: 18px;
    left: 50%;
    transform: translateX(-50%);
    font-size: .7rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: #8a7a74;
    background: rgba(255,255,255,.6);
    padding: 5px 14px;
    border-radius: 100px;
    backdrop-filter: blur(4px);
    white-space: nowrap;
    z-index: 12;
    transition: opacity .35s ease, visibility .35s ease;
}

/* All THREE properties, not opacity alone, and this is the lesson css/styles.css
   records twice — once on .demo-label and once on .env-flip in envelope.css. An
   element at opacity: 0 still hit-tests, and this strip carries z-index: 12 across
   the bottom of the stage. With only the opacity dropped, an invisible label would
   swallow every tap on the band underneath it — and what is underneath it here is
   the envelope itself, which is the one control the whole tile is about, plus the
   lower edge of the settled card.

   Keyed off .opened rather than .settled so the instruction is gone before the card
   arrives over it, and comes back if the envelope is tapped shut, which is when it
   means something again. :has() rather than a class from JS, matching the rules in
   envelope.css that read the photo's and the voice note's presence the same way: the
   state is already in the DOM, and a copy of it in a script is a second thing to
   keep in step. The descendant combinator is required because .env-host is
   `display: contents`, so .envelope is a child of the host, not of the stage.

   No prefers-reduced-motion guard: the global rule at the end of css/styles.css
   already collapses every transition to .01ms, so under that setting the label
   simply disappears instead of fading. */
.sample-stage:has(.envelope.opened) .sample-hint {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}


/* ===== The message, in the page as text =====
 *
 * js/samples.js reads the card's message OUT of this element rather than from a
 * table in a script — the reasons are in that file's header, and the short version
 * is that _headers forbids an inline <script> data block and 17 occasions' worth of
 * sample copy has no business in js/data.js.
 *
 * It stays visible after the envelope is mounted, which is the more interesting
 * half. A shut envelope is a nice object that says nothing, and most people who
 * scroll past a tile will never tap it: the caption is what they actually read, and
 * the envelope is what makes them curious enough to open it. Tapping then shows them
 * the same words on paper, which is the entire argument the product is making.
 *
 * Set in the reading face and not in the handwriting: this is the text of the note,
 * quoted, and the card above is where it is shown as handwriting. */
.sample-msg {
    font-family: var(--font-heading);
    font-size: 1.04rem;
    line-height: 1.6;
    color: var(--ink);
    margin: 16px auto 12px;
    max-width: 420px;
}

/* Reuses .msg-write from css/styles.css for the link under each sample, so the
   affordance under a sample is identical to the one under a message on the 62 guide
   pages. Only the centring is new, because those pages left-align a list item and
   this one is a centred tile. */
.sample .msg-write { display: block; text-align: center; }


@media (max-width: 560px) {
    /* The stage stops being width-capped and takes the content box, which at this end
       is narrower than the 380px above. No min-height here on purpose: 780px was
       measured at 320 as well as at 360, 390 and 1200, across all seventeen occasion
       pages, and it was the worst of them that set it — so a second copy of the same
       number would only be a second thing to forget to change. */
    .sample-stage {
        max-width: none;
        padding: 22px 14px 56px;
    }

    /* 92%, not the 82% above, and this is the narrow-phone half of the fix described
       at the card floor. Below about 330px the envelope gets small enough that the card
       gets narrow enough to gain lines for the same words, which is the counter-
       intuitive trade the shell-width comment spells out — so the answer at the narrow
       end is a BIGGER envelope, not a smaller one. Widening it from 82% to 92% at 320px
       wide took the overflow on rakhi.html's three samples from 43/62/81px to 25/43/43px
       on its own, before the floor went up; the floor then closed the rest. Not applied
       above 560px because there the stage is width-capped and 82% already produces a
       card with room to spare. */
    .sample-stage .envelope-shell { width: 92%; }

    .sample { margin-bottom: 28px; }
    .sample-msg { font-size: 1rem; }
}
