/* nctype.com — "the specimen is the interface"
 *
 * Hand-authored replacement for the legacy compiled styles.css (which shipped as a
 * 42KB minified artifact with no source in the repo). Loaded INSTEAD of styles.css on
 * pages that opt in via <Site legacy={false}>; every other page still gets the old
 * sheet until it is ported.
 *
 * Direction: David Carson / Ray Gun — brutal scale contrast, broken grid, rotated and
 * overlapping type, photocopier grain. Deliberately concentrated: the masthead, hero
 * and section breaks carry the attitude; the catalog and any commerce surface stay on
 * a hard grid and stay legible. Attitude sells the foundry, the grid sells the fonts.
 *
 * The site is set in the product. Display = NC Kobyla, UI = NC Burrata, both variable.
 */

/* ---------------------------------------------------------------------------
 * 1. The product, used as the site's own typography
 * ------------------------------------------------------------------------- */

@font-face {
  font-family: 'NC Burrata';
  src: url('/fonts/preview/NCBurrata-VF.woff2') format('woff2-variations');
  font-weight: 200 700;
  font-style: oblique 0deg 5deg;
  font-display: swap;
}

@font-face {
  font-family: 'NC Kobyla';
  src: url('/fonts/preview/NCKobyla-VF.woff2') format('woff2-variations');
  font-weight: 200 700;
  font-display: swap;
}

/* Fontina's second axis is `ital` (0-12), NOT `slnt` — so it gets no oblique range
 * here. Declaring `oblique 0deg 9deg` would advertise a slant the font cannot perform,
 * and the browser would fall back to synthesising one. Its italics are selected
 * through font-variation-settings on the `ital` axis instead.
 *
 * Weight floor is 200 per the fvar table, not the 400 the CMS metadata claims. */
@font-face {
  font-family: 'NC Fontina';
  src: url('/fonts/preview/NCFontina-VF.woff2') format('woff2-variations');
  font-weight: 200 700;
  font-style: normal;
  font-display: swap;
}

/* ---------------------------------------------------------------------------
 * 2. Tokens
 * ------------------------------------------------------------------------- */

:root {
  /* Warm near-black rather than #000 — reads as ink on newsprint, not as an OLED void. */
  --paper: #0e0d0b;
  --paper-raised: #161512;
  --ink: #f4f1ea;
  --ink-dim: #8c877c;
  --ink-faint: #46423b;

  /* One hot spot colour, used sparingly and always as a signal (price, live axis,
   * hover). Vermilion over the legacy purple: closer to Ray Gun, and it survives
   * being printed badly, which is the whole aesthetic. */
  --hot: #ff2d00;
  --acid: #c6f000;
  --blue: #1b3fd8;

  --display: 'NC Kobyla', 'Times New Roman', serif;
  --ui: 'NC Burrata', 'Helvetica Neue', Arial, sans-serif;

  /* Fluid scale. The top end is intentionally enormous — the hero is meant to be
   * cropped by the viewport, not to fit inside it. */
  --step-mono: 0.6875rem;
  --step-0: clamp(0.9375rem, 0.9rem + 0.2vw, 1.0625rem);
  --step-1: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
  --step-2: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem);
  --step-3: clamp(2.25rem, 1.5rem + 3.5vw, 4.5rem);
  /* Sized so shorter specimen lines land inside the viewport while longer ones crop.
   * Uniform truncation on every row reads as a bug; varied crop reads as a choice. */
  /* No upper clamp on the two display steps: the page is full-bleed, so these are
   * meant to keep growing with the viewport and keep cropping against its edges. */
  --step-catalog: clamp(3rem, 1rem + 11vw, 18rem);
  --step-hero: clamp(5rem, -2rem + 32vw, 40rem);

  /* Tight — the design wants type running to the edges, not a comfortable margin. */
  --gutter: clamp(1rem, 0.4rem + 1.6vw, 2.25rem);
  --measure: 34ch;
}

/* ---------------------------------------------------------------------------
 * 3. Reset
 * ------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--ui);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: 1.45;
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
h4,
p,
figure,
ul,
ol {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: inherit;
  text-decoration: none;
}

::selection {
  background: var(--hot);
  color: var(--ink);
}

:focus-visible {
  outline: 2px solid var(--acid);
  outline-offset: 3px;
}

/* ---------------------------------------------------------------------------
 * 4. Grain
 *
 * A single fixed feTurbulence layer over the whole document. This is what stops the
 * flat near-black reading as a generic dark-mode background — it gives the page the
 * dirty photocopied surface the rest of the design is pretending to be printed on.
 * pointer-events:none so it never eats a click.
 * ------------------------------------------------------------------------- */

