@font-face {
  font-family: 'Oswald';
  src: url('/fonts/oswald-600.ttf') format('truetype');
  font-weight: 600;
  font-display: swap;
}

/* site/public/share.css
   Styles for /c/[steamid], the share landing page.

   A separate file rather than an inline <style> block because the site ships
   `style-src 'self'` (see functions/_middleware.js and public/_headers).
   Inline styles are parsed into the DOM and then silently refused: the <style>
   element is present in <head>, document.styleSheets is empty, and nothing
   appears in the console. The page renders correctly in markup and completely
   unstyled in a browser.

   Kept separate from style.css so the landing page pulls one small file rather
   than the whole SPA stylesheet. Token values are copied from :root in
   style.css and must be kept in step if that moves. */

:root {
  --bg: #14100d; --surface: #1c1611; --line: #3a2f26; --line-strong: #4a3b30;
  --bone: #e8ddd0; --dim: #8a7f70; --steel: #939fa6;
  --rust: #cd5c2a; --rust-hot: #e8703a; --rust-deep: #8f3d1c;
  /* card-template.mjs palette. Warmer than the site tokens above: the card
     uses LABEL for its eyebrow and a bone warmed a few degrees. Contrast on
     --bg measured: label 6.32:1, bone 15.02:1, name 4.65:1. All clear AA. */
  --card-label: #a2937f; --card-bone: #efe4cf;
  --mono: ui-monospace, 'Cascadia Mono', Consolas, monospace;
  --emboss: inset 0 1px 0 rgba(232,221,208,.06), inset 0 -1px 0 rgba(0,0,0,.35);
  /* Body stack gap. A variable because several rules pull against it with
     negative margins to group things (eyebrow to name, the two footer lines);
     those use calc() off this, so narrowing the gap for mobile cannot silently
     turn a 2px seam into an overlap. */
  --stack-gap: 20px;
}

* { box-sizing: border-box; margin: 0; }
/* The background sits on html as well as body. Body's background propagates to
   the canvas only while body is unconstrained; the desktop layout below caps it
   at 900px, and without this the area either side would paint browser-default
   white. */
html, body { overflow-x: clip; background: var(--bg); }

body {
  background: var(--bg); color: var(--bone);
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
  display: flex; flex-direction: column; align-items: center;
  min-height: 100vh; padding: 24px 16px 40px; gap: var(--stack-gap);
}

.eyebrow {
  font-family: var(--mono); font-size: 12px; letter-spacing: .26em;
  color: var(--card-label); text-transform: uppercase; text-align: center;
}

/* The banner from the top of the card. Two eyebrows read as one thing because
   the rust rule separates them: this one brands the page, the one below it is
   the card's own line and belongs to the record. */
.banner {
  font-family: var(--mono); font-size: 12px; letter-spacing: .26em;
  color: var(--steel); text-transform: uppercase; text-align: center;
  width: 100%; padding-bottom: 14px; border-bottom: 3px solid var(--rust);
  margin-bottom: 2px;
}

.card {
  width: 100%; max-width: 380px;
  border: 1px solid var(--line-strong); border-radius: 4px;
  box-shadow: var(--emboss); background: var(--surface);
  overflow: hidden; line-height: 0;
}

/* Cropped to the portrait box. Geometry measured off the rendered card rather
   than derived from card-template.mjs: the portrait's 2px rust border runs
   x 798..1135, y 42..441 (re-measured 21 Jul after the line-through-the-brag
   fix changed the template's bottom padding and dropped the portrait ~4px),
   so the box is 337x400. A template-derived guess put the top at 112 and
   showed the ID/RARITY lines hanging below the face, because the portrait row
   is not centred in the content box the way the source reads.
     width  1200/337 = 356.08%
     left   -798/337 = -236.80%
     top     -42/400 =  -10.50% */
.card.crop { aspect-ratio: 337 / 400; position: relative; }
/* Direct child only. A bare `.card.crop img` also caught the corner mug nested
   inside .stamp and positioned it like the portrait: 356% wide and off-screen,
   so the mug simply never appeared. */
.card.crop > img {
  position: absolute; width: 356.08%; height: auto;
  left: -236.80%; top: -10.50%; display: block;
}

