/* Spore Software — site styles.

   Every colour here comes from branding/spore-software/BRAND.md. The
   contrast ratios in the comments are computed against the pairing actually
   used, not estimated. Brown is the page: BRAND.md names it "brand ground, and
   the default page", which is what makes this site look unlike Coral River's
   paper ground even though both carry the same structure.

   Purple sits only 1.36 : 1 against the brown, so every purple panel is held
   by a gold hairline rather than left to float. Olive is 1.40 : 1 against the
   brown and is used the same way — edge furniture, never a text ground without
   a border to define it.

   Olive's place is the header tabs: it fills the tab under the pointer and the
   tab for the page you are on, carrying a paper label at 6.55 : 1. Purple's
   place is the panel. */

:root {
  --brown: #5C3D2E;          /* page ground */
  --brown-deep: #3C281E;     /* the banner and footer bands — the page brown at 65% */
  --tan-warm: #A87F5E;       /* a content surface — 2.71:1 against the brown frame */
  --block: var(--tan-warm);  /* which surface a section's block takes */
  --ink-dark: #241508;       /* text on the tan — 4.94:1 */
  --ink-dark-quiet: #2A190C; /* secondary prose on the tan — 4.72:1 */

  /* The screen's left and right margins fall away into shadow, which frames
     the centred column without drawing a border around it. One shade laid over
     whatever ground it sits on, so the banner and the page darken together and
     the edge reads as continuous down the whole screen. Text only gains
     contrast where it reaches the shade: paper goes 9.18 : 1 → 13.51 : 1.

     The ramp's stops trace a cubic falloff — steep against the screen edge,
     stretched thin toward the middle — because banding shows where the ramp
     is light: the eye picks out equal-alpha steps standing shoulder to
     shoulder unless the light end of the curve is given most of the width.
     The shade clears at 46% a side, leaving a narrow untouched band at
     centre, and it lands at zero slope, so no crease marks where it ends. */
  --edge-shade: linear-gradient(90deg,
                  rgba(0, 0, 0, 0.38)  0%,
                  rgba(0, 0, 0, 0.23)  6.9%,
                  rgba(0, 0, 0, 0.13)  13.8%,
                  rgba(0, 0, 0, 0.063) 20.7%,
                  rgba(0, 0, 0, 0.024) 27.6%,
                  rgba(0, 0, 0, 0.006) 34.5%,
                  rgba(0, 0, 0, 0)    46%,
                  rgba(0, 0, 0, 0)    54%,
                  rgba(0, 0, 0, 0.006) 65.5%,
                  rgba(0, 0, 0, 0.024) 72.4%,
                  rgba(0, 0, 0, 0.063) 79.3%,
                  rgba(0, 0, 0, 0.13)  86.2%,
                  rgba(0, 0, 0, 0.23)  93.1%,
                  rgba(0, 0, 0, 0.38)  100%);

  /* Grain riding on every shaded surface: black flecks capped at 6% opacity
     (alpha is 0.06 × the turbulence's red channel), which is enough amplitude
     to dither the 8-bit banding a long dark ramp produces, and too little to
     read as texture. Tiles at its natural 128px, never stretched — a scaled
     tile turns dither into visible blotches. */
  --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='128' height='128'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0 0 0 0'/></filter><rect width='128' height='128' filter='url(%23g)'/></svg>");

  --gold: #D8BB6B;           /* headings, the mark, hairlines — 5.20:1 on brown */
  --purple: #47204F;         /* panels — gold reads 7.08:1, paper 12.50:1 */
  --crimson: #6F1628;        /* reserved for errors and announcements — gold reads 6.17:1, paper 10.90:1 */
  --olive-deep: #343314;     /* panels — gold reads 6.90:1, paper 12.18:1 */
  --olive: #5E5C24;          /* edge furniture — paper label 6.55:1 */
  --paper: #FBF8F5;          /* body text on brown — 9.18:1 */

  --ink: var(--paper);
  --ink-quiet: #E4D8CB;      /* 6.85:1 on brown — secondary prose */
  --hairline: rgba(216, 187, 107, 0.34);

  --measure: 66ch;
  /* One flat gap at every width: the tan blocks' 1rem side frame consumes
     its first 1rem, prose keeps 1rem of tan inside the frame, and heads,
     header, footer and section interiors share one left line — 2rem off
     the screen edge — that no breakpoint moves. */
  --gap: 2rem;
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

/* On a phone the desktop ramp's two 46% reaches leave an untouched band
   of 8% — under 30px at 360 — so the whole screen reads shaded and the
   tan cards sit unevenly lit at their outer edges. The ramp keeps its
   edge alpha and its cubic falloff — steep at the edge, most of the width
   in the light tail, landing at zero slope — compressed to clear by 20%
   a side, which frees the middle 60% of the screen and keeps the shade
   as a frame instead of a wash. The shade still only darkens the ground,
   so every light-ink ratio stays at or above its unshaded baseline. */
@media (max-width: 48rem) {
  :root {
    --edge-shade: linear-gradient(90deg,
                    rgba(0, 0, 0, 0.38)  0%,
                    rgba(0, 0, 0, 0.23)  3%,
                    rgba(0, 0, 0, 0.13)  6%,
                    rgba(0, 0, 0, 0.063) 9%,
                    rgba(0, 0, 0, 0.024) 12%,
                    rgba(0, 0, 0, 0.006) 15%,
                    rgba(0, 0, 0, 0)    20%,
                    rgba(0, 0, 0, 0)    80%,
                    rgba(0, 0, 0, 0.006) 85%,
                    rgba(0, 0, 0, 0.024) 88%,
                    rgba(0, 0, 0, 0.063) 91%,
                    rgba(0, 0, 0, 0.13)  94%,
                    rgba(0, 0, 0, 0.23)  97%,
                    rgba(0, 0, 0, 0.38)  100%);

  }
}

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

/* The ground sits on the root element rather than the body so the shade is
   measured against the screen, not against however tall the content runs. */
html {
  -webkit-text-size-adjust: 100%;
  background-color: var(--brown);
  background-image: var(--grain), var(--edge-shade);
  background-repeat: repeat, no-repeat;
}

body {
  margin: 0;
  background: transparent;
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(1rem, 0.35vw + 0.95rem, 1.125rem);
  line-height: 1.65;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* Skip link — visible only once focused. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--gold);
  color: var(--brown);
  padding: 0.6rem 1rem;
  font-weight: 600;
  z-index: 10;
}
.skip:focus { left: 0; }

