/* Micacchi Architecture — MA7
   Carries MA6's visual language onto a multi-page, server-rendered site.
   Edit this file and re-run build/build.py to see changes. */

:root{
  /* ---- type scale ----
     Five roles, used everywhere. Headings share the body size and are set apart by colour
     and space rather than by scale, which is what keeps the pages quiet. */
  --t-quote:30px;    /* the practice statement */
  --t-h1:21px;       /* page title */
  --t-h2:16px;       /* section heading */
  --t-body:16px;
  --t-caption:13px;
  --t-wordmark:32px;
  /* The weight for the practice statement on the Studio page and the project name and
     location on the category pages. Light rather than the Regular everything else uses,
     so those lines separate from the wordmark by weight instead of by family and the
     page stays on one typeface. Ultra Light was tried and read too fragile. */
  --w-quote:300;

  --bg:#F9FBFD; --ink:#000; --muted:#7A7A7A; --ph:#E7E8EA;
  /* Dark grey for the large Light-weight lines — the project name and location
     on the category pages and the practice statement on Studio. Sits between
     the black of body copy and the grey the section headings use. */
  --ink-soft:#3A3A3A;
  --font:"univers-next-pro","Univers LT Std","Univers","Helvetica Neue",Helvetica,Arial,sans-serif;
  /* Equal margins left and right, at double the old 20px. Content runs the full width
     between them — the old one-column indent is gone, so --indent is now zero and kept
     only so the rules that reference it still read clearly. */
  --pad:40px; --gap:20px; --colgap:48px; --track:-0.01em;
  --indent:0px;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:var(--font);font-size:var(--t-body);
  font-weight:400;line-height:1.5;letter-spacing:var(--track);-webkit-font-smoothing:antialiased}
img{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
h1{font-size:var(--t-h1);font-weight:400;line-height:1.35;color:var(--ink)}
h2{font-size:var(--t-h2);font-weight:400;line-height:1.5;color:var(--muted)}
h3{font-size:var(--t-h2);font-weight:400;line-height:1.5;color:var(--ink)}
p{font-size:var(--t-body);line-height:1.5}

/* Reusable type roles, for anything that is not a heading or a paragraph. */
.t-quote{font-size:var(--t-quote);font-weight:var(--w-quote);line-height:1.3;
  letter-spacing:-.005em;color:var(--ink-soft)}
.t-caption{font-size:var(--t-caption);line-height:1.45;color:var(--muted)}

/* ---- chrome ---- */
.topbar{position:sticky;top:0;z-index:50;display:flex;justify-content:space-between;
  align-items:baseline;gap:24px;padding:18px var(--pad);background:var(--bg)}
.wordmark{font-size:var(--t-wordmark);font-weight:400;letter-spacing:.02em;line-height:1}
@media(max-width:820px){:root{--t-wordmark:24px}}
.mainnav{display:flex;gap:22px;color:var(--muted)}
.mainnav a:hover,.mainnav a[aria-current]{color:var(--ink)}

/* The Studio page carries all of this in its own Contact block, so it drops the footer. */
body.no-footer .sitefoot{display:none}

/* One line, no columns. Wraps rather than overflows on a narrow window. */
.sitefoot{display:flex;flex-wrap:wrap;align-items:baseline;gap:0;
  padding:120px var(--pad) 56px;color:var(--muted)}
.sitefoot a:hover{color:var(--ink)}
.sitefoot .sep{padding:0 .6em;opacity:.5}
@media(max-width:820px){
  .sitefoot{padding:88px var(--pad) 44px}
  .sitefoot .sep{padding:0 .45em}
}

/* ---- shared layout ---- */
main{padding:0 var(--pad)}
/* Present to assistive technology and to crawlers, not drawn on screen. */
.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}
/* The work page opens straight onto the images, with the header's own space above them. */
/* The grid ends on a margin rather than a footer, so it needs its own bottom edge —
   the same measure as the sides, which closes the frame. */
