/* ==========================================================================
   Sasha Shuttleworth — site styles
   Brief: large images, room to breathe, clear boundaries between projects,
   small type, navigation on the left.
   ========================================================================== */

/* Fonts -------------------------------------------------------------------
   Instrument Sans is loaded from Google Fonts in inc/layout.php. To use a
   licensed font instead (Favorit, Suisse…), put its .woff2 files in
   /assets/fonts/, add an @font-face rule here, and put its name first in
   --font below. */

:root {
  --bg: #fbfbf9;
  --ink: #161616;
  --muted: #8a8a86;
  --line: #e4e4df;
  --ph: #ecebe6;          /* placeholder image tone */

  --font: "Instrument Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --size: 14.3px;
  --size-lg: 18.7px;
  --lh: 1.5;

  --sidebar: 300px;
  --gutter: 40px;
  --edge: 16px;           /* the wordmark, menu and corner caption sit this close to the left edge */
  --gap: 18vh;            /* vertical space between images on project pages */
  --text-width: 34em;
  /* Where every page's text ends on a big screen: the right edge of a 2:3
     portrait photo, centred and as tall as the screen (never past the
     column). Most projects open on just such a photo, so their text lines
     up with it, and every page shares the same edge - nothing jumps when
     moving between pages. Used as a right margin, so 100% is the column. */
  --text-ar: 0.6667;
  --text-edge: calc((100% - min(var(--screen) * var(--text-ar), 100%)) / 2);
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* Always leave room for the scrollbar, so short and long pages are the
     same width and nothing shifts sideways between them. (On a Mac with
     scrollbars set to appear only when scrolling, this changes nothing.) */
  overflow-y: scroll;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--size);
  line-height: var(--lh);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
a:hover { color: var(--muted); }
p a, .text a { text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }

img { display: block; max-width: 100%; height: auto; }

h1, h2, h3 { font-weight: 400; font-size: inherit; margin: 0; }
p { margin: 0 0 1em; }

/* Layout ------------------------------------------------------------------
   A thin info bar across the top (name / description / contact), the menu
   down the left, the work to the right. The bar tucks away as you scroll
   down and comes back as soon as you scroll up (see site.js).
   --bar-h is measured by site.js; the value here is only a first guess. */

:root { --bar-h: 67px; --bar-shift: 0px; --foot-h: 0px; }

/* One screen: the height a picture can fill, with a margin above and below.
   On desktop the left column (name, menu, caption) stays put and the right
   side scrolls on its own, so a picture can use the whole height of the
   window. Pictures are sized from this, so each one reads at the scale of
   the window, one at a time. */
:root { --screen: calc(100vh - 2 * var(--gutter)); }
@supports (height: 100svh) {
  :root { --screen: calc(100svh - 2 * var(--gutter)); }
}

.topbar {
  position: fixed;
  top: var(--cms-bar, 0px); left: 0;
  width: max-content;                 /* just the name - never across the pictures */
  z-index: 20;
  display: grid;
  align-items: start;
  padding: 18px var(--edge) 18px var(--edge);
  background: var(--bg);
}

/* A project's title and year, in the bottom-left corner under the menu.
   The menu column stops above it (--foot-h), so a long menu never runs
   into it. */
.page-caption {
  position: fixed;
  left: 0; bottom: 0;
  width: var(--sidebar);
  z-index: 10;
  padding: var(--gutter) var(--gutter) var(--gutter) var(--edge);
}
.caption-title { font-weight: 700; }
.caption-detail { color: var(--muted); }
.caption-detail p { margin: 0; }
.caption-detail a { color: var(--ink); }
.caption-year { color: var(--muted); font-size: 12.1px; margin-top: 0.2em; }
.slide-count { display: none; }
/* Contact details, at the very bottom of the corner on desktop. */
.caption-contact { margin-top: 1.4em; }
.contact-only .caption-contact { margin-top: 0; }
.caption-contact p { margin: 0; }


