/* Practice + Pixel: layout and components.
   Tokens live in tokens.css. The spacing scale there comes from the
   mark's 12-unit pitch; everything here is a multiple of --unit. */

/* ---- Two accessibility overrides -------------------------------
   Both are deliberate, and both are explained in README.md so they
   can be argued with. tokens.css is not modified.

   1. Secondary text. --muted (#6E747C) on --paper is 4.36:1, just
      under the 4.5:1 that WCAG AA asks of normal-size text. This
      nudges the *text* role four percent toward charcoal to reach
      4.76:1. --muted itself is untouched and still correct for the
      non-text roles the brand document gives it. Dark mode already
      clears AA and is left alone.

   2. Link colour. tokens.css sets `a { color: var(--ice) }`, but ice
      on paper is 3.14:1 and CLAUDE.md says never to set body copy in
      ice. Inline links therefore keep ink for the text and carry ice
      as a one-pixel underline, so ice stays the link signal without
      ever being the thing you have to read. */

:root { --ink-soft: #686E76; }

@media (prefers-color-scheme: dark) {
  :root { --ink-soft: #9AA1A9; }
}

/* Overscroll paints the canvas, not the nearest band, so rubber-banding
   past a dark footer showed a slab of paper below it, and past the light
   hero would show the reverse. The canvas is split at the halfway mark:
   paper above, night below, which is the right colour at both ends of
   the document no matter which way you pull. Bands paint over all of it
   in normal scrolling; this is only ever visible outside the page. */
html {
  background-image: linear-gradient(to bottom, #F5F6F7 0 50%, #16181C 50% 100%);
}

body { background: transparent; }

/* ---- Skip link ------------------------------------------------- */

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--bg);
  color: var(--ink);
  padding: var(--s-1) var(--s-2);
  z-index: 10;
}
.skip:focus { left: 0; }

/* ---- Shell ------------------------------------------------------ */

.wrap {
  max-width: 60rem;             /* 80 units */
  margin: 0 auto;
  padding-inline: var(--s-2);
}

@media (min-width: 48rem) {
  .wrap { padding-inline: var(--s-4); }
}

/* Two steps for large desktops, both still on the 12 grid: 88 units, then
   100. Line length is held by the ch measures on the elements themselves,
   so this buys the multi-column layouts room without stretching prose. */
@media (min-width: 84rem) {
  .wrap { max-width: 66rem; padding-inline: var(--s-6); }
}

@media (min-width: 100rem) {
  .wrap { max-width: 75rem; }
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

main { flex: 1; }

/* ---- Header ----------------------------------------------------- */

.site-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s-2);
  padding-block: var(--s-3);
}

.brand {
  display: block;
  line-height: 0;               /* no descender gap under the lockup */
  padding: var(--unit);
  margin: calc(var(--unit) * -1); /* clear space without shifting layout */
}

.brand img { width: 10.25rem; }

@media (min-width: 48rem) {
  .brand img { width: 11.5rem; }
}

.site-nav {
  display: flex;
  flex-wrap: wrap;              /* four links plus an action will not fit at 320px */
  align-items: center;
  gap: var(--s-1) var(--s-2);
  font-size: var(--t-sm);
}

.site-nav a {
  color: var(--ink-soft);
  text-decoration: none;
  padding-block: var(--unit);
}

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

.site-nav a[aria-current="page"] {
  color: var(--ink);
  box-shadow: inset 0 -1px 0 var(--ice);
}

/* ---- Hero ------------------------------------------------------- */
/* The one loud moment. Everything after this stays quiet. */

.hero { padding-block: var(--s-4) var(--s-3); }

@media (min-width: 48rem) {
  .hero { padding-block: var(--s-6) var(--s-6); }
}

.hero h1 {
  font-size: clamp(1.875rem, 4.4vw, 4rem);
  font-weight: 500;
  letter-spacing: -0.025em;
  max-width: 23ch;        /* "We handle the pixels." stays on one line */
}

.hero h1 .line { display: block; }

.hero .lede {
  margin-top: var(--s-3);
  font-size: var(--t-md);
  color: var(--ink-soft);
  max-width: 52ch;
}

/* ---- Sections --------------------------------------------------- */

.section { padding-block: var(--s-4); }

/* No rule between sections: the column grid and the spacing already
   separate them, and a full-width rule crossing six vertical ones turns
   the page into a table. */

/* Every h2 inside a .section is currently a .section-label. This sizes
   anything that is not, and is scoped so it cannot out-specify the label
   the way a bare `.section > h2` did. */
.section > h2:not(.section-label) {
  font-size: var(--t-lg);
  margin-bottom: var(--s-2);
}

.section p + p { margin-top: var(--s-2); }

.section p { margin-block: 0; }

/* Two-column split: heading rail on the left, prose on the right.
   Collapses to a single column below the breakpoint. */
.split {
  display: grid;
  gap: var(--s-2);
}

@media (min-width: 52rem) {
  .split {
    grid-template-columns: 14rem 1fr;
    gap: var(--s-4);
  }
  .split > h2 { margin-bottom: 0; }
}

/* ---- Work list -------------------------------------------------- */
/* Rule-separated rows rather than a row of matching cards. */

.work-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.work-list > li { border-top: 1px solid var(--rule); }

.work-item {
  display: grid;
  gap: var(--unit);
  padding-block: var(--s-3);
  text-decoration: none;
  color: inherit;
}

@media (min-width: 52rem) {
  .work-item {
    grid-template-columns: 14rem 1fr;
    gap: var(--s-4);
    align-items: baseline;
  }
}

.work-item h3 {
  font-size: var(--t-md);
  font-weight: 500;
}

.work-item .place {
  color: var(--ink-soft);
  font-size: var(--t-sm);
}

.work-item p {
  color: var(--ink-soft);
  margin: 0;
  max-width: 52ch;
}

a.work-item:hover h3 { box-shadow: inset 0 -1px 0 var(--ice); }

/* ---- Case studies ----------------------------------------------- */

.case { padding-block: var(--s-6); }

.case + .case { border-top: 1px solid var(--rule); }

.case h2 {
  font-size: var(--t-xl);
  max-width: 20ch;
}

.case .place {
  display: block;
  margin-top: var(--unit);
  font-size: var(--t-sm);
  color: var(--ink-soft);
  font-weight: 400;
  letter-spacing: 0;
}

.case-body { margin-top: var(--s-3); }

.case-body h3 {
  font-size: var(--t-base);
  font-weight: 500;
  color: var(--ink-soft);
  margin-top: var(--s-3);
  margin-bottom: var(--unit);
}

.case-body h3:first-child { margin-top: 0; }

/* ---- Figures ---------------------------------------------------- */