/* Both the grid and the category lists open straight under the header. The
   bottom padding closes the frame; .sec supplies the rest. */
.worksec{padding-bottom:var(--pad)}

.pagehead{padding:56px 0 40px;max-width:60ch}
.pagehead h1{margin-bottom:.6em}
.pagehead p{color:var(--muted)}
.sec{padding:0 0 76px}
.sec>h2{margin-bottom:20px}
.narrow{max-width:60ch}
.more{color:var(--muted);text-decoration:underline;text-underline-offset:3px}
.more:hover{color:var(--ink)}
.plain{list-style:none}
.plain li{margin-bottom:.4em}
.plain a{text-decoration:underline;text-underline-offset:3px;color:var(--muted)}
.plain a:hover{color:var(--ink)}

/* ---- home ----
   One screen, no scroll. The frame is made with padding on <main>, not a margin on the
   image: a top margin on the first child collapses out through its parent and shifts the
   whole page down instead of creating space inside it, which is what hid the header.
   Equal margin left, right and bottom; larger at the top so the image starts below the
   wordmark. All from --pad, so the frame keeps its proportions as the window changes. */
/* Height of exactly one screen, but NOT overflow:hidden — that stopped the page moving
   at all, so on a touch screen the home page felt frozen rather than finished. Left
   scrollable-but-exact, it rubber-bands against the edge the way the other pages do,
   which is the signal that there is nothing further down. */
body.home{height:100svh;overflow-x:hidden}
body.home main{padding:var(--hero-top) var(--pad) var(--pad);height:100svh}
.hero{position:relative;height:100%;margin:0;overflow:hidden;background:var(--ph)}

:root{--hero-top:96px}
@media(max-width:1100px){:root{--hero-top:84px}}
@media(max-width:820px){:root{--hero-top:68px}}

/* Height of the sticky header: 18px padding, the wordmark's line box, 18px padding.
   Project pages subtract it so their cover starts at the same --hero-top as the home
   page's, even though their header sits in the flow rather than over the image. */
:root{--topbar-h:68px}
@media(max-width:820px){:root{--topbar-h:60px}}

/* The header sits in the band above the image, on the page background, so it keeps the
   normal ink and grey. position:fixed rather than absolute: body has overflow:hidden here
   and a fixed element is anchored to the viewport, so it cannot be clipped by it. */
body.home .topbar{position:fixed;top:0;left:0;right:0;background:none;z-index:60}
body.home .wordmark{color:var(--ink)}
body.home .mainnav{color:var(--muted)}
body.home .mainnav a:hover,body.home .mainnav a[aria-current]{color:var(--ink)}

/* The image cycle, done in CSS so the page still needs no JavaScript. Every slide is
   stacked in the same place; each runs the same keyframes on a negative delay so they take
   their turn. The keyframes are generated by build.py, because keyframe percentages must
   be literal numbers and the number of images is only known at build time. */
.hero .hslide{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:center;opacity:0;animation-name:heroCycle;animation-timing-function:linear;
  animation-iteration-count:infinite;animation-fill-mode:both}
.hero .hslide:first-of-type{opacity:1}
@media(prefers-reduced-motion:reduce){
  .hero .hslide{animation:none;opacity:0}
  .hero .hslide:first-of-type{opacity:1}
}

/* With the script running the keyframes are switched off and it sets .on instead, so a
   click can change which image shows. Without the script none of this applies and the
   keyframes above keep cycling. The halves are a click target, not a visible control:
   right for the next image, left for the one before. */
.hero.js{cursor:pointer;-webkit-tap-highlight-color:transparent}
.hero.js .hslide{animation:none;opacity:0;transition:opacity .55s ease}
/* A click is answered immediately; the unhurried fade is for the automatic cycle. */
.hero.js.quick .hslide{transition-duration:.1s}
.hero.js .hslide:first-of-type{opacity:0}
.hero.js .hslide.on{opacity:1}