/* Selected text takes the brand's own ink instead of the browser's blue:
   a gold ground with the deep-brown text on it — 7.42:1, the same pairing
   the filled button carries — which holds on every surface, dark or tan. */
::selection { background: var(--gold); color: var(--brown-deep); }

a { color: var(--gold); text-underline-offset: 0.18em; }
a:hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

h1, h2, h3 {
  color: var(--gold);
  line-height: 1.15;
  text-wrap: balance;
  margin: 0;
}
h1 { font-size: clamp(1.9rem, 4.5vw, 3rem); letter-spacing: -0.015em; }
h2 { font-size: clamp(1.3rem, 2.4vw, 1.7rem); letter-spacing: -0.01em; }
h3 { font-size: 1.05rem; }

/* `pretty` balances a paragraph's last lines against orphans. Enhancement
   only: a browser without it sets the same text greedily. */
p { margin: 0; max-width: var(--measure); text-wrap: pretty; }

.wrap {
  width: 100%;
  max-width: 70rem;
  margin: 0 auto;
  padding-inline: var(--gap);
}

/* ---- header ---------------------------------------------------------- */

/* The banner takes the footer's own deep brown, so the page is bookended:
   the same dark band opens the screen and closes it, with the lighter page
   between. On it the lockup's gold reads 7.42 : 1 and a paper label
   13.11 : 1. The band runs full-bleed — its wrap gives up the column width,
   so the lockup sits at the screen's left edge and the tabs at its right,
   with only the column's own gap padding holding them off the corners. It
   carries the same edge shade, which keeps the darkened margins unbroken
   from the top of the screen to the bottom. */