body::after {
  content: '';
  position: fixed;
  inset: -50%;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.055;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------------------
 * 5. Layout primitives
 * ------------------------------------------------------------------------- */

.bleed {
  padding-inline: var(--gutter);
}

/* Full-bleed: no measure cap. Only the gutter holds type off the viewport edge, and
 * the display type is allowed to cross even that. */
.shell {
  max-width: none;
  margin-inline: auto;
}

/* Positioning context for the loose tape scraps, and containment so their rotation
 * never widens the document. */
.section {
  position: relative;
  overflow: clip;
}

.custom__intro {
  position: relative;
}

.eyebrow {
  font-family: var(--ui);
  font-size: var(--step-mono);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* ---------------------------------------------------------------------------
 * 6. Masthead
 * ------------------------------------------------------------------------- */

.main_header {
  position: sticky;
  top: 0;
  z-index: 100;
  /* Plain rgba first: color-mix is Safari 16.2+, and an unsupported value
   * invalidates the declaration outright, leaving the sticky bar transparent. */
  background: rgba(14, 13, 11, 0.88);
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(10px);
}

.main_header > .inner {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gutter);
  max-width: none;
  padding: 0.75rem var(--gutter);
}

.main_header__logo a {
  font-family: var(--ui);
  font-size: var(--step-mono);
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
}

.main_header__nav ul {
  display: flex;
  gap: clamp(1rem, 3vw, 2.5rem);
}

.main_header__nav a {
  font-size: var(--step-mono);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
  transition: color 0.15s;
}

.main_header__nav a:hover {
  color: var(--hot);
}

/* ---------------------------------------------------------------------------
 * 7. Hero
 *
 * The grid-breaking moment. A single word set at --step-hero, allowed to run past the
 * right edge of the viewport (overflow is clipped on body, never scrolled). The
 * metadata slabs are rotated and overlap the letterforms; mix-blend-mode:difference on
 * the hot slab means it inverts whatever it crosses instead of sitting on top of it.
 * ------------------------------------------------------------------------- */

/* Breaks out of the shell's gutter so the specimen word crops against the true viewport
 * edge, then clips its own overflow. Clipping HERE rather than relying on body
 * overflow-x is what keeps the nowrap headline from expanding the document width. */
.hero {
  position: relative;
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  padding-block: clamp(3rem, 8vh, 7rem) clamp(2rem, 6vh, 5rem);
  overflow: clip;
}

/* Toner-streak plate behind the specimen word. The source PNG is black marks on
 * transparent, pre-inverted at build time so the marks read light on the dark paper —
 * no blend mode needed, which matters because .hero__blurb below is already using
 * mix-blend-mode and a second blending layer would fight it.
 *
 * Masked to fade out before it reaches the blurb, for the same reason. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: url('/textures/photocopy-06.webp');
  background-size: cover;
  background-position: 50% 20%;
  background-attachment: fixed;
  opacity: 0.16;
  /* Fades in below the eyebrow row and out above the blurb, so the plate sits behind
   * the specimen word only. Both ends matter: at full bleed the streaks dropped the
   * eyebrow's contrast to unreadable, and at the bottom they fought the blend. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 20%, #000 52%, transparent 80%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 20%, #000 52%, transparent 80%);
}

/* Keep the type above the plate. */
.hero__meta,
.hero__word,
.hero__sub {
  position: relative;
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.5rem;
  margin-bottom: clamp(1rem, 3vh, 2.5rem);
}

.hero__meta .eyebrow:not(:last-child)::after {
  content: ' /';
  color: var(--ink-faint);
}

/* The oversized specimen word. white-space:nowrap + negative margin is what produces
 * the "cropped by the viewport" read at every breakpoint. */
/* ---------------------------------------------------------------------------
 * 7b. Hero collage
 *
 * Pasted-up rather than typeset: letters, cut-out photographs and paper scraps are
 * absolutely positioned by percentage inside a fixed-aspect box, each with its own
 * rotation and z-index so pieces pass both in front of and behind one another.
 * ------------------------------------------------------------------------- */

.collage {
  position: relative;
  width: 100%;
  /* Aspect ratio rather than a height, so the whole composition scales as one unit and
   * the percentage positions stay true at every viewport. Tightened from 16/7 once the
   * photographs were placed: the pieces settled into the upper two thirds and the
   * remainder was dead air between the collage and the blurb. */
  aspect-ratio: 16 / 6.1;
  margin-block: clamp(0.5rem, 2vh, 1.5rem);
}

.collage__letter,
.collage__object,
.collage__scrap {
  position: absolute;
  transform: rotate(var(--rot, 0deg));
  transform-origin: center;
}

.collage__letter {
  display: block;
  line-height: 0.72;
  letter-spacing: -0.03em;
  user-select: none;
}

.collage__object {
  height: auto;
  /* Cut-outs arrive with their own alpha; a hard shadow separates them from whatever
   * they overlap without needing a matte. */
  filter: drop-shadow(0 clamp(2px, 0.6vw, 10px) 0 rgba(0, 0, 0, 0.45));
}

.collage__scrap {
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

/* The accessible name for the hero. The collage letters are decorative once assembled —
 * they do not reliably read as a word to a screen reader — so the real text lives here,
 * visually hidden but present in the document. */
.collage__label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (max-width: 40rem) {
  .collage {
    aspect-ratio: 4 / 5;
  }
}

.hero__word {
  position: relative;
  display: block;
  margin: 0;
  margin-left: -0.06em;
  font-family: var(--display);
  font-size: var(--step-hero);
  font-weight: 700;
  line-height: 0.78;
  letter-spacing: -0.045em;
  white-space: nowrap;
  font-variation-settings: 'wght' var(--hero-wght, 700);
  /* Small negative-ish clearance: the descenders are meant to reach into the blurb
   * below, which stays readable via mix-blend-mode on .hero__blurb. */
  margin-bottom: 0.04em;
}

/* Live weight axis: driven by pointer X from the inline script, so moving across the
 * hero literally interpolates the variable font. This is the one piece of motion that
 * is actually *about* the product rather than decoration. */
.hero__word--live {
  transition: font-variation-settings 0.18s ease-out;
}

/* Deliberately NOT a stacking context (no position/z-index): the blurb inside uses
 * mix-blend-mode to stay readable where it crosses the specimen word, and an isolating
 * ancestor would make it blend against transparency instead of the letterform. */
.hero__sub {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem var(--gutter);
  /* Clearance from the specimen word above: its line-height is 0.78, so descenders
   * hang well past the box and would otherwise collide with the blurb. */
  margin-top: clamp(1.75rem, 5vh, 3.5rem);
}

/* The blurb deliberately overlaps the specimen word's descenders — that collision is
 * the point. `difference` is what keeps it readable through the collision: the text is
 * exactly --ink, so where it crosses the bone letterform it resolves to pure black, and
 * over the near-black paper it stays bone. No knockout box, no extra clearance. */
.hero__blurb {
  max-width: var(--measure);
  font-size: var(--step-1);
  font-weight: 300;
  line-height: 1.28;
  color: var(--ink);
  text-wrap: balance;
  mix-blend-mode: difference;
}

.hero__blurb em {
  font-family: var(--display);
  font-style: normal;
  font-weight: 500;
  color: var(--hot);
}

/* Labels stuck onto the page. Two materials on purpose: --a is a printed vermilion
 * sticker (carries the hot accent), --b is a real strip of masking tape. The tape is a
 * transparent PNG of actual torn tape, stretched to the label's width — it reads as
 * physically applied in a way a CSS rectangle never does. */
.hero__stamp {
  position: absolute;
  z-index: 3;
  padding: 0.3rem 0.6rem;
  font-size: var(--step-mono);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
}

.hero__stamp--a {
  top: clamp(1rem, 4vh, 3.5rem);
  right: var(--gutter);
  transform: rotate(6deg);
  background: var(--hot);
  color: #fff;
}

/* The cursor prompt only makes sense where there is a cursor. On touch it is both
 * untrue and crowding the eyebrow row, so it is dropped rather than reworded — the
 * hover-driven weight axis it advertises does not exist there either. */
@media (hover: none), (pointer: coarse) {
  .hero__stamp--a {
    display: none;
  }
}

/* Sits on the right, crossing the specimen word's lower half. Kept clear of the left
 * column: the blurb lives there, and an overlapping stamp made it unreadable.
 *
 * Real masking tape, stretched to whatever width the label needs — border-image would
 * preserve the torn ends better, but the ends here are irregular in both axes and
 * stretching a 385px strip across a short label distorts them imperceptibly. Extra
 * horizontal padding keeps the text off the torn edges. */
.hero__stamp--b {
  top: 62%;
  right: clamp(2rem, 12vw, 12rem);
  transform: rotate(-4deg);
  /* Generous block padding: the strip's natural aspect is ~3:1, so a wide short label
   * squashes its torn ends. More height keeps them legible as tears. */
  padding: 0.85rem 1.6rem;
  background-image: url('/textures/tape-06.webp');
  background-size: 100% 100%;
  background-repeat: no-repeat;
  color: var(--paper);
}

/* ---------------------------------------------------------------------------
 * 8. Section headings — the "torn" break
 * ------------------------------------------------------------------------- */

.break {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding-top: clamp(2.5rem, 7vh, 5.5rem);
  padding-bottom: clamp(1rem, 2.5vh, 2rem);
}

/* Section titles are display-scale now, not heading-scale, and sit slightly off the
 * horizontal so they never line up with the rules they sit against. */
.break__title {
  font-family: var(--display);
  font-size: clamp(3rem, 1rem + 7vw, 9rem);
  font-weight: 700;
  line-height: 0.82;
  letter-spacing: -0.05em;
  transform: rotate(-1.5deg);
  transform-origin: left bottom;
}

/* Torn strip tucked under the section title, bleeding off the left edge. */
.break__title::after {
  content: '';
  position: absolute;
  left: calc(var(--gutter) * -1);
  bottom: -0.35em;
  width: clamp(14rem, 34vw, 40rem);
  height: 0.42em;
  background-image: url('/textures/torn-04.webp');
  background-size: 100% 100%;
  background-repeat: no-repeat;
  opacity: 0.85;
  transform: rotate(0.8deg);
  z-index: -1;
}

.break__count {
  font-size: var(--step-mono);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--hot);
  transform: translateY(-2.2em) rotate(-8deg);
}

/* ---------------------------------------------------------------------------
 * 9. Catalog
 *
 * Restrained on purpose. This is the part that has to sell fonts, so it keeps a hard
 * grid, real hit targets and honest hierarchy. The only theatrics: the specimen line
 * is enormous, set in the font it is selling, and its weight axis animates on hover.
 * ------------------------------------------------------------------------- */

.catalog__item {
  position: relative;
}

/* Same containment as the hero: the specimen inside is nowrap and deliberately wider
 * than the viewport on the longer lines, so each row crops it rather than letting it
 * push the document sideways. */
/* Full-bleed so the specimen inside can be pushed past BOTH viewport edges and crop
 * against them. Clips its own overflow so that never widens the document. */
.catalog__link {
  display: block;
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  padding-block: clamp(1.5rem, 4vh, 3rem);
  transition: background-color 0.25s;
  overflow: clip;
}

.catalog__link:hover,
.catalog__link:focus-visible {
  background: var(--paper-raised);
}

/* The specimen. Cropped like the hero, set in the product, weight-animated on hover. */
.catalog__specimen {
  display: block;
  margin: 0 0 0.15em -0.04em;
  font-size: var(--step-catalog);
  font-weight: 400;
  line-height: 0.85;
  letter-spacing: -0.035em;
  white-space: nowrap;
  font-variation-settings: 'wght' var(--row-wght, 400);
  transform: rotate(var(--row-tilt, 0deg));
  transform-origin: left center;
  transition: font-variation-settings 0.45s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Per-row variation, driven off nth-child so it holds for any number of fonts. Each
 * row gets a different resting weight, tilt and horizontal offset — uniform rows read
 * as a table, varied ones read as a spread. The metadata strip below stays on grid,
 * which is what keeps this scannable while the specimens misbehave.
 *
 * --row-hover is always the OPPOSITE end of the axis from --row-wght, alternating
 * direction row to row. An earlier version sent every row toward the maximum, which
 * meant a row already resting at its maximum had nothing to animate to and sat dead
 * under the cursor. */
.catalog__item:nth-child(3n + 1) .catalog__specimen {
  --row-wght: var(--wght-hi, 700);
  --row-hover: var(--wght-lo, 200);
  --row-tilt: -0.6deg;
  margin-left: -0.06em;
}

.catalog__item:nth-child(3n + 2) .catalog__specimen {
  --row-wght: var(--wght-lo, 200);
  --row-hover: var(--wght-hi, 700);
  --row-tilt: 0.9deg;
  /* Crops on the leading edge too, not just the trailing one — but only just. Deeper
   * than this and the first letter loses enough of its skeleton to read as a typo
   * ("Dreamy" became "Oreamy") rather than as a crop. */
  margin-left: -0.09em;
}

.catalog__item:nth-child(3n + 3) .catalog__specimen {
  --row-wght: var(--wght-hi, 700);
  --row-hover: var(--wght-lo, 200);
  --row-tilt: -1.4deg;
  margin-left: 0.08em;
}

/* Hover drives each row to the far end of its own axis — heavy rows go light, light
 * rows go heavy — so every specimen traverses the full range under the cursor. */
.catalog__link:hover .catalog__specimen,
.catalog__link:focus-visible .catalog__specimen {
  font-variation-settings: 'wght' var(--row-hover, 700);
}

/* Metadata strip: name / styles / axes / price on one honest baseline. */
.catalog__meta {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr) auto;
  align-items: baseline;
  gap: 0.75rem var(--gutter);
}

.catalog__name {
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1;
}

.catalog__styles {
  font-size: var(--step-0);
  font-weight: 300;
  line-height: 1.35;
  color: var(--ink-dim);
}

/* Each style name previews itself — its own weight and slant. The whole reason a
 * designer trusts a foundry site is that nothing is faked with synthetic bold. */
.catalog__styles span {
  color: var(--ink);
}

.catalog__styles span::after {
  content: ', ';
  color: var(--ink-faint);
}

.catalog__styles span:last-child::after {
  content: '';
}

.catalog__price {
  justify-self: end;
  text-align: right;
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1;
  color: var(--hot);
  font-variant-numeric: tabular-nums;
}

.catalog__price small {
  display: block;
  font-size: var(--step-mono);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* ---------------------------------------------------------------------------
 * 8b. Inverted section
 *
 * A whole section flipped to bone with dark type. This is the page's main contrast
 * event — without at least one inversion the design is a single dark field end to end,
 * which is not what the reference actually looks like. Redeclaring the ink/paper
 * tokens rather than restyling each child keeps every descendant correct for free.
 * ------------------------------------------------------------------------- */

.section--invert {
  --paper: #f4f1ea;
  --paper-raised: #e8e3d8;
  --ink: #12100d;
  --ink-dim: #5f594e;
  --ink-faint: #b7b0a1;
  background: var(--paper);
  color: var(--ink);
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  padding-bottom: clamp(2rem, 6vh, 4rem);
}

/* ---------------------------------------------------------------------------
 * 9a. Glyph macro crops
 *
 * A single character set enormously larger than its container, so the container frames
 * a detail of the letterform rather than the whole glyph. Live type, not an image:
 * sharp at any size and driven off the family's real axes (scrubbed on scroll by the
 * inline script). Purely decorative — the same characters exist as text elsewhere.
 * ------------------------------------------------------------------------- */

.glyph {
  position: relative;
  height: clamp(12rem, 42vh, 28rem);
  margin-inline: calc(var(--gutter) * -1);
  overflow: clip;
  z-index: 0;
}

/* Zoom: 62vw was smaller than the band itself and read as a stray shape; 150vw was so
 * deep that a Q became a plain rectangle. ~95vw crops hard into the letterform while
 * the junctions stay readable as type — which only holds because the characters were
 * re-picked for mid-height interest (&, R, ae) rather than tails and descenders. */
.glyph__char {
  position: absolute;
  display: block;
  left: 50%;
  top: 50%;
  font-size: clamp(24rem, 95vw, 88rem);
  line-height: 0.7;
  /* The letterform itself stays clean: the print texture lives on the colour field
   * behind it instead. Filling the glyph was the first approach and it fought the
   * crop — the streaks broke up the very strokes the close-up exists to show. */
  color: var(--glyph-ink, var(--ink));
  z-index: 1;
  opacity: 0.95;
  /* Offsets are applied as a second translate so the percentages resolve against the
   * glyph's own box, not the band's — which is what makes the framing predictable. */
  transform: translate(-50%, -50%) translate(var(--gx, 0%), var(--gy, 0%));
  /* No transition: the value is driven by scroll position and must track it exactly
   * rather than chase it. */
  font-variation-settings: 'wght' 400;
  will-change: font-variation-settings;
}

/* Each crop frames a different part of its character, so three bands on one page do
 * not read as the same effect repeated. */
/* Offsets push each glyph partly past one edge so the crop is horizontal as well as
 * vertical, and so the three bands sit at different points across the page.
 *
 * Vertical offsets stay near zero: these characters carry their interest at mid-height,
 * so centring them is what puts the junctions in frame. Large --gy shifts were only
 * needed while the set included a Q and a g, whose details sat below the baseline. */
/* Each band is a flat colour field, cycling so no two adjacent rows share one. The
 * glyph's texture fill reads differently on each, which is the point: same mechanism,
 * three distinct results. */
/* The print texture sits on the field, screened over the flat colour: the plate's white
 * marks come through as bare paper and its black areas leave the colour untouched, so
 * the field reads as badly-printed ink. Viewport-anchored, so all three bands are
 * windows onto one continuous plate rather than three scaled copies. */
.glyph::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url('/textures/photocopy-07.webp');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-attachment: fixed;
  /* overlay, not screen: screening a white-marks plate over the field pushed it toward
   * white and desaturated it — the vermilion drifted to salmon and the acid went pale,
   * which defeats the point of having colour fields at all. overlay keeps the hue and
   * pushes contrast both ways instead. */
  mix-blend-mode: overlay;
  opacity: 0.65;
}

/* Glyph ink is set per field for contrast — bone on the two dark fields, paper-dark on
 * the acid, which is far too light to carry bone type. */
.glyph--1 {
  background: var(--hot);
  --glyph-ink: #f4f1ea;
}

.glyph--2 {
  background: var(--acid);
  --glyph-ink: #0e0d0b;
}

.glyph--3 {
  background: var(--blue);
  --glyph-ink: #f4f1ea;
}

.glyph--1 .glyph__char {
  --gx: -30%;
  --gy: 2%;
}

.glyph--2 .glyph__char {
  --gx: 24%;
  --gy: -4%;
}

.glyph--3 .glyph__char {
  --gx: -10%;
  --gy: 0%;
}

@media (max-width: 40rem) {
  .glyph {
    height: clamp(7rem, 22vh, 12rem);
  }
}

/* ---------------------------------------------------------------------------
 * 9b. Plate — full-bleed torn strip between sections
 *
 * Purely decorative (aria-hidden in the markup). Breaks the shell gutter so it runs
 * edge to edge, and masks to transparent at both ends so it reads as a strip torn out
 * of a printed sheet rather than a banner with hard borders.
 * ------------------------------------------------------------------------- */

/* Two stacked layers. The opacity lives on ::before rather than on .plate itself —
 * opacity on the parent would cascade into ::after and mute the torn paper too. */
.plate {
  position: relative;
  /* Tall enough that the torn strip inside keeps something near its native 4.6:1
   * aspect. Squeezed flatter, the tear detail smooths out and it reads as a painted
   * swipe rather than as paper. */
  height: clamp(6rem, 16vh, 11rem);
  margin-block: clamp(2rem, 6vh, 4rem);
  margin-inline: calc(var(--gutter) * -1);
  /* The torn strip inside is inset -2% and rotated so it always spans the full width;
   * without clipping here that bleed pushes the document 30px wider than the viewport. */
  overflow: clip;
}

.plate::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url('/textures/photocopy-07.webp');
  /* Viewport-anchored like the glyph fills, so every photocopy layer on the page is a
   * window onto one continuous plate instead of each scaling or tiling its own copy.
   * Tiling this produced visible repeat seams straight across the divider. */
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-attachment: fixed;
  opacity: 0.4;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}