figure { margin: var(--s-4) 0 0; }

figcaption {
  margin-top: var(--unit);
  font-size: var(--t-sm);
  color: var(--ink-soft);
}

/* Frame standing in for artwork that has not been produced yet.
   Delete the frame and the .pending class along with the placeholder. */
.pending {
  border: 1px dashed var(--rule);
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  padding: var(--s-2);
  text-align: center;
  color: var(--ink-soft);
  font-size: var(--t-sm);
}

.pending.portrait { aspect-ratio: 4 / 5; max-width: 18rem; }

.shots {
  display: grid;
  gap: var(--s-2);
  margin-top: var(--s-4);
}

@media (min-width: 44rem) {
  .shots { grid-template-columns: 2fr 1fr; align-items: start; }
}

/* The phone figure is 442x952, so at the full width of its column it ran
   638px tall against a 461px desktop figure and dominated the row. Capped
   and centred in its column instead, which also keeps it from becoming a
   full-bleed slab on a phone. */
.shots figure:last-child .vid-hold,
.shots figure:last-child img {
  max-width: 15rem;
  margin-inline: auto;
}

.shots figure:last-child img { display: block; height: auto; }

/* ---- Prose ------------------------------------------------------ */

.prose a,
.notfound a,
.dl a {
  color: var(--ink);
  text-decoration: none;
  box-shadow: inset 0 -1px 0 var(--ice);
}

.prose a:hover,
.notfound a:hover,
.dl a:hover { box-shadow: inset 0 -2px 0 var(--ice); }


.prose p { margin-block: 0; }
.prose p + p { margin-top: var(--s-2); }
.prose h2 { margin-top: var(--s-4); font-size: var(--t-lg); }
.prose h2:first-child { margin-top: 0; }

.dl {
  margin: var(--s-2) 0 0;
  max-width: var(--measure);
}

.dl dt {
  font-weight: 500;
  margin-top: var(--s-2);
}

.dl dd {
  margin: 0;
  color: var(--ink-soft);
}

/* ---- Call to action --------------------------------------------- */

.cta { padding-block: var(--s-6); }

.cta p { margin-top: var(--s-2); }

.mail {
  display: inline-block;
  margin-top: var(--s-3);
  font-size: var(--t-md);
  color: var(--ink);
  text-decoration: none;
  box-shadow: inset 0 -1px 0 var(--ice);
  padding-bottom: 2px;
}

.mail:hover { box-shadow: inset 0 -2px 0 var(--ice); }

/* ---- Footer ----------------------------------------------------- */

.site-foot a { color: var(--ink-soft); }
.site-foot a:hover { color: var(--ink); box-shadow: inset 0 -1px 0 var(--ice); }

/* ---- 404 -------------------------------------------------------- */

.notfound { padding-block: var(--s-8) var(--s-6); }
.notfound h1 { font-size: var(--t-2xl); }
.notfound p { margin-top: var(--s-2); color: var(--ink-soft); }

/* ---- Rail ------------------------------------------------------- */
/* Heading plus a supporting image in the left column of a .split. */

.rail h2 { margin-bottom: var(--s-2); }

.rail figure { margin-top: 0; }

@media (min-width: 52rem) {
  .rail h2 { margin-bottom: var(--s-3); }
}

/* A mail link set inside running prose sits on the text baseline. */
.prose p > .mail { margin-top: 0; }

/* ---- Bands ------------------------------------------------------
   The page opens on paper and ends on night. The switch happens once,
   at the storyline, and the page stays dark from there down.

   This is a committed design rather than a system-preference one: the
   light half stays light for everyone, because a light-to-dark
   progression that only some visitors see is not a design, it is an
   accident of their settings. Ice does not change across the boundary,
   which is the brand's own rule and the reason the switch reads as
   deliberate.

   Each band redefines the ink tokens for everything inside it, so the
   components below need no dark-mode variants of their own. */

.band {
  background: var(--bg);
  color: var(--ink);
}

.band-light {
  --bg: #F5F6F7;          /* paper */
  --ink: #2E3238;         /* charcoal */
  --ink-soft: #686E76;    /* AA on paper, see the note at the top */
  --rule: #DEE2E5;
}

.band-dark {
  --bg: #16181C;          /* night */
  --ink: #EDE9E3;         /* bone */
  --ink-soft: #9AA1A9;
  --rule: #2C3138;
}

.band-dark + .band-dark { border-top: 0; }

/* A band's first section should not double its padding against the join. */
.band > .wrap > .section:first-child,
.band > .wrap > .cta:first-child { border-top: 0; }

/* ---- Storyline --------------------------------------------------- */

.story { padding-block: var(--s-8) var(--s-6); }

.story p {
  font-size: var(--t-md);
  max-width: 54ch;
}

.story p + p { margin-top: var(--s-2); }

.story .close {
  margin-top: var(--s-4);
  font-size: clamp(1.5rem, 3.6vw, var(--t-2xl));
  line-height: var(--leading-tight);
  letter-spacing: -0.02em;
  max-width: 26ch;
  font-weight: 500;
}

/* ---- Services ---------------------------------------------------- */

.services {
  list-style: none;
  margin: 0;
  padding: 0;
}

.services > li {
  border-top: 1px solid var(--rule);
  padding-block: var(--s-3);
}

.services h3 {
  font-size: var(--t-md);
  font-weight: 500;
  margin-bottom: var(--unit);
}

@media (min-width: 52rem) {
  .services > li {
    display: grid;
    grid-template-columns: 14rem 1fr;
    gap: var(--s-4);
  }
  .services h3 { margin-bottom: 0; }
}

.services p {
  margin: 0;
  color: var(--ink-soft);
  max-width: 54ch;
}

.not-offered {
  margin-top: var(--s-4);
  color: var(--ink-soft);
  max-width: 54ch;
}

/* ---- Utilities --------------------------------------------------- */

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* ---- Service rows ------------------------------------------------ */

.services h3 .tag {
  display: block;
  margin-top: 2px;
  font-size: var(--t-sm);
  font-weight: 400;
  color: var(--ink-soft);
  letter-spacing: 0;
}

.ticks {
  margin: var(--s-2) 0 0;
  padding: 0;
  list-style: none;
  max-width: 54ch;
}

.ticks li {
  position: relative;
  padding-left: var(--s-2);
  margin-bottom: 6px;
  color: var(--ink-soft);
}

/* A 4px square rather than a bullet or a tick glyph: the mark's unit,
   scaled down, doing the job a dingbat would otherwise do. */
.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 4px; height: 4px;
  background: var(--ice);
}

.fine {
  margin-top: var(--s-2);
  font-size: var(--t-sm);
  color: var(--ink-soft);
  max-width: 54ch;
}

