/* ============================================================
   JENNA MARI — artist site.
   One idea: a white room with the work loosely pinned to it.
   No grid, no cards, no chrome. Everything theme-able lives in
   :root so #edit → Theme colours can retune the whole site.
   ============================================================ */
:root{
	--paper:      #ffffff;
	--ink:        #101010;
	--ink-soft:   rgba(16,16,16,0.45);
	--ink-faint:  rgba(16,16,16,0.22);
	--rule:       rgba(16,16,16,0.12);

	/* Resolved at use time, so load order between main.css and fonts.css
	   does not matter. The fallback is the site's original stack, which
	   is what shows if fonts.css is absent — the site never depends on
	   a webfont having arrived. */
	--sans: var(--font-body, 'Helvetica Neue', Helvetica, Arial, sans-serif);
	--display: var(--font-display, var(--sans));
	/* One multiplier the editor writes, so the whole type scale moves
	   together and the hierarchy between nav / caption / body survives.
	   Setting each size independently is how a scale stops being a scale. */
	--type-scale:   1;
	--type-nav:     calc(13px * var(--type-scale));
	--type-caption: calc(11px * var(--type-scale));
	--type-body:    calc(15px * var(--type-scale));
	--ls-caption:   0.06em;

	/* How tall the scattered canvas is. Deliberately in vw, not vh: the
	   pieces are sized in vw, so positioning them in vw too means the
	   whole composition scales as one unit and holds its shape on any
	   window proportion. Mixing vw sizes with vh positions would pile
	   the pieces up on a short window and strand them on a tall one. */
	--canvas-height: 238vw;
}

*{ box-sizing: border-box; }
html{ -webkit-text-size-adjust: 100%; background: var(--paper); }
body{
	margin: 0;
	font-family: var(--sans);
	font-size: var(--type-body);
	line-height: 1.5;
	color: var(--ink);
	background: var(--paper);
	overflow-x: hidden;   /* rotated pieces can graze the edge */
}
a{ color: inherit; text-decoration: none; }
img{ display: block; width: 100%; height: auto; }

/* ---------- masthead ------------------------------------------------
   Fixed, tiny, out of the way. The name is the only fixed thing on the
   page; the work moves past it. */
.masthead{
	position: fixed; top: 0; left: 0; right: 0; z-index: 100;
	display: flex; align-items: baseline; justify-content: space-between;
	padding: 1.6rem 2rem;
	pointer-events: none;                 /* let clicks fall through to the work */
	mix-blend-mode: difference;           /* stays legible over a light photo */
	color: #fff;                          /* difference-blended against white → black */
}
.masthead a, .masthead .name{ pointer-events: auto; }
.masthead .name{
	font-family: var(--display);
	font-size: var(--type-nav); font-weight: 400;
	letter-spacing: 0.02em;
	line-height: 0;
}
.masthead .logo{
	/* Sized by height so the wordmark keeps its weight against the nav
	   type; width follows. The masthead is mix-blend-mode:difference, and
	   a black-on-transparent mark inverts to black over the white page. */
	/* 19px → 23px, +20% at the owner's request (2026-08-05). Both values
	   move together: the mobile one is the same mark and would otherwise
	   read as a different size decision below 760px. */
	height: 23px; width: auto;
	display: block;
}
@media (max-width: 760px){ .masthead .logo{ height: 19px; } }
.masthead nav{ display: flex; gap: 1.5rem; }
.masthead nav a{
	font-size: var(--type-nav);
	opacity: 0.72;
	transition: opacity .25s ease;
}
.masthead nav a:hover,
.masthead nav a[aria-current="page"]{ opacity: 1; }

/* ---------- the scatter ---------------------------------------------
   Each piece carries its own position/size/tilt as inline custom
   properties (--x --y --w --r). That keeps index.html readable as a
   list of coordinates and means one piece can be nudged without
   touching any other. */
.scatter{
	position: relative;
	height: var(--canvas-height);
	margin: 0;
}
.piece{
	position: absolute;
	left: var(--x);
	top: var(--y);
	width: var(--w);
	margin: 0;
	transform: rotate(var(--r, 0deg));
	transition: transform .5s cubic-bezier(.2,.8,.25,1);
}
.piece img{
	/* A hair of shadow so overlaps read as depth rather than collage. */
	box-shadow: 0 1px 24px rgba(16,16,16,0.06);
}
/* Cut-outs get no box-shadow. box-shadow follows the element's BOX, so on a
   transparent image it draws a faint rectangle around an object that is not
   rectangular — which is exactly the illusion the cut-out exists to create.
   drop-shadow() would follow the alpha, but it is expensive on a 1400px
   image and the piece reads better floating clean on the white anyway. */
