/* =========================================================================
   Fleet Street Books — holding page
   Type system per FSB typography spec. Tokens only; no off-scale values.
   Font faces live in fonts/fonts.css, linked ahead of this file.
   ========================================================================= */

/* --- Tokens ------------------------------------------------------------ */

:root {
  /* Families — two, as specified. One weight each. */
  --font-display: "FSB Display", "FSB Display Fallback", Georgia, "Times New Roman", serif;
  --font-mono: "Pitch", "Pitch Fallback", "Courier New", Courier, monospace;

  /* Scale (fluid, rem-based) */
  --type-display: clamp(2.75rem, 6vw + 1rem, 5.5rem);
  --type-h1:      clamp(2rem, 4vw + 0.5rem, 3.5rem);
  --type-h2:      clamp(1.5rem, 2.5vw + 0.5rem, 2.25rem);
  --type-h3:      clamp(1.25rem, 1.5vw + 0.5rem, 1.5rem);
  --type-body-lg: clamp(1.125rem, 0.5vw + 1rem, 1.25rem);
  --type-body:    1.125rem;
  --type-small:   0.9375rem;
  --type-caption: 0.8125rem;

  /* Line height */
  --lh-display: 1;
  --lh-h1: 1.05;
  --lh-h2: 1.1;
  --lh-h3: 1.25;
  --lh-body-lg: 1.5;
  --lh-body: 1.55;
  --lh-small: 1.5;
  --lh-caption: 1.4;

  /* Pitch is set tighter than the spec's 1.5 body steps, by direction. 1.2 is
     effectively the font's own natural line spacing — its metrics give 1.22
     (hhea 900/-320/0; OS/2 typo 663/-238/319). Applies to everything in Pitch;
     the display serif keeps --lh-display. */
  --lh-mono: 1.2;

  /* Letter-spacing: default (normal) everywhere, by direction. The spec's
     optical tracking is deliberately not applied on this page — see CLAUDE.md.
     Do not reintroduce it without asking. */

  /* Measure */
  --measure-display: 30ch;
  --measure-heading: 45ch;
  --measure-body: 65ch;

  /* Spacing — one scale, used everywhere.
     Upper steps interpolate between adjacent steps of the same scale, so the
     rhythm compresses with the type instead of drifting apart on small screens.
     Endpoints are always on-scale. */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs:  0.75rem;
  --space-s:   1rem;
  --space-m:   1.5rem;
  --space-l:   clamp(1.5rem, 2vw + 0.5rem, 2.5rem);
  --space-xl:  clamp(2.5rem, 3vw + 0.5rem, 4rem);
  --space-2xl: clamp(4rem, 4vw + 1rem, 6rem);

  /* Inline page padding, as a token because --display-fit below needs it. */
  --page-pad: var(--space-m);

  /* Ink */
  --paper: #faf4ed;
  --ink: #002bad;

  /* --- Lockup proportions -----------------------------------------------
     The mark and "Coming soon" are sized as multiples of the address, not by
     clamps of their own. Three independent clamps hit three different floors
     on the way down, so the hierarchy collapsed on small screens: at 1512px
     the heading was 3.37x the address, at 390px only 1.87x. Tying both to
     --fs-mono holds the relationship at every width.

     The ratios are the proportions measured on a 14" MacBook Pro (1512px CSS
     wide), where all three tokens sit at their clamp maxima:
       address 20px | heading 67.32px = 3.366x | mark 67.55px = 3.378x

     To resize the lockup as a whole, move both ratios. To change one against
     the other, move one. */
  --display-ratio: 3.366;
  --mark-ratio: 3.378;

  /* "Coming soon" is 5.21x its font-size wide in this face. Below ~380px the
     ratio-derived size would break it onto two lines, so cap it at what fits
     the column. 5.6 rather than 5.21 leaves ~7% headroom for a face swap —
     the licensed cut may set slightly wider. */
  --display-fit: calc((100vw - 2 * var(--page-pad)) / 5.6);

  --type-display-set: min(
      calc(var(--fs-mono) * var(--display-ratio)), var(--display-fit));
  --mark-size-set: calc(var(--fs-mono) * var(--mark-ratio));

  /* The eye reads a geometrically centred block as sitting slightly low, so
     the lockup is lifted above true centre by this much. Applied as extra
     padding at the foot (twice the lift, since the grid re-centres in what is
     left) rather than a transform, so it can never push content off-screen on
     a short viewport. */
  --optical-lift: 4svh;

  /* --- Optical vertical rhythm -------------------------------------------
     The four blocks are spaced by a single gap measured between what the eye
     actually reads as their edges — the logo's frame, cap-height at the top of
     a text block, baseline at the bottom — not between CSS boxes. A box carries
     half-leading and the slack between cap-height and ascender, so equal
     margins produce visibly unequal gaps.

     The offsets below are derived from each face's own metrics:

       display  hhea 944/-258, cap 660, line-height 1.0
                -> cap sits 0.183em below box top; baseline 0.157em above bottom
       Pitch    hhea 900/-320, cap 570, line-height 1.2
                -> cap sits 0.320em below box top; baseline 0.310em above bottom

     Each margin subtracts the ink offsets on either side of it, so each gap
     resolves to its target exactly: --gap-optical above "Coming soon",
     --gap-address above the address. They stay correct at every viewport
     because the offsets are in em and track their own element's font-size. If
     a block is ever added below the address, its margin needs the mono
     baseline offset too: calc(var(--fs-mono) * 0.310). */
  --gap-base: var(--space-xl);

  /* Gap opened 10% beyond the scale step, by direction. Same pattern as
     the lockup ratios: return --gap-scale to 1 to go back to the plain step. --gap-base is what the short-viewport query overrides, so
     the increase carries through there too. */
  --gap-scale: 1.21;   /* two 10% increases, 1.1 x 1.1 */
  --gap-optical: calc(var(--gap-base) * var(--gap-scale));

  /* The address sits 10% further below "Coming soon" than the mark sits above
     it, by direction — the two gaps are deliberately no longer equal. Set
     --gap-address to var(--gap-optical) to even them up again. Still measured
     optically: the margin subtracts the same ink offsets either side. */
  --gap-address: calc(var(--gap-optical) * 1.1);

  --fs-display: var(--type-display-set);
  --fs-mono: var(--type-body-lg);

  --display-cap-top:        calc(var(--fs-display) * 0.183);
  --display-baseline-btm:   calc(var(--fs-display) * 0.157);
  --mono-cap-top:           calc(var(--fs-mono) * 0.320);

  /* Minimum comfortable tap target. Not a type value, but it belongs in the
     token set rather than loose in a rule. */
  --touch-target: 44px;

  /* Rollover underline, set to the weight of the Pitch glyphs themselves:
     the stem measures 75/1000 em (CFF StdVW 74; stems in "l" and "i" are 75).
     Kris Sowersby set the font's own post.underlineThickness to 75 as well, so
     `from-font` below resolves to the same number — it is used in preference
     because it tracks the metric if the face is ever swapped. In em, so it
     scales with the type. */
  --rule: 0.075em;
}