.sidebar {
  position: fixed;
  top: calc(var(--cms-bar, 0px) + var(--bar-h)); left: 0; bottom: var(--foot-h);
  width: var(--sidebar);
  padding: var(--gutter) var(--gutter) var(--gutter) var(--edge);
  overflow-y: auto;
  z-index: 10;
}

main {
  margin-left: var(--sidebar);
  padding: calc(var(--bar-h) + var(--gutter)) var(--gutter) 20vh;
  min-height: 100vh;
}

/* Sidebar / nav ------------------------------------------------------------ */

.site-title { display: block; font-weight: 700; font-size: 30.8px; line-height: 1.1; letter-spacing: -0.015em; white-space: nowrap; }
/* The wordmark stays exactly as it is when hovered, clicked or visited. */
.site-title,
.site-title:hover,
.site-title:active,
.site-title:visited { color: var(--ink); -webkit-tap-highlight-color: transparent; }

.nav ul { list-style: none; margin: 0; padding: 0; }
.nav > ul > li { margin-bottom: 0.15em; }
/* The page you are on: bold, like the page titles. */
.nav a[aria-current="page"] { color: var(--ink); font-weight: 700; }

.nav .group-toggle {
  all: unset;
  cursor: pointer;
}
.nav .group-toggle:hover { color: var(--muted); }
.nav .group-toggle:focus-visible { outline: 1px solid var(--ink); outline-offset: 2px; }

.nav .sub {
  padding: 0.3em 0 0.6em 1em;
  display: none;
}
.nav .open > .sub { display: block; }
.nav .sub li { margin-bottom: 0.1em; }

.menu-button { display: none; }

/* Home: stacked project covers --------------------------------------------- */

.covers { display: flex; flex-direction: column; gap: var(--gap); }
.cover { display: block; width: fit-content; max-width: 100%; margin-inline: auto; --fit: var(--screen); }
.cover .frame { margin-bottom: 0.9em; }
.cover .meta { display: flex; gap: 1.5em; }
.cover:hover .meta { color: var(--muted); }

/* Project page -------------------------------------------------------------- */

/* A project's title, caption and year, above its statement: the same
   styling as the corner caption on other pages. */
.project-head { margin-bottom: 1.6em; }
.project-head .caption-title { font: inherit; font-weight: 700; margin: 0; color: var(--ink); }

.sequence { display: flex; flex-direction: column; gap: var(--gap); }

/* Picture sizes, as a share of one screen's height. A picture never gets
   wider than the column; the figure shrinks to the picture so a caption
   lines up with its left edge. */
.sequence figure {
  --fit: var(--screen);
  margin: 0;
  width: fit-content;
  max-width: 100%;
}
.sequence .large  { --fit: calc(var(--screen) * 0.85); }
.sequence .medium { --fit: calc(var(--screen) * 0.65); }
.sequence .small  { --fit: calc(var(--screen) * 0.45); }
.sequence .center { margin-inline: auto; }
/* About's portrait: never wider than the shared text edge, so photo and
   text end together (a slightly wider photo is shown a little smaller). */
.sequence .to-edge img,
.sequence .to-edge .ph { width: min(var(--screen) * var(--text-ar), var(--fit) * var(--ar, 1.5)); }   /* max-width: 100% as for any photo */
.sequence .right  { margin-left: auto; }

/* The title and statement come first, set to the right against the shared
   text edge (--text-edge). The pictures follow below. */
.opening {
  text-align: right;
  margin-right: var(--text-edge);
  /* The first picture follows closely; the big gap is only between pictures. */
  margin-bottom: calc(var(--gutter) * 2 - var(--gap));
}
.opening-text { max-width: var(--text-width); margin-left: auto; }
.opening-text p:last-child { margin-bottom: 0; }

/* No statement yet: outside edit mode there's nothing there at all; in
   edit mode a prompt shows where it will go. Empty text anywhere else
   behaves the same. */
body:not(.cms-on) .cms-empty { display: none; }