/* ---- project cards ----
   Used by the work index and by any simple grid of projects. The caption is inside the
   image, hidden until hover, and stays real markup so crawlers and screen readers read
   every project name. */
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap);margin-left:var(--indent)}
@media(min-width:1280px){.grid{grid-template-columns:repeat(5,1fr)}}
@media(max-width:820px){.grid{grid-template-columns:1fr;gap:40px;margin-left:0}}

.card{display:block;min-width:0}
.cardimg{display:block;position:relative;aspect-ratio:3/2;overflow:hidden;background:var(--ph)}
.cardimg img{width:100%;height:100%;object-fit:cover;object-position:center}

/* The image itself is untouched on hover. The name appears over it in white, on a short
   gradient confined to the bottom strip — enough to stay legible over a pale rendering
   without washing out the photograph. */
.cardmeta{position:absolute;inset:0;display:flex;flex-direction:column;
  justify-content:flex-end;padding:16px;color:#fff;
  background:linear-gradient(to top,rgba(0,0,0,.58) 0,rgba(0,0,0,.28) 28%,rgba(0,0,0,0) 56%);
  opacity:0;transition:opacity .2s ease;pointer-events:none}
.card:hover .cardmeta{opacity:1}
.cardmeta .ct{display:block}
.cardmeta .cl{display:block;font-size:var(--t-caption);color:rgba(255,255,255,.78)}
/* No hover on a touch screen, so the caption sits below the image instead. */
@media(hover:none){
  .cardmeta{position:static;opacity:1;background:none;padding:10px 0 0;color:var(--muted)}
  .cardmeta .cl{color:var(--muted)}
}

/* ---- work index ----
   A plain uniform grid: every project the same size, rows even, columns added as the
   window widens. Order still comes from editorial priority, so the strongest work is
   first — but nothing is sized or offset differently. */
.worklist{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap)}
@media(min-width:1200px){.worklist{grid-template-columns:repeat(4,1fr)}}
@media(min-width:1600px){.worklist{grid-template-columns:repeat(5,1fr)}}
@media(min-width:2000px){.worklist{grid-template-columns:repeat(6,1fr)}}
@media(max-width:820px){.worklist{grid-template-columns:1fr;gap:40px}}

.worklist .card{display:block;min-width:0}
.worklist .cardimg{aspect-ratio:3/2;height:auto;width:100%}
.worklist .cardimg img{width:100%;height:100%;object-fit:cover;object-position:center}

/* ---- project page ---- */
.projhead{display:grid;grid-template-columns:repeat(6,1fr);gap:var(--gap);
  align-items:start;padding-top:0}
body.project main{position:relative}
/* The cover sits in the same frame as the home page: the side margins come from main's
   own --pad, the space above it from --hero-top, and the height is what is left of one
   screen once that frame is taken out. Inset, not full bleed. */
.projcover{grid-column:1/-1;display:block;width:100%;
  height:calc(100svh - var(--hero-top) - var(--pad));
  margin:calc(var(--hero-top) - var(--topbar-h)) 0 40px;
  object-fit:cover;object-position:center;background:var(--ph)}
@media(max-width:820px){
  .projcover{height:auto;aspect-ratio:3/2}
}
.projmeta{grid-column:2}
.projmeta h1{margin-bottom:.2em}
.projmeta .loc{color:var(--muted)}
.projbody{grid-column:3/7;max-width:68ch}
.projbody p{margin-bottom:1.4em;text-align:justify;-webkit-hyphens:auto;hyphens:auto}
.credits{display:grid;grid-template-columns:210px 1fr;column-gap:20px;
  margin-top:var(--gap);color:var(--muted)}
.credits .ck{white-space:nowrap}
.credits a{text-decoration:underline;text-underline-offset:2px}
.credits a:hover{color:var(--ink)}
@media(max-width:820px){
  .projhead{grid-template-columns:1fr}
  .projmeta,.projbody{grid-column:1}
  .projmeta{margin:16px 0 8px}
  .credits{grid-template-columns:max-content 1fr}
}