/* ---- Rate tables ------------------------------------------------- */

.table-scroll { overflow-x: auto; margin-bottom: var(--s-4); }

.rates {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
  min-width: 30rem;
}

.rates caption {
  text-align: left;
  color: var(--ink-soft);
  padding-bottom: var(--s-1);
}

.rates th,
.rates td {
  text-align: left;
  padding: var(--s-1) var(--s-2) var(--s-1) 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: baseline;
}

.rates thead th {
  font-weight: 400;
  color: var(--ink-soft);
  border-bottom-color: var(--ink-soft);
}

.rates tbody th { font-weight: 500; }

.rates td { font-variant-numeric: tabular-nums; }

/* ---- Process ----------------------------------------------------- */

.process {
  list-style: none;
  counter-reset: step;
  margin: var(--s-2) 0 0;
  padding: 0;
}

.process > li {
  counter-increment: step;
  position: relative;
  padding-left: var(--s-4);
  padding-block: var(--s-2);
  border-top: 1px solid var(--rule);
}

.process > li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: calc(var(--s-2) + 0.15em);
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

.process h3 {
  font-size: var(--t-base);
  font-weight: 500;
  margin-bottom: 2px;
}

.process p { margin: 0; color: var(--ink-soft); max-width: 54ch; }

/* Grid and flex children default to min-width:auto, which lets a wide
   child (the rates table) push the column open instead of scrolling
   inside it. This is what makes .table-scroll actually scroll. */
.split > *,
.work-item > *,
.services > li > * { min-width: 0; }

.table-scroll { max-width: 100%; }

/* Links in running text keep ink with an ice underline everywhere, not
   just inside .prose, since a bare ice link reads as a stray colour. */
.not-offered a,
.services a,
.fine a {
  color: var(--ink);
  text-decoration: none;
  box-shadow: inset 0 -1px 0 var(--ice);
}

.not-offered a:hover,
.services a:hover,
.fine a:hover { box-shadow: inset 0 -2px 0 var(--ice); }


/* ---- Home ---------------------------------------------------------
   Home runs on five short moves and a lot of air. Anything that wants a
   paragraph lives on services/, work/ or about/. */

.hero .proof {
  margin-top: var(--s-2);
  font-size: var(--t-sm);
  color: var(--ink-soft);
  max-width: 46ch;
}

/* The second scale peak, after the hero. It is the whole reason the page
   goes dark here, so it gets the space to land. */
.statement {
  padding-block: var(--s-12) var(--s-8);
}

.statement p {
  margin: 0;
  font-size: clamp(1.625rem, 4vw, 3.5rem);
  line-height: 1.14;
  letter-spacing: -0.025em;
  font-weight: 500;
  max-width: 34ch;        /* both lines fit whole; the <br> does the breaking */
  text-wrap: balance;
}

/* A quiet label rather than a heading competing with the content under it.
   Not tracked out, not upper-case, just small and set back. */
.section-label {
  font-size: var(--t-base);
  font-weight: 400;
  color: var(--ink-soft);
  margin-bottom: var(--s-2);
}

.offer { margin: 0; }

.offer dt {
  font-weight: 500;
  padding-top: var(--s-2);
}

.offer dd {
  margin: 0 0 var(--s-2);
  color: var(--ink-soft);
  max-width: 52ch;
  min-width: 0;
}

@media (min-width: 48rem) {
  .offer {
    display: grid;
    grid-template-columns: 12rem minmax(0, 1fr);
    column-gap: 0;                /* a gap here breaks each row's rule in two */
  }
  .offer dt { padding-right: var(--s-4); }
  .offer dt,
  .offer dd {
    padding-top: var(--s-2);
    padding-bottom: var(--s-2);
    margin: 0;
  }
}

p.more { margin-top: var(--s-4); }

.more a {
  color: var(--ink);
  text-decoration: none;
  box-shadow: inset 0 -1px 0 var(--ice);
}

.more a:hover { box-shadow: inset 0 -2px 0 var(--ice); }

/* ---- Hero grid ----------------------------------------------------
   The headline alone left the right two-thirds of the container empty,
   which reads as an unfinished layout rather than as breathing room. The
   mark at scale holds that side. It is the brand's own asset, shown
   unaltered, with one grid unit of clear space as the brand document
   requires. */

.hero-grid { display: grid; gap: var(--s-4); }

.hero-mark {
  margin: 0;
  padding: var(--unit);
  line-height: 0;
}

.hero-mark { display: none; }

/* Two columns from the same breakpoint the nav goes horizontal at, so
   there is no width where the page looks like desktop but has lost its
   one graphic. The mark scales with the room available. */
@media (min-width: 48rem) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--s-4);
  }
  .hero-mark { display: block; }
  .hero-mark svg { width: 168px; height: auto; }
}

@media (min-width: 60rem) {
  .hero-grid { gap: var(--s-6); }
  .hero-mark svg { width: 240px; }
}

/* The twelve-discipline list that used to live here was cut. It said
   "practice is a broad word" by enumerating, which reads as keyword
   stuffing, and its own caveat underneath contradicted it: naming a
   field in 31px type and then admitting you have not worked in it is a
   small bait-and-switch. One sentence does the same work honestly. */

.broad {
  margin: var(--s-2) 0 0;
  font-size: var(--t-lg);
  line-height: 1.3;
  letter-spacing: -0.015em;
  max-width: 34ch;
}

@media (min-width: 52rem) {
  .broad { font-size: var(--t-xl); max-width: 30ch; }
}

p.aside {
  margin-top: var(--s-6);
  color: var(--ink-soft);
  max-width: 54ch;
}

/* The column grid was removed. It made the desktop layout read as
   constructed, but it never worked on a phone and it was doing that job
   at the cost of a busier page than this brand wants. Composition and
   spacing have to carry it instead. */

/* ---- Buttons ------------------------------------------------------
   Every reference site leads with a real button, and a practice owner
   should not have to work out that an underlined address is the action.
   Filled in ink rather than ice: ice stays for links, focus rings and
   the accent square, per the brand document. Square corners, since the mark
   has no radius anywhere in it. */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  background: var(--ink);
  color: var(--bg);
  text-decoration: none;
  font-size: var(--t-base);
  font-weight: 500;
  padding: var(--s-1) var(--s-2);
  border: 1px solid var(--ink);
}

.btn::before {
  content: "";
  width: 8px;
  height: 8px;
  background: var(--ice);
  flex: none;
}

.btn:hover { background: transparent; color: var(--ink); }

.btn--quiet {
  background: transparent;
  color: var(--ink);
  font-size: var(--t-sm);
  padding: 6px var(--s-1);
}

.btn--quiet:hover { background: var(--ink); color: var(--bg); }