/* --- Reset ------------------------------------------------------------- */

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

html {
  font-size: 100%; /* never overridden — scaling lives in the clamps */
  -webkit-text-size-adjust: 100%;
}

body,
h1,
p,
address,
figure {
  margin: 0;
}

/* --- Page -------------------------------------------------------------- */

body {
  min-height: 100vh;  /* fallback for browsers without svh (iOS < 15.4) */
  min-height: 100svh;
  background-color: var(--paper);
  color: var(--ink);
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--type-body);
  line-height: var(--lh-mono);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

.holding {
  min-height: 100vh;  /* fallback, as above */
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding-block: var(--space-2xl) calc(var(--space-2xl) + var(--optical-lift) * 2);
  padding-inline: var(--page-pad);
}

.holding__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-inline-size: var(--measure-body);
}

/* --- Mark -------------------------------------------------------------- */

.mark {
  line-height: 0;
}

.mark svg {
  display: block;
  block-size: auto;

  /* The frame is a 24-unit stroke on a 753-unit grid — at the sizes this mark
     is used, that is under two device pixels. The geometry is exactly
     symmetric, but --mark-size-set is fluid and lands on a fractional pixel at
     most viewports (60.1016px at 1280, say). A box of fractional size cannot put
     both of its edges on the device grid: one side falls on a whole pixel and
     renders crisp, the opposite side falls mid-pixel, spreads across two, and
     reads visibly thinner. That is why the bottom and right looked light.

     Rounding the rendered size to a whole pixel makes opposite edges share the
     grid, so all four sides carry the same weight. The unrounded value is
     declared first as a fallback for browsers without round(). */
  inline-size: var(--mark-size-set);
  inline-size: round(var(--mark-size-set), 1px);
}

