/* ==========================================================================
   Troll Games — Armadillo Throw
   Holding page. Static HTML + CSS, self-hosted fonts, zero external requests.

   Spacing architecture: every element is reset to margin 0, and all vertical
   rhythm comes from `gap` on flex containers. Nothing sets both padding and
   margin for the same purpose, so no two spacing rules can cancel each other.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonts — self-hosted, latin subset, variable. See fonts/LICENSES.md.
   Fredoka carries the display voice: rounded and built, in keeping with a
   world of stacked stone. Nunito Sans shares its rounded terminals at text
   sizes without competing with it.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Fredoka';
  src: url('fonts/fredoka-variable-latin.woff2') format('woff2');
  font-weight: 300 600;
  font-stretch: 75% 125%;
  font-style: normal;
  font-display: swap;
}

/* Used for one thing only: the text on the signposts. */
@font-face {
  font-family: 'Permanent Marker';
  src: url('fonts/permanent-marker-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Nunito Sans';
  src: url('fonts/nunito-sans-variable-latin.woff2') format('woff2');
  font-weight: 200 1000;
  font-stretch: 75% 125%;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   Palette — warm earth, moss and gold, taken from the game itself.
   Every other colour is derived from these.

   --nugget is the in-game currency colour and is deliberately rare: it appears
   exactly twice on the page, on the tagline and on the closing line. That is a
   hard ceiling. It is never used for the axis, section labels, body text,
   borders or link states.

   --pitch is used in exactly one place: the footer band.
   -------------------------------------------------------------------------- */

:root {
  --loam:   #26221E;
  --bark:   #34302B;
  --pitch:  #171513;
  --bone:   #F2EADD;
  --ash:    #C9BFAE;
  --moss:   #B8C7A1;
  --nugget: #FDC400;

  /* Derived. Static fallbacks first, color-mix second where supported. */
  --text:        var(--bone);
  --text-muted:  var(--ash);
  --rule:        #767D66;
  --surface:     var(--bark);
}

@supports (color: color-mix(in srgb, red, blue)) {
  :root {
    --rule: color-mix(in srgb, var(--moss) 55%, var(--loam));
  }
}

/* Brand colours, used only as the hover and focus state of their own icon. */
:root {
  --brand-instagram: #E4405F;
  --brand-x:         #FFFFFF;
  --brand-mastodon:  #6364FF;
  --brand-youtube:   #FF0000;
  --brand-discord:   #5865F2;
}

/* --------------------------------------------------------------------------
   Type scale — a ~1.25 ratio, fluid between 360px and 1400px.
   -------------------------------------------------------------------------- */

:root {
  --text-xs: clamp(0.78rem, 0.76rem + 0.10vw, 0.86rem);
  --text-sm: clamp(0.88rem, 0.85rem + 0.13vw, 0.97rem);
  --text-md: clamp(1.03rem, 0.99rem + 0.20vw, 1.16rem);
  --text-lg: clamp(1.22rem, 1.13rem + 0.42vw, 1.50rem);
  --text-xl: clamp(1.42rem, 1.20rem + 0.95vw, 2.10rem);

  --space-1: 0.5rem;
  --space-2: 0.875rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  --space-5: 4rem;
  --space-6: clamp(3.5rem, 1.8rem + 6.5vw, 7rem);

  --gutter: clamp(1.125rem, 0.5rem + 2.5vw, 3rem);

  /* Mobile baseline. Both widen at min-width breakpoints near the end of the
     file, so the page fills a wide window instead of stranding a narrow
     column in it. */
  --measure: 60ch;
  --page-max: 1080px;

  /* Marker offsets are compressed so the widest label still fits at 320px. */
  --mark-scale: 0.45;

  /* How far the prose column follows its marker along the range. Zero on
     mobile, opened up at desktop width. */
  --cascade: 0;
}

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

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

:where(body, h1, h2, p, ul, li, figure) {
  margin: 0;
  padding: 0;
}

:where(ul) {
  list-style: none;
}

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

/* --------------------------------------------------------------------------
   Background pattern — one 240x240 inline-SVG tile as a data URI.
   Motifs are drawn from this game's own world: a mountain ridge, a curled
   armadillo, wind lines, an arc of trajectory, a club, and a row of
   measurement ticks along the tile's baseline. Static; it never animates.
   The footer band paints over it with an opaque colour.
   -------------------------------------------------------------------------- */

body {
  min-height: 100vh; /* Fallback for browsers without dynamic viewport units. */
  min-height: 100dvh;
  background-color: var(--loam);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240' viewBox='0 0 240 240'%3E%3Cg fill='none' stroke='%23B8C7A1' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round' opacity='.06'%3E%3Cpath d='M14 106 42 62 57 80 78 48 108 106'/%3E%3Cpath d='M60 76 69 65'/%3E%3Ccircle cx='178' cy='58' r='18'/%3E%3Ccircle cx='178' cy='58' r='7'/%3E%3Cpath d='M178 40v9M192 68l-8-5M164 68l8-5'/%3E%3Cpath d='M150 124q20-7 38 0M156 136q24-8 44 0M164 148q16-6 30 0'/%3E%3Cpath d='M110 202q58-64 116-18'/%3E%3Cpath d='M34 208 64 180'/%3E%3Cellipse cx='82' cy='166' rx='21' ry='11' transform='rotate(-42 82 166)'/%3E%3C/g%3E%3Cg stroke='%23B8C7A1' stroke-width='1.4' opacity='.09'%3E%3Cpath d='M0 226v10M24 230v6M48 230v6M72 230v6M96 230v6M120 226v10M144 230v6M168 230v6M192 230v6M216 230v6'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: repeat;
  background-position: center top;
  color: var(--text);
  font-family: 'Nunito Sans', 'Segoe UI', system-ui, sans-serif;
  font-size: var(--text-md);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

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

.page {
  width: min(100% - (var(--gutter) * 2), var(--page-max));
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  padding-block: clamp(3rem, 1.5rem + 5vw, 6rem) var(--space-6);
}

.main {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.section {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* The prose column follows its own marker down the range once there is room
   for it to do so. --cascade is 0 below the desktop breakpoint. */
.prose {
  max-width: var(--measure);
  margin-inline-start: calc(var(--mark, 0%) * var(--cascade));
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  text-wrap: pretty;
}

/* --------------------------------------------------------------------------
   Signature element — the range, told with the game's own props.

   Each section boundary is a strip of ground, and a wooden signpost planted in
   it carries the section name. The signpost's horizontal offset advances from
   left to right as you move down the page: vertical progress through the
   document is read as horizontal distance travelled. The offsets are the
   structure, not decoration — they are what makes each section a position on
   a range rather than an item in a list.

   The sign is drawn with border-image so the board stretches to whatever its
   text needs while the angled cut on the left and the arrow point on the right
   are drawn at fixed size and never distort. The slice widths are expressed as
   a ratio of the sign height, so they stay proportional at every breakpoint
   without a per-breakpoint value: 90/512 and 110/512 of the source.
   -------------------------------------------------------------------------- */

:root {
  --grass-h: clamp(2.5rem, 2.15rem + 1.5vw, 3.25rem);
  /* Permanent Marker ships a single weight and font-synthesis-weight is off, so
     bold is unavailable: the label has to clear 24px at every width for WCAG's
     large-text 3:1 threshold to apply to yellow-on-wood. This never drops below
     1.5rem. The board is sized from the label so it always has room. */
  --sign-label: clamp(1.5rem, 1.42rem + 0.35vw, 1.75rem);
  --sign-h: calc(var(--sign-label) * 2.05);
  /* How far the sign's foot sinks into the grass. The grass paints over the
     sign, so this also governs how close the blades come to the label: too
     deep and they clip the glyphs. */
  --sign-sink: 0.15;
  /* The post behind the board. */
  --post-w: clamp(0.625rem, 0.45rem + 0.7vw, 1.25rem);
  --post-offset: calc(var(--post-w) * -0.9);
}

/* No `--mark` default is declared here on purpose. A direct declaration on
   .axis would beat the value inherited from the section, regardless of the
   ancestor's specificity, and every marker would collapse to zero. The default
   is supplied at the point of use instead, via the var() fallback below. */
.axis {
  display: flex;
  flex-direction: column;
}

/* The ground. Its bottom edge is the section boundary, so nothing sits under
   it — no bottom padding, no bottom margin. The fade at both ends hides the
   horizontal tile seam.

   z-index is explicit rather than left to default paint order: the grass has
   to cover the post's foot, and the post lives inside the sign's stacking
   context, so the whole sign must paint below the grass. */
.axis__ruler {
  position: relative;
  z-index: 1;
  height: var(--grass-h);
  /* Two layers of the same strip, the second offset by half a tile.
     The asset carries a transparent margin at each end, so a single repeat-x
     butts two empty margins together and opens a visible gap every tile —
     2048/128 x the height, i.e. 16 x --grass-h. The offset layer's grass falls
     across the first layer's join, and vice versa, so the ground is unbroken
     at any width. The end fade below hides only the strip's two outer ends. */
  background-image:
    url("assets/title_grass_x_web_01.webp"),
    url("assets/title_grass_x_web_01.webp");
  background-repeat: repeat-x, repeat-x;
  background-position: left bottom, calc(var(--grass-h) * 8) bottom;
  background-size: auto 100%, auto 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, black 6%, black 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, black 6%, black 94%, transparent);
}

/* The signpost. It is the label: one element, no separate tick.
   align-self keeps it shrink-to-fit — stretched, the board would run the full
   width of the column. The negative bottom margin is what plants it: it pulls
   the grass up over the sign's foot, and the grass carries a higher z-index,
   so the blades overlap the board and the post below it. */
.axis__label {
  align-self: flex-start;
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  box-sizing: border-box;
  block-size: var(--sign-h);
  margin-inline-start: calc(var(--mark, 0%) * var(--mark-scale));
  margin-block-end: calc(var(--sign-h) * var(--sign-sink) * -1);
  padding-inline: 0.5em 0.7em;
  font-family: 'Permanent Marker', 'Segoe Script', cursive;
  font-weight: 400;
  font-size: var(--sign-label);
  line-height: 1.1;
  letter-spacing: 0;
  color: var(--nugget);
  white-space: nowrap;
  /* Yellow with a black outline, as the in-game signs are set.
     paint-order puts the stroke down first and the fill over it, so the stroke
     only grows outward and Permanent Marker's small counters stay open. */
  -webkit-text-stroke: 3px #000;
  paint-order: stroke fill;
  /* Fallback for engines that ignore paint-order (where the stroke would paint
     over the fill and thin the glyph) or text-stroke altogether: eight offsets
     build the outline so the letters never render hollow. */
  text-shadow:
    -1px -1px 0 #000,  1px -1px 0 #000,
    -1px  1px 0 #000,  1px  1px 0 #000,
     0   -1px 0 #000,  0    1px 0 #000,
    -1px  0   0 #000,  1px  0   0 #000;
}

/* The board sits on a pseudo-element so the text is never affected by anything
   applied to it. The asset is used exactly as supplied — no brightness change.
   The drop shadow follows the board's alpha silhouette, which lifts it off the
   background without touching the wood's colour or the label's contrast. */
.axis__label::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-style: solid;
  border-color: transparent;
  border-width: 0 calc(var(--sign-h) * 0.2148) 0 calc(var(--sign-h) * 0.1758);
  border-image-source: url("assets/distance_sign_01_x_web.webp");
  border-image-slice: 0 110 0 90 fill;
  border-image-width: 0 calc(var(--sign-h) * 0.2148) 0 calc(var(--sign-h) * 0.1758);
  border-image-repeat: stretch;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
}

/* The post holding the sign up. Pure CSS, no new asset: a horizontal gradient
   reads as a rounded shaft. It sits at z-index -2, behind the board at -1, so
   the board hides its top, and the whole sign paints below the grass, so the
   blades hide its foot — it reads as driven in rather than standing on top.

   The height is bounded so the post can never reach past the grass: its foot
   lands 55% down the strip, while the grass extends a full --grass-h below a
   top edge only 15% of the sign height up. Offset from centre by roughly a
   post's width, because dead centre reads as machined rather than hand-made. */
.axis__label::after {
  content: '';
  position: absolute;
  z-index: -2;
  inset-block-start: 52%;
  inset-inline-start: calc(50% - (var(--post-w) / 2) + var(--post-offset));
  inline-size: var(--post-w);
  block-size: calc(var(--sign-h) * 0.48 + var(--grass-h) * 0.55);
  background-image: linear-gradient(90deg, #4A3720, #6B532F 35%, #7B653E 55%, #4A3720);
  border-radius: 0 0 1px 1px;
}

/* Positions along the range: five sections, even 17% steps. */
#section-setup      { --mark: 14%; }
#section-swing      { --mark: 31%; }
#section-simulation { --mark: 48%; }
#section-gear       { --mark: 65%; }
#section-status     { --mark: 82%; }

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */

/* Centred at every width, mobile included. Only the hero and the closing line
   are centred — section body copy stays left-aligned throughout. */
.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(2.5rem, 1.8rem + 2.8vw, 4.5rem);
}

/* The render and the subtitle are one mark, not two stacked elements, so the
   gap between them stays tighter than any other vertical space on the page. */
/* Tight: the render and the subtitle are one lockup. The larger gap between the
   lockup and the tagline is set on .hero, so the tagline reads as a separate
   statement rather than a third line of the title. */
.lockup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.1rem, 0.02rem + 0.35vw, 0.45rem);
  font-weight: 400;
}

/* Mobile-first: fills the content frame, capped so it cannot dominate. The cap
   opens up at each desktop breakpoint. block-size stays auto and the
   width/height attributes carry the 1224x705 ratio, so the reserved box is
   correct before the file loads and the image can never be distorted.
   align-self is deliberate: .lockup is a column flex container, and the
   default `align-items: stretch` is what squashed the engine logo. */
.lockup__image {
  display: block;
  align-self: center;
  inline-size: 100%;
  max-inline-size: 28rem;
  block-size: auto;
}

/* Second in the visual order, below the render and above the tagline. Roughly
   double its former size at desktop so the yellow tagline can no longer read as
   outranking the game's own name. */
.lockup__title {
  font-family: 'Fredoka', 'Trebuchet MS', 'Segoe UI', sans-serif;
  font-weight: 400;
  font-size: clamp(1.9rem, 1.05rem + 3.5vw, 4.2rem);
  line-height: 1.1;
  letter-spacing: 0.005em;
  color: var(--moss);
  text-wrap: balance;
}

/* First of the two permitted --nugget appearances. */
.tagline {
  max-width: min(30ch, 100%);
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: 1.45;
  color: var(--nugget);
  text-wrap: balance;
}

/* Each sentence is one inline-block, so a wrap can only ever fall between
   sentences, never inside one. */
.tagline__line {
  display: inline-block;
}

/* --------------------------------------------------------------------------
   Status
   -------------------------------------------------------------------------- */

.status {
  font-family: 'Fredoka', 'Trebuchet MS', 'Segoe UI', sans-serif;
  font-weight: 500;
  font-size: var(--text-sm);
  line-height: 1.5;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--bone);
}