/* ---- Labels -------------------------------------------------------
   A square, then the label. Sentence case and untracked, since CLAUDE.md
   rules out the all-caps tracked-out eyebrow, and the square is the
   thing that makes it ours rather than a generic kicker. */

.section-label {
  display: flex;
  align-items: baseline;
  gap: var(--s-1);
}

.section-label::before {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--ice);
  flex: none;
  transform: translateY(-1px);
}

.cta-actions { margin-top: var(--s-3); }

.cta .mail {
  margin-top: var(--s-2);
  font-size: var(--t-sm);
  display: inline-block;
}

/* ---- The travelling pixel -----------------------------------------
   One ice square, riding the same six columns the page is drawn on,
   stepping left to right as you move down the document. It is the
   mark's detached square doing a job (how far through this you are)
   rather than decoration.

   Deliberately not an animation of the mark itself. The brand document
   rules out moving the accent square into the letterform, which is the
   animation everyone reaches for first, and a logo that assembles on
   load reads as a template.

   Pure CSS: a scroll-driven timeline, no script, nothing to load. Where
   scroll timelines are unsupported the square is hidden rather than
   parked somewhere meaningless, and reduced-motion hides it outright. */

/* The hero carries the primary action, as every reference site does.
   The line under it is the reassurance, not a credential; the résumé
   belongs on about/, and naming two industries in the hero quietly
   tells everyone else this is not for them. */
.hero-action { margin-top: var(--s-3); }

.hero .proof { margin-top: var(--s-2); }

/* ---- Work cards ---------------------------------------------------
   Proof, shown rather than described. Not the identical rounded cards
   with a shadow under each that CLAUDE.md rules out: no radius, no
   shadow, no box. The screenshot is the object; the rule under it is
   the only chrome. */

.work-cards {
  list-style: none;
  margin: var(--s-2) 0 0;
  padding: 0;
  display: grid;
  gap: var(--s-4);
}

@media (min-width: 44rem) {
  .work-cards { grid-template-columns: 1fr 1fr; gap: var(--s-4); }
}

/* Three pieces of work, so two columns leave the third stranded on a row
   of its own and cost most of a screen in height for nothing. One row of
   three, from the width where a card is still big enough to read. */
@media (min-width: 58rem) {
  .work-cards {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-2);
  }
}

.work-cards a {
  display: block;
  text-decoration: none;
  color: inherit;
}

.work-cards .shot {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  margin-bottom: var(--s-2);
}

.work-cards img.shot {
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
  border-top: 0;
}

.work-cards h3 {
  font-size: var(--t-md);
  font-weight: 500;
}

.work-cards .place {
  display: block;
  margin-top: 2px;
  font-size: var(--t-sm);
  color: var(--ink-soft);
}

.work-cards .note {
  display: block;
  margin-top: var(--s-1);
  color: var(--ink-soft);
  max-width: 42ch;
}

.work-cards a:hover h3 { box-shadow: inset 0 -1px 0 var(--ice); }

/* The couplet, now closing the name section on about/ rather than
   interrupting Home. */