.site-header {
  background-color: var(--brown-deep);
  background-image: var(--grain), var(--edge-shade);
  background-repeat: repeat, no-repeat;
  border-bottom: 1px solid var(--hairline);
  padding-block: 1.1rem;
}
.site-header .wrap {
  max-width: none;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.brand {
  display: inline-flex;
  align-items: center;
  color: var(--gold);            /* the lockup's ink is currentColor */
}
.brand img { height: 34px; width: auto; display: block; }
.header-nav {
  margin-left: auto;
  display: flex;
  gap: 0.35rem;
  font-size: 0.95rem;
  flex-wrap: wrap;
}

/* Tabs. Olive is 2.00 : 1 against the deep brown, so a filled tab is held by
   the gold hairline exactly as a purple panel is; the border is reserved on
   every tab so filling one shifts nothing. The label stays paper — 13.11 : 1
   idle on the deep brown, 6.55 : 1 on the olive fill, where gold would give
   3.7 : 1 and miss AA at this size. */
.header-nav a {
  color: var(--ink);
  text-decoration: none;
  padding: 0.35rem 0.75rem;
  border: 1px solid transparent;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.header-nav a:hover,
.header-nav a[aria-current="page"] {
  background: var(--olive);
  border-color: var(--hairline);
  color: var(--paper);
}

/* On a phone the band wraps to two rows — the lockup, then the tabs — and
   a fingertip replaces the pointer. Each tab grows to a 44px target: 0.6rem
   pads plus the 0.95rem label's 1.65 line is 44.3px, and the brand link
   carries matching pads so the lockup is as easy to hit. Only the targets
   grow; every colour pairing is the header's own. The rows sit on a tighter
   row gap so the taller tabs do not bloat the band. The tabs' side pads
   ease to 0.6rem: inside the wrap's 2rem phone padding the three tabs and
   their gaps hold 360's row at 0.6rem (289px in 296) and split it at the
   desktop 0.75rem; every tab stays far wider than its 44px bar. */
@media (max-width: 48rem) {
  .site-header { padding-block: 0.75rem; }
  .site-header .wrap { row-gap: 0.4rem; }
  .brand { padding-block: 0.35rem; }
  /* The three tabs need real slack in a 296px row at 360 — font metrics
     differ by renderer, and a zero-margin fit wraps "Contact" ragged in
     some of them. 0.9rem labels leave ~20px spare; the taller pads keep
     the 44px touch target. */
  .header-nav { font-size: 0.9rem; }
  .header-nav a { padding-block: 0.65rem; padding-inline: 0.6rem; }
}

/* ---- sections -------------------------------------------------------- */

/* The content sits on a central tan block, with the page brown left as the
   frame around it. The tan is the surface every colour is laid on, which is
   what stops a dark panel from sitting on a dark ground: purple is 1.36 : 1 on
   the brown and 3.69 : 1 here, the dark olive 1.33 and 3.60.

   The whole ink scheme inverts on it. Gold reads 1.92 : 1 on the tan and paper
   3.39 : 1, so neither of the dark-ground inks survives; text, headings, links
   and borders all take the dark brown. The edge shade is the first layer, over
   the block, so the darkened margins stay unbroken down the screen. */
main {
  flex: 1;
  display: flex;
  flex-direction: column;
  background-image: var(--grain), var(--edge-shade);
  background-size: auto, 100% 100%;
  background-repeat: repeat, no-repeat;
  color: var(--ink-dark);
}

/* On a screen taller than the page, the leftover height collects above the
   closing band rather than below it, so the call to action and the footer
   close the page together instead of leaving a void between them. */
main > section.close { margin-top: auto; }

/* One tan block per section, laid on the page brown rather than replacing it.
   Each block is inset top and bottom, so the page brown shows through as a
   gutter between blocks at full strength — in the middle of the screen, where
   the edge shade does not reach — and the layering stays visible. On screens
   past 74rem the sides gain the same relation, the brown framing the block's
   full perimeter. */
main > section {
  background-image: linear-gradient(var(--block), var(--block));
  background-size: min(74rem, 100% - 2rem) calc(100% - 1.6rem);
  background-position: center 0.8rem;
  background-repeat: no-repeat;
}
main > section + section { border-top: 0; }

/* The block's 1rem side inset lives in the base sizing above — the 100%−2rem
   arm — so the brown frame reads around every tan surface at every viewport
   width, not just past the block's 74rem cap: the card-on-ground relation
   the product slots and the developer portraits hold. The wrap's 2rem
   padding leaves prose 1rem of tan before the block's edge everywhere. */

main h1,
main h2,
main h3,
main a,
main .eyebrow { color: var(--ink-dark); }

main .lede { color: var(--ink-dark); }
main .cols p { color: var(--ink-dark-quiet); }

/* The hero and each page head carry no tan block — they sit on the page
   brown itself, in the dark-ground ink scheme: gold heading at 5.20 : 1,
   paper lede at 9.18 : 1, quiet paper at 6.93 : 1. Every page opens in the
   brand's own register, and the alternation — dark head, tan content — is
   what gives a page its rhythm instead of one unbroken slab. */
main > section.hero,
main > section.page-head { background-image: none; }
.hero h1,
.page-head h1 { color: var(--gold); }             /* 5.20:1 on the brown */
.hero .lede,
.page-head .lede { color: var(--ink); }           /* paper — 9.18:1 */
.page-head .eyebrow { color: var(--gold); }       /* 5.20:1, mono label */

/* The closing call to action is the same move at the other end: no tan
   block, so the page closes in the register it opened in. On both of the
   dark-ground sections — the hero's action and the close's — the button
   takes its dark-ground build: gold fill, deep-brown ink. */
main > section.close { background-image: none; }
.close h2 { color: var(--gold); }                 /* 5.20:1 on the brown */
.close p { color: var(--ink); }                   /* paper — 9.18:1 */
main .hero .cta,
main .close .cta {
  background: var(--gold);
  color: var(--brown-deep);                       /* 7.42:1 on the gold */
}
main .hero .cta:hover,
main .hero .cta:focus-visible,
main .close .cta:hover,
main .close .cta:focus-visible {
  background: var(--paper);                       /* edge 9.18:1 on brown */
  color: var(--brown-deep);                       /* 13.11:1 on paper */
}

/* A panel keeps its own dark fill, its gold ink and its gold border. The
   border reads from inside — 7.08 : 1 on the purple, 6.90 on the olive,
   6.17 on the crimson — and the fill separates the panel from the tan it
   sits on at 3.2 : 1 or better, above the 3 : 1 bar for non-text edges,
   with the gold border sharing the work. */
main .panel { border-color: var(--gold); }
main .panel,
main .panel h2,
main .panel h3,
main .panel a { color: var(--ink); }
main .panel h2,
main .panel h3 { color: var(--gold); }
main .panel a { color: var(--gold); }
.panel.light .spec { color: var(--brown); }

/* The rules that divide and focus on the tan take the dark ink too — the gold
   hairline is 1.92 : 1 here and barely shows. */
main section + section { border-top-color: rgba(60, 40, 30, 0.32); }
main :focus-visible { outline-color: var(--ink-dark); }

section { padding-block: clamp(2.5rem, 6vw, 4.5rem); }
section + section { border-top: 1px solid var(--hairline); }

.stack { display: grid; gap: 1.1rem; }

/* The eyebrow is a label on the page ground, so it carries gold at 5.20 : 1
   and needs no border to define it. */
.eyebrow {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--gold);
  justify-self: start;
}

/* A page's opening block: the eyebrow naming the section, then its h1. */
.page-head { padding-block: clamp(2rem, 5vw, 3.5rem); }
.page-head .stack { gap: 0.9rem; }
.page-head h1 { font-size: clamp(1.7rem, 3.6vw, 2.5rem); letter-spacing: -0.015em; }

.lede { font-size: clamp(1.1rem, 1.4vw, 1.3rem); color: var(--ink); }

/* Hero: the mark rides the ground, oversized and faint, its centre held a
   fixed 26rem right of the page's centreline — the composition belongs to
   the centred column, so however wide the screen runs, the mark balances
   the left-set text instead of clinging to the viewport edge. On narrow
   screens the same offset carries it mostly out of frame. It is a
   watermark, not a figure: gold at 8% opacity, laid under the text.

   A decorative mark's cap opens toward the content it accompanies —
   the standing rule (Stephen, 2026-08-10). Right-facing is the default
   for a mark sitting left of its content, which is where the files of
   record (the lockup, the roundel) and the product slots all place it.
   This mark sits right of the text, so it opens leftward into the
   heading — the same relation the lockup's cap holds to its type. Left
   is mark.svg's own facing, so the mark rides unmirrored.

   Where text overlaps the mark's solid bars, the worst-case ground is gold
   at 0.08 composited over the brown — #664733, a 1.16:1 whisper off the
   page — and every ink that can reach it still clears: the paper lede reads
   7.89:1 (bar 4.5:1), quiet paper 5.96:1, and the gold h1 4.47:1 against
   its 3:1 large-text bar. The gills and stem highlights are holes in the
   path, so between the bars the ground is the plain brown and the baseline
   ratios hold. The edge shade only darkens the ground, which raises every
   one of these light-ink ratios, so the un-shaded centre is the worst case. */
.hero {
  padding-block: clamp(3rem, 8vw, 6rem);
  position: relative;
  overflow: hidden;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("/assets/mark.svg");
  background-repeat: no-repeat;
  background-position: left calc(50% + 26rem) top -10%;
  background-size: auto 130%;
  opacity: 0.08;
  pointer-events: none;
}
.hero .wrap { position: relative; }   /* the text rides above the watermark */
.hero .stack { gap: 1.4rem; }

/* At phone widths the column-tied offset leaves only an accidental sliver
   of the arc in frame, so the composition changes hands: the mark anchors
   to the frame itself, its right side cropped a fixed 6rem past the edge,
   so the cap's open face and the gills are what shows — leaning in from
   the right, still opening leftward into the heading. Same ink, same 8%
   opacity; the overlap ratios stated above hold unchanged. */
@media (max-width: 48rem) {
  .hero::before {
    background-position: right -6rem top -10%;
    background-size: auto 115%;
  }
}

/* ---- calls to action -------------------------------------------------- */

/* A primary action is a filled button, not an underlined line of prose. The
   fill follows the ground the button sits on, the same inversion the inks
   make. On the tan surface the fill is the dark ink itself — gold cannot
   hold a shape there (1.92 : 1 against the tan) — and the label takes gold
   at 9.48 : 1, with the fill's own 4.94 : 1 edge against the tan carrying
   the boundary. On the dark grounds the fill is gold with the deep brown
   ink on it at 7.42 : 1, and the fill stands 5.20 : 1 off the brown. */
.cta {
  display: inline-block;
  padding: 0.65rem 1.3rem;
  background: var(--gold);
  color: var(--brown-deep);        /* 7.42:1 on the gold fill */
  font-weight: 600;
  text-decoration: none;
  transition: background-color 120ms ease, color 120ms ease,
              transform 120ms ease;
}
.cta:hover,
.cta:focus-visible {
  background: var(--paper);        /* edge 9.18:1 on brown */
  color: var(--brown-deep);        /* 13.11:1 on paper */
  text-decoration: none;
  transform: translateY(-2px);     /* a step up under the pointer */
}
.cta:active { transform: none; }
/* A phone-width button wraps its longer labels; balanced lines keep the
   wrap from stranding the arrow alone on the second line. */
@media (max-width: 48rem) {
  .cta { text-wrap: balance; }
}
main .cta {
  background: var(--ink-dark);
  color: var(--gold);              /* 9.48:1 on the dark fill */
}
main .cta:hover,
main .cta:focus-visible {
  background: var(--brown-deep);   /* label holds 7.42:1; edge 3.87:1 on tan */
  color: var(--gold);
}

/* ---- panels ---------------------------------------------------------- */

/* A purple panel is 1.36 : 1 against the page, so the border is doing the work
   of separating it, not decoration. It is full-strength gold — the same ink as
   the headings — which reads 7.08 : 1 against the fill it encloses and 5.20 : 1
   against the page it sits on, so the panel keeps its shape from either side
   and holds up where the edge shade darkens the ground beneath it. */
.panel {
  background: var(--panel-fill, var(--purple));
  border: 2px solid var(--gold);
  padding: clamp(1.4rem, 3vw, 2.2rem);
  display: grid;
  gap: 0.9rem;
  /* A soft black drop under the panel lifts it off the tan; black over the
     tan is shading, not a colour, the same claim the edge shade makes. */
  box-shadow: 0 12px 28px -14px rgba(0, 0, 0, 0.5);
}
.panel p { max-width: 60ch; }

/* `--panel-fill` is the one knob: set it on a panel and the rest of the build
   holds. Purple is the default; the dark olive is the second fill. Gold reads
   6.90 : 1 on it, so the border, the heading and a body-size link all clear
   their bars from the one ink, and the fill sits 3.60 : 1 off the tan. */
.panel.olive { --panel-fill: var(--olive-deep); }
/* Crimson is reserved for error messages and important announcements — it
   decorates nothing. The build waits for the notice that needs it: gold
   reads 6.17 : 1 on the fill, and the fill sits 3.22 : 1 off the tan. */
.panel.crimson { --panel-fill: var(--crimson); }

/* The fills side by side on one ground, which is the only way to judge them
   against each other rather than against memory. */
.swatches {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  align-items: start;
}
/* A light fill inverts the panel's ink. Gold falls to between 1.0 and 1.6 : 1
   on a yellow this saturated and paper is worse, so the text, the heading and
   the border all take the dark brown — which is also the ground the panel sits
   on, so one value serves as ink and as edge. */
.panel.light {
  color: var(--brown-deep);
  border-color: var(--brown-deep);
}
.panel.light h2,
.panel.light h3,
.panel.light a { color: var(--brown-deep); }

/* A band of the footer's dark brown, for judging a fill against the ground it
   actually has to sit on rather than against the page brown alone. */
.on-deep {
  background-color: var(--brown-deep);
  background-image: var(--grain), var(--edge-shade);
  background-size: auto, 100% 100%;
  background-position: 0 0;
  background-repeat: repeat, no-repeat;
  color: var(--ink);
}
.on-deep h2, .on-deep h3 { color: var(--gold); }

.swatches .spec {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-quiet);
  max-width: none;
}

