/* page.css: the only stylesheet. Tokens first; everything below uses them.
   Editorial agency language, never another brand's assets (D-004). No accent colour (D-006). */

@font-face { font-family: "Hipton Sans"; src: url(fonts/hipton-sans.woff2) format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Apercu"; src: url(fonts/apercu-regular.woff2) format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Apercu"; src: url(fonts/apercu-medium.woff2) format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "Apercu"; src: url(fonts/apercu-bold.woff2) format("woff2"); font-weight: 700; font-display: swap; }

:root {
  --ground: #000;   /* the black of both fluid-art images (hero #010204, contact #000), D-026 */
  --ink: #fff;
  --ink-2: rgba(255, 255, 255, 0.74);   /* ~11:1 on --ground */
  --ink-3: rgba(255, 255, 255, 0.58);   /* ~6.5:1 on --ground, labels only */
  --rule: rgba(255, 255, 255, 0.16);
  --gutter: clamp(16px, 4vw, 72px);
  --display: "Hipton Sans", "Apercu", system-ui, sans-serif;
  --body: "Apercu", system-ui, sans-serif;
  --measure: 38ch;
  --section-pad: clamp(96px, 14vh, 220px);
}

*, *::before, *::after { box-sizing: border-box; }
html { background: var(--ground); color-scheme: dark; }
body { margin: 0; background: var(--ground); color: var(--ink); font: 400 18px/1.55 var(--body); -webkit-font-smoothing: antialiased; }
@media (max-width: 599px) { body { font-size: 16px; } }
img { display: block; max-width: 100%; }
a { color: inherit; }
p, h1, h2, h3, dl, dd, ul, figure, blockquote { margin: 0; }
ul { padding: 0; list-style: none; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

.label { font: 500 12px/1.3 var(--body); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); }
.fine { font-size: 14px; color: var(--ink-3); }
.ph { color: var(--ink-3); font-style: italic; outline: 1px dashed var(--rule); outline-offset: 4px; }

/* ---- menu: three floating lines, no top bar (D-013) ---- */
.menu-btn {
  position: fixed; top: 16px; right: 16px; z-index: 30;
  width: 56px; height: 56px; display: grid; place-items: center;
  background: transparent; border: 0; color: var(--ink); cursor: pointer; border-radius: 50%;
}
.menu-btn .bars { display: grid; gap: 6px; width: 28px; }
.menu-btn i { display: block; height: 2px; background: currentColor; transition: transform .3s, opacity .3s; }
.menu-open .menu-btn i:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.menu-open .menu-btn i:nth-child(2) { opacity: 0; }
.menu-open .menu-btn i:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }
/* A dropdown under the three lines, not a full page (D-062), sized to its links (D-077). */
.menu {
  position: fixed; top: 76px; right: 16px; z-index: 20; width: max-content; max-width: calc(100vw - 32px);
  max-height: calc(100vh - 92px); max-height: calc(100dvh - 92px); overflow-y: auto; overscroll-behavior: contain;
  padding: 12px 20px 20px; display: flex; flex-direction: column; gap: 12px;
  background: var(--ground); border: 1px solid var(--rule); box-shadow: 0 16px 48px rgba(0, 0, 0, 0.6);
}
.menu[hidden] { display: none; }
.menu ul { display: grid; }
.menu a:not(.btn-primary) { display: flex; align-items: center; min-height: 44px; font: 400 18px/1.3 var(--body); text-decoration: none; }
.menu a:not(.btn-primary):hover { text-decoration: underline; text-underline-offset: 4px; }
/* Book a call at the bottom, compact, still a 44px touch target (D-077). */
.menu .btn-primary { align-self: flex-start; min-height: 44px; padding: 10px 20px; font-size: 16px; }

.btn-primary {
  display: inline-flex; align-items: center; min-height: 48px; padding: 12px 28px; border-radius: 999px;
  background: var(--ink); color: var(--ground); font: 500 18px/1 var(--body); text-decoration: none; align-self: flex-start;
}