.statement-line {
  margin-top: var(--s-4);
  font-size: clamp(1.5rem, 3.6vw, 2.25rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 500;
  max-width: 30ch;
}

/* ---- Footer -------------------------------------------------------
   One row: who made it, the legal line, two ways to reach it. It had
   grown into three stacked regions with an availability sentence that
   wrapped to three lines, which is a lot of page for a colophon. The
   availability moved to contact/, where somebody deciding whether to
   write is actually standing. */

.site-foot {
  border-top: 1px solid var(--rule);
  margin-top: var(--s-4);
  padding-block: var(--s-2);
  display: grid;
  gap: var(--s-1) var(--s-2);
  font-size: var(--t-sm);
  color: var(--ink-soft);

  /* One size and one line box for everything in the row. The three
     columns are then the same height, so centring them actually centres
     them. It was three sizes and three box heights before, which is why
     nothing lined up. */
  line-height: 1.5;
}

.site-foot p,
.site-foot a { font-size: var(--t-sm); }

/* Three positions on one line: who made it at the left, the legal line
   truly centred rather than nudged, the icons at the right. Below the
   breakpoint it stacks, because a phone has no middle to speak of.

   minmax(0, 1fr) rather than 1fr: a plain 1fr floors at min-content, so a
   long brand block widens its own column and shoves the centre off. */
@media (min-width: 62rem) {
  .site-foot {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
  }
  .foot-brand { justify-self: start; }
  .foot-legal { justify-self: center; }
  .foot-social { justify-self: end; }
}

.site-foot p { margin: 0; max-width: none; }

/* The row is only level while this stays on one line. Wrapped, the brand
   became two lines against a 22px icon and the icons dropped 16px below
   the logo, which was the misalignment. Nothing here wraps mid-phrase
   now, and the whole footer stacks rather than half-wrapping. */
.foot-brand {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
}

@media (min-width: 62rem) {
  .credit, .avail { white-space: nowrap; }
}

.credit {
  display: flex;
  align-items: center;
  gap: var(--unit);
  color: var(--ink);
}

.credit svg { width: 12px; height: auto; flex: none; }

.foot-social {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: var(--unit);
}

.foot-social a {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  color: var(--ink-soft);
}

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

/* Schibsted's ink sits about a pixel above the middle of its own line
   box, so the boxes being level leaves the drawn glyphs looking a pixel
   high against the mark and the icons. This gives that back. */
.credit svg,
.foot-social a { transform: translateY(-1px); }
.foot-social svg { width: 16px; height: 16px; }

/* ---- The turn -----------------------------------------------------
   The page goes dark here, and this is what the switch is for: the
   light half is the promise, this is the honest part. It is also the
   only place the reader is invited to recognise their own site, which
   the page had no moment for at all. */

.turn { padding-block: var(--s-6) var(--s-4); }

/* This was set at 40px over seven lines, which repeated the hero's
   register instead of contrasting with it, and CLAUDE.md asks for the
   boldness to be spent on the hero. The teardown underneath carries the
   argument now, so the statement only has to open it. */
.turn-lead {
  margin: var(--s-2) 0 0;
  font-size: clamp(1.25rem, 2.4vw, 2.125rem);
  line-height: 1.25;
  letter-spacing: -0.018em;
  max-width: none;
}

.turn-note {
  margin-top: var(--s-4);
  color: var(--ink-soft);
  max-width: 46ch;
}

/* ---- Teardown -------------------------------------------------------
   The recognition beat, made operable. A drawing of the site that keeps
   turning up, and four things wrong with it that the visitor steps
   through at their own pace, then the same page restructured.

   Deliberately abstract. Naming a real practice as the bad example is
   not something this site can do, and a stock "bad website" screenshot
   would be a lie about what actually arrives.

   The drawing is aria-hidden; every step's meaning is in the panel text.
   Same opt-in rule as the other two enhancements: with no script the
   step list is hidden, all five panels show in order, and the drawing
   sits in its undimmed "before" state. */

.teardown { margin-top: var(--s-4); display: grid; gap: var(--s-3); }

@media (min-width: 56rem) {
  .teardown {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    column-gap: var(--s-4);
    align-items: start;
  }
}

.wire-hold { margin: 0; }

.wire-hold figcaption {
  margin-top: var(--unit);
  font-size: var(--t-xs);
  color: var(--ink-soft);
  max-width: 34ch;
}

.wire { display: block; width: 100%; height: auto; }

.wire .frame { fill: none; stroke: var(--rule); }
.wire .rule  { stroke: var(--rule); }
.wire .blk   { fill: currentColor; opacity: 0.17; }
.wire .bar   { fill: currentColor; opacity: 0.42; }
.wire .bar.strong { opacity: 0.6; }
.wire .dot   { fill: currentColor; opacity: 0.42; }
.wire .ice   { fill: var(--ice); }

.wire .before,
.wire .after,
.wire .before > g,
.wire .halo { transition: opacity 220ms ease; }

/* The two states are stacked. Only .before renders until the script
   runs, so a scriptless page never sees half a drawing. */
.wire .after { opacity: 0; visibility: hidden; }
.wire[data-step="after"] .before { opacity: 0; visibility: hidden; }
.wire[data-step="after"] .after { opacity: 1; visibility: visible; }

/* Dimming sits on the region group, so the fill opacities above multiply
   down instead of being restated per state. */
.wire[data-step] .before > g { opacity: 0.45; }
.wire[data-step] .before > g[data-on] { opacity: 1; }

.wire .halo { fill: none; stroke: var(--ice); stroke-width: 1.5; opacity: 0; }
.wire[data-step] .before > g[data-on] .halo { opacity: 1; }

.steplist { list-style: none; margin: 0; padding: 0; display: none; }
.teardown[data-ready] .steplist { display: block; }

.steplist button {
  font: inherit;
  font-size: var(--t-sm);
  display: flex;
  align-items: baseline;
  gap: var(--s-1);
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-left: 2px solid var(--rule);
  padding: 10px var(--s-1);
  color: var(--ink-soft);
  cursor: pointer;
}

.steplist button:hover { color: var(--ink); border-left-color: var(--ink-soft); }

.steplist button[aria-selected="true"] {
  color: var(--ink);
  border-left-color: var(--ice);
}

.steplist .n { font-variant-numeric: tabular-nums; opacity: 0.65; }

/* The last step is the resolution, not another fault. */
.steplist li:last-child { margin-top: var(--s-1); }

.steplist button:focus-visible,
.steplist button[aria-selected="true"]:focus {
  outline: 2px solid var(--ice);
  outline-offset: 2px;
}

.steppanel h3 { font-size: var(--t-md); text-wrap: balance; }
.steppanel p { margin: var(--unit) 0 0; color: var(--ink-soft); max-width: 44ch; }
.steppanel:focus-visible { outline: 2px solid var(--ice); outline-offset: 6px; }

.steppanel + .steppanel { margin-top: var(--s-3); }
.teardown[data-ready] .steppanel { margin-top: var(--s-3); }

/* Reserve the tallest panel so stepping does not move the drawing. */
.teardown[data-ready] .steppanel { min-height: 12rem; }

@media (min-width: 56rem) {
  .teardown[data-ready] .steppanel { min-height: 10rem; }
}

/* The closing ask was set at paragraph scale and filled a quarter of the
   page. It is the last thing anyone reads; it should carry.

   It is a paragraph rather than a heading now. Every section on the site
   opens with one h2.section-label and then says its piece, and the CTA
   was the one place that broke the pattern: no label, and a heading at
   display size where the others have a quiet one. The label is the
   heading; this is the statement under it, as on .turn. */
.cta-lead {
  margin-top: var(--s-2);
  font-size: clamp(1.75rem, 3.6vw, 3.25rem);
  line-height: 1.15;
  letter-spacing: -0.022em;
  max-width: 22ch;
}

/* ---- Mobile navigation --------------------------------------------
   Four links wrapping under the lockup was fine as a fallback and wrong
   as a design: it read as leftover text rather than as navigation, and
   it pushed the headline down the phone screen.

   The collapse is opt-in by the script: nothing is hidden until nav.js
   has set data-nav-open on the header. Without JavaScript the links
   stay visible and the toggle never appears, so the site is still
   navigable rather than trapping its own nav behind a button that
   cannot work. */

.nav-toggle {
  display: none;                       /* only shown once the script runs */
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-right: calc(var(--unit) * -1);
  padding: 0;
  background: none;
  border: 0;
  color: var(--ink);
  cursor: pointer;
}

.nav-toggle svg { stroke: currentColor; stroke-width: 1.5; fill: none; }
.nav-toggle .i-shut { display: none; }
.nav-toggle[aria-expanded="true"] .i-open { display: none; }
.nav-toggle[aria-expanded="true"] .i-shut { display: block; }

@media (max-width: 47.999rem) {
  .site-head { flex-wrap: wrap; align-items: center; }

  .site-head[data-nav-open] .nav-toggle { display: inline-flex; }
  .site-head[data-nav-open] .site-nav { display: none; }
  .site-head[data-nav-open="true"] .site-nav { display: flex; }

  .site-nav {
    width: 100%;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding-top: var(--s-2);
  }

  .site-nav a {
    width: 100%;
    padding-block: var(--s-1);
    font-size: var(--t-md);
    color: var(--ink);
  }

  .site-nav a[aria-current="page"] { box-shadow: none; color: var(--ink-soft); }
}

@media (min-width: 48rem) {
  .nav-toggle { display: none !important; }
}

/* ---- Tabs ---------------------------------------------------------
   The strip is hidden until the script marks the set ready, so with no
   JavaScript this is four headed sections and every panel is readable.
   Nothing is ever locked behind a control that cannot work. */

.tabs { margin-top: var(--s-2); }

.tablist { display: none; }

.tabs[data-ready] .tablist {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
  margin-bottom: var(--s-3);
}

.tablist button {
  font: inherit;
  font-size: var(--t-sm);
  color: var(--ink-soft);
  background: none;
  border: 1px solid var(--rule);
  padding: 8px var(--s-2);
  cursor: pointer;
}

.tablist button:hover { color: var(--ink); border-color: var(--ink-soft); }

.tablist button[aria-selected="true"] {
  color: var(--bg);
  background: var(--ink);
  border-color: var(--ink);
}

.tabpanel:focus-visible { outline: 2px solid var(--ice); outline-offset: 6px; }

/* Without the script each panel needs its own separation; with it there
   is only ever one, so it needs none. */
.tabpanel + .tabpanel { margin-top: var(--s-3); }
.tabs[data-ready] .tabpanel + .tabpanel { margin-top: 0; }

.panel-name {
  font-size: var(--t-lg);
  margin-bottom: var(--s-2);
}

.tabs[data-ready] .panel-name { display: none; }

.panel-lead {
  margin: 0;
  font-size: var(--t-md);
  max-width: 52ch;
}

.panel-price {
  display: block;
  margin-top: var(--unit);
  font-size: var(--t-sm);
  color: var(--ink-soft);
}

/* A floor tall enough for the longest panel, so switching tabs does not
   move everything below it. Measured, not guessed: the tallest panel is
   422px at 390 and 281px from 768 up. */
.tabs[data-ready] .tabpanel { min-height: 27rem; }

@media (min-width: 48rem) {
  .tabs[data-ready] .tabpanel { min-height: 18rem; }
}

/* Tabs are reached by keyboard, so they need the same ice ring as
   everything else. focus-visible alone does not fire for the
   programmatic focus() the arrow keys use, so this covers both. */
.tablist button:focus-visible,
.tablist button[aria-selected="true"]:focus {
  outline: 2px solid var(--ice);
  outline-offset: 2px;
}


/* ---- The pixel builds the practice ----------------------------------
   Hero only. The header lockup and footer mark are still.

   The ice pixel enters at the foot of the stem, climbs it, lays the
   middle bar, climbs the open side of the bowl, lays the top bar, comes
   back down to finish the stem, then threads out through the open bowl
   and settles where the brand document puts it. Nine squares appear
   exactly where it passes. Every move is one cell along a row or column.

   Build: 0.3s in, 0.36s per cell, 12 cells, done at 4.62s. Then the
   letterform is complete and never changes again. The pixel keeps one
   habit: every 9s it slips back into the bowl, touches the stem,
   and returns. The P is always whole; only the pixel is ever away.

   Reduced motion: the finished mark, from the first frame. */

.mark-anim .sq {
  transform-box: fill-box;
  transform-origin: center;
}

.mark-anim .body {
  opacity: 0;
  animation: lay 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.mark-anim .c-10-58 { animation-delay: 0.30s; }
.mark-anim .c-10-46 { animation-delay: 0.66s; }
.mark-anim .c-10-34 { animation-delay: 1.02s; }
.mark-anim .c-22-34 { animation-delay: 1.38s; }
.mark-anim .c-34-34 { animation-delay: 1.74s; }
.mark-anim .c-34-10 { animation-delay: 2.46s; }
.mark-anim .c-22-10 { animation-delay: 2.82s; }
.mark-anim .c-10-10 { animation-delay: 3.18s; }
.mark-anim .c-10-22 { animation-delay: 3.54s; }

@keyframes lay {
  from { opacity: 0; transform: scale(0.4); }
  to   { opacity: 1; transform: none; }
}

.mark-anim .sq-pixel {
  animation:
    build 4.32s linear 0.3s both,
    excursion 9s cubic-bezier(0.45, 0, 0.25, 1) 7.62s infinite;
}

@keyframes build {
  0.000% { transform: translate(-36px, 36px); }
  2.333% { transform: translate(-36px, 36px); }
  8.333% { transform: translate(-36px, 24px); }
  10.667% { transform: translate(-36px, 24px); }
  16.667% { transform: translate(-36px, 12px); }
  19.000% { transform: translate(-36px, 12px); }
  25.000% { transform: translate(-24px, 12px); }
  27.333% { transform: translate(-24px, 12px); }
  33.333% { transform: translate(-12px, 12px); }
  35.667% { transform: translate(-12px, 12px); }
  41.667% { transform: translate(-12px, 0px); }
  44.000% { transform: translate(-12px, 0px); }
  50.000% { transform: translate(-12px, -12px); }
  52.333% { transform: translate(-12px, -12px); }
  58.333% { transform: translate(-24px, -12px); }
  60.667% { transform: translate(-24px, -12px); }
  66.667% { transform: translate(-36px, -12px); }
  69.000% { transform: translate(-36px, -12px); }
  75.000% { transform: translate(-36px, 0px); }
  77.333% { transform: translate(-36px, 0px); }
  83.333% { transform: translate(-24px, 0px); }
  85.667% { transform: translate(-24px, 0px); }
  91.667% { transform: translate(-12px, 0px); }
  94.000% { transform: translate(-12px, 0px); }
  100.000% { transform: translate(0px, 0px); }
}

@keyframes excursion {
  0.000% { transform: translate(0px, 0px); }
  3.333% { transform: translate(-12px, 0px); }
  6.667% { transform: translate(-24px, 0px); }
  10.000% { transform: translate(-36px, 0px); }
  15.333% { transform: translate(-36px, 0px); }
  18.667% { transform: translate(-24px, 0px); }
  22.000% { transform: translate(-12px, 0px); }
  25.333% { transform: translate(0px, 0px); }
  100% { transform: translate(0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .mark-anim .sq { animation: none !important; transform: none !important; opacity: 1 !important; }
}

/* ---- Work recordings ------------------------------------------------
   A silent scroll through a finished site, which is the closest this
   page gets to handing someone the real thing.

   Ships as a plain <video controls>: correct with no script, and correct
   under reduced motion, where it stays a poster until asked. Only when
   motion is welcome does the script drop the controls and run it as a
   loop, and it adds its own pause button when it does, because WCAG
   2.2.2 wants a way to stop anything moving for more than five seconds. */

.vid-hold { position: relative; }

.shot-video { display: block; width: 100%; height: auto; background: var(--rule); }

.vid-toggle {
  position: absolute;
  right: var(--unit);
  bottom: var(--unit);
  font: inherit;
  font-size: var(--t-xs);
  color: #EDE9E3;
  background: rgba(22, 24, 28, 0.82);
  border: 1px solid rgba(237, 233, 227, 0.3);
  padding: 6px 10px;
  cursor: pointer;
}

.vid-toggle:hover { border-color: rgba(237, 233, 227, 0.62); }

.vid-toggle:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }

/* ---- Section rail ---------------------------------------------------
   A long home page gave no sense of where you were in it or what was
   left. The square that used to travel across the top of the window does
   that job vertically now, and carries the section names with it, so it
   answers "what am I reading" and not only "how far down am I".

   It is real navigation, not an ornament: a nav landmark of in-page
   links, keyboard reachable, with aria-current on the section you are
   in. Built by script from the sections themselves, so there is no
   second list of headings to keep in step with the first.

   Shown only from 84rem, which is the width at which it fits beside the
   content column instead of on top of it. */

.rail-nav {
  position: fixed;
  left: var(--s-6);
  top: 50%;
  transform: translateY(-50%);
  z-index: 15;
  display: none;

  /* It floats over whichever band is behind it, and the band changes as
     you scroll, so the script tells it which one it is on. */
  --rail-dim: #686E76;
  --rail-on: #2E3238;
}

.rail-nav[data-on-dark] {
  --rail-dim: #9AA1A9;
  --rail-on: #EDE9E3;
}

@media (min-width: 84rem) {
  .rail-nav:not([hidden]) { display: block; }
}

.rail-nav ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 2px;
}

.rail-nav a {
  display: flex;
  align-items: center;
  gap: var(--unit);
  padding: 5px 0;
  font-size: var(--t-xs);
  line-height: 1.3;
  color: var(--rail-dim);
  text-decoration: none;
  transition: color 200ms ease;
}

/* The mark's square, at the size the ticks use. */
.rail-nav a::before {
  content: "";
  width: 6px;
  height: 6px;
  flex: none;
  background: currentColor;
  transition: background-color 200ms ease, transform 200ms ease;
}

.rail-nav a:hover { color: var(--rail-on); }

.rail-nav [aria-current="true"] { color: var(--rail-on); }

.rail-nav [aria-current="true"]::before {
  background: var(--ice);
  transform: scale(1.5);
}

.rail-nav a:focus-visible { outline: 2px solid var(--ice); outline-offset: 3px; }

/* ---- Breadcrumbs ----------------------------------------------------
   Small, quiet, and above the h1 rather than competing with it. A real
   ol inside a labelled nav, so it is a list to a screen reader and
   skippable, with the current page as text rather than a dead link.

   The separator is a light slash on the li, not a character in the
   markup, so it is never read aloud and never selectable with the text. */

.crumbs {
  padding-top: var(--s-4);
  font-size: var(--t-sm);
  color: var(--ink-soft);
}

.crumbs ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--unit);
}