/* Corner marks: the brag bottom-right, the URL top-left.
   Mono rather than a display face: everything on this page speaks Oswald and
   monospace, and a novelty face here would read as a sticker stuck on the
   record rather than part of it.
   The scrims are the load-bearing part. Tier backdrops run from bright fog to
   dark treeline to sunset, so a flat translucent silver that reads well on one
   disappears on another. Measured across four characters, the top-left corner
   swings 147 luminance points (Hazel 200, Adam 53); pale text on Hazel's fog
   lands at 1.33:1 unprotected. A feathered corner gradient plus a text shadow
   keeps both legible across the range without turning into a label.
   ONE scrim, bottom-right only. The top-left had a matching one and it had to
   go: on the pale fog backdrops it read as a smudge of dirt in the corner of
   the photo rather than as a treatment. The URL there carries its own contrast
   instead, in rust with a layered shadow (see .site-url). */
.card.crop::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 68% at 100% 100%,
              rgba(8, 6, 5, .82) 0%, rgba(8, 6, 5, .45) 38%, rgba(8, 6, 5, 0) 72%);
}
.stamp {
  position: absolute; right: 11px; bottom: 8px; z-index: 1;
  display: flex; align-items: center; gap: 7px; max-width: 92%;
}
/* Runs UP the left edge rather than sitting in a corner. Two reasons it won
   over a corner: it keeps the face completely clear, and the left edge is
   consistently darker than the sky across the roster, which is where a corner
   mark struggled (rust on Nick's dusk sky measured 1.30:1).
   transform-origin at the bottom-left corner so the lockup grows upward from
   where it is anchored instead of pivoting off-frame.
   `left` is NOT the visible inset. Rotating about the bottom-left corner sweeps
   the box left by its own line-height, so `left` lands the strip's RIGHT edge
   and the strip occupies (left - ~13px) to left. 16px therefore rendered 5px
   from the photo edge; 28px is what puts it ~15px in.
   Colour and shadow are set AFTER the shared `.watermark, .site-url` rule
   below, not here: both are single-class selectors, so whichever comes last
   wins, and setting the rust here silently lost to the bone in that rule. */
.site-url {
  /* Matching .stamp's `bottom` is NOT enough to make the pair look level. The
     brag is text in a 12.5px line box whose glyphs sit centred inside it, while
     the mug is an image whose pixels reach its own box edge, so equal box
     bottoms leave the mug's optical centre ~4px high. This is set so the two
     CENTRES agree, which is what the eye reads across the photo.
     `left` is not the visible inset either: rotating about the bottom-left
     corner sweeps the box left by its own line-height, so `left` lands the
     strip's RIGHT edge and the strip occupies (left - ~19px) to left. */
  position: absolute; left: 26px; bottom: 4px; z-index: 1;
  display: flex; align-items: center; gap: 7px; white-space: nowrap;
  transform: rotate(-90deg); transform-origin: left bottom;
}
/* The mug leads the lockup. Rotated -90deg the reading order runs bottom to
   top, so the first child sits at the foot of the strip, and the mug turns with
   it. Sizing and knock-back are shared with the brag stamp's mug below rather
   than set here, so the same mark cannot end up at two different sizes on the
   same photo. */
/* The mug carries its own rust, so it needs less knocking back than the text;
   at full opacity it competes with the character for attention. Only the site
   lockup has one now: the brag stamp's was removed rather than printing the
   same mark twice on a 343px photo. */
.site-url img {
  width: 22px; height: auto; flex: none; opacity: .8;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .8));
  /* No transform of its own. The lockup's rotate(-90deg) already turns the mug
     90deg left of upright, lying it on its side with the steam running left and
     level with the wordmark. A second rotate(-90deg) here composes to 180deg,
     which puts it upside down with the steam falling; that was tried and
     rejected. Rotation is about the element centre, so adding or removing this
     never disturbs the centre alignment with the brag set below. */
}
/* One treatment for both corners, so they read as a matched pair rather than
   two unrelated stickers stuck on the same photo. */