/* A strip of torn white paper laid across the photocopy band. This is the loudest
 * moment on the page and the only place the value scale inverts — white paper on the
 * near-black — which is what makes the section break read as a physical edit rather
 * than a horizontal rule. Slight rotation so it never aligns with the grid. */
.plate::after {
  content: '';
  position: absolute;
  left: -2%;
  right: -2%;
  top: 50%;
  height: 78%;
  background-image: url('/textures/torn-02.webp');
  background-size: 100% 100%;
  background-repeat: no-repeat;
  transform: translateY(-50%) rotate(-1.2deg);
  opacity: 0.9;
}

/* ---------------------------------------------------------------------------
 * 9c. Loose tape
 *
 * Decorative scraps stuck onto the page (aria-hidden in the markup, pointer-events
 * none so they never intercept a click). Positioned as percentages of their section
 * so they drift with the layout instead of pinning to a breakpoint.
 * ------------------------------------------------------------------------- */

.tape {
  position: absolute;
  z-index: 4;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

/* Crossed strips, as if holding the section down. */
.tape--x {
  top: clamp(1rem, 4vh, 4rem);
  right: clamp(1rem, 6vw, 7rem);
  width: clamp(4rem, 8vw, 8rem);
  aspect-ratio: 1;
  background-image: url('/textures/tape-08.webp');
  transform: rotate(12deg);
  opacity: 0.92;
}

/* Vertical strip taping the section rule down. Kept well inside the section box: the
 * section clips its own overflow, so anything hung past the edge is simply cut away
 * rather than bleeding off as intended. Also kept clear of the price column. */
.tape--strip {
  top: clamp(1rem, 3vh, 3rem);
  right: clamp(4rem, 12vw, 14rem);
  width: clamp(2.25rem, 3.5vw, 4rem);
  height: clamp(7rem, 14vh, 11rem);
  background-image: url('/textures/tape-03.webp');
  transform: rotate(-6deg);
  opacity: 0.85;
}

/* A torn scrap wedged under the custom-fonts intro. */
.tape--scrap {
  bottom: -1.5rem;
  left: clamp(1rem, 22vw, 26rem);
  width: clamp(7rem, 16vw, 15rem);
  aspect-ratio: 316 / 173;
  background-image: url('/textures/torn-05.webp');
  transform: rotate(-7deg);
  opacity: 0.5;
}

@media (max-width: 40rem) {
  /* At phone width these stop reading as scraps and start reading as clutter over
   * the content they are meant to decorate. */
  .tape--strip,
  .tape--scrap {
    display: none;
  }
}

/* ---------------------------------------------------------------------------
 * 10. Custom-fonts section
 * ------------------------------------------------------------------------- */

.custom__intro {
  max-width: 52ch;
  margin-bottom: clamp(1.5rem, 5vh, 3rem);
  font-size: var(--step-1);
  font-weight: 300;
  line-height: 1.35;
  color: var(--ink);
  text-wrap: pretty;
}

.custom__intro a {
  color: var(--hot);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 2px;
}

.custom__intro a:hover {
  background: var(--hot);
  color: var(--paper);
  text-decoration: none;
}

.custom-slider {
  margin-bottom: clamp(2rem, 6vh, 4rem);
}

.custom-slider ul {
  display: flex;
  gap: 1px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}

.custom-slider li {
  flex: 0 0 min(38rem, 82vw);
  scroll-snap-align: start;
}

.custom-slider img {
  width: 100%;
  filter: grayscale(1) contrast(1.15);
  transition: filter 0.3s;
}

.custom-slider li:hover img {
  filter: none;
}

.custom-slider h3 {
  margin-top: 0.6rem;
  font-size: var(--step-mono);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.custom-slider--right {
  margin-left: auto;
}

/* ---------------------------------------------------------------------------
 * 10b. Font detail page
 *
 * This page keeps tester.css for the interactive islands (tester, glyph table, buy
 * form) and takes only its page chrome from here. `.inner` is the one piece it still
 * needed from the legacy sheet — without it every section ran flush to the viewport
 * edge, since styles.css is no longer loaded.
 * ------------------------------------------------------------------------- */

.inner {
  padding-inline: var(--gutter);
}

.font-detail {
  padding-block: clamp(1rem, 3vh, 2rem) clamp(3rem, 8vh, 6rem);
}

/* Sticky title bar: name, in-page links, and the buy CTA. */



.preview__submenu {
  display: flex;
  gap: 1.25rem;
}

.preview__submenu a {
  font-size: var(--step-mono);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.preview__submenu a:hover {
  color: var(--hot);
}

/* The buy CTA is styled in tester.css, which owns the component and loads after
 * this sheet — duplicating it here just created two sources of truth. */


/* About / styles / trials, beside the buy island. */
.font-detail__below {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
  margin-top: clamp(2.5rem, 7vh, 5rem);
}

.font-detail__copy {
  max-width: 62ch;
  color: var(--ink);
}

.font-detail__copy p {
  margin-bottom: 1rem;
  color: var(--ink-dim);
  line-height: 1.55;
}

.font-detail__copy a {
  color: var(--hot);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.font-detail__copy .h1,
.font-detail__copy .h2 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.03em;
  margin-block: clamp(2rem, 5vh, 3rem) 0.75rem;
}

.font-detail__copy .h1 {
  font-size: clamp(1.75rem, 1rem + 2.5vw, 3.25rem);
}

.font-detail__copy .h2 {
  font-size: clamp(1.35rem, 1rem + 1.4vw, 2.25rem);
}

.font-detail__styles {
  columns: 2;
  column-gap: 2rem;
  margin-block: 0.75rem 1.5rem;
  font-size: var(--step-0);
  color: var(--ink-dim);
}

.font-detail__styles li {
  break-inside: avoid;
  padding-block: 0.15rem;
}

@media (max-width: 60rem) {
  .font-detail__below {
    grid-template-columns: minmax(0, 1fr);
  }

  .font-detail__styles {
    columns: 1;
  }
}

/* ---------------------------------------------------------------------------
 * 10c. Font detail — collage masthead + specimen band
 *
 * The editorial top of the page gets the full treatment; everything below the tester
 * sections (about, styles, buy) deliberately stays on a calm grid. Attitude sells the
 * foundry, but this is the page that takes the money.
 *
 * The whole page inherits --ui/--display from the family it sells (set inline per
 * font in [slug].astro), so every rule here renders in the product.
 * ------------------------------------------------------------------------- */

.fd-masthead {
  position: relative;
  overflow: clip;
  padding-bottom: clamp(3rem, 9vh, 7rem);
}

.fd-masthead__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem var(--gutter);
  padding-block: clamp(0.75rem, 2vh, 1.5rem);
}

/* The family name at display scale, allowed to run past the right edge like the
 * homepage hero. Set in itself — this is the page's primary specimen. */
/* Cut-outs sit in the masthead box, interleaved with the name by z-index. */
.fd-masthead__cut {
  position: absolute;
  height: auto;
  transform: rotate(var(--rot, 0deg));
  transform-origin: center;
  pointer-events: none;
  filter: drop-shadow(0 clamp(2px, 0.5vw, 8px) 0 rgba(0, 0, 0, 0.45));
}

.fd-masthead__name {
  position: relative;
  z-index: 5;
  margin: 0;
  margin-left: -0.04em;
  padding-inline: var(--gutter);
  font-family: var(--display);
  font-size: clamp(3.5rem, 15vw, 16rem);
  font-weight: 700;
  line-height: 0.82;
  letter-spacing: -0.045em;
  white-space: nowrap;
  transform: rotate(-1.2deg);
  transform-origin: left center;
}

.fd-masthead__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.5rem;
  margin-top: clamp(0.75rem, 2vh, 1.5rem);
}

.fd-masthead__price {
  margin-left: auto;
  font-size: clamp(1.5rem, 1rem + 1.6vw, 2.75rem);
  font-weight: 700;
  line-height: 1;
  color: var(--hot);
  font-variant-numeric: tabular-nums;
}

.fd-masthead__price small {
  display: block;
  font-size: var(--step-mono);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.fd-masthead__tape {
  top: 8%;
  right: 6%;
  width: clamp(3.5rem, 7vw, 7rem);
  aspect-ratio: 1;
  opacity: 0.9;
}

/* Full-bleed specimen band on a colour field, cropping at both edges. */
.fd-specimen {
  margin-block: clamp(1.5rem, 4vh, 3rem);
  padding-block: clamp(1rem, 3vh, 2rem);
  background: var(--hot);
  overflow: clip;
}

.fd-specimen__line {
  display: block;
  margin-left: -0.03em;
  padding-inline: var(--gutter);
  font-family: var(--display);
  font-size: clamp(2.5rem, 9vw, 9rem);
  font-weight: 400;
  line-height: 0.95;
  letter-spacing: -0.03em;
  white-space: nowrap;
  color: #fff;
}

@media (max-width: 40rem) {
  .fd-masthead__tape {
    display: none;
  }

  .fd-masthead__price {
    margin-left: 0;
  }
}

/* ---------------------------------------------------------------------------
 * 10d. Prose pages (info, licensing)
 *
 * These are CMS-authored long-form documents — licence terms, FAQs, the EULA. The
 * display treatment used elsewhere would actively hurt them, so the attitude is
 * confined to the page title and the section nav; the body is set for reading:
 * one column, a real measure, generous leading.
 * ------------------------------------------------------------------------- */

.prose-page {
  padding-block: clamp(1.5rem, 4vh, 3rem) clamp(3rem, 8vh, 6rem);
}

/* Section nav — the run of links above the title. */
.text__submenu {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin-bottom: clamp(2rem, 6vh, 4rem);
  font-size: var(--step-mono);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.text__submenu li {
  color: var(--hot);
}

.text__submenu a {
  color: var(--ink-dim);
  transition: color 0.15s;
}

.text__submenu a:hover {
  color: var(--ink);
}

.prose {
  max-width: 62ch;
}

/* The page title, and the only display-scale type on these pages. */
.prose h1,
.prose .h1 {
  font-family: var(--display);
  font-size: clamp(2.5rem, 1rem + 6vw, 7rem);
  font-weight: 700;
  line-height: 0.85;
  letter-spacing: -0.04em;
  margin-bottom: clamp(1.5rem, 4vh, 2.5rem);
  max-width: 18ch;
}

.prose h2,
.prose .h2 {
  font-family: var(--display);
  font-size: clamp(1.5rem, 1rem + 1.8vw, 2.5rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  margin-block: clamp(2.5rem, 6vh, 4rem) 0.75rem;
}

.prose h3,
.prose .h3,
.prose h4 {
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.25;
  margin-block: clamp(1.75rem, 4vh, 2.5rem) 0.4rem;
  color: var(--ink);
}

/* The lede: the first paragraph after the title carries the page's argument, so it
 * is set larger than the body that follows. */
.prose h1 + p,
.prose .h1 + p {
  font-size: var(--step-1);
  font-weight: 300;
  line-height: 1.35;
  color: var(--ink);
  margin-bottom: clamp(2rem, 5vh, 3rem);
}

/* Brighter than --ink-dim, which measured 5.43:1 against the page — fine for a caption
 * but not for the licence terms and EULA these pages carry, which people have to read
 * in full. This clears AAA for body text. */
.prose p,
.prose li {
  font-size: var(--step-0);
  line-height: 1.6;
  color: #c9c4b9;
  margin-bottom: 1rem;
}

.prose strong {
  color: var(--ink);
  font-weight: 600;
}

.prose a {
  color: var(--hot);
  text-decoration: underline;
  text-underline-offset: 0.16em;
  text-decoration-thickness: 1px;
}

.prose a:hover {
  background: var(--hot);
  color: var(--paper);
  text-decoration: none;
}

.prose ul,
.prose ol {
  margin-block: 0.5rem 1.5rem;
  padding-left: 1.25rem;
}

.prose ul li {
  list-style: none;
  position: relative;
  margin-bottom: 0.4rem;
}

/* Vermilion marker rather than a bullet glyph, so lists read as part of the design. */
.prose ul li::before {
  content: '';
  position: absolute;
  left: -1.25rem;
  top: 0.65em;
  width: 0.5rem;
  height: 1px;
  background: var(--hot);
}

.prose ol {
  list-style: decimal;
}

.prose ol li {
  margin-bottom: 0.4rem;
}

.prose hr {
  border: 0;
  height: 1px;
  background: var(--ink-faint);
  margin-block: clamp(2rem, 5vh, 3rem);
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  margin-block: 1.5rem;
  font-size: var(--step-0);
}

.prose th,
.prose td {
  text-align: left;
  padding: 0.6rem 0.75rem 0.6rem 0;
  vertical-align: top;
}

.prose th {
  font-weight: 600;
  color: var(--ink);
  text-transform: uppercase;
  font-size: var(--step-mono);
  letter-spacing: 0.14em;
}

.prose td {
  color: var(--ink-dim);
}

/* ---------------------------------------------------------------------------
 * 10e. Buttons + transactional pages
 *
 * The Stripe cancel/success landings. Short, high-stakes moments — someone has just
 * paid, or just backed out — so they stay plain and unambiguous. The only expressive
 * element is the CTA.
 * ------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.85rem 1.5rem;
  background: var(--hot);
  /* Dark label, not white: white on vermilion is 3.73:1, which only passes AA for
   * large text, and these labels are 11px. Paper-dark gets 5.26:1. */
  color: var(--paper);
  font-size: var(--step-mono);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transform: rotate(-1deg);
  transition: transform 0.15s;
}

.btn:hover {
  transform: rotate(-1deg) scale(1.04);
}

/* Secondary: outlined, for the lower-priority action on the same page. */
.btn--ghost {
  background: transparent;
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink-faint);
}

.btn--ghost:hover {
  color: var(--hot);
  box-shadow: inset 0 0 0 1px var(--hot);
}

/* Inside .prose, `a` colour (specificity 0,1,1) beats `.btn` (0,1,0) — the primary
 * button rendered as vermilion text on a vermilion block, i.e. an invisible label.
 * These re-assert the button's own colours at matching specificity. */
.prose .btn,
.prose .btn:hover {
  color: var(--paper);
  text-decoration: none;
}

.prose .btn--ghost {
  color: var(--ink);
}

.prose .btn--ghost:hover {
  color: var(--hot);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: clamp(1.5rem, 4vh, 2.5rem);
}

/* Order reference on the success page — tabular so a support agent can read it back
 * over the phone without transposing characters. */
.order-ref {
  display: inline-block;
  margin-block: 0.5rem 1.5rem;
  padding: 0.6rem 1rem;
  background: var(--paper-raised);
  box-shadow: inset 0 0 0 1px var(--ink-faint);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  color: var(--ink);
}

.order-ref span {
  display: block;
  font-size: var(--step-mono);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* ---------------------------------------------------------------------------
 * 11. Footer
 * ------------------------------------------------------------------------- */

.site-footer {
  margin-top: clamp(3rem, 10vh, 8rem);
  padding-block: clamp(2rem, 6vh, 4rem) 2rem;
}

.footer--nonsticky {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1.5rem var(--gutter);
  font-size: var(--step-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.footer--nonsticky a {
  color: var(--ink);
}

.footer--nonsticky a:hover {
  color: var(--hot);
}

/* ---------------------------------------------------------------------------
 * 12. Responsive
 * ------------------------------------------------------------------------- */

@media (max-width: 60rem) {
  .catalog__meta {
    grid-template-columns: 1fr auto;
  }

  .catalog__styles {
    grid-column: 1 / -1;
    order: 3;
  }

  .hero__stamp--b {
    left: auto;
    right: 18%;
  }
}

@media (max-width: 40rem) {
  /* --a is also hidden by the (hover: none) rule above, but narrow viewports are
   * overwhelmingly touch and it crowds the eyebrow row here regardless, so the width
   * rule carries it independently rather than relying on pointer detection. */
  .hero__stamp--a,
  .hero__stamp--b {
    display: none;
  }

  .break__count {
    transform: translateY(-0.6em) rotate(-8deg);
  }
}

/* ---------------------------------------------------------------------------
 * 13. Reduced motion
 *
 * Everything expressive here is a transition or a pointer-driven axis, so honouring
 * the preference is just switching them off. The layout itself carries the design.
 * ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}


/* ---------------------------------------------------------------------------
 * Fallbacks for older engines
 *
 * `overflow: clip` is Safari 16+. Everything below crops content that is
 * intentionally wider than its box (nowrap specimens, oversized glyphs, rotated
 * scraps). Where clip is unsupported none of it crops, the document grows
 * sideways and the layout collapses over the content beneath it.
 * ------------------------------------------------------------------------- */

@supports not (overflow: clip) {
  .hero,
  .catalog__link,
  .section,
  .glyph,
  .plate,
  .fd-masthead,
  .fd-specimen,
  .collage {
    overflow: hidden;
  }

  /* `hidden` makes an ancestor a scroll container, which disables sticky inside it.
   * Pin the CTA rather than let it scroll away mid-masthead. */
  .font-detail__cta {
    position: static;
  }
}