/* Desktop: a project page snaps from photo to photo as you scroll, each one
   settling in the middle of the screen (the statement is the first stop),
   the way a phone swipes sideways. */
@media (min-width: 801px) {
  /* The title is on the page itself, so the corner keeps only Back to top. */
  .caption-inline .caption-text { display: none; }
  .caption-inline .to-top { margin-top: 0; }
  /* Any page laid out as a sequence snaps: projects, Live Coverage,
     Clients, About. */
  html:has(main .sequence) { scroll-snap-type: y mandatory; }
  /* site.js does the pulling itself, more gently (see "a gentle pull to the
     nearest stop"); this is only for when scripts don't run. */
  html.settle:has(main .sequence) { scroll-snap-type: none; }
  .sequence > figure { scroll-snap-align: center; scroll-snap-stop: always; }
  /* The statement's stop is the top of the page, level with the menu. */
  .sequence > .opening { scroll-snap-align: start; scroll-margin-top: calc(var(--bar-h) + var(--gutter)); }
}

figcaption { color: var(--muted); margin-top: 0.8em; }

.interlude { max-width: var(--text-width); }

/* Back to top: the last line of the corner caption, in the left column
   where there are never pictures. Its space is always kept, so nothing
   moves when it appears (once you have scrolled past the first screen). */
.to-top {
  display: inline-block;
  margin-top: 1.4em;
  color: var(--muted);
  visibility: hidden;
}
.to-top:hover { color: var(--ink); }
body.show-top .to-top { visibility: visible; }

/* Portrait images shouldn't exceed the viewport height */
/* Width from the screen height and the picture's shape (--ar, set on each
   picture by the CMS), never wider than the column. Placeholders the same. */
.sequence figure img,
.cover .frame img,
.sequence .ph,
.cover .ph {
  width: calc(var(--fit) * var(--ar, 1.5));
  max-width: 100%;
  height: auto;
}

/* Placeholder images (swap for <img> once photos are in) ------------------ */
.ph {
  background: var(--ph);
  width: 100%;
  aspect-ratio: 3 / 2;
  display: flex;
  align-items: flex-end;
  padding: 12px;
  color: var(--muted);
}
.ph          { --ar: 1.5; aspect-ratio: var(--ar); }
.ph.portrait { --ar: 0.8; }
.ph.square   { --ar: 1; }
.ph.wide     { --ar: 1.7778; }
.ph.upright  { --ar: 0.6667; }   /* a 2:3 portrait, like most project photos */

/* Text pages (About, Words) ------------------------------------------------- */

.page { max-width: var(--text-width); }
.page h1 { margin-bottom: 2.5em; color: var(--muted); }
.page .lead { font-size: var(--size-lg); line-height: 1.4; margin-bottom: 1.5em; }
.page .block { margin-top: 3em; }
.page .block h2 { color: var(--muted); margin-bottom: 0.6em; }


/* Lists: Clients, Features, Live Coverage ---------------------------------- */


.list-page > h1 { color: var(--muted); margin-bottom: 2.5em; }

/* Clients: project-style photographs, each with a small caption underneath
   (the photo leaves room for it, so photo and caption fit on one screen). */
.sequence .card { --fit: calc(var(--screen) - 2.5em); }
.card img { display: block; }
.card figcaption { margin-top: 0.7em; }
.card .c-detail { color: var(--muted); }
.card a:hover + figcaption .c-title { color: var(--muted); }

/* One grid for the whole list (rows borrow its columns via subgrid), so the
   year column is exactly as wide as the longest year and every row lines up. */