/* ---- project gallery ---- */
.gallery{display:flex;flex-direction:column;gap:var(--gap);align-items:flex-start;
  padding:var(--gap) 0 76px}
.gi{background:var(--ph);overflow:hidden}
.gi img{width:100%;height:100%;object-fit:cover}
.grow{display:flex;gap:var(--gap);width:100%;align-items:flex-start}
.grow .gi{min-width:0}
@media(max-width:820px){
  .gallery .gi{width:100% !important}
  .grow{flex-direction:column}
}

/* ---- studio ----
   The opening passage runs two columns at a readable measure; the headed sections below
   go to three when there is room. */
.opening{padding-bottom:84px}
.opening p{margin:0 0 1.3em;max-width:62ch}
@media(min-width:900px){
  .opening{column-count:2;column-gap:var(--colgap)}
  .opening p{max-width:none}
}
/* The top band of the studio page: the practice statement held in the first column, the
   portrait in the last, nothing between them. A real grid rather than a max-width, so the
   statement's edges land on the same lines as the first column of text below it and the
   photograph's right edge lands on the page margin. */
.studiotop{display:grid;grid-template-columns:1fr;gap:var(--colgap);
  align-items:start;padding:0 0 56px}
@media(min-width:900px){.studiotop{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1280px){.studiotop{grid-template-columns:repeat(3,1fr)}}
.studiotop .intro{grid-column:1;padding:0}
.studiotop .portrait{grid-column:-2;margin:0;max-width:none;width:100%}
.intro .lead{font-size:var(--t-quote);font-weight:var(--w-quote);line-height:1.3;
  letter-spacing:-.005em;margin:0;color:var(--ink-soft)}
@media(max-width:1100px){.intro .lead{font-size:calc(var(--t-quote) * .83)}}
@media(max-width:820px){
  .studiotop{padding:0 0 44px;gap:28px}
  .intro .lead{font-size:calc(var(--t-quote) * .73)}
}

/* A grid of sections, not a continuous multi-column flow. The old column-count made one
   stream that broke wherever it landed, so no two headings or paragraphs lined up. Here
   each section owns a column and spans two rows — heading, then body — and the sections
   in a row share those two rows through subgrid, so every body starts on the same line
   even when a heading runs to two. Browsers without subgrid still align, because the
   headings are all one line; they just lose the guarantee. */
.cols{max-width:none;display:grid;grid-template-columns:1fr;
  gap:40px var(--colgap);align-items:start}
@media(min-width:900px){.cols{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1280px){.cols{grid-template-columns:repeat(3,1fr)}}
.colsec{display:grid;grid-template-rows:subgrid;grid-row:span 2;gap:0}
.cols h2{margin:0 0 .3em}
.cols p{margin:0 0 1.3em;text-align:justify;-webkit-hyphens:auto;hyphens:auto}
.cols p:last-child{margin-bottom:0}
.portrait{display:block;width:100%;height:auto}
.people{display:grid;grid-template-columns:1fr;gap:32px}
@media(min-width:900px){.people{grid-template-columns:repeat(2,1fr);gap:40px var(--colgap)}}
@media(min-width:1280px){.people{grid-template-columns:repeat(3,1fr)}}
.person .pname{color:var(--ink)}
.person .pmeta{color:var(--muted);font-size:var(--t-body)}
.person .pmeta a:hover{color:var(--ink)}

/* ---- press ---- */
.pubs{display:grid;grid-template-columns:1fr;gap:36px var(--gap);align-items:start}
@media(min-width:900px){.pubs{grid-template-columns:repeat(3,1fr)}}
@media(min-width:1280px){.pubs{grid-template-columns:repeat(5,1fr)}}
.pub{min-width:0;color:var(--muted)}
.pubthumb{width:100%;aspect-ratio:16/9;object-fit:cover;filter:grayscale(1);
  margin-bottom:12px;background:var(--ph)}
.pubpub{color:var(--muted);font-size:var(--t-body)}
.pubtitle,.pubmeta{color:var(--muted)}
.pubmeta a,.publinks a{text-decoration:underline;text-underline-offset:2px}
.pubmeta a:hover,.publinks a:hover{color:var(--ink)}
.publinks{margin-top:.4em;display:flex;gap:16px;flex-wrap:wrap}

/* ---- passive house ----
   Uses the Studio layout (.studiotop, .cols, .people). The only additions: the statement
   is an h1 here rather than a p, and the questions are h3s set to match the h2 headings. */
h1.lead{font-size:var(--t-quote);font-weight:var(--w-quote);line-height:1.3}
@media(max-width:1100px){h1.lead{font-size:calc(var(--t-quote) * .83)}}
@media(max-width:820px){h1.lead{font-size:calc(var(--t-quote) * .73)}}
.cols h3{margin:0 0 .3em;color:var(--muted)}
.phcard{display:block;min-width:0}
.phcard .cardimg img{transition:opacity .2s ease}
.phcard:hover .cardimg img,.phcard:focus-visible .cardimg img{opacity:.8}
.phmeta{display:block;padding-top:12px}
.phmeta .ct{display:block;color:var(--ink)}
.phmeta .cl{display:block;color:var(--muted)}
/* Three projects, so they sit on the same columns as the text above rather than the
   five-up work grid. */
@media(min-width:900px){.phprojects .grid{grid-template-columns:repeat(3,1fr);gap:var(--colgap)}}

/* ---- instagram ----
   One linked screenshot of the profile, so the visitor sees where they are going before
   they leave the site. A hairline frame separates Instagram's white page from ours. */
.igbare main{min-height:calc(100vh - var(--topbar-h));min-height:calc(100dvh - var(--topbar-h));display:grid;place-items:center;
  padding:40px var(--pad)}
.igcard{display:block;width:100%;max-width:720px}
.igcard img{width:100%;height:auto;border:1px solid var(--ph);transition:opacity .18s ease}
.igcard:hover img,.igcard:focus-visible img{opacity:.85}
.iglink{display:block;margin-top:14px;text-align:center;color:var(--muted);font-size:var(--t-caption);
  text-decoration:underline;text-underline-offset:3px}
.igcard:hover .iglink{color:var(--ink)}

/* ---- category list ----
   The categories are a stacked list, not a line of buttons. On /work/ it occupies the
   first cell of the grid, where a project thumbnail would otherwise be; on a category
   page it sits in the same place, top left, in the space the right-aligned rows leave
   empty. Same position in both views, so it does not move when you click it. */
/* Set like the practice statement on the Studio page: the same size, the same Light
   weight, the same tracking. Stacked, with the line-height doing the spacing. Grey
   until it is the page you are on. */
.catnav{display:flex;flex-direction:column;align-items:flex-start;gap:0;
  font-size:var(--t-quote);font-weight:var(--w-quote);line-height:1.15;
  letter-spacing:-.005em;color:var(--muted);align-self:start}
.catnav a{padding:0 0 .18em}
.catnav a:hover{color:var(--ink-soft)}
.catnav a[aria-current]{color:var(--ink-soft)}
@media(max-width:1100px){.catnav{font-size:calc(var(--t-quote) * .83)}}
@media(max-width:820px){
  .catnav{flex-direction:row;flex-wrap:wrap;gap:0 22px;margin-bottom:20px;
    font-size:var(--t-body);font-weight:400;line-height:1.5;
    letter-spacing:var(--track)}
  .catnav a{padding:0}
}

/* ---- category list rows ----
   The page is one grid. The category list holds the first column and the rows occupy
   the columns to its right, so the left of the page stays open — the Pawson
   arrangement. Column counts and gap match the "All" grid at every breakpoint, so a
   list image is exactly the size of a grid card and its edges land on the same lines.
   No rules between rows; the white space separates them. */
.lwrap{display:grid;grid-template-columns:repeat(3,1fr);gap:0 var(--gap)}
@media(min-width:1200px){.lwrap{grid-template-columns:repeat(4,1fr)}}
@media(min-width:1600px){.lwrap{grid-template-columns:repeat(5,1fr)}}
@media(min-width:2000px){.lwrap{grid-template-columns:repeat(6,1fr)}}

/* The column stretches to the full height of the list, and the list inside it sticks,
   so the categories hold their place while the projects scroll past. The offset clears
   the sticky header above. */
.catcol{grid-column:1}
/* Exactly the header height: the list starts flush under the header and is
   already in its stuck position at the top of the page, so moving between All
   and a category does not shift it. A larger offset would push it down the
   moment the page loaded. */
.catcol .catnav{position:sticky;top:var(--topbar-h)}

/* One column narrower than its parent, sitting in the tracks to the right of the list,
   so its own columns land exactly on the parent's. */
.lrows{grid-column:2/-1;display:grid;grid-template-columns:repeat(2,1fr);gap:0 var(--gap)}
@media(min-width:1200px){.lrows{grid-template-columns:repeat(3,1fr)}}
@media(min-width:1600px){.lrows{grid-template-columns:repeat(4,1fr)}}
@media(min-width:2000px){.lrows{grid-template-columns:repeat(5,1fr)}}

.lrow{grid-column:-3/-1;display:grid;grid-template-columns:1fr 1fr;gap:var(--gap);
  align-items:start;padding:0 0 var(--gap)}
.linfo{grid-column:1;display:block}
.limg{grid-column:2;display:block;aspect-ratio:3/2;overflow:hidden;background:var(--ph)}
.limg img{width:100%;height:100%;object-fit:cover;object-position:center}

/* Title and location are both Light. Matching the weights is what makes the pair work:
   the fault before was a light title over a Regular line, so the smaller text carried
   the heavier stroke. Same weight means size alone sets the order. */
.lt{display:block;color:var(--ink-soft);font-size:var(--t-quote);font-weight:var(--w-quote);
  line-height:1.15;letter-spacing:-.005em}
.lm{display:block;color:var(--ink-soft);font-size:var(--t-h1);font-weight:var(--w-quote);
  line-height:1.3;letter-spacing:-.005em;margin-top:.35em}
@media(max-width:1100px){
  .lt{font-size:calc(var(--t-quote) * .83)}
  .lm{font-size:calc(var(--t-h1) * .9)}
}
@media(max-width:820px){
  .lt{font-size:calc(var(--t-quote) * .73)}
  .lm{font-size:calc(var(--t-h1) * .85)}
}

/* No spare column to give the list, so it goes back above the content and stops sticking. */
@media(max-width:820px){
  .lwrap{grid-template-columns:1fr}
  .catcol{grid-column:1}
  .catcol .catnav{position:static}
  .lrows{grid-column:1;grid-template-columns:1fr 1fr}
  .lrow{grid-column:1/-1;padding-bottom:var(--gap)}
}
@media(max-width:560px){
  .lrow{grid-template-columns:1fr;gap:14px;padding-bottom:32px}
  .linfo,.limg{grid-column:1}
  .limg{order:-1}
}

/* The list as the first cell of the /work/ grid: no picture, just the links at the top
   of the cell where a thumbnail would start. At this size it needs the square below it
   too, so it spans two rows and two thumbnails give way to it. */
.worklist .catnav{align-self:start;grid-row:span 2}
@media(max-width:820px){.worklist .catnav{grid-row:auto}}


/* generated by build.py — 7 images, 5s each */
@keyframes heroCycle{
  0%{opacity:0}
  2.000%{opacity:1}
  16.286%{opacity:1}
  18.286%{opacity:0}
  100%{opacity:0}
}
@keyframes heroCapCycle{
  0%{opacity:0}
  2.000%{opacity:1}
  12.286%{opacity:1}
  14.286%{opacity:0}
  100%{opacity:0}
}