.watermark, .site-url {
  margin: 0;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; line-height: 1.25;
  color: rgba(226, 220, 210, .82);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .85);
}
.watermark { text-align: right; }
/* Rust, not bone, and no scrim beneath it. The brag opposite is pale text that
   needs a dark corner to sit on; this is the brand mark, so it reads as the
   brand colour, and the two corners stop looking like the same kind of
   information. MUST stay after the shared rule above, which also sets colour.
   The shadow is layered rather than single, and it is doing the job the scrim
   used to: a 1px opaque pass puts a dark edge on every side of the glyphs (a
   faux stroke), a 2px pass grounds them, and a 5px soft pass lifts them off the
   photo. Contrast carried by the text instead of by darkening the picture. */
.site-url {
  color: var(--rust-hot);
  text-shadow:
    0 0 1px rgba(0, 0, 0, .95),
    0 1px 2px rgba(0, 0, 0, .95),
    0 0 5px rgba(0, 0, 0, .75);
}

.eyebrow + .name { margin-top: -6px; }

.name {
  font-family: 'Oswald', 'Arial Narrow', sans-serif; font-weight: 600;
  font-size: clamp(46px, 13vw, 74px); line-height: .96;
  letter-spacing: .02em; text-transform: uppercase;
  color: var(--rust); text-align: center;
}

.sub-verdict {
  font-family: 'Oswald', 'Arial Narrow', sans-serif; font-weight: 600;
  font-size: clamp(17px, 4.4vw, 23px); letter-spacing: .12em;
  text-transform: uppercase; color: var(--card-bone); text-align: center;
  margin-top: -4px;
}

/* Oswald, not the mono stack. The page runs two families with distinct jobs:
   Oswald carries anything the reader is meant to act on or take away (name,
   sub-verdict, dossier values, pitch, wordmark), while the mono is quiet system
   chrome (banner, labels, readout, legal). The CTA is the one action on the
   page and it was wearing the chrome font, which is why it read as thin and
   off-pattern beside the pitch line directly beneath it.
   It could not be fixed by adding weight: ui-monospace resolves to Consolas or
   Cascadia Mono, which ship Regular and Bold only, so at 700 it was already at
   the top of its range and anything heavier would have been a synthesised fake.
   Oswald is condensed, so it needs a few px more to hold the same optical size.
   Measured, not assumed: #14100d on --rust-hot is 6.14:1, on --rust 4.65:1.
   Both clear AA. min-height keeps the 48px touch target. */
.cta {
  font-family: 'Oswald', 'Arial Narrow', sans-serif; font-weight: 600;
  font-size: 17px; letter-spacing: .14em; text-transform: uppercase;
  text-decoration: none;
  color: #14100d; background: var(--rust-hot);
  padding: 14px 30px; border-radius: 3px; box-shadow: 0 2px 0 var(--rust-deep);
  min-height: 48px; display: inline-flex; align-items: center;
}
.cta:hover, .cta:focus-visible { background: var(--rust); }
.cta:focus-visible { outline: 2px solid var(--bone); outline-offset: 3px; }

/* The record as real text. The card image carries it visually, but at 375px the
   card is ~343 wide and its baked-in text is unreadable, so this is what a
   phone user actually reads. Mirrors the dossier styling on the main site:
   mono --steel labels, Oswald-ish --bone values. */
/* A fixed two-column grid, not flex-wrap. Wrapping sized every cell to its own
   content, so "SHAVED" and "BROWN FULL BEARD" sat in columns that did not line
   up with each other or with the row below. Two equal columns give the block a
   spine. max-width matches .card so the two stack flush on desktop. */
.dossier {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 20px; max-width: 380px; width: 100%;
  padding: 18px 20px; margin: 0;
  border: 1px solid var(--line); border-radius: 4px;
  background: var(--surface); box-shadow: var(--emboss);
}
/* An odd cell count strands one on the last line; span it so it centres under
   the pair rather than sitting in column 1. Every female is an odd count (no
   beard row), so this is half the roster, not an edge case. */
.dossier .row:last-child:nth-child(odd) { grid-column: 1 / -1; }
.dossier .row {
  display: flex; flex-direction: column; align-items: center;
  gap: 3px; min-width: 0; text-align: center;
}
.dossier dt {
  font-family: var(--mono); font-size: 11px; letter-spacing: .16em;
  color: var(--steel); text-transform: uppercase;
}
.dossier dd {
  font-family: 'Oswald', 'Arial Narrow', sans-serif; font-size: 18px;
  letter-spacing: .04em; color: var(--bone); text-transform: uppercase;
  margin: 0;
}