/* ---- hero: rotated art on desktop, upright on phones (D-020, D-021) ---- */
.hero { position: relative; height: 100svh; min-height: 560px; overflow: hidden; }
.hero-img, .hero-img img, .contact-img, .contact-img img { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero-img img { object-fit: cover; object-position: left center; }
.hero-scrim { position: absolute; inset: 0; background: linear-gradient(90deg, rgba(0,0,0,.55) 0%, rgba(0,0,0,0) 60%); }
/* Name bottom left; the three lines stacked top left on the same left edge (D-031). */
.hero-title { position: absolute; inset: calc(var(--gutter) + 12px) var(--gutter) var(--gutter); display: flex; flex-direction: column; pointer-events: none; }
.hero-title > * { pointer-events: auto; }
.hero-name { order: 2; margin-top: auto; }
.hero-lines { order: 1; display: grid; gap: 6px; }
.hero-name { font: 400 clamp(56px, 10vw, 240px)/0.9 var(--display); text-transform: uppercase; }
.hero-main { font: 400 clamp(20px, 1.9vw, 36px)/1.25 var(--body); color: var(--ink); }
.hero-main > * + *::before { content: " \00b7  "; }
.hero-sub { font: 400 clamp(16px, 1.2vw, 22px)/1.35 var(--body); color: var(--ink); }
@media (orientation: portrait) {
  .hero-img img { object-position: center bottom; }
  .hero-scrim { background: linear-gradient(180deg, rgba(0,0,0,.65) 0%, rgba(0,0,0,0) 28%), linear-gradient(0deg, rgba(0,0,0,.7) 0%, rgba(0,0,0,0) 45%); }
  /* Phones (D-051): the name always stacks on two lines, and the title block sits further
     in from the left and up from the bottom. */
  .hero-name { width: min-content; }
  /* Smaller title lines on phones (D-055): short labels, not body text. */
  .hero-main { font-size: 16px; }
  .hero-sub { font-size: 13px; }
  /* The photo's own bottom edge is black; crop it so the art runs to the bottom of the
     screen behind the name (D-051). */
  .hero-img img { height: 118%; }
  .hero-title { inset: calc(var(--gutter) + 12px) calc(var(--gutter) + 12px) calc(var(--gutter) + 40px + env(safe-area-inset-bottom, 0px)) calc(var(--gutter) + 12px); }
}

/* ---- statement: scroll-linked reveal, pinned while it plays ---- */
.statement { position: relative; }
.moving .statement { height: 200vh; }
/* Phones (D-065): the statement is exactly one screen, with no pinned travel, and a
   light snap point at its top, so one scroll from the hero lands on it whole. page.js
   then plays the reveal on a clock. */
@media (orientation: portrait) {
  .moving .statement { height: 100svh; }
  html { scroll-snap-type: y proximity; }
  .hero, .statement { scroll-snap-align: start; }
}
.statement-pin { min-height: 100svh; display: flex; flex-direction: column; justify-content: center; gap: clamp(24px, 4vh, 56px); padding: var(--section-pad) var(--gutter); }
.moving .statement-pin { position: sticky; top: 0; height: 100svh; padding-block: 0; }
/* The statement's background art (D-050): desktop landscape with the colour along the
   bottom, phone upright with the colour down the right. It pins with the text. */
.statement-pin { position: relative; overflow: hidden; }
.moving .statement-pin { position: sticky; }
.statement-img, .statement-img img { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; }
.statement-img img { object-fit: cover; object-position: center bottom; }
.statement-headline, .statement-sub { position: relative; z-index: 2; }
/* Desktop: the text sits in the upper part of the screen, clear of the colour band. */
@media (orientation: landscape) { .moving .statement-pin { justify-content: flex-start; padding-top: clamp(96px, 20vh, 240px); } }
/* Phone: the colour runs down the right behind the text, so a soft dark band sits behind
   the lines (contrast stays AA) while the art stays vivid above and below. */
@media (orientation: portrait) {
  .statement-img img { object-position: right center; }
  .statement-pin::before { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background: linear-gradient(180deg, rgba(0,0,0,0) 25%, rgba(0,0,0,.6) 38%, rgba(0,0,0,.6) 72%, rgba(0,0,0,0) 85%); }
}
.statement-headline { font: 400 clamp(24px, calc((100vw - 2 * var(--gutter)) / 13), 150px)/0.95 var(--display); text-transform: uppercase; }
.statement-sub { font: 400 clamp(18px, calc((100vw - 2 * var(--gutter)) / 32), 36px)/1.3 var(--body); --lh: 1.3em; }
/* One sentence per line (D-029). The headline always holds its two lines; the sub line
   holds two long lines where there is room and wraps within each sentence on phones. */
.statement .line { display: block; }
.statement-headline .line { white-space: nowrap; }
@media (min-width: 1024px) { .statement-sub .line { white-space: nowrap; } }
/* The reveal (D-027): each word starts invisible and rises into place; page.js drives it. */
.moving .reveal .w { display: inline-block; opacity: 0; transform: translateY(.35em); transition: opacity .25s ease-out, transform .25s ease-out; }

/* the word roll (D-023) */
.w { white-space: nowrap; } /* fix: keeps a roll's trailing punctuation from orphaning onto its own line at narrow widths */
.roll { display: inline-block; height: var(--lh); overflow: hidden; vertical-align: top; }
.roll-track { display: block; transition: transform .5s cubic-bezier(.2,.7,.2,1); }
/* The slot is an inline-block whose block children are max-content wide, so it takes
   the width of its widest item with no script involved (D-033). */
.roll-track > span { display: block; width: max-content; line-height: var(--lh); }
html:not(.moving) .roll-track > span:not(:first-child) { display: none; }

/* ---- black sections (D-022) ---- */
.block { padding: var(--section-pad) var(--gutter); border-top: 1px solid var(--rule); display: flex; flex-direction: column; gap: 32px; }
.brand-say { font: 400 clamp(22px, 2.6vw, 44px)/1.25 var(--body); max-width: 32ch; text-wrap: balance; }
.brand { gap: clamp(40px, 6vh, 72px); position: relative; min-height: 100svh; justify-content: center; }
.brand { overflow: hidden; }
.brand > :not(.brand-img) { position: relative; z-index: 1; }
/* The art fades to black at the section's foot, so it never ends in a hard line above
   the first case study (D-072); the fades now live in the D-075 block below. */

/* The personal brand's background art (D-052): colour on the right, text on the left. */
.brand-img, .brand-img img { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; }
.brand-img img { object-fit: cover; object-position: right center; }
.brand-row { display: grid; gap: 10px; }
.nw { white-space: nowrap; }
.brand-do { max-width: none; }
.brand-lead { font: 400 clamp(18px, 1.5vw, 24px)/1.35 var(--body); color: var(--ink-2); }
/* The mission runs on one line where it fits (D-034); on phones it wraps to stay readable. */
@media (min-width: 1024px) { .brand-say[data-copy="brand.mission"] { max-width: none; white-space: nowrap; } }
.values { display: flex; flex-wrap: wrap; gap: 8px 24px; font: 400 clamp(28px, 3.4vw, 64px)/1.1 var(--display); text-transform: uppercase; }
/* The values appear one at a time as the row comes into view (D-079): the first at once,
   then one every 0.5s. Only with the script and without reduced motion (.moving). */
.moving .values li { opacity: 0; transform: translateY(0.35em); }
.moving .values.shown li { opacity: 1; transform: none; transition: opacity .4s ease, transform .4s ease; transition-delay: calc(var(--i) * 0.5s); }
.logo { min-height: 64px; }
/* Every brand mark shows as solid white on the black ground (D-036). */
/* One optical size for every mark (D-037): height = base x k, with k from src/logos.ts. */
img.logo { --logo-h: clamp(64px, 8vw, 116px); height: calc(var(--logo-h) * var(--k, 0.6)); width: auto; max-width: 80vw; min-height: 0; align-self: flex-start; filter: brightness(0) invert(1); }
.logo.ph { width: 180px; display: grid; place-items: center; }
/* The logos lead (D-057): the brand name is a small caption, smaller than any logo. */
.case-brand { font: 400 clamp(20px, 1.8vw, 28px)/1.1 var(--display); text-transform: uppercase; letter-spacing: 0.04em; }
/* Phones (D-056): the personal brand is not forced to a full screen, and track record
   starts closer below it, so there is less empty black between the values and the awards. */
@media (max-width: 599px) { .brand { min-height: auto; padding-bottom: 32px; } .track { padding-top: 32px; } }
.case { position: relative; }
.case-meta { color: var(--ink-2); }
.case-dates { position: absolute; top: var(--section-pad); right: var(--gutter); margin: 0; font: 400 clamp(16px, 1.1vw, 18px)/1.3 var(--body); color: var(--ink-2); text-align: right; }
.case-grid, .track-grid { display: grid; gap: 40px; }
.case-grid p, .track-grid p { max-width: var(--measure); margin-top: 12px; color: var(--ink-2); }
.track-grid p { color: var(--ink); } /* track record in white (D-048) */
.case-outcome { color: var(--ink) !important; font-size: clamp(20px, 1.6vw, 28px); }
.plain { margin-top: 12px; display: grid; gap: 6px; color: var(--ink); padding-left: 1.1em; list-style: disc; }
.plain li::marker { color: var(--ink-3); }
@media (min-width: 1024px) { .case-grid, .track-grid { grid-template-columns: repeat(3, 1fr); } }
/* No dividers before the first case study (D-038): personal brand and track record run
   straight on from the statement. */
.brand, .track { border-top: 0; }
.band { margin-inline: calc(-1 * var(--gutter)); }
.band img { width: 100%; height: auto; }
/* A photo opens in a pop-up and a link opens elsewhere; neither shows anything on the page (D-058). */
.case-more { display: inline-flex; align-items: center; gap: 0.5em; margin: 20px 24px 0 0; min-height: 44px; font: 500 14px/1.3 var(--body); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--ink-3); cursor: pointer; }
@media (max-width: 599px) { .case-more { font-size: 16px; } } /* the 16px readability floor */
.case-more:hover, .case-more:focus-visible { border-bottom-color: var(--ink); }
.case-open::after { content: "+"; font-size: 1.3em; line-height: 1; }
.case-link::after { content: "\2197"; }
.lightbox { width: min(96vw, 1600px); max-width: none; max-height: 92vh; padding: 0; border: 0; background: transparent; overflow: visible; }
.lightbox::backdrop { background: rgba(0, 0, 0, 0.94); }
.lightbox img { width: 100%; height: auto; max-height: 86vh; object-fit: contain; }
.lightbox-close { display: block; margin: 0 0 12px auto; min-height: 44px; padding: 0 4px; background: none; border: 0; color: var(--ink); font: 500 14px/1 var(--body); letter-spacing: 0.14em; text-transform: uppercase; cursor: pointer; }
.team { align-items: center; text-align: center; }
/* The references and the track record share the team background (D-072). */
.team-track { position: relative; overflow: hidden; }
.team-track > .team, .team-track > .track { position: relative; z-index: 1; }
/* Fifth fluid-art background (D-063): upright on phones, turned clockwise on desktop so
   the paint runs along the bottom right, clear of the title. */