.piece--cutout img{ box-shadow: none; }
/* Lift on hover — the only interaction on the page. */
.piece:hover{
	z-index: 50;
	transform: rotate(var(--r, 0deg)) translateY(-6px);
}
.piece figcaption{
	/* --font-local / --block-scale are per-element overrides written by the
	   editor. Unset, they fall through to the site's choice. */
	font-family: var(--font-local, var(--sans));
	font-size: calc(var(--type-caption) * var(--block-scale, 1));
	letter-spacing: var(--ls-caption);
	color: var(--ink-soft);
	margin-top: 0.6rem;
	opacity: 0;
	transition: opacity .3s ease;
}
.piece:hover figcaption{ opacity: 1; }

/* ---------- colophon ------------------------------------------------ */
.colophon{
	/* The SAME twelve columns as the pages, aligned by construction.

	   No horizontal padding: --grid-colw and --grid-gutter are percentages,
	   so they only resolve to the page's column widths if this element is
	   the full page width. The left margin is a leading track instead, minus
	   one gutter because column-gap adds one after it — so the first column
	   starts at exactly --grid-margin, same as a piece at --col:0.

	   It was flex + space-between with four children. That is why replacing
	   the children with one editable block collapsed it into a run-on line:
	   space-between had nothing left to space. */
	display: grid;
	grid-template-columns:
		calc(var(--grid-margin) - var(--grid-gutter))
		repeat(12, var(--grid-colw));
	column-gap: var(--grid-gutter);
	row-gap: 1.2rem;
	align-items: start;
	padding: 4rem 0 3rem;
	font-size: var(--type-caption);
	letter-spacing: var(--ls-caption);
	color: var(--ink-soft);
}
.foot-cell{
	/* +2: one for CSS grid lines being 1-based, one for the leading margin
	   track. So --col:0 lands on the same edge as a piece at --col:0. */
	grid-column: calc(var(--col, 0) + 2) / span var(--span, 3);
	min-height: 1em;
	outline: none;
}
.foot-cell p{ margin: 0; }
.colophon a:hover{ color: var(--ink); }

/* ---------- info page ------------------------------------------------ */
.info{
	max-width: 30rem;
	padding: 32vh 2rem 6rem;
	margin: 0;
}
.info h1{
	font-size: var(--type-body); font-weight: 400;
	margin: 0 0 2rem; letter-spacing: 0.02em;
}
.info p{ margin: 0 0 1.4rem; }
.info p:last-of-type{ margin-bottom: 2.5rem; }
.info .meta{
	font-size: var(--type-caption); letter-spacing: var(--ls-caption);
	color: var(--ink-soft);
}
.info .meta a{ border-bottom: 1px solid var(--rule); }
.info .meta a:hover{ border-color: var(--ink); }

/* ---------- small screens -------------------------------------------
   The scatter is a desktop idea — it depends on room to be loose in.
   Below 760px it collapses to a single honest column: same order, same
   images, no absolute positioning, no tilt. */
@media (max-width: 760px){
	.masthead{ padding: 1.1rem 1.25rem; }
	.masthead nav{ gap: 1rem; }

	/* NO mobile collapse here. This block used to force
	     .scatter{height:auto}  .piece{position:static; width:100%!important}
	   which was written for the ORIGINAL scatter — free --x/--y/--w
	   coordinates — and turned destructive the moment grid.css arrived:

	     · grid.css says `.scatter .piece{position:absolute}`. That outranks
	       a bare `.piece{position:static}`, so the pieces stayed absolutely
	       placed at their grid coordinates — while `width:100%!important`
	       DID win. Every photograph stretched to the full width and kept its
	       original position, so they landed on top of one another.
	     · `height:auto` on a container whose children are all absolute
	       collapses it to nothing, so the colophon rose to the top of the
	       document and the photographs covered the text.

	   The grid is expressed in % and vw, so it already scales on its own:
	   leaving it alone gives the desktop composition at phone size, which is
	   what it is for. Anything mobile-specific goes in its own stylesheet —
	   see css/mobile-messy.css — never as an override of the grid here. */
	/* Captions stay hidden, as on the desktop, where they are a hover
	   affordance. There is no hover on a touch screen, so the rule that used
	   to sit here forced them permanently on — and at phone scale a title
	   like "heart-says-what" wraps to three lines and spills out of a 60px
	   figure onto the photograph beneath it. The name is not lost: tapping a
	   piece opens the window, which is what a tap is for. */

	/* Clear the masthead. Flowlines are 4vw, so the piece on row 2 sits
	   115px down on a 1440px desktop and only 31px down on a 390px phone —
	   straight behind the wordmark and the nav, which are fixed. The offset
	   goes on the PAGE, not the scatter: an absolutely positioned child is
	   laid out against its containing block's PADDING BOX, so padding on
	   .scatter would not move the pieces at all, and a margin there would
	   collapse through the section. Padding on .page cannot collapse and
	   pushes the whole scatter down as one.
	   Sized against the masthead itself: 1.1rem padding twice plus a 19px
	   wordmark is ~54px, so 4.5rem leaves about 18px of air. */
	.pages > .page{ padding-top: 4.5rem; }

	.colophon{ padding: 2rem 1.25rem 3rem; }
	.info{ padding: 22vh 1.25rem 4rem; }
}