/* Prose running beside a panel rather than above it, which puts the purple
   off to one side of the page instead of across it. The panel column is the
   narrower of the two so the prose keeps its measure and the panel reads as
   furniture set against the edge. Below the breakpoint the two stack and the
   panel follows the prose it belongs to. */
.with-aside {
  display: grid;
  gap: var(--gap);
  grid-template-columns: minmax(0, 1.65fr) minmax(0, 1fr);
  align-items: start;
}
@media (max-width: 48rem) {
  .with-aside { grid-template-columns: 1fr; }
}

.cols {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
/* Each column opens with a kicker rule over its heading, so a row of
   columns reads as set cards rather than loose paragraphs. The rule is
   the dark ink — a 4.94:1 edge on the tan, where gold at 1.92:1 cannot
   hold a line. */
.cols > div {
  display: grid;
  gap: 0.5rem;
  align-content: start;
  border-top: 2px solid var(--ink-dark);
  padding-top: 0.9rem;
}
.cols p { color: var(--ink-quiet); max-width: 34ch; }
/* A column heading is a subsection of the page's h1, so it is an h2 in the
   outline and sized as a card title rather than at the h2 display size. */
.cols h2 { font-size: 1.1rem; }

/* ---- engagement stages (how-we-work) ---------------------------------- */

/* The process as three rows in reading order: a stage's drawing at the
   left, its prose to the right. The drawings are draft lifecycle glyphs —
   a spore, the spread, the fruited mushroom — telling in pictures what the
   words beside them say plainly. Each rides the purple emblem seat the
   product slots established: the drawing in gold on a disc of the panel
   purple darkened (#2E1533, the purple at 0.65), ringed in the same gold.
   Gold reads 8.84:1 on the disc; the disc sits 4.61:1 off the tan, above
   the 3:1 non-text bar, so the boundary is the disc's own and the ring
   (1.92:1 on the tan) only ties the seat to the gold of the drawing. On
   the tan a bare gold drawing reads 1.92:1 and cannot carry itself, which
   is what the seat is for. */
.stages {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(1.6rem, 3vw, 2.4rem);
  max-width: 56rem;
}
.stage {
  display: flex;
  align-items: center;
  gap: 1.8rem;
}
.stage-text { display: grid; gap: 0.5rem; }
/* A stage heading is a step in the page's process, so it is an h2 in the
   outline and sized between the card title and the display h2. */
.stage h2 { font-size: 1.2rem; }
.stage-glyph {
  flex: none;
  width: 5.2rem;
  height: 5.2rem;
  border-radius: 50%;
  background-color: #2E1533;
  border: 0.11rem solid var(--gold);  /* 11/512 of the diameter, outer edge flush */
  background-origin: border-box;
  background-position: center;
  background-repeat: no-repeat;
}
/* Sizes are per drawing, not one figure: the spore is a small form and
   sits small in its seat; the spread and the mushroom stand near the
   emblem's own 348:512. Every glyph carries gold as its default ink, so
   no file is recoloured, and the mushroom's own drawing leans right —
   toward the text it accompanies — so nothing here mirrors. */
.stage-glyph.spore    { background-image: url("/assets/glyph-spore.svg");    background-size: auto 44%; }
.stage-glyph.mycelium { background-image: url("/assets/glyph-mycelium.svg"); background-size: auto 66%; }
.stage-glyph.mushroom { background-image: url("/assets/glyph-mushroom.svg"); background-size: auto 68%; }
/* A drawing stays beside its stage text at every width — on phones it
   shrinks so the prose keeps a workable measure rather than climbing on
   top of the words. */
@media (max-width: 48rem) {
  .stage { gap: 1.2rem; }
  .stage-glyph { width: 4rem; height: 4rem; }
}

/* ---- placeholder racks ------------------------------------------------ */

/* Decorative empty states, aria-hidden in the markup: the space future
   content takes, drawn so the page shows where things land without claiming
   they exist. Both racks are tan surfaces laid straight on the page brown —
   2.71 : 1 against it, the same separation every content block makes — with
   the panel's soft drop lifting each card. Nothing in a rack is text; the
   prose above already says what is and is not published. */
main > section.rack { background-image: none; }

/* The product slots: one row per future product — the symbol's seat at the
   left, the shapes of a name and its line to the right. */
.slots {
  display: grid;
  gap: clamp(1.4rem, 2.5vw, 2rem);
  max-width: 56rem;
}
.slot {
  display: flex;
  align-items: center;
  gap: 1.8rem;
  min-height: 9rem;
  padding: 1.8rem 2.2rem;
  background: var(--tan-warm);
  box-shadow: 0 12px 28px -14px rgba(0, 0, 0, 0.5);
}
/* The symbol seat is the purple emblem — BRAND.md's construction: the mark
   in gold on a disc of the panel purple darkened (#2E1533, the purple at
   0.65), ringed in the same gold. The proportions are the emblem files'
   own: the mark stands 68% of the disc (348 : 512) and the ring is 11/512
   of the diameter — 0.1rem on this 4.6rem disc — with its outer edge flush
   with the disc's. Gold reads 8.84 : 1 on the disc; the disc sits 4.61 : 1
   off the tan, above the 3 : 1 non-text bar, so the boundary is the disc's
   own and the ring (1.92 : 1 on the tan) only ties the seat to the gold of
   the drawing. The drawing stays gold here as it does everywhere — on the
   tan, a bare mark can only be a dark silhouette, which is not the mark —
   and mark.svg carries gold as its default ink, so the file needs no
   recolouring. Mirrored to the decorative mark's right-facing default;
   the disc is symmetric, so only the drawing flips. */
/* A filled slot carries a real product; the empty slots stay skeletons.
   It keeps the emblem and drops the placeholder bars; its text takes the
   documented tan inks — the name 4.94:1, the description 4.72:1. */
.slot.filled::after { content: none; }
.slot-body { display: grid; gap: 0.4rem; }
.slot-body h2 { font-size: 1.15rem; }
.slot-body p { font-size: 0.95rem; max-width: 34rem; color: var(--ink-dark-quiet); }

.slot::before {
  content: "";
  flex: none;
  width: 4.6rem;
  height: 4.6rem;
  border-radius: 50%;
  background-color: #2E1533;
  border: 0.1rem solid var(--gold);
  background-image: url("/assets/mark.svg");
  background-origin: border-box;
  background-size: auto 68%;
  background-position: center;
  background-repeat: no-repeat;
  transform: scaleX(-1);
}
/* A name and its line, as shapes: two bars of the slot's own ink at low
   strength. They composite to #936E50 — 1.28 : 1 on the tan, a deliberate
   whisper, because they are the size of text without being text. */
.slot::after {
  content: "";
  flex: 1;
  max-width: 32rem;
  height: 2.9rem;
  background-image:
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16));
  background-size: 38% 1rem, 62% 0.65rem;
  background-position: left top, left bottom;
  background-repeat: no-repeat;
}
/* On a phone the slot sheds its desktop air — tighter pads, the row's own
   height — so the emblem and the bars share the narrow card instead of the
   padding crowding them; the emblem keeps its documented proportions. */