.index {
  list-style: none; margin: 0; padding: 0; max-width: 50em;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) max-content;
  column-gap: 1.5em;
}
.index li,
.index a, .index .row {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
}
.index a, .index .row { padding: 0.3em 0; }
@supports not (grid-template-columns: subgrid) {
  .index li, .index a, .index .row { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) 5.5em; column-gap: 1.5em; }
}
.index .muted { color: var(--muted); }
.index .i-year { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Radio: collage ------------------------------------------------------------ */

.radio-intro { max-width: var(--text-width); margin-bottom: 3.5em; }
.radio-intro p:last-child { margin-bottom: 0; }
.radio-outro { margin-top: 1em; }
.radio-intro h1 { color: var(--muted); margin-bottom: 2.5em; }
.radio-intro .lead { font-size: var(--size-lg); line-height: 1.4; margin-bottom: 1.5em; }

.collage {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: calc(var(--gap) * 0.6);
  align-items: start;
}
.collage > * { margin: 0; }
/* Every tile is a small landscape photograph, whatever shape was uploaded. */
.collage .ph,
.collage img { width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; }

.episode { display: block; }
.episode .ep-meta { padding-top: 0.6em; display: flex; justify-content: space-between; gap: 1em; }
.episode .ep-meta span { color: var(--muted); }
.episode:hover .ep-meta { color: var(--muted); }
.episode:hover .ph, .episode:hover img { opacity: 0.85; }


/* Section headings (Clients, Radio…): the menu already says where you are,
   so on a big screen the page opens straight on the work. Still read out by
   screen readers, and shown on a phone where the menu is tucked away. */
.page-title {
  position: absolute !important; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* Every page, on a big screen: text is set to the right and ends at the
   same place as on a project (--text-edge). Captions end at their
   picture's right edge. A phone keeps ordinary left-aligned text. */
@media (min-width: 801px) {
  .page,
  .radio-intro { margin-left: auto; margin-right: var(--text-edge); text-align: right; }
  .opening .page { margin-right: 0; }         /* the opening already has the margin */
  .index { margin-left: auto; text-align: right; }
  /* Except Features: a plain list, left-aligned from the start of the column. */
  .list-page > .index { margin-left: 0; text-align: left; }

  /* Lists (Features, Words): every entry on one line. The list is as wide
     as its longest entry, up to the column; a description that still won't
     fit ends in "…" (the whole of it shows on hover). */
  .index { width: max-content; max-width: 100%; grid-template-columns: max-content minmax(0, max-content) max-content; }
  .index .i-name, .index .i-detail { white-space: nowrap; }
  .index .i-detail { overflow: hidden; text-overflow: ellipsis; }
  .card figcaption,
  .cover .meta { justify-content: flex-end; text-align: right; }
}

/* Utilities ------------------------------------------------------------------ */
.muted { color: var(--muted); }
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* Mobile --------------------------------------------------------------------- */

@media (max-width: 800px) {
  :root { --gutter: 16px; --gap: 12vh; }

  /* The bar collapses to the name and a menu button. The menu slides down
     from under it. */
  .topbar {
    right: 0;
    width: auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 var(--gutter);
    height: 60px;
  }
  /* On a phone the name and menu button run across the top, so pictures fit below them. */
  :root { --screen: calc(100vh - var(--bar-h) - 2 * var(--gutter)); }
  @supports (height: 100svh) { :root { --screen: calc(100svh - var(--bar-h) - 2 * var(--gutter)); } }
  .site-title { font-size: 24.2px; }

  /* Hamburger: three short hairlines that fold into an X when open.
     The button is a comfortable 36px tap target; the icon inside is 16px. */
  .menu-button {
    all: unset;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-end;
    gap: 4px;
    width: 36px;
    height: 36px;
    margin-right: -10px;
    cursor: pointer;
  }
  .menu-button:focus-visible { outline: 1px solid var(--ink); outline-offset: -4px; }
  .menu-button span {
    display: block;
    width: 16px;
    height: 1px;
    margin-right: 10px;
    background: var(--ink);
  }
  .menu-open .menu-button span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
  .menu-open .menu-button span:nth-child(2) { opacity: 0; }
  .menu-open .menu-button span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

  .sidebar {
    position: fixed;
    left: 0; right: 0;
    width: auto;
    background: var(--bg);
    visibility: hidden;
  }
  .sidebar {
    top: calc(var(--cms-bar, 0px) + var(--bar-h));
    bottom: 0;
    padding: 2.5em var(--gutter) 10em;
    font-size: var(--size-lg);
  }

  /* The corner caption spans the bottom of the screen on a phone, with the
     picture counter on the right. It steps aside while the menu is open. */
  .page-caption {
    right: 0;
    width: auto;
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: 1em;
    padding: 0 var(--gutter) calc(var(--gutter) + env(safe-area-inset-bottom));
    background: var(--bg);
  }
  .page-caption .caption-text { grid-column: 1; }
  .slide-count {
    display: block;
    grid-column: 2;
    grid-row: 1;
    align-self: end;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
  }
  .menu-open .page-caption { visibility: hidden; }

  /* A project on a phone: one screen, one picture at a time, swipe sideways.
     (Pairs split into two pictures; the statement gets a screen of its own.) */
  main.project-page { padding: var(--bar-h) 0 0; min-height: 0; }
  .project-page {
    --slide-h: calc(100vh - var(--bar-h) - var(--foot-h));
  }
  @supports (height: 100svh) {
    .project-page { --slide-h: calc(100svh - var(--bar-h) - var(--foot-h)); }
  }
  .project-page .sequence {
    flex-direction: row;
    gap: 0;
    height: var(--slide-h);
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  .project-page .sequence::-webkit-scrollbar { display: none; }
  .project-page .opening { display: contents; }
  .project-page .opening > *,
  .project-page .sequence > * {
    flex: 0 0 100%;
    width: 100%;
    max-width: none;
    height: 100%;
    margin: 0;
    padding: var(--gutter);
    scroll-snap-align: center;
    scroll-snap-stop: always;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
  }
  .project-page .sequence figure { --fit: calc(var(--slide-h) - 2 * var(--gutter) - 3em); }
  .project-page .sequence figcaption { align-self: stretch; }
  .to-top { display: none; }
  .project-page .sequence > .opening { display: contents; }
  /* Every project on a phone: the statement is the first screen, then the photos. */
  .project-page .opening-text { order: -1; justify-content: center; align-items: flex-start; overflow-y: auto; max-width: none; }
  /* On a phone the title stays in the bottom corner, and the statement is
     ordinary left-aligned text; with no statement there's no first screen. */
  .opening { text-align: left; margin-right: 0; }
  .project-page .project-head {
    position: absolute !important; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; margin: 0;
  }
  body.cms-on .project-page .project-head { position: static !important; width: auto; height: auto; overflow: visible; clip: auto; white-space: normal; margin-bottom: 1.6em; }
  body:not(.cms-on) .opening.is-empty { display: none; }
  .menu-open .sidebar { visibility: visible; }

  main { margin-left: 0; padding: calc(var(--bar-h) + var(--gutter)) var(--gutter) calc(12vh + var(--foot-h)); }

  .desktop-only { display: none; }
  .page-caption.contact-only { display: none; }   /* nothing left in it on a phone */
  .page-title {
    position: static !important; width: auto; height: auto; overflow: visible;
    clip: auto; white-space: normal;
  }

  /* A phone is narrow enough that every picture simply fills the width. */
  .sequence .large, .sequence .medium, .sequence .small { --fit: var(--screen); }
  /* Phone: a simple list. The name, then the description with the year
     after it, unless the description says it. */
  .index a, .index .row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.4em;
    padding: 0.6em 0;
  }
  .index .i-name   { flex-basis: 100%; }
  .index .i-year:empty,
  .index .i-year.dup { display: none; }
  .index .i-year:not(.alone)::before { content: "· "; }

  .radio-intro { margin-bottom: 2.5em; }

  /* Two small pictures side by side on a phone, no scatter. */
  .collage { grid-template-columns: 1fr 1fr; row-gap: 2.5em; }
  .collage > * { grid-column: auto !important; grid-row: auto !important; margin-top: 0 !important; }
}