.crumbs li { display: flex; align-items: baseline; gap: var(--unit); }

.crumbs li + li::before {
  content: "/";
  color: var(--rule);
}

.crumbs a { color: var(--ink-soft); text-decoration: none; }
.crumbs a:hover { color: var(--ink); box-shadow: inset 0 -1px 0 var(--ice); }
.crumbs [aria-current] { color: var(--ink); }

/* The hero owns the top padding on pages without a breadcrumb. With one,
   the breadcrumb owns it and the hero closes up. */
.crumbs + .hero { padding-top: var(--s-3); }

/* ---- Work index -----------------------------------------------------
   /work/ was one page carrying every case study end to end, so the third
   one was three screens down and nothing had a URL of its own. It is an
   index now, and each project is its own page.

   Rule-separated rows rather than a row of matching cards: no radius, no
   shadow, no box, per CLAUDE.md. The screenshot is the object and the
   rule above it is the only chrome. */

.work-index {
  list-style: none;
  margin: var(--s-4) 0 0;
  padding: 0;
}

.work-index > li { border-top: 1px solid var(--rule); }

.work-index a {
  display: grid;
  gap: var(--s-2);
  padding-block: var(--s-4);
  text-decoration: none;
  color: inherit;
}

@media (min-width: 52rem) {
  .work-index a {
    grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
    gap: var(--s-4);
    align-items: start;
  }
}