@media (max-width: 48rem) {
  .slot {
    min-height: 0;
    padding: 1.3rem 1.4rem;
    gap: 1.3rem;
  }
}

/* The developers: one full row per person, so the two of them carry the
   page instead of huddling in its corner. Each row is a large portrait
   upright with the shape of a description beside it — two tan cards with
   the page brown reading between them, the same layering every rack
   makes. Below the breakpoint the description follows its portrait. */
.people {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
}
.person {
  display: grid;
  grid-template-columns: minmax(0, 24rem) minmax(0, 1fr);
  gap: var(--gap);
  align-items: stretch;
}
/* The switch is hard at the breakpoint: above it the row — portrait at the
   left, description to its right; below it a centred stack — the portrait
   on the page's centreline with its description centred beneath. The
   pair's gap holds at 1rem rather than riding the token: under .people's
   1.5rem row gap, a portrait stays closer to its own bio than to the
   neighbouring person, so each pair still reads as one unit. */
@media (max-width: 48rem) {
  .person { grid-template-columns: 1fr; gap: 1rem; justify-items: center; }
  .person .bio { width: 100%; justify-items: center; }
}

/* A headshot-shaped upright holding the quiet figure of a head and
   shoulders where a photograph will sit. The figure is the slot bars' own
   low-strength ink — 1.28 : 1 on the tan, decorative by the same intent. */