/* --- Display ----------------------------------------------------------- */

.display {
  margin-block-start: max(0px, calc(var(--gap-optical) - var(--display-cap-top)));
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--type-display-set);
  line-height: var(--lh-display);
  max-inline-size: var(--measure-display);
  text-wrap: balance;
}

/* --- Contact ----------------------------------------------------------- */

.contact {
  margin-block-start: max(0px, calc(
      var(--gap-address) - var(--display-baseline-btm) - var(--mono-cap-top)));
  font-style: normal;
  font-size: var(--fs-mono);
  line-height: var(--lh-mono);
  max-inline-size: var(--measure-body);
}

/* --- Links --------------------------------------------------------------
   The email address is the only link on the page: set plain, rule on
   rollover. */

.contact a {
  color: inherit;
  text-decoration: none;
  overflow-wrap: anywhere;

  /* The default grey tap flash reads as a bug against this palette; :active
     below provides the feedback instead. */
  -webkit-tap-highlight-color: transparent;

  /* Grow the hit area to --touch-target without disturbing the layout:
     vertical padding on an inline box extends the border box that receives
     the tap, but does not affect line height or push anything around. */
  padding-block: max(0px, calc((var(--touch-target) - 1.2em) / 2));
}

/* Rollover, but only where there is a real pointer. On touch, :hover latches
   after a tap and the rule would stay on until you tapped elsewhere. */
@media (hover: hover) and (pointer: fine) {
  .contact a:hover {
    text-decoration: underline;
    text-decoration-thickness: var(--rule);
    text-decoration-thickness: from-font;
    text-decoration-skip-ink: none; /* unbroken rule — no gaps at descenders */
    text-underline-offset: 0.2em;
  }
}

/* Keyboard focus, and touch feedback in place of the suppressed tap flash. */
.contact a:focus-visible,
.contact a:active {
  text-decoration: underline;
  text-decoration-thickness: var(--rule);
  text-decoration-thickness: from-font;
  text-decoration-skip-ink: none;
  text-underline-offset: 0.2em;
}

/* --- Narrow viewports ---------------------------------------------------
   The email address is 28 monospace characters — the widest atom on the page.
   Below ~368px it stops fitting at --type-body-lg, so the contact block steps
   down one step of the scale rather than overflowing or breaking mid-address. */

@media (max-width: 23rem) {
  :root { --fs-mono: var(--type-small); }

  :root { --page-pad: var(--space-s); }
}

/* --- Short viewports ----------------------------------------------------
   A phone held in landscape leaves ~330-390px of height. At the full rhythm
   the lockup is ~450px tall and the page scrolls, which a holding page should
   never do. Compress the gap one step of the scale, drop the optical lift and
   pull the block padding in — the composition stays centred and fits. */

@media (max-height: 34rem) {
  :root {
    --gap-base: var(--space-l);
    --optical-lift: 0svh;
    /* The lockup is proportional now, so stepping the address down scales the
       mark and heading with it — which is what keeps landscape fitting. */
    --fs-mono: var(--type-small);
  }

  .holding { padding-block: var(--space-l); }
}

/* --- Focus ------------------------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: var(--space-3xs);
  border-radius: 1px;
}

/* --- Print ------------------------------------------------------------- */

@media print {
  body { background: #fff; color: #000; }
  .holding { min-height: auto; padding: var(--space-l); }
}