/* --------------------------------------------------------------------------
   Closing line — the page's last word, after the range has closed.

   A flex column with a gap and its own padding, so a call-to-action button can
   be dropped in as a sibling directly beneath the line and spaced correctly
   without touching anything around it. The padding is what separates the line
   from the terminus above and the band below; nothing else contributes, so
   there is no pair of rules to cancel each other.

   Second and last of the two permitted --nugget appearances.
   -------------------------------------------------------------------------- */

.closing {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding-block: var(--space-5);
}

.closing__line {
  max-width: min(34ch, 100%);
  /* Deliberately between --text-md and --text-lg: louder than body copy,
     quieter than the tagline. */
  font-size: clamp(1.12rem, 1.05rem + 0.32vw, 1.34rem);
  font-weight: 600;
  line-height: 1.4;
  color: var(--nugget);
  text-wrap: balance;
}

/* --------------------------------------------------------------------------
   Footer — a full-bleed dark band.

   The element spans the viewport so the band reaches both edges; its inner
   wrapper repeats the .page measurements so the content stays on the same
   grid as everything above it. The opaque background covers the body pattern.
   -------------------------------------------------------------------------- */

.footer {
  background-color: var(--pitch);
  padding-block: clamp(3rem, 2rem + 4vw, 5.5rem);
}