.portrait {
  position: relative;
  overflow: hidden;
  width: min(24rem, 100%);
  aspect-ratio: 3 / 4;
  background: var(--tan-warm);
  box-shadow: 0 12px 28px -14px rgba(0, 0, 0, 0.5);
}
.portrait::before {
  content: "";
  position: absolute;
  top: 18%;
  left: 50%;
  transform: translateX(-50%);
  width: 36%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(36, 21, 8, 0.16);
}
.portrait::after {
  content: "";
  position: absolute;
  top: 64%;
  left: 50%;
  transform: translateX(-50%);
  width: 74%;
  aspect-ratio: 2 / 1.2;
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
  background: rgba(36, 21, 8, 0.16);
}

/* The shape of the description beside the portrait: a name-length bar and
   a paragraph of line bars, all the racks' whisper ink — they composite to
   #936E50, 1.28 : 1 on the tan, the size of text without being text. The
   block is capped at a prose measure and set on the card's centre, and the
   card itself stretches to the portrait's height, so the row reads as one
   seat however tall the portrait runs. */
.person .bio {
  background: var(--tan-warm);
  box-shadow: 0 12px 28px -14px rgba(0, 0, 0, 0.5);
  padding: clamp(1.6rem, 3vw, 2.4rem);
  display: grid;
  align-content: center;
  justify-items: start;
}
.person .bio::before {
  content: "";
  width: 100%;
  max-width: 36rem;
  height: 18.9rem;
  max-height: 100%;
  background-image:
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16)),
    linear-gradient(rgba(36, 21, 8, 0.16), rgba(36, 21, 8, 0.16));
  background-size:
    34% 0.9rem,
    100% 0.55rem, 95% 0.55rem, 98% 0.55rem, 90% 0.55rem, 100% 0.55rem,
    96% 0.55rem, 99% 0.55rem, 92% 0.55rem, 97% 0.55rem, 58% 0.55rem;
  background-position:
    0 0,
    0 2.6rem, 0 4.35rem, 0 6.1rem, 0 7.85rem, 0 9.6rem,
    0 11.35rem, 0 13.1rem, 0 14.85rem, 0 16.6rem, 0 18.35rem;
  background-repeat: no-repeat;
}