.team-img, .team-img img { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; }
.team-img { overflow: hidden; } /* the art layer crops itself; the quotes are never clipped */
.team-img img { object-fit: cover; object-position: right bottom; }
/* Desktop: the art is enlarged from the bottom-right corner, so the paint sits beside
   the centred quotes rather than under them. */
/* Desktop (D-080): the image is turned so its top points left, enlarged to twice the
   screen's height and pinned left with its foot at the bottom of the references, so the
   paint climbs the left edge from bottom to top; it fades out before What's on paper. */
@media (orientation: landscape) { .team-img img { inset: -95vh auto auto 0; width: auto; height: 200vh; max-width: none; object-fit: fill;
  -webkit-mask-image: linear-gradient(to bottom, #000 82%, transparent 100%); mask-image: linear-gradient(to bottom, #000 82%, transparent 100%); } }
/* Phones: the upright image keeps its black left side in view and the paint on the right. */
/* Phones (D-076): the whole upright image at the screen's width, top-aligned and not
   zoomed; its bottom is already black, and the page's black carries on below it. */
@media (orientation: portrait) { .team-img img { height: auto; bottom: auto; } }
.team-title { font: 400 clamp(40px, 6vw, 120px)/0.95 var(--display); text-transform: uppercase; }
/* The record's title matches the team title above it, centred over the grid (D-073). */
.track-title { font: 400 clamp(40px, 6vw, 120px)/0.95 var(--display); text-transform: uppercase; text-align: center; margin-bottom: clamp(24px, 4vh, 48px); }
/* The quotes stack in one centred column (D-046). */
.quotes { display: grid; gap: clamp(48px, 8vh, 96px); justify-items: center; margin-top: clamp(24px, 4vh, 48px); }
/* Quotes at reading size, not display size (D-043). */
.quote p { font: 400 18px/1.55 var(--body); color: var(--ink); max-width: 60ch; margin-inline: auto; }
.quote cite { display: block; margin-top: 16px; font-style: normal; color: var(--ink-3); }

/* ---- contact: second fluid-art image, block on the black side (D-022) ---- */
.contact { position: relative; min-height: 100svh; overflow: hidden; display: flex; align-items: center; }
.contact-img img { object-fit: cover; object-position: right top; }
/* The contact art fades in from black at its top edge, the same blend as the personal
   brand's foot and the team art (D-074), so "What's on paper" runs into it with no edge. */
.contact { border-top: 0; }
.contact-img img { -webkit-mask-image: linear-gradient(to bottom, transparent, #000 30%); mask-image: linear-gradient(to bottom, transparent, #000 30%); }
.contact-body { position: relative; padding: var(--section-pad) var(--gutter); display: flex; flex-direction: column; gap: 32px; max-width: min(720px, 100%); }
.contact-ask { font: 400 clamp(40px, 6vw, 120px)/0.95 var(--display); text-transform: uppercase; }
.contact-lines { display: grid; gap: 12px; }
.contact-lines li { display: grid; gap: 2px; }
@media (orientation: portrait) {
  .contact { align-items: flex-end; }
  .contact-img img { object-position: center top; }
}

/* Fine print (D-045): small, centred, faint, and taking almost no room. 50% white is
   the faintest that still passes AA on black at this size (5.3:1). */
.foot { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding: 12px var(--gutter) calc(16px + env(safe-area-inset-bottom, 0px)); display: grid; gap: 2px; text-align: center; }
.foot .fine { font-size: 11px; line-height: 1.4; color: rgba(255, 255, 255, 0.5); }
.imprint > * + *::before { content: " · "; }

/* ---- fades between sections (D-075) ----
   Every change of section blends through black; only the rules between case studies
   stay, for structure. The masks sit on each image's frame, which is exactly its
   section's size, so an oversized image (the phone hero at 118%) still fades at the
   section's edge. */
.hero-img { -webkit-mask-image: linear-gradient(to bottom, #000 85%, transparent); mask-image: linear-gradient(to bottom, #000 85%, transparent); }
.statement-img, .brand-img { -webkit-mask-image: linear-gradient(to bottom, transparent, #000 15%, #000 80%, transparent); mask-image: linear-gradient(to bottom, transparent, #000 15%, #000 80%, transparent); }
.team-img { -webkit-mask-image: linear-gradient(to bottom, transparent, #000 140px); mask-image: linear-gradient(to bottom, transparent, #000 140px); }
.brand + .case, .team { border-top: 0; }