.work-index-shot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top center;
}

.work-index-body { display: block; }

.work-index-name {
  display: block;
  font-size: var(--t-lg);
  font-weight: 500;
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
}

.work-index-body .place {
  display: block;
  margin-top: var(--unit);
  font-size: var(--t-sm);
  color: var(--ink-soft);
}

.work-index-body .summary {
  display: block;
  margin-top: var(--s-2);
  color: var(--ink-soft);
  max-width: 46ch;
}

/* Reads as the affordance without an arrow bolted onto it. */
.work-index-go {
  display: inline-block;
  margin-top: var(--s-2);
  font-size: var(--t-sm);
  color: var(--ink);
  box-shadow: inset 0 -1px 0 var(--rule);
}

.work-index a:hover .work-index-go { box-shadow: inset 0 -1px 0 var(--ice); }
.work-index a:hover .work-index-name { box-shadow: inset 0 -1px 0 var(--ice); }
.work-index a:focus-visible { outline: 2px solid var(--ice); outline-offset: 4px; }

/* ---- Case page ------------------------------------------------------ */

.case-meta {
  margin-top: var(--unit);
  font-size: var(--t-sm);
  color: var(--ink-soft);
}

.case-next {
  margin-top: var(--s-6);
  padding-top: var(--s-2);
  border-top: 1px solid var(--rule);
  font-size: var(--t-sm);
}

.case-next a { color: var(--ink); text-decoration: none; box-shadow: inset 0 -1px 0 var(--ice); }

/* ---- Work tabs ------------------------------------------------------
   Three projects on one page, one at a time. The same control the
   services use, so there is no second interaction to learn, and reading
   the second project costs nothing: no page load, no scroll back.

   Deliberately not a carousel. Nothing moves unless it is asked to. */

.work-tabs { margin-top: var(--s-4); }

.work-tabs .tablist { gap: var(--unit); }

.work-tabs .tabpanel > h2 {
  font-size: var(--t-xl);
  letter-spacing: -0.02em;
}

.work-tabs .case-meta { margin-top: var(--unit); }

.work-tabs .panel-summary {
  margin-top: var(--s-2);
  font-size: var(--t-md);
  color: var(--ink-soft);
  max-width: 52ch;
}

/* The recordings lead, then the account of what was wrong. Showing the
   thing before explaining it is the whole argument of the page. */
.work-tabs .shots { margin-top: var(--s-4); }
.work-tabs .case-body { margin-top: var(--s-4); }

/* Without script every project renders in full, one after another, so
   each needs separating. With it there is only ever one on screen. */
.work-tabs .tabpanel + .tabpanel {
  margin-top: var(--s-6);
  padding-top: var(--s-6);
  border-top: 1px solid var(--rule);
}