/* On a phone the desktop rack would run to four screens of near-empty tan
   — a 24rem portrait and an eleven-line skeleton per person. The shapes
   stay the shapes: the portrait keeps its 3/4 upright at 70% of the
   column, and the bio's block clips to the name bar and five lines (the
   bars below 10.2rem fall outside the box), so the two of them still
   carry the page without owning most of its scroll. */
@media (max-width: 48rem) {
  .portrait { width: min(16rem, 70%); }
  .person .bio::before {
    height: 10.2rem;
    /* The same bars, with the fifth line taking the desktop block's short
       closer, so the clipped paragraph still ends the way a paragraph
       does; the lines below the box keep their sizes and stay clipped. */
    background-size:
      34% 0.9rem,
      100% 0.55rem, 95% 0.55rem, 98% 0.55rem, 90% 0.55rem, 58% 0.55rem,
      96% 0.55rem, 99% 0.55rem, 92% 0.55rem, 97% 0.55rem, 58% 0.55rem;
  }
}

/* ---- contact --------------------------------------------------------- */

/* The address is 23 characters of mono inside the aside column, whose inner
   width runs about 20rem at the widest layout down to 13.7rem at the stack
   breakpoint. Mono advance is ~0.6em, so the size is capped where the line
   keeps clear air before the panel's right edge across that whole range;
   the wrap rule is a guard for widths no layout above produces. Its ink is
   the panel's gold, 6.90 : 1 on the olive fill behind it. */