/* Terminal readout, as on the card. Centred like everything else: stacking the
   // separators needed a left edge, and one left edge on an otherwise centred
   page reads as a mistake rather than as alignment. */
.readout { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.readout p {
  font-family: var(--mono); font-size: 13px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--steel); margin: 0; text-align: center;
}

/* Tier ink, from the rarity-ladder brief (card-template.mjs STAMP_INK and
   style.css .tier-stamp). Only epic and legendary are coloured; common,
   uncommon and rare stay in the readout's steel, deliberately quiet so the
   colour keeps meaning something when it does appear. */
.tier.epic { color: #b48ae0; }
.tier.legendary { color: var(--rust-hot); }


.pitch {
  font-family: 'Oswald', 'Arial Narrow', sans-serif; font-weight: 600;
  font-size: clamp(15px, 3.8vw, 18px); letter-spacing: .06em;
  text-transform: uppercase; color: var(--bone); text-align: center;
}

/* Mark over wordmark, as the card sets it. alt="" on the image because the
   wordmark beneath already carries the name; two readings of "RustMug" in a
   row is noise for a screen reader. */
/* gap:0 and a negative pull: Oswald carries noticeable leading above its caps,
   so the optical gap under the mug is larger than the box gap suggests. The
   wordmark should read as sitting on the mug, not floating below it. */
.brand { display: flex; flex-direction: column; align-items: center; gap: 0; }
.mark { width: 132px; height: auto; opacity: .9; display: block; }
.wordmark {
  font-family: 'Oswald', 'Arial Narrow', sans-serif; font-weight: 600;
  font-size: 21px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--card-bone); margin: -10px 0 0; line-height: 1;
}

/* Language picker. A native <details>, so the picker costs no JavaScript on a
   page whose whole point is loading instantly from a Discord tap. Collapsed it
   is one dim line in the footer, where the chrome already lives. It is not at
   the top for two reasons: it would compete with the banner, and it would spend
   the headroom that puts ID and RARITY above the fold on a phone.
   Every touch target here clears 44px, unlike the main site's #lang-toggle. */
.langpick {
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  color: var(--dim); text-align: center;
}
/* Deliberately NOT uppercased, unlike the rest of the footer chrome. The
   summary now shows a language name rather than a label, and the same name sits
   in the list a few pixels below in its own casing: DEUTSCH above Deutsch reads
   as a mistake. Several of these scripts have no case to transform anyway. */
.langpick summary {
  cursor: pointer; list-style: none;
  min-height: 44px; padding: 0 12px;
  display: inline-flex; align-items: center; gap: 7px;
}
/* Same globe the main site puts on #lang-toggle. With the "Language" caption
   gone, this is what says "picker" to someone whose eye lands on a lone word in
   a footer. currentColor, so it dims and brightens with the summary. */
.langpick .lang-globe { flex: none; opacity: .85; }
/* Safari still paints its own marker without this. */
.langpick summary::-webkit-details-marker { display: none; }
/* Chevron rather than the platform triangle, which differs per browser. */
.langpick summary::after {
  content: ''; width: 5px; height: 5px; margin-top: -3px;
  border-right: 1px solid currentColor; border-bottom: 1px solid currentColor;
  transform: rotate(45deg); transition: transform 140ms ease;
}
.langpick[open] summary::after { transform: rotate(-135deg); margin-top: 2px; }
.langpick summary:hover, .langpick summary:focus-visible { color: var(--bone); }
.langpick summary:focus-visible { outline: 2px solid var(--rust); outline-offset: 2px; }
.langpick ul {
  list-style: none; margin: 4px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 2px 6px;
  max-width: 330px;
}
/* Endonyms keep their own casing: uppercasing a list of 11 language names is
   shouty, and several of these scripts have no case at all. */
.langpick a {
  display: inline-flex; align-items: center; min-height: 44px; padding: 4px 9px;
  color: var(--steel); text-decoration: none; letter-spacing: .04em;
  border-radius: 3px;
}
.langpick a:hover, .langpick a:focus-visible {
  color: var(--bone); background: var(--surface);
}
.langpick a:focus-visible { outline: 2px solid var(--rust); outline-offset: 1px; }
.langpick a[aria-current] { color: var(--rust-hot); }