.work-tabs[data-ready] .tabpanel + .tabpanel {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* The services panels reserve a fixed height so switching does not move
   the page. These are far too long and too uneven for that to help. */
.work-tabs[data-ready] .tabpanel { min-height: 0; }

/* ---- Services page --------------------------------------------------
   The four offers, the packages and the process were three static lists
   stacked down a white page. They are three tab sets now, the same
   control Home and work/ use, so the page is a set of short choices
   rather than a long scroll. */

.section-lead {
  margin: 0 0 var(--s-3);
  color: var(--ink-soft);
  max-width: 54ch;
}

.care-lead { margin-top: var(--s-6); }

/* Package tiers. The tab carries the name and the price together, so the
   number is visible before anything is opened: a table's whole advantage
   was letting people compare without clicking, and this keeps it. */

.tier-price { margin-left: var(--unit); opacity: 0.7; }

.tiers[data-ready] .tabpanel { min-height: 12rem; }

.spec {
  margin: var(--s-3) 0 0;
  display: grid;
  gap: var(--s-2);
}

@media (min-width: 34rem) {
  .spec { grid-template-columns: repeat(2, minmax(0, 14rem)); }
}

.spec dt {
  font-size: var(--t-sm);
  color: var(--ink-soft);
}

.spec dd {
  margin: 2px 0 0;
  font-size: var(--t-md);
}

/* The asterisk is a real link to the note, not a floating glyph. */
.asterisk {
  color: var(--ice);
  text-decoration: none;
  font-size: var(--t-sm);
  vertical-align: super;
  margin-left: 2px;
}

.asterisk:hover { box-shadow: inset 0 -1px 0 var(--ice); }

.scope-note {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--rule);
  display: grid;
  gap: var(--s-2);
}

@media (min-width: 34rem) {
  .scope-note { grid-template-columns: auto minmax(0, 1fr); gap: var(--s-2); }
}

.scope-mark {
  margin: 0;
  font-size: var(--t-2xl);
  line-height: 1;
  color: var(--ice);
}

.scope-note p { max-width: 60ch; }
.scope-note .fine { margin-top: var(--s-2); }

/* ---- The phase map --------------------------------------------------
   A linear track made eight stages look like a queue you had to walk. It
   is a map now: three phases side by side, every stage visible at once,
   and any of them opens. Sequential work, but you can enter it anywhere
   and see the whole shape without reading your way to it.

   Each stage also says what it asks of you, which is the question
   somebody weighing this actually has. */

.jmap { margin-top: var(--s-2); }

.tabs[data-ready] .jmap-grid {
  display: grid;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}

@media (min-width: 46rem) {
  .tabs[data-ready] .jmap-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.jmap-phase { display: grid; gap: var(--unit); align-content: start; }

.jmap-phase-name {
  margin: 0 0 2px;
  font-size: var(--t-sm);
  color: var(--ink-soft);
  padding-bottom: var(--unit);
  border-bottom: 1px solid var(--rule);
}

.jmap-grid button {
  font: inherit;
  font-size: var(--t-sm);
  display: grid;
  gap: 2px;
  text-align: left;
  width: 100%;
  padding: var(--unit) var(--s-1);
  background: none;
  border: 1px solid var(--rule);
  color: var(--ink-soft);
  cursor: pointer;
}

.jmap-n {
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
}

.jmap-step { font-size: var(--t-sm); color: var(--ink); }

.jmap-needs { font-size: var(--t-xs); text-wrap: pretty; }

.jmap-grid button:hover { border-color: var(--ink-soft); }

/* Selected is the ice edge, not a fill: eight filled cells at once would
   be a lot of weight for a diagram. */
.jmap-grid button[aria-selected="true"] {
  border-color: var(--ice);
  box-shadow: inset 2px 0 0 var(--ice);
  color: var(--ink);
}

.jmap-grid button:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }

.tabs[data-ready] .jmap-panel { min-height: 9rem; }

/* Without script each stage needs its own separation. */
.jmap .tabpanel + .tabpanel { margin-top: var(--s-3); }
.tabs[data-ready] .jmap .tabpanel + .tabpanel { margin-top: 0; }

/* ---- Packages -------------------------------------------------------
   Two different things that were reading as one list: what the build
   costs, paid once, and what keeping it standing costs, paid monthly.
   Each gets its own label, its own note and a rule between them. */

.pack { display: grid; gap: var(--s-4); margin-top: var(--s-2); }

.pack-part + .pack-part {
  padding-top: var(--s-4);
  border-top: 1px solid var(--rule);
}

.group-label {
  font-size: var(--t-md);
  font-weight: 500;
  margin-bottom: 2px;
}

.group-note {
  margin: 0 0 var(--s-2);
  font-size: var(--t-sm);
  color: var(--ink-soft);
  max-width: 52ch;
  text-wrap: pretty;
}

/* The eligibility rule is a condition on the offer, not small print. */
.gate {
  margin-top: var(--s-3);
  padding-left: var(--s-2);
  border-left: 2px solid var(--ice);
  color: var(--ink-soft);
  max-width: 56ch;
  text-wrap: pretty;
}

/* A single word alone on the last line reads as a mistake. */
.fine,
.panel-lead,
.section-lead,
.scope-note p,
.lede { text-wrap: pretty; }

/* ---- Using the column ------------------------------------------------
   Measured at 1800: every text block on this page was capped near 52ch
   inside an 1128px column, so the widest thing on it stopped 303px short
   of the edge and the right third read as a margin. Home fills the same
   column to the pixel. These put the second half of the column to work
   rather than widening the prose, which has to stay readable. */

@media (min-width: 64rem) {
  /* Lead on the left, what is included on the right. Only panels with a
     list to put there: a tier panel lives inside half a column already
     and splitting it again gave it 186px to set text in. */
  /* :not([hidden]) is load-bearing. The UA stylesheet hides a panel with
     [hidden] { display: none }, and any rule here that sets display wins
     on specificity and un-hides it. Without this every panel in the set
     renders at once, stacked, which is exactly what it looks like. */
  .tabs[data-ready] .tabpanel:not([hidden]):has(.ticks) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--s-4);
    align-items: start;
  }

  .tabs[data-ready] .tabpanel .ticks { margin-top: 0; }

  .tabs[data-ready] .tabpanel .fine { grid-column: 1 / -1; }

  /* The floors were measured for one-column panels. Two columns halve the
     height, and the old floor then reserved 101px of nothing under the
     content, which is what read as broken. At this width the panels size
     to what is in them. */
  .tabs[data-ready] .tabpanel { min-height: 0; }

  /* Paid once and paid monthly, side by side, which is also the clearest
     way to show they are two things rather than one list. */
  .pack {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--s-6);
  }

  .pack-part + .pack-part {
    padding-top: 0;
    padding-left: var(--s-6);
    border-top: 0;
    border-left: 1px solid var(--rule);
  }

  /* The map keeps its three phases and the stage you opened reads beside
     it, so nothing jumps and the width is used. */
  .tabs[data-ready].jmap {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    column-gap: var(--s-4);
    align-items: start;
  }

  .tabs[data-ready] .jmap-grid { margin-bottom: 0; }
  .tabs[data-ready] .jmap-panel { min-height: 0; }
}