.footer__inner {
  width: min(100% - (var(--gutter) * 2), var(--page-max));
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.colophon {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--text-muted);
}

.colophon__studio {
  color: var(--bone);
  font-weight: 600;
}

.colophon__legal {
  font-size: var(--text-xs);
}

/* The engine credit: the mark sits with the copyright line it belongs to, and
   nothing else. No wording of our own accompanies it. */
.colophon__engine {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* `flex: none` is load-bearing. As a flex item the logo would otherwise shrink
   along the main axis while its height stayed fixed, distorting it — the same
   failure `align-items: stretch` caused when it lived in the prose column.
   The width/height attributes supply the ratio, so the box is reserved before
   the file loads. */
.engine-logo {
  flex: none;
  display: block;
  block-size: clamp(1.25rem, 1.05rem + 0.7vw, 1.6rem);
  inline-size: auto;
  max-inline-size: 100%;
}

/* inline-flex with a 44px minimum so the one real link on the page is a
   comfortable touch target on mobile. */
.link {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  color: var(--bone);
  text-decoration: underline;
  text-decoration-color: var(--rule);
  text-underline-offset: 0.22em;
  transition: text-decoration-color 160ms ease;
}

/* Moss, not nugget: --nugget is now spent on the tagline and the closing line,
   which is the ceiling of two. */
.link:hover {
  text-decoration-color: var(--moss);
}

/* --------------------------------------------------------------------------
   Social channels

   The logo artwork is never inlined. Each icon is a CSS mask pointing at a
   same-origin file in assets/icons/, tinted with currentColor, so colour stays
   under CSS control and no third party is contacted.

   Every channel is disabled. The icons sit grey and dimmed to read as
   inactive, and take their own brand colour on hover and keyboard focus.
   -------------------------------------------------------------------------- */

.social {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Positioned so the tooltips can anchor to the row rather than to individual
   buttons. Anchoring to a button would push the tooltip for the right-hand
   icons past the viewport edge at 320px; anchoring to the row cannot. All
   five carry the same message, so nothing is lost by placing them alike. */
.social__list {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.social__control {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background-color: transparent;
  cursor: pointer;
  text-decoration: none;
  transition: border-color 160ms ease, background-color 160ms ease;
}

.social__control:hover,
.social__control:focus-visible {
  border-color: var(--ash);
  background-color: var(--surface);
}

/* Default: grey and dimmed, so the row reads as inactive at a glance. */
.social__icon {
  display: block;
  inline-size: 1.25rem;
  block-size: 1.25rem;
  color: var(--ash);
  opacity: 0.55;
  background-color: currentColor;
  transition: color 160ms ease, opacity 160ms ease;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}

.social__control:hover .social__icon,
.social__control:focus-visible .social__icon {
  opacity: 1;
}

.social__icon--instagram {
  -webkit-mask-image: url("assets/icons/instagram.svg");
          mask-image: url("assets/icons/instagram.svg");
}

.social__icon--x {
  -webkit-mask-image: url("assets/icons/x.svg");
          mask-image: url("assets/icons/x.svg");
}

.social__icon--mastodon {
  -webkit-mask-image: url("assets/icons/mastodon.svg");
          mask-image: url("assets/icons/mastodon.svg");
}

.social__icon--youtube {
  -webkit-mask-image: url("assets/icons/youtube.svg");
          mask-image: url("assets/icons/youtube.svg");
}

.social__icon--discord {
  -webkit-mask-image: url("assets/icons/discord.svg");
          mask-image: url("assets/icons/discord.svg");
}

/* One colour declaration per icon. The mask is tinted with currentColor, so
   setting `color` is all that is needed. X's official mark is #000000, which
   is unusable on this band, so it takes white. */
.social__control:is(:hover, :focus-visible) .social__icon--instagram { color: var(--brand-instagram); }
.social__control:is(:hover, :focus-visible) .social__icon--x         { color: var(--brand-x); }
.social__control:is(:hover, :focus-visible) .social__icon--mastodon  { color: var(--brand-mastodon); }
.social__control:is(:hover, :focus-visible) .social__icon--youtube   { color: var(--brand-youtube); }
.social__control:is(:hover, :focus-visible) .social__icon--discord   { color: var(--brand-discord); }

/* Tooltip. Kept in the accessibility tree at all times — it is referenced by
   aria-describedby, and a target hidden with display:none or visibility:hidden
   is not reliably announced — so it is revealed with opacity alone. It is
   absolutely positioned and overlays what follows, so showing it never moves
   the layout. max-inline-size is bounded by the row, which keeps it inside the
   viewport at every width. */
.social__tooltip {
  position: absolute;
  inset-block-start: calc(100% + 0.5rem);
  inset-inline-start: 0;
  z-index: 2;
  inline-size: max-content;
  max-inline-size: min(34ch, 100%);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--rule);
  border-radius: 3px;
  background-color: var(--bark);
  color: var(--bone);
  font-size: var(--text-sm);
  line-height: 1.45;
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}

.social__control:hover + .social__tooltip,
.social__control:focus-visible + .social__tooltip,
.social__item.is-open .social__tooltip {
  opacity: 1;
}

/* --------------------------------------------------------------------------
   Focus and skip link
   -------------------------------------------------------------------------- */

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

.skip-link {
  position: absolute;
  inset-inline-start: -100vw;
  inset-block-start: 0;
  z-index: 3;
  padding: var(--space-1) var(--space-2);
  background-color: var(--bark);
  color: var(--bone);
  font-size: var(--text-sm);
  text-decoration: underline;
}

.skip-link:focus {
  inset-inline-start: var(--space-1);
  inset-block-start: var(--space-1);
}

/* --------------------------------------------------------------------------
   Wider viewports — min-width only.

   The base rules above describe the mobile layout. Three things open up with
   available width: how far the section markers travel, how wide the text
   measure is allowed to grow, and whether the hero is centred. Everything else
   scales continuously via clamp() and needs no breakpoint at all.
   -------------------------------------------------------------------------- */

@media (min-width: 34em) {
  :root {
    --mark-scale: 0.7;
  }
}

/* Tablet and up: the hero centres and becomes the title card it is, and the
   measure takes the first of three steps. */
@media (min-width: 48em) {
  :root {
    --mark-scale: 1;
    --measure: 64ch;
  }

  .lockup__image {
    max-inline-size: 34rem;
  }

  .tagline {
    max-width: min(40ch, 100%);
  }
}

/* Desktop: the prose column starts following its marker along the range, so
   the body of the page occupies the width rather than hugging the left edge. */
@media (min-width: 64em) {
  :root {
    --measure: 68ch;
    --cascade: 0.3;
  }

  .lockup__image {
    max-inline-size: 42rem;
  }
}

/* Large desktop: the frame itself widens. */
@media (min-width: 90em) {
  :root {
    --measure: 72ch;
    --page-max: 1200px;
  }

  .lockup__image {
    max-inline-size: 48rem;
  }
}

/* --------------------------------------------------------------------------
   Reduced motion — the page has no animation; this removes the hover and
   focus transitions as well. The background pattern is static in all cases.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