@media (prefers-reduced-motion: reduce){
	.piece, .piece figcaption{ transition: none; }
	.piece:hover{ transform: rotate(var(--r, 0deg)); }
}

/* ============================================================
   THE PIECE WINDOW
   A small floating panel, not a full-screen lightbox. There is no
   dimming scrim: the scatter and the murmuration stay exactly as
   they were at the moment of the click, visible around and through
   the panel. The only near-opaque surface is the frosted card the
   text sits on — the one place readability actually demands one.
   ============================================================ */
.piece{ cursor: zoom-in; }
.piece:focus-visible{ outline: 1px solid var(--ink); outline-offset: 6px; }

.lb{
	position: fixed; inset: 0; z-index: 200;
	display: flex; align-items: center; justify-content: center;
	padding: 5vh 4vw;
	/* Catches the click that closes. Deliberately NOT tinted — the
	   background is meant to survive the interaction untouched. */
	background: transparent;
}
.lb[hidden]{ display: none; }

.lb-panel{
	position: relative;
	display: flex; align-items: stretch;
	gap: 0;
	/* Deliberately modest. This is a window that opens ON the page, not
	   one that takes it over — the scatter has to stay readable around it. */
	max-width: min(760px, 90vw);
	max-height: 66vh;
	outline: none;
	transform-origin: center center;
	transition:
		transform .52s cubic-bezier(.16,.84,.28,1),
		opacity   .30s ease;
	/* The panel's own shadow is what separates it from the page —
	   soft and wide, so it reads as hovering rather than pasted. */
	filter: drop-shadow(0 24px 60px rgba(16,16,16,0.22));
}

/* The photo carries no backing of its own — it is already opaque. */
.lb-figure{
	margin: 0; flex: 0 1 auto; min-width: 0;
	display: flex;
}
.lb-img{
	display: block; width: auto; height: auto;
	max-height: 62vh; max-width: min(42vw, 440px);
	object-fit: contain;
}

/* The one readability surface. */
.lb-meta{
	flex: 0 0 clamp(200px, 24vw, 268px);
	display: flex; flex-direction: column;
	padding: 1.75rem 1.5rem 1.25rem;
	/* 0.78, not lower. Blur alone does not rescue small text over a
	   photograph: the imagery behind is low-frequency, so blurring barely
	   changes its luminance. Measured at 0.62 the description ran ~2.5:1
	   against its backing — this brings it over 4.5:1 while still reading
	   clearly as glass. */
	background: rgba(255,255,255,0.78);
	-webkit-backdrop-filter: blur(26px) saturate(1.3);
	backdrop-filter: blur(26px) saturate(1.3);
	border-left: 1px solid rgba(255,255,255,0.5);
	overflow-y: auto;
}
@supports not (backdrop-filter: blur(4px)){
	.lb-meta{ background: rgba(255,255,255,0.92); }
}

.lb-title{
	font-size: var(--type-body); font-weight: 400;
	letter-spacing: 0.02em; margin: 0 0 1.25rem; color: var(--ink);
}
.lb-dl{
	margin: 0 0 1.5rem;
	font-size: var(--type-caption); letter-spacing: var(--ls-caption);
	line-height: 1.9;
}
.lb-dl div{ display: flex; justify-content: space-between; gap: 1rem; }
.lb-dl dt{ color: rgba(16,16,16,0.40); }
.lb-dl dd{ margin: 0; color: rgba(16,16,16,0.72); text-align: right; }
.lb-desc{
	margin: 0 0 auto; font-size: 13px; line-height: 1.65;
	/* Darker than --ink-soft: this text sits on glass, not on paper. */
	color: rgba(16,16,16,0.74); text-wrap: pretty;
}
.lb-acts{
	margin-top: 1.1rem;
	display: flex; flex-wrap: wrap; gap: .35rem .9rem;
}
.lb-acts button{
	font: inherit;
	font-size: var(--type-caption);
	letter-spacing: var(--ls-caption);
	color: var(--ink-soft);
	background: none; border: 0;
	border-bottom: 1px solid var(--rule);
	padding: 0 0 2px; cursor: pointer;
	transition: color .25s ease, border-color .25s ease;
}
.lb-acts button:hover{ color: var(--ink); border-color: var(--ink); }
.lb-acts button[hidden]{ display: none; }