/* Trademark line. Smallest thing on the page and the last, matching the site
   footer's treatment: mono, --dim, 10px. */
.legal {
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em;
  color: var(--dim); text-transform: uppercase; text-align: center;
  max-width: 46ch; line-height: 1.5; margin-top: 4px;
}
/* The body gap is too much between two footer lines that read as one block;
   pull the copyright up under the attribution. Computed off --stack-gap so it
   stays a 2px seam at any gap rather than becoming an overlap. */
.legal + .legal { margin-top: calc(2px - var(--stack-gap)); }

/* --dim at 11px measures 4.82:1 on --bg. Clears AA but sits closest to the
   floor of anything here; do not darken. */
.tagline {
  font-family: var(--mono); font-size: 11px; letter-spacing: .13em;
  color: var(--dim); text-transform: uppercase; text-align: center;
  max-width: 44ch;
}

/* Desktop: the record beside the portrait, not stacked above it.
   The stack is a phone layout, and on a wide screen it stretched into a 380px
   ribbon with ~450px of dead space each side, a rust rule 3.2x wider than
   anything beneath it, and the CTA at 922px against an 800px fold. A desktop
   visitor saw a name and a face and had to scroll to discover there was
   anything else, including the thing we want them to click.
   This is the composition the card itself already uses: record left, face
   right. 900px is where both columns have room to breathe; below that a single
   column still reads better than two cramped ones, so tablets keep the stack.
   Grid rather than wrapper divs, so the markup stays one flat list and the
   phone layout stays a plain flex column. Every one of these children is
   always rendered, so placing them by explicit row is safe; auto-flow would
   drop the portrait into the middle of the record. */
@media (min-width: 900px) {
  body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 380px;
    column-gap: 44px;
    justify-items: center;
    align-content: start;
    max-width: 900px;
    margin: 0 auto;
  }
  .banner { grid-column: 1 / -1; }
  .eyebrow    { grid-area: 2 / 1; }
  .name       { grid-area: 3 / 1; }
  .sub-verdict{ grid-area: 4 / 1; }
  .dossier    { grid-area: 5 / 1; }
  .readout    { grid-area: 6 / 1; }
  .cta        { grid-area: 7 / 1; }
  /* spans the whole record stack, pinned to the top so a short record (a
     female character has no beard row) does not leave the face floating */
  .card.crop  { grid-area: 2 / 2 / 8 / 3; align-self: start; }
  /* below the split, everything returns to full width */
  .pitch, .brand, .langpick, .legal { grid-column: 1 / -1; }
}

/* Phones. The mark is the readout: ID and RARITY are what make this a record
   rather than a poster, and someone arriving from a Discord tap should not have
   to scroll to reach them. At 375x812 the readout used to end 14px past the
   fold. Nothing here shrinks the portrait or the name -- the payoff and the
   hero keep their size; the space comes out of the gaps between them.
   Desktop is left alone: there is no fold pressure there. */
@media (max-width: 480px) {
  /* 13px, not 15. Translated pages wrap two lines that English fits on one (the
     eyebrow and the rarity readout), which cost German 29px and Japanese 31px
     of fold headroom and left them 6px and 4px clear. Two pixels off each gap
     and off the dossier padding buys ~16px back. Imperceptible in English,
     and it is the difference between a comfortable margin and one that any
     device with slightly heavier font metrics would break. */
  :root { --stack-gap: 13px; }
  body { padding-top: 18px; }
  .banner { padding-bottom: 11px; }
  .dossier { padding: 12px 18px; gap: 12px 18px; }
  /* 9px is the floor for mono uppercase: below it the counters start filling in
     on a 2x screen. The 46ch measure is dropped so the trademark sentence gets
     the full column: at 96 characters it needs ~50 per line to fit in two, and
     word breaks put it just over, so it was wrapping to three. Full width gives
     a ~57ch measure, still inside the comfortable range. */
  .legal { font-size: 9px; letter-spacing: .06em; max-width: 100%; }
}