.mail {
  font-family: var(--mono);
  font-size: clamp(0.95rem, 0.5vw + 0.72rem, 1.1rem);
  display: inline-block;
  overflow-wrap: anywhere;
}
/* The address is the site's one primary action; on a phone it takes a
   44px tap target (0.6rem pads plus the 0.95rem line's 1.65 is 44.3px).
   Padding only — the gold on olive pairing above is untouched. */
@media (max-width: 48rem) {
  .mail { padding-block: 0.6rem; }
}

/* ---- footer ---------------------------------------------------------- */

/* The footer is a dark-brown band closing the page — the page brown at 65%. It
   carries the edge shade like the banner does, so the darkened margins run
   unbroken from the top of the screen to the bottom and the band's ends fall
   away with them. It sits 1.43 : 1 against the page, so the gold rule is what
   holds its top edge rather than decorating it. */
.site-footer {
  background-color: var(--brown-deep);
  background-image: var(--grain), var(--edge-shade);
  background-repeat: repeat, no-repeat;
  border-top: 1px solid var(--gold);
  padding-block: 2rem;
  font-size: 0.9rem;
  color: var(--ink-quiet);
}
.site-footer .wrap {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  flex-wrap: wrap;
}
.site-footer img { height: 30px; width: auto; display: block; }
.site-footer .meta { margin-left: auto; text-align: right; }

/* The footer carries the site's navigation a second time, so the way off
   a page's end does not require scrolling back up. Links take gold — the
   link ink of every dark ground — at 7.42:1 on the deep brown. */
.footer-nav {
  display: flex;
  gap: 1.1rem;
  flex-wrap: wrap;
}
.footer-nav a {
  color: var(--gold);              /* 7.42:1 on the deep brown */
  text-decoration: none;
}
.footer-nav a:hover { text-decoration: underline; }
/* The footer's nav takes the same fingertip allowance the header's tabs
   do: inline-block with 0.75rem pads lifts the 0.9rem line to a 47px
   target. The type and its gold stay as they are. */
@media (max-width: 48rem) {
  .footer-nav a { display: inline-block; padding-block: 0.75rem; }
}

/* ---- motion ----------------------------------------------------------- */

/* A page opens with its head rising one step into place. Enhancement only:
   the keyframes end at the resting state, so with animation removed — by
   the reduced-motion switch below or by a browser that never runs it — the
   still page is the same page. */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(0.6rem); }
  to   { opacity: 1; transform: none; }
}
.hero .stack,
.page-head .stack {
  animation: rise-in 480ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---- print ------------------------------------------------------------ */

/* On paper the whole scheme collapses to its light-ground register: white
   ground, dark ink. Gold cannot carry itself on white — 1.87:1 — so the
   gold token itself takes the dark ink, and every heading, link, hairline
   and eyebrow follows from that one move. The dark ink reads 17.71:1 on
   white and the quiet ink 16.90:1. Grounds, shades and grain fall away;
   fills become outlines; the lockup takes its mono black cut. The roundel
   is a two-colour construction a flat filter cannot invert, so it stays
   off the sheet and the footer's own name carries the line. */
@media print {
  :root {
    --gold: var(--ink-dark);       /* 17.71:1 on white */
    --ink: var(--ink-dark);
    --ink-quiet: var(--ink-dark-quiet);
    --hairline: rgba(36, 21, 8, 0.4);
  }
  html { background: #FFF none; }
  * { animation: none !important; transition: none !important; }
  .skip { display: none; }
  /* The placeholder racks are decorative empty states; on paper they would
     print as blank cards claiming nothing, so they stay off the sheet. */
  section.rack { display: none; }
  .site-header, .site-footer, .on-deep { background: none; }
  main, main > section { background-image: none; }
  .hero::before { display: none; }
  main .cols p, main .lede { color: var(--ink-dark-quiet); }
  .header-nav a,
  .header-nav a[aria-current="page"] {
    background: none;
    border-color: transparent;
    color: var(--ink-dark);
  }
  .panel {
    background: none;
    border-color: var(--ink-dark);
    box-shadow: none;
  }
  .cta,
  main .cta,
  main .hero .cta,
  main .close .cta {
    background: none;
    border: 2px solid var(--ink-dark);
    color: var(--ink-dark);
  }
  .brand img { filter: brightness(0); }   /* the lockup's mono black cut */
  .site-footer img { display: none; }
}