.lb-share{
	margin-top: .7rem;
	display: flex; flex-wrap: wrap; gap: .3rem .8rem;
	padding-top: .7rem;
	border-top: 1px solid var(--rule);
}
.lb-share[hidden]{ display: none; }
.lb-share button{
	font: inherit;
	font-size: var(--type-caption);
	letter-spacing: var(--ls-caption);
	color: var(--ink-soft);
	background: none; border: 0; padding: 0; cursor: pointer;
	transition: color .2s ease;
}
.lb-share button:hover{ color: var(--ink); }
.lb-share button[hidden]{ display: none; }

/* ---- full screen ----------------------------------------------------
   An in-page expansion, not the Fullscreen API: this keeps the site's own
   framing and exits on a click or Esc like everything else here, instead
   of handing the viewer the browser's fullscreen chrome and its own way
   out. The panel is hidden rather than removed so closing still runs the
   FLIP back to the photograph it came from. */
.lb-full{
	position: fixed; inset: 0; z-index: 250;
	display: none; align-items: center; justify-content: center;
	background: var(--paper);
	cursor: zoom-out;
}
.lb-full.on{ display: flex; }
.lb-full img{
	max-width: 94vw; max-height: 94vh;
	width: auto; height: auto; object-fit: contain;
}
.lb-full .lb-full-close{
	position: absolute; top: 1.2rem; right: 1.4rem;
	font: inherit; font-size: var(--type-caption);
	letter-spacing: var(--ls-caption);
	color: var(--ink-soft); background: none; border: 0; cursor: pointer;
}
.lb-full .lb-full-close:hover{ color: var(--ink); }
body.lb-is-full .lb-panel{ visibility: hidden; }
.lb-counter{
	margin-top: 1.5rem;
	font-size: var(--type-caption); letter-spacing: var(--ls-caption);
	color: var(--ink-faint);
}

/* Contents cross-fade when stepping with the arrows; the panel itself
   stays put, so only what changed moves. */
.lb-figure, .lb-meta > *{ transition: opacity .16s ease; }
.lb-panel.is-swapping .lb-figure,
.lb-panel.is-swapping .lb-meta > *{ opacity: 0; }

/* ---- controls ---------------------------------------------------- */
.lb-close, .lb-nav{
	position: absolute; z-index: 2;
	border: 0; padding: 0; cursor: pointer;
	background: rgba(255,255,255,0.62);
	-webkit-backdrop-filter: blur(12px);
	backdrop-filter: blur(12px);
	color: var(--ink);
	font: inherit; line-height: 1;
	border-radius: 999px;
	transition: opacity .25s ease, transform .25s ease;
	opacity: 0.55;
}
.lb-close:hover, .lb-nav:hover{ opacity: 1; }
.lb-close{
	top: -14px; right: -14px;
	width: 30px; height: 30px; font-size: 17px;
}
.lb-nav{
	top: 50%; margin-top: -18px;
	width: 36px; height: 36px; font-size: 18px;
}
.lb-prev{ left: -18px; }
.lb-next{ right: -18px; }
.lb-nav:hover{ transform: scale(1.08); }

/* ---- small screens ------------------------------------------------
   Stack the card under the photo and let it use the width it has. */
@media (max-width: 760px){
	.lb{ padding: 3vh 4vw; }
	.lb-panel{ flex-direction: column; max-height: 88vh; max-width: 92vw; }
	.lb-img{ max-width: 92vw; max-height: 50vh; }
	.lb-meta{
		flex: 1 1 auto; border-left: 0;
		border-top: 1px solid rgba(255,255,255,0.5);
		padding: 1.25rem 1.25rem 1rem;
	}
	.lb-prev{ left: 8px; }
	.lb-next{ right: 8px; }
	.lb-close{ top: 8px; right: 8px; }
}

@media (prefers-reduced-motion: reduce){
	.lb-panel{ transition: opacity .2s ease; }
	.lb-nav:hover{ transform: none; }
}

/* ============================================================
   PAGES
   Every page is in the one document; the hash picks one. Hidden
   with display:none rather than moved off-screen, so the pieces
   of an inactive page cost no layout and the grid editor never
   measures a box that is not on screen.
   ============================================================ */
.page{ display: none; }
.page.is-on{ display: block; }

/* The footer is shared by every page and is editable as one block.
   It sits outside .pages on purpose — one footer, not one per page,
   which is the whole reason it is a separate system. */
.colophon [data-editable]{ outline: none; }
.colophon p{ margin: 0; }
