/* ==========================================================================
   LATEMILK — rebuilt from the Figma (green rectangle spec)
   Order: tokens → reset → chrome → landing → film → studio → shop → contact
          → overlays → responsive
   ========================================================================== */

/* ----------------------------------------------------------------- type ---- */

/**
 * Helvetica Condensed Bold, the file the client sent — the chrome face is no longer
 * borrowed from whatever the visitor happens to have installed.
 *
 * The weight and width are declared as ranges on purpose. The layout asks for 78–82%
 * stretch and 700–800 weight in a dozen places (that was the Archivo fallback talking,
 * where both are real axes); a single-cut face that claims those ranges is picked as-is
 * instead of being smeared with a synthetic bold on top of an already-bold drawing.
 */
@font-face {
	font-family: 'LM Helvetica Condensed';
	src: url('../fonts/helvetica-condensed-bold.woff2') format('woff2'),
	     url('../fonts/helvetica-condensed-bold.woff') format('woff');
	font-weight: 400 900;
	font-stretch: 50% 100%;
	font-style: normal;
	font-display: swap;
}

:root {
	--lm-ink:    #000000;
	--lm-paper:  #ffffff;
	--lm-mute:   #a6a6a6;
	--lm-blue:   #0245d6;  /* About — Film */
	--lm-red:    #af0003;  /* About — Studio */

	--lm-gut:    clamp(16px, 1.4vw, 24px);   /* page edge padding */
	/* The bars sit tighter to the edge than the page body does — measured off the Figma,
	   where LATEMILK starts 16px in on a 1400px board. Kept separate from --lm-gut so
	   pulling the chrome to the edge doesn't drag the Contact column with it. */
	--lm-edge:   clamp(10px, 1vw, 16px);
	--lm-bar:    clamp(52px, 6.5vh, 62px);   /* mobile bottom bar height */
	/* The height of the fixed top bar, derived from what actually builds it: its own
	   padding top and bottom plus one line of nav type. Studio's gallery hangs off it —
	   "da appena sotto la navbar al margine inferiore" — so it has to be a number the
	   layout can read, not one measured after the fact in JS. */
	--lm-topbar: calc(var(--lm-edge) * 2 + var(--lm-fs-nav) * 1.4);

	--lm-fs-nav:  clamp(13px, 0.95vw, 16px);
	/* One point down from the drawing (was 15/19): Film and Studio list many more
	   projects than the mockup did, and the rows had to fit the window. */
	--lm-fs-list: clamp(14px, 1.08vw, 18px);
	--lm-fs-body: clamp(13px, 0.95vw, 16px);

	--lm-ease: cubic-bezier(0.22, 0.61, 0.36, 1);

	/* The two vertical guides the whole design is hung on, measured off the mockups.
	   26%   — "+ ABOUT", the Film list, the Studio "campaign" column
	   50.6% — the top-bar nav, WATCH NOW, the Contact form
	   Getting these right is most of what makes a page look like the drawing. */
	--lm-g1: 26%;
	--lm-g2: 50.6%;

	/**
	 * Helvetica, as asked — condensed bold for the chrome, the regular cut for copy.
	 *
	 * The condensed cut is now served by us (see the @font-face above), so every visitor
	 * gets the same drawing instead of only the Macs that had it installed. Those local
	 * names stay behind it as a net for a failed download, and Archivo at font-stretch
	 * 82% is the last resort: a variable grotesque on the same skeleton, so it shifts the
	 * texture rather than the layout.
	 */
	--lm-cond: 'LM Helvetica Condensed',
	           'Helvetica Neue Condensed Bold', 'HelveticaNeue-CondensedBold',
	           'Helvetica Neue LT Condensed', 'Helvetica Condensed', 'HelveticaNeue-Condensed',
	           'Archivo', 'Archivo Narrow', system-ui, sans-serif;
	/* Copy used to be the regular, wide Helvetica. The client wants the whole site in the
	   condensed (2026-10), so the copy token now points at the same face — anything still
	   asking for --lm-text gets the condensed rather than a stray wide line. */
	--lm-text: var(--lm-cond);
}

/* --------------------------------------------------------------- reset ---- */

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

html, body {
	margin: 0;
	padding: 0;
	background: var(--lm-paper);
	color: var(--lm-ink);
}

/* Condensed everywhere, copy included: the client wants one face across the site, the
   same one the bars and the lists are set in. */
body {
	font-family: var(--lm-cond);
	font-stretch: 82%;
	font-weight: 700;
	font-size: var(--lm-fs-body);
	line-height: 1.35;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

img, video, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
/* No grey box when a phone taps a control. The + and the − are the whole mark here, so
   anything the browser draws around them reads as a stray little square. */
button, a { -webkit-tap-highlight-color: transparent; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
ul, ol { list-style: none; margin: 0; padding: 0; }
h1, h2, p { margin: 0; }

html.lm-locked, html.lm-locked body { overflow: hidden; }

/* Panels declare display:grid/flex, which outranks the UA's [hidden] rule — without
   this the player and the drawers sit on top of every page, permanently open. */
[hidden] { display: none !important; }

.screen-reader-text {
	position: absolute; width: 1px; height: 1px;
	overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.lm-skip {
	position: absolute; left: -9999px; top: 0; z-index: 100;
	background: var(--lm-ink); color: var(--lm-paper); padding: 10px 16px;
}
.lm-skip:focus { left: 8px; top: 8px; }

:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
/* Set while focus is handed back to a control that was activated by tap, and cleared on
   the next key press. Without it, closing a panel paints a ring on the button that opened
   it — the square the client kept seeing around the + after closing About on a phone. */
html.lm-quiet-focus :focus-visible { outline: none; }

/* Black/white switch, driven by the wordmark (client note #49). Swapping the two
   tokens flips every page that has a solid background. */
:root[data-lm-theme="invert"] {
	--lm-ink:   #ffffff;
	--lm-paper: #000000;
	--lm-mute:  #6f6f6f;
}
html, body, .lm-main { transition: background-color 0.3s var(--lm-ease), color 0.3s var(--lm-ease); }

/* Film is ink-on-video: its ground is the footage, not a colour, so it opts out of the
   switch. It does that by pinning the tokens back to their *normal* meaning — paper is
   white, ink is black — not by swapping them. Swapping turned every Film element that
   asks for --lm-paper (the top bar, the active row, WATCH NOW) black against black
   footage. What makes Film dark is the background declaration below, nothing else. */
.lm--light { --lm-ink: #000000; --lm-paper: #ffffff; --lm-mute: #a6a6a6; }
.lm--light { background: #000; color: #fff; }
.lm--light ::selection { background: #fff; color: #000; }

.lm-main { min-height: 100svh; }

/* --------------------------------------------------------------- chrome --- */

.lm-top {
	position: fixed; inset: 0 0 auto 0; z-index: 40;
	/* Same guides as the page body, so the bar sits on the layout rather than over it. */
	display: grid;
	grid-template-columns: var(--lm-g1) calc(var(--lm-g2) - var(--lm-g1)) 1fr auto;
	align-items: start;
	padding: var(--lm-edge) 0;
	font-family: var(--lm-cond);
	font-stretch: 82%;
	font-weight: 700;
	font-size: var(--lm-fs-nav);
	letter-spacing: 0.01em;
	pointer-events: none; /* the video underneath stays hoverable between items */
}
.lm-top > * { pointer-events: auto; }
.lm-top--light { color: var(--lm-paper); }
.lm-top--dark  { color: var(--lm-ink); }

/* Caps belong to the wordmark and to ABOUT; the four sections are set as written
   (client: "eviterei nelle voci di menu di mettere tutto in capslock"). */
.lm-top__brand { font-weight: 700; letter-spacing: 0.005em; padding-left: var(--lm-edge); text-align: left; text-transform: uppercase; }

.lm-top__about { grid-column: 2; display: inline-flex; align-items: center; gap: 7px; font-weight: 700; text-transform: uppercase; }

/* The drawn + / −. Sized in em so it tracks whatever type it sits beside, and given a
   touch of optical lift because a geometric cross centres lower than a cap-height word. */
.lm-mark { width: 0.95em; height: 0.95em; flex: none; }

/* Column 3 is stated explicitly so the nav still lands on guide 2 on Shop and
   Contact, which have no ABOUT button to hold column 2 open. */
.lm-top__nav { grid-column: 3; display: flex; gap: clamp(16px, 2.2vw, 36px); }
.lm-top__cart { display: inline-flex; align-items: center; gap: 6px; padding-right: var(--lm-edge); }

/**
 * The bar is caps, on every page.
 *
 * The Figma draws it that way and the client confirmed it: the note about caps lock was
 * never about the menu, it was about the list of projects on Film (see .lm-film__pick).
 * Casing is decided here rather than in the labels so LMT_SECTIONS stays one source of
 * truth — it holds "Film", and the bar and the splash both dress it in caps.
 */
.lm-top__link,
.lm-mbot__link { text-transform: uppercase; }

/* Inactive nav items sit back; the active one and hover come forward. */
.lm-top__link { color: var(--lm-mute); transition: color 0.2s var(--lm-ease); }
.lm-top__link.is-active,
.lm-top__link:hover,
.lm-top__link:focus-visible { color: currentColor; }
.lm-top--light .lm-top__link.is-active,
.lm-top--light .lm-top__link:hover { color: var(--lm-paper); }
.lm-top--dark  .lm-top__link.is-active,
.lm-top--dark  .lm-top__link:hover { color: var(--lm-ink); }

.lm-bag { width: 16px; height: 14px; flex: none; }

/* Mobile chrome — brand up top, sections at thumb height. */
.lm-mtop, .lm-mbot { display: none; }

.lm-mtop {
	position: fixed; inset: 0 0 auto 0; z-index: 40;
	align-items: flex-start; justify-content: space-between;
	padding: var(--lm-edge);
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
}
.lm-mtop--light { color: var(--lm-paper); }
.lm-mtop--dark  { color: var(--lm-ink); }
.lm-mtop__brand { font-size: clamp(28px, 9vw, 40px); line-height: 0.9; letter-spacing: -0.01em; }
.lm-mtop__about { display: inline-flex; font-size: clamp(26px, 8vw, 36px); line-height: 0.8; }

.lm-mbot {
	position: fixed; inset: auto 0 0 0; z-index: 40;
	align-items: center; justify-content: space-between; gap: 8px;
	padding: 10px var(--lm-edge) calc(10px + env(safe-area-inset-bottom));
	min-height: var(--lm-bar);
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	/* Sized for a thumb rather than to match the desktop bar: the client found the old
	   12–15px items too small to hit. Each item also carries its own tap padding. */
	font-size: clamp(16px, 4.6vw, 20px);
}
.lm-mbot__link, .lm-mbot__cart { padding: 8px 0; }
.lm-mbot .lm-bag { width: 18px; height: 16px; }
.lm-mbot--light { color: var(--lm-paper); }
.lm-mbot--dark  { color: var(--lm-ink); }
.lm-mbot__link { color: var(--lm-mute); }
.lm-mbot__link.is-active { color: currentColor; text-decoration: underline; text-underline-offset: 4px; }
.lm-mbot__cart { display: inline-flex; align-items: center; gap: 4px; }

/**
 * DIFFERENCE, on the pages where type sits on footage.
 *
 * Only Film: the bars are white ink over a moving image, so blending them means the
 * wordmark and the nav read against whatever frame is underneath instead of fighting
 * it. Studio, Shop and Contact are ink on white — difference there would paint black
 * type on a black ground the moment the invert switch is thrown.
 *
 * The blend group is the root: the bar is a sibling of <main>, painted above the fixed
 * Film video, and nothing between them isolates. Keep it that way — an `isolation` or a
 * stray `filter` on <body> or .lm-main would drop the backdrop to the white page and
 * turn the whole bar black.
 */
.lm-page--film .lm-top,
.lm-page--film .lm-mtop,
.lm-page--film .lm-mbot { mix-blend-mode: difference; }

/* -------------------------------------------------------------- landing --- */

.lm-landing { min-height: 100svh; background: var(--lm-paper); }

.lm-landing__stage {
	position: relative;
	min-height: 100svh;
	display: grid;
	place-items: center;
	overflow: hidden;
}

/* Black behind the montage: between two cuts there can be a frame with nothing in it,
   and the white page flashing through reads as a fault. */
.lm-landing__video { position: absolute; inset: 0; background: var(--lm-ink); }
.lm-landing__clip {
	position: absolute; inset: 0;
	width: 100%; height: 100%; object-fit: cover;
	opacity: 0;
}
/* Hard cut, no fade — "a stacco", as briefed. */
.lm-landing__clip.is-on { opacity: 1; }
.lm-landing.is-open .lm-landing__video { opacity: 1; }
.lm-landing__video { opacity: 0; transition: opacity 0.45s var(--lm-ease); }

.lm-landing__mark {
	position: relative;
	grid-area: 1 / 1;
	display: grid;
	place-items: center;
	z-index: 2;
}

.lm-landing__word {
	font-family: var(--lm-cond);
	font-stretch: 78%;
	font-weight: 800;
	font-size: clamp(34px, 5.2vw, 74px);
	letter-spacing: -0.005em;
	line-height: 1;
	text-transform: uppercase;
	color: var(--lm-ink);
	transition: opacity 0.3s var(--lm-ease);
}

.lm-landing__canvas { position: absolute; opacity: 0; pointer-events: none; }
/* Once the canvas is drawn it takes over and the DOM text steps aside — but stays
   in the accessibility tree and in the HTML source. */
.lm-landing.is-drawn .lm-landing__word { opacity: 0; }
.lm-landing.is-drawn .lm-landing__canvas { opacity: 1; }

/* Once the video is up the mark steps aside entirely and the menu has the screen —
   which is how the second frame of the design is drawn. */
.lm-landing__mark { transition: opacity 0.3s var(--lm-ease); }
.lm-landing.is-open .lm-landing__mark { opacity: 0; }

/* Set larger than the top bar's nav and blended into the montage rather than laid on
   top of it: white through `difference` inverts whatever frame is behind, so the menu
   never washes out on a pale cut and never disappears on a dark one. */
.lm-landing__menu {
	grid-area: 1 / 1;
	z-index: 3;
	display: flex; gap: clamp(24px, 3.4vw, 64px);
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: clamp(18px, 1.7vw, 28px);
	text-transform: uppercase;
	color: #fff;
	mix-blend-mode: difference;
	opacity: 0; visibility: hidden;
	transition: opacity 0.35s var(--lm-ease), visibility 0.35s;
}
.lm-landing.is-open .lm-landing__menu { opacity: 1; visibility: visible; }
.lm-landing__link { transition: opacity 0.2s var(--lm-ease); }
.lm-landing__link:hover { opacity: 0.6; }

/* ----------------------------------------------------------------- film --- */

.lm-film { position: relative; min-height: 100svh; }

.lm-film__bg { position: fixed; inset: 0; z-index: 0; background: var(--lm-ink); }

/**
 * Two clips crossfading, and nothing else on this layer.
 *
 * There used to be a still frame over them and a slight zoom under them, both there to
 * cover the moment a frozen poster gave way to moving footage. The poster is gone — the
 * incoming clip is held invisible until it is genuinely playing (see film.js) — so there
 * is no such moment left to cover, and a plain dissolve between two running films is all
 * this needs. No transform: a zoom nobody asked for on every hover is worse than none.
 */
.lm-film__video {
	position: absolute; inset: 0;
	width: 100%; height: 100%; object-fit: cover;
	opacity: 0; transition: opacity 0.45s var(--lm-ease);
}
.lm-film__video.is-on { opacity: 1; }

.lm-film__deck {
	position: relative; z-index: 10;
	min-height: 100svh;
	display: grid;
	grid-template-columns: var(--lm-g1) calc(var(--lm-g2) - var(--lm-g1)) auto;
	align-content: center;
	padding: 0;
	color: var(--lm-paper);
	/* Helvetica Condensed, as asked — the list now reads as the same voice as the bar
	   above it instead of as body copy. Applied to the whole deck rather than to the
	   list alone: the index and WATCH NOW are slid to the hovered row by measuring its
	   box, so they have to share its face and its leading or they drift off the line.

	   A point off both. The design was drawn against eight projects and the client has
	   more than twenty, so the window has to hold more rows than the artwork ever
	   showed — this is the cheapest way to buy them without touching the composition. */
	font-family: var(--lm-cond);
	font-stretch: 82%;
	font-weight: 700;
	font-size: var(--lm-fs-list);
	line-height: 1.25;
	/* Same treatment as the bar above it: the list is ink on footage, so it blends into
	   the frame instead of sitting on it. z-index already makes this a stacking context,
	   which is exactly the group we want blended — the fixed video behind is its backdrop. */
	mix-blend-mode: difference;
}

/* Index and WATCH NOW ride to the hovered row; the list itself never moves. */
.lm-film__index,
.lm-film__watch {
	grid-row: 1;
	align-self: start;
	transition: transform 0.28s var(--lm-ease), opacity 0.2s var(--lm-ease);
	will-change: transform;
}
/* Their row has been scrolled out of the list's window (film.js). */
.lm-film__index.is-out,
.lm-film__watch.is-out { opacity: 0; pointer-events: none; }
/* Same inset as the wordmark directly above it — the two share column 1, so a
   different gutter here would read as a misalignment rather than as a margin. */
.lm-film__index { grid-column: 1; padding-left: var(--lm-edge); }

/* The design was drawn against eight projects; the client has many more. Rather than
   let the list run off the screen, it scrolls inside a fixed window — the index and
   WATCH NOW track it, so the composition holds at any length. */
/* The window was cut down after the client's sketch: about eleven rows on show, so the list
   reads as a compact block in the middle of the frame rather than as a wall of credits.
   Measured in rows (1.25em is the deck's line-height), never in screen height, so it
   holds the same number of titles on every desktop; the svh cap is only for short
   windows, where eleven rows would not fit.

   The edges fade where there is more to scroll — and only there. film.js marks the list
   .is-top / .is-end, and each fade collapses to nothing at its own end, so the first
   title is never faded at rest and the last is never faded once you reach it. The mask
   sits on the list, inside the deck's blended group, so it fades the text and leaves the
   footage behind it alone. */
.lm-film__list {
	grid-column: 2; grid-row: 1;
	--lm-fade: 2.5em;
	--lm-fade-top: var(--lm-fade);
	--lm-fade-bot: var(--lm-fade);
	max-height: min(calc(1.25em * 11.5), 62svh);
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: none;
	-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lm-fade-top), #000 calc(100% - var(--lm-fade-bot)), transparent 100%);
	        mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lm-fade-top), #000 calc(100% - var(--lm-fade-bot)), transparent 100%);
}
.lm-film__list.is-top { --lm-fade-top: 0px; }
.lm-film__list.is-end { --lm-fade-bot: 0px; }
.lm-film__list::-webkit-scrollbar { width: 0; height: 0; }
.lm-film__watch { grid-column: 3; text-decoration: underline; text-underline-offset: 3px; }

/* No text-transform, on purpose: this list is the one place the note about caps lock
   was actually about, and the right casing is not something CSS can work out. The names
   were typed shouting in the CMS and have been rewritten there instead (Ferrari, AC
   Milan x Puma, AJ Tracey, Pi'erre Bourne) — the capitals that carry meaning survive,
   the rest don't. So this column shows the field exactly as an editor writes it, which
   also means a name typed in caps tomorrow will show in caps. */
.lm-film__pick {
	display: block; width: 100%; text-align: left;
	color: var(--lm-mute);
	transition: color 0.2s var(--lm-ease);
}
.lm-film__item.is-active .lm-film__pick,
.lm-film__pick:hover { color: var(--lm-paper); }

/* --------------------------------------------------------------- studio --- */

.lm-studio {
	position: relative;
	min-height: 100svh;
	display: grid;
	align-content: center;
	padding: 0;
	/* Pinned to exactly one screen: the gallery is positioned against this box, and its
	   height is the distance from the top bar to the bottom of it. */
	height: 100svh;
	overflow: hidden;
}

.lm-studio__table {
	/* Same face and the same point off as Film's list — the two are the one index the
	   client reads across both pages, so they are set the same way. */
	font-family: var(--lm-cond);
	font-stretch: 82%;
	font-weight: 700;
	font-size: calc(var(--lm-fs-body) - 1px);
	line-height: 1.34;
	/* A long list scrolls inside its own window instead of running into the bars — the
	   same faded edges as Film, shown only where there is more. With today's projects it
	   all fits and none of this is visible. */
	--lm-fade: 2.2em;
	--lm-fade-top: var(--lm-fade);
	--lm-fade-bot: var(--lm-fade);
	max-height: min(calc(1.34em * 18.5), calc(100svh - 2 * var(--lm-topbar) - 40px));
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: none;
	-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lm-fade-top), #000 calc(100% - var(--lm-fade-bot)), transparent 100%);
	        mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lm-fade-top), #000 calc(100% - var(--lm-fade-bot)), transparent 100%);
}
.lm-studio__table::-webkit-scrollbar { width: 0; height: 0; }
.lm-studio__table.is-top { --lm-fade-top: 0px; }
.lm-studio__table.is-end { --lm-fade-bot: 0px; }
.lm-studio__link {
	display: grid;
	/* Column starts measured off the mockup: 1.4% / 4.8% / 25.9% / 47%. */
	grid-template-columns: 3.4% 21.1% 21.1% auto;
	padding-left: 1.4%;
	color: var(--lm-mute);
	transition: color 0.2s var(--lm-ease);
}
.lm-studio__row.is-active .lm-studio__link,
.lm-studio__link:has(> span:hover) { color: var(--lm-ink); }
.lm-studio__year { text-align: left; justify-self: start; } /* only the year is hot, not the rest of the line */

/**
 * The gallery.
 *
 * Two states and nothing between them, which is what the client asked for after the
 * scrolling dock: at rest four stills sit small against the right edge; hovering a
 * project swells its still and shrinks the other three by exactly as much.
 *
 * The height is the fixed thing. The column runs from just under the top bar to the
 * bottom of the page in both states, and the four heights always add up to that same
 * box (studio.js divides it), so swelling one still can never push the gallery past an
 * edge or leave a gap at the bottom. Nothing here scrolls, and nothing here moves the
 * page: only the four heights change, and the widths that follow them.
 *
 * Right-aligning a flex column is what hangs it on the edge — each still keeps its own
 * aspect ratio, so the widths differ and only the right edges line up, and the swelling
 * one grows inward across the white page rather than off the screen.
 */
.lm-studio__dock {
	position: absolute;
	top: var(--lm-topbar);
	bottom: 0;
	right: 0;
	left: auto;
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	justify-content: flex-start;
	gap: 0;
}
/* Past LMT_STUDIO_FIT projects the column is a window onto the list (studio.js): it
   scrolls, with no bar, and comes to rest on a still's edge. The edges fade only where
   there is more to see, like the Film list. overflow-anchor is off because the stills
   resize on hover — the browser "helpfully" shifting the scroll to compensate would move
   the window under the pointer. */
.lm-studio__dock.is-scroll {
	overflow-x: hidden;
	overflow-y: auto;
	overscroll-behavior: contain;
	overflow-anchor: none;
	scrollbar-width: none;
	--lm-fade: 44px;
	--lm-fade-top: var(--lm-fade);
	--lm-fade-bot: var(--lm-fade);
	-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lm-fade-top), #000 calc(100% - var(--lm-fade-bot)), transparent 100%);
	        mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lm-fade-top), #000 calc(100% - var(--lm-fade-bot)), transparent 100%);
}
.lm-studio__dock.is-scroll::-webkit-scrollbar { width: 0; height: 0; }
.lm-studio__dock.is-top { --lm-fade-top: 0px; }
.lm-studio__dock.is-end { --lm-fade-bot: 0px; }

.lm-studio__shot {
	margin: 0;
	flex: none;
	overflow: hidden;
	cursor: pointer;
	/* Positioned so the film can be laid over the photograph inside the same box: the
	   clip then inherits the still's growth instead of being positioned against it frame
	   by frame, which never stays in step. */
	position: relative;
	transition: width 0.42s var(--lm-ease), height 0.42s var(--lm-ease);
}

/* The stacked mobile view lives on Film; desktop-hidden, see the media query below. */
.lm-film__stack { display: none; }
.lm-studio__shot img,
.lm-studio__clip { width: 100%; height: 100%; object-fit: cover; }

/**
 * The film sits exactly on top of its own poster frame, and is only shown once it is
 * genuinely running (studio.js). The photograph underneath never goes away, so there is
 * no black rectangle to cover while the clip buffers and nothing to fall back to if the
 * connection gives up — the strip just stays a strip of photographs.
 */
.lm-studio__clip {
	position: absolute;
	inset: 0;
	opacity: 0;
	transition: opacity 0.3s linear;
	pointer-events: none; /* clicks belong to the figure, which opens the player */
}
.lm-studio__clip.is-on { opacity: 1; }

/**
 * Studio on phones: the accordion (studio-mobile.js), chosen by the client out of three.
 * One big row per project fills the screen between the bars; the open one shows its film, the
 * others close up. The table stays in the markup (the script reads it) but steps aside.
 */
.lm-sv { display: none; }
@media (max-width: 900px) {
	.lm-studio.has-sv { display: block; height: auto; min-height: 100svh; overflow: visible; padding: 0; }
	.lm-studio.has-sv .lm-studio__table { display: none; }
	.lm-studio.has-sv .lm-sv { display: block; }

	.lm-sv {
		max-width: 100vw; overflow-x: hidden;
		--sv-top: clamp(62px, 17vw, 84px);
		--sv-bot: calc(var(--lm-bar) + 20px);
		color: var(--lm-ink);
	}
	:where(.lm-sv) button { display: block; width: 100%; text-align: left; color: inherit; font: inherit; }
	.lm-sv-media { position: relative; display: block; width: 100%; height: 100%; background: var(--lm-ink); overflow: hidden; }
	.lm-sv-media img, .lm-sv-clip { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
	.lm-sv-clip { opacity: 0; transition: opacity 0.3s linear; }
	.lm-sv-clip.is-on { opacity: 1; }
	.lm-sv__n { color: var(--lm-mute); }
	.lm-sv__meta { font-size: var(--lm-fs-body); color: var(--lm-mute); padding-bottom: 6px; }
	.lm-sv__meta:empty { display: none; }
	/* WATCH sits on the film, bottom-right, clear of the caption. */
	.lm-sv__watch {
		position: absolute; right: 12px; bottom: 10px; z-index: 2;
		width: auto !important;
		font-size: var(--lm-fs-body);
		text-decoration: underline; text-underline-offset: 4px;
		color: #fff; mix-blend-mode: difference;
	}

	.lm-sv3 { display: flex; flex-direction: column; height: 100svh; padding: var(--sv-top) var(--lm-gut) var(--sv-bot); }
	.lm-sv3__row { flex: 1 1 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; transition: flex-grow 0.42s var(--lm-ease); }
	.lm-sv3__row.is-open { flex-grow: 5.5; }
	.lm-sv3__head {
		display: grid; grid-template-columns: 30px minmax(0, 1fr) auto; gap: 10px; align-items: baseline;
		padding: 10px 0;
		/* Smaller than the first cut (20–28px, 6vw): the client found the names too
		   loud on a phone — 2026-10-08. */
		font-size: clamp(16px, 4.4vw, 20px); line-height: 1;
		color: var(--lm-mute); transition: color 0.2s var(--lm-ease);
	}
	.lm-sv3__row.is-open .lm-sv3__head { color: var(--lm-ink); }
	.lm-sv3__head .lm-sv__n { font-size: var(--lm-fs-body); color: inherit; }
	.lm-sv3__name { overflow-wrap: anywhere; } /* long names wrap instead of widening the page */
	.lm-sv3__year { font-size: 1em; } /* same size as the name: two columns, one size (2026-10-08) */
	.lm-sv3__body { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 8px; opacity: 0; transition: opacity 0.3s var(--lm-ease); }
	.lm-sv3__row.is-open .lm-sv3__body { opacity: 1; }
	.lm-sv3__screen { position: relative; display: block; flex: 1; min-height: 0; cursor: pointer; }

	/* Six projects and up: smaller names, and every row keeps at least its own line —
	   past what a short phone can hold the list grows a little and the page scrolls,
	   rather than names being clipped. The open one keeps a usable film. */
	/* The height stays a definite one, as in the normal accordion: Safari on iPhone
	   gives the open row's film no height at all when the list is sized by its content
	   (height: auto), which showed every project as an empty white box. When the rows
	   can't all fit their line plus the open film, the list is made exactly that much
	   taller instead — --sv-count comes from studio-mobile.js. */
	.lm-sv3--dense {
		--sv-head: clamp(14px, 3.8vw, 18px);
		--sv-row: calc(var(--sv-head) + 14px);
		height: max(100svh, calc(var(--sv-top) + var(--sv-bot) + (var(--sv-count, 9) - 1) * var(--sv-row) + 36svh));
	}
	.lm-sv3--dense .lm-sv3__head { font-size: var(--sv-head); padding: 7px 0; }
	.lm-sv3--dense .lm-sv3__row { min-height: var(--sv-row); }
	/* Belt and braces for the same Safari behaviour: the open film never collapses. */
	.lm-sv3--dense .lm-sv3__row.is-open .lm-sv3__screen { min-height: 22svh; }
}

/* ----------------------------------------------------------------- shop --- */

.lm-shop { min-height: 100svh; display: grid; align-content: center; padding: clamp(80px, 10vh, 130px) 2.35% clamp(60px, 8vh, 100px); }

/**
 * Three columns, centred rather than left-packed.
 *
 * Grid can't do this: its tracks exist whether or not anything sits in them, so a
 * single product stays pinned to the left third and a pair leaves a hole on the right.
 * Flex lays out only the cards that exist and `justify-content: center` puts them in
 * the middle of the page — one product centres, two straddle the middle, three fill
 * the row exactly as the Figma has it, and a short last row stays centred under the
 * full ones.
 */
.lm-shop__grid {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	row-gap: clamp(24px, 4vw, 80px);
	column-gap: 0;
}
.lm-shop__card { flex: 0 0 33.3333%; max-width: 33.3333%; min-width: 0; }

.lm-shop__shot { width: 76%; margin: 0 auto; aspect-ratio: 10 / 7; display: grid; place-items: center; }
.lm-shop__shot img { width: 100%; height: 100%; object-fit: contain; }
/* Front, back, details: all the product's photos share one cell and take turns (shop.js).
   A slow dissolve rather than a cut — this is a garment turning, not a montage. */
/* The cell is held to the card's 10:7 whatever the photos are — a portrait campaign
   shot in the gallery would otherwise stretch the box and shove the controls down. */
.lm-shop__shot.is-loop { grid-template: minmax(0, 1fr) / minmax(0, 1fr); min-height: 0; overflow: hidden; }
.lm-shop__shot.is-loop img { grid-area: 1 / 1; min-height: 0; opacity: 0; transition: opacity 0.9s var(--lm-ease); }
.lm-shop__shot.is-loop img.is-on { opacity: 1; }

.lm-shop__name {
	margin-top: 14px;
	text-align: center;
	font-size: var(--lm-fs-body);
	font-weight: 400;
	color: var(--lm-mute);
}
.lm-shop__sold {
	display: block; width: max-content; margin: 8px auto 0;
	padding: 2px 10px;
	background: var(--lm-ink); color: var(--lm-paper);
	font-size: 0.8em;
}
.lm-shop__card.is-sold .lm-shop__shot { opacity: 0.45; }
.lm-shop__price {
	margin-top: 2px;
	text-align: center;
	font-size: var(--lm-fs-body);
	color: var(--lm-mute);
}

/* Coming soon (inc/shop.php): seen, not sold. The garment arrives already pixelated
   (lmt_soon_pixel_url: a few dozen pixels across, background left transparent), so
   only the tee breaks into blocks, never the square around it — the client's call on
   2026-10-08 over the old blur. Scaled up here without smoothing, which is what makes
   the blocks big and sharp. Nothing on the card answers the pointer. */
.lm-shop__card.is-soon { cursor: default; user-select: none; }
.lm-shop__card.is-soon .lm-shop__shot img { image-rendering: pixelated; pointer-events: none; }

/* Buy controls — quiet by design. The garment is the loud element on this page;
   size, quantity and the bag sit under it as plain type on a single line. */
.lm-buy {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: 6px 14px;
	margin-top: 10px;
	font-size: 0.9em;
	color: var(--lm-mute);
}
.lm-buy__sizes { display: flex; gap: 8px; }
.lm-buy__size { text-transform: uppercase; transition: color 0.15s var(--lm-ease); }
.lm-buy__size.is-on { color: var(--lm-ink); text-decoration: underline; text-underline-offset: 3px; }
.lm-buy__size:hover { color: var(--lm-ink); }

.lm-buy__qty { display: flex; align-items: center; gap: 9px; }
.lm-buy__qty button { line-height: 1; }
.lm-buy__qty button:hover { color: var(--lm-ink); }
.lm-buy__qty [data-lm-qty] { min-width: 1ch; text-align: center; color: var(--lm-ink); }

/* A line of its own under size and quantity: flex-basis 100% breaks the row, so the
   card reads name / controls / action down the page. Plain underlined type, no box. */
.lm-buy__add {
	flex-basis: 100%;
	display: inline-flex; justify-content: center;
	margin-top: 4px;
	color: var(--lm-ink);
	text-transform: uppercase;
	text-decoration: underline; text-underline-offset: 3px;
	transition: opacity 0.15s var(--lm-ease);
}
.lm-buy__add:hover { opacity: 0.55; }
.lm-buy__add[disabled] { opacity: 0.35; cursor: default; }

.lm-buy__msg:empty { display: none; }
.lm-buy__msg { flex-basis: 100%; text-align: center; }

/* ------------------------------------------------------------- checkout --- */

/* The legal line: ragione sociale, P.IVA, the three policies. Shop, checkout, legal. */
.lm-legal {
	display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 18px;
	padding: 14px var(--lm-gut) 18px;
	font-size: calc(var(--lm-fs-body) - 2px); color: var(--lm-mute);
	text-transform: uppercase;
}
.lm-legal a:hover { color: var(--lm-ink); }
/* The shop stays one screen: the line takes its height out of the grid's, not below it. */
.lm-page--shop .lm-shop { min-height: calc(100svh - 52px); }

/* Plain pages: the legal texts and the checkout. One column hung on the first guide. */
.lm-doc {
	padding: calc(var(--lm-topbar) + clamp(40px, 8vh, 90px)) var(--lm-gut) clamp(60px, 10vh, 120px);
	padding-left: var(--lm-g1);
	max-width: calc(var(--lm-g1) + 760px);
}
.lm-doc__title {
	margin-bottom: clamp(28px, 5vh, 56px);
	font-size: clamp(28px, 3vw, 48px); line-height: 0.95; text-transform: uppercase;
}
.lm-doc__body { font-weight: 400; line-height: 1.45; }
.lm-doc__body h2, .lm-doc__body h3 { margin: 2em 0 0.6em; font-size: 1.1em; text-transform: uppercase; }
.lm-doc__body p, .lm-doc__body ul, .lm-doc__body ol { margin: 0 0 1em; }
.lm-doc__body ul { list-style: disc; padding-left: 1.2em; }
.lm-doc__body a { text-decoration: underline; text-underline-offset: 3px; }

/* Checkout: the form on the left from the first guide, the order on the right from
   the second, both columns ruled by the same thin lines as the rest of the site. */
.lm-doc--checkout { max-width: none; padding-left: var(--lm-gut); }
.lm-doc--checkout .lm-doc__title { padding-left: calc(var(--lm-g1) - var(--lm-gut)); }
.lm-doc--checkout .lm-doc__body { font-weight: 700; }

.lm-doc--checkout form.checkout {
	display: grid;
	grid-template-columns: calc(var(--lm-g1) - var(--lm-gut)) minmax(0, 1fr) minmax(0, 0.8fr);
	column-gap: clamp(24px, 4vw, 72px);
	align-items: start;
}
.lm-doc--checkout #customer_details { grid-column: 2; }
.lm-doc--checkout #order_review_heading { grid-column: 3; grid-row: 1; margin: 0 0 18px; }
.lm-doc--checkout #order_review { grid-column: 3; grid-row: 1; margin-top: 2.4em; position: sticky; top: calc(var(--lm-topbar) + 20px); }
.lm-doc--checkout .woocommerce-NoticeGroup { grid-column: 2 / -1; }
.lm-doc--checkout .col2-set .col-1, .lm-doc--checkout .col2-set .col-2 { width: auto; float: none; }
.lm-doc--checkout .col-2 { margin-top: 28px; }
.lm-doc--checkout h3, .lm-doc--checkout #order_review_heading { font-size: 1em; text-transform: uppercase; color: var(--lm-mute); }
.lm-doc--checkout h3 { margin: 0 0 18px; }

.lm-doc--checkout .woocommerce-billing-fields__field-wrapper,
.lm-doc--checkout .woocommerce-shipping-fields__field-wrapper { display: grid; grid-template-columns: 1fr 1fr; column-gap: 18px; }
.lm-doc--checkout .form-row { margin: 0 0 16px; padding: 0; grid-column: 1 / -1; }
.lm-doc--checkout .form-row-first { grid-column: 1; }
.lm-doc--checkout .form-row-last { grid-column: 2; }
.lm-doc--checkout .form-row label { display: block; margin-bottom: 4px; font-size: 0.85em; color: var(--lm-mute); text-transform: uppercase; }
.lm-doc--checkout .form-row .required { color: inherit; text-decoration: none; border: 0; }
.lm-doc--checkout .form-row .optional { display: none; }
.lm-doc--checkout .input-text,
.lm-doc--checkout select {
	width: 100%;
	padding: 8px 0;
	font: inherit; font-size: 16px; /* 16px: iOS zooms into anything smaller */
	color: var(--lm-ink); background: transparent;
	border: 0; border-bottom: 1px solid var(--lm-ink); border-radius: 0;
	-webkit-appearance: none; appearance: none;
}
.lm-doc--checkout select {
	padding-right: 18px;
	background: linear-gradient(45deg, transparent 50%, currentColor 50%) right 6px center / 6px 6px no-repeat;
}
.lm-doc--checkout .input-text:focus, .lm-doc--checkout select:focus { outline: none; border-bottom-width: 2px; }
.lm-doc--checkout .woocommerce-invalid .input-text,
.lm-doc--checkout .woocommerce-invalid select { border-bottom-color: var(--lm-red); }
.lm-doc--checkout .description { display: block; margin-top: 4px; font-size: 0.8em; font-weight: 400; color: var(--lm-mute); }
.lm-doc--checkout .woocommerce-form__label-for-checkbox,
.lm-doc--checkout .lm-invoice-toggle label { display: flex; gap: 10px; align-items: center; color: var(--lm-ink); font-size: 1em; cursor: pointer; }
.lm-doc--checkout input[type="checkbox"], .lm-doc--checkout input[type="radio"] { accent-color: var(--lm-ink); width: 16px; height: 16px; margin: 0; }
.lm-doc--checkout #ship-to-different-address { font-size: 1em; }

/* The order: one ruled table, totals at the bottom in full ink. */
.lm-doc--checkout .shop_table { width: 100%; border-collapse: collapse; }
.lm-doc--checkout .shop_table th, .lm-doc--checkout .shop_table td { padding: 10px 0; text-align: left; vertical-align: top; border-bottom: 1px solid var(--lm-mute); font-weight: 700; }
.lm-doc--checkout .shop_table td:last-child, .lm-doc--checkout .shop_table th:last-child { text-align: right; }
.lm-doc--checkout .shop_table thead { color: var(--lm-mute); text-transform: uppercase; font-size: 0.85em; }
.lm-doc--checkout .shop_table .product-quantity { color: var(--lm-mute); font-weight: 400; }
.lm-doc--checkout .shop_table .order-total th, .lm-doc--checkout .shop_table .order-total td { border-bottom: 1px solid var(--lm-ink); font-size: 1.15em; }
.lm-doc--checkout .shop_table small, .lm-doc--checkout .includes_tax { color: var(--lm-mute); font-weight: 400; font-size: 0.8em; }
.lm-doc--checkout #shipping_method { display: grid; gap: 6px; justify-items: end; }
.lm-doc--checkout #shipping_method li { display: flex; gap: 8px; align-items: center; }

.lm-doc--checkout #payment { margin-top: 26px; }
.lm-doc--checkout .wc_payment_methods { display: grid; gap: 12px; margin-bottom: 20px; }
.lm-doc--checkout .wc_payment_method > label { display: inline-flex; gap: 10px; align-items: center; cursor: pointer; text-transform: uppercase; }
.lm-doc--checkout .wc_payment_method label img { max-height: 22px; width: auto; display: inline-block; }
.lm-doc--checkout .payment_box { margin-top: 10px; font-weight: 400; font-size: 0.9em; color: var(--lm-mute); }
.lm-doc--checkout .woocommerce-info { color: var(--lm-mute); font-weight: 400; }
.lm-doc--checkout .woocommerce-privacy-policy-text { margin: 0 0 12px; font-size: 0.85em; font-weight: 400; color: var(--lm-mute); }
.lm-doc--checkout .woocommerce-privacy-policy-text a, .lm-doc--checkout .woocommerce-terms-and-conditions-link { text-decoration: underline; text-underline-offset: 3px; color: inherit; }
.lm-doc--checkout .woocommerce-terms-and-conditions { display: none !important; } /* no inline terms; the link opens the page */
.lm-doc--checkout #place_order {
	width: 100%; margin-top: 18px; padding: 16px;
	background: var(--lm-ink); color: var(--lm-paper);
	font-size: clamp(16px, 1.3vw, 20px); text-transform: uppercase; letter-spacing: 0.02em;
	transition: opacity 0.2s var(--lm-ease);
}
.lm-doc--checkout #place_order:hover { opacity: 0.8; }
.lm-doc--checkout .blockUI.blockOverlay { background: var(--lm-paper) !important; opacity: 0.6 !important; }

/* Coupon line above the form, quiet. */
.lm-doc--checkout .woocommerce-form-coupon-toggle { margin: 0 0 20px calc(var(--lm-g1) - var(--lm-gut)); font-weight: 400; color: var(--lm-mute); }
.lm-doc--checkout .woocommerce-form-coupon-toggle a { color: var(--lm-ink); text-decoration: underline; text-underline-offset: 3px; }
.lm-doc--checkout form.checkout_coupon { display: flex; gap: 12px; align-items: end; max-width: 420px; margin: 0 0 28px calc(var(--lm-g1) - var(--lm-gut)); }
.lm-doc--checkout form.checkout_coupon p:not(.form-row) { display: none; }
.lm-doc--checkout form.checkout_coupon .form-row { margin: 0; }
.lm-doc--checkout form.checkout_coupon button { text-decoration: underline; text-underline-offset: 3px; text-transform: uppercase; padding: 8px 0; }

/* Errors and notices: plain lines, the error in the About red. */
.lm-doc--checkout .woocommerce-error,
.lm-doc--checkout .woocommerce-message { margin: 0 0 24px; padding: 0; display: grid; gap: 4px; font-weight: 400; }
.lm-doc--checkout .woocommerce-error { color: var(--lm-red); }
.lm-doc--checkout .woocommerce-error a { text-decoration: underline; }

/* Order received. */
.lm-doc--checkout .woocommerce-order { max-width: 760px; margin-left: calc(var(--lm-g1) - var(--lm-gut)); display: grid; gap: 28px; }
.lm-doc--checkout .woocommerce-thankyou-order-received { font-size: 1.15em; }
.lm-doc--checkout .order_details:not(table) { display: flex; flex-wrap: wrap; gap: 14px 36px; }
.lm-doc--checkout .order_details:not(table) li { display: grid; gap: 2px; color: var(--lm-mute); text-transform: uppercase; font-size: 0.85em; }
.lm-doc--checkout .order_details:not(table) li strong { color: var(--lm-ink); font-size: 1.15em; }
.lm-doc--checkout .woocommerce-order h2 { font-size: 1em; text-transform: uppercase; color: var(--lm-mute); margin-bottom: 10px; }
.lm-doc--checkout address { font-style: normal; font-weight: 400; line-height: 1.45; }
.lm-doc--checkout .woocommerce-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; }

@media (max-width: 900px) {
	.lm-doc { padding: calc(var(--lm-edge) * 2 + 50px) var(--lm-gut) 40px; max-width: none; }
	.lm-doc--checkout .lm-doc__title,
	.lm-doc--checkout .woocommerce-form-coupon-toggle,
	.lm-doc--checkout form.checkout_coupon,
	.lm-doc--checkout .woocommerce-order { padding-left: 0; margin-left: 0; }
	.lm-doc--checkout form.checkout { display: block; }
	.lm-doc--checkout #order_review_heading { margin-top: 36px; }
	.lm-doc--checkout #order_review { position: static; margin-top: 0; }
	.lm-doc--checkout .woocommerce-columns { grid-template-columns: 1fr; }
	.lm-page--shop .lm-shop { min-height: calc(100svh - var(--lm-bar) - 70px); }
	.lm-legal { padding-bottom: 8px; }
	/* Company details on one centred line (client, 2026-10-08): the type follows the
	   screen width just enough for the whole line to fit — at 11px it needs about 39
	   characters' worth of em, more than a phone has — and the policies go underneath. */
	.lm-legal > span {
		flex-basis: 100%; text-align: center; white-space: nowrap;
		font-size: min(1em, calc((100vw - 2 * var(--lm-gut)) / 39));
	}
}

/* -------------------------------------------------------------- contact --- */

.lm-contact {
	display: grid;
	grid-template-columns: var(--lm-g2) auto;
	min-height: 100svh;
	/* --lm-bar is the *mobile* bottom bar; reserving it here held the left column some
	   80px clear of a bottom edge that has nothing on it, which is why the credits sat
	   short of where the mockup puts them. On desktop the column runs to the gutter, the
	   same margin the top bar keeps above it. */
	padding: clamp(90px, 12vh, 150px) 0 var(--lm-gut);
}

/* Three groups spread down the column — address+socials, selected clients, credits —
   set far apart, and bottom-aligned so the last credit line lands on the gutter. */
.lm-contact__col { display: grid; gap: clamp(30px, 5.6vh, 56px); align-content: end; padding-left: var(--lm-gut); font-size: var(--lm-fs-body); }
.lm-contact__group { display: grid; gap: 14px; }
/* One size, one colour, top to bottom. The labels used to be set grey to head their
   groups; the client wants no headings standing out of the column at all, so they are
   now just the first line of their block. Same for the credits. */
.lm-contact__label { display: block; color: inherit; margin-bottom: 14px; }
/* The mockup sets these lists almost solid — the body's 1.35 leading made the column
   some 40px taller than the artwork, which no amount of bottom-aligning could absorb. */
.lm-contact__lines,
.lm-contact__block--social,
.lm-contact__clients,
.lm-contact__credits { line-height: 1.12; }
.lm-contact__lines p { margin: 0; }
/* Socials as a row of marks, sized to the body type so they sit in the column as one
   more line of it rather than as a badge strip. A link with no mark (a network the
   theme has no icon for) stays a word, and underlines like before. */
.lm-contact__social-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; list-style: none; margin: 0; padding: 0; }
.lm-contact__social { display: inline-flex; align-items: center; width: max-content; }
.lm-contact__social:not(.has-mark):hover { text-decoration: underline; text-underline-offset: 3px; }
.lm-contact__social.has-mark { height: 1.35em; transition: opacity 0.2s var(--lm-ease), transform 0.2s var(--lm-ease); }
.lm-contact__social svg { height: 1.15em; width: auto; display: block; }
.lm-contact__social-row:hover .lm-contact__social.has-mark { opacity: 0.35; }
.lm-contact__social-row .lm-contact__social.has-mark:hover { opacity: 1; transform: translateY(-2px); }

/**
 * Mail button, bottom-right, Contact only — an envelope, for anyone who'd rather write
 * from their own mail app. Drawn in the same line weight as the bag in the top bar.
 * Pointed at, the flap folds open and the address slides out to its left; the icon
 * itself never moves, so the corner stays still.
 */
.lm-mailto {
	position: fixed; z-index: 40;
	right: var(--lm-edge); bottom: var(--lm-gut);
	display: inline-flex; align-items: center; gap: 12px;
	padding: 6px 0 6px 6px; /* a thumb-sized target around a small drawing */
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: clamp(14px, 1.05vw, 18px); line-height: 1;
	text-transform: uppercase;
	color: var(--lm-ink);
}
.lm-mailto__icon { width: clamp(26px, 2vw, 32px); height: auto; overflow: visible; display: block; }
.lm-mailto__flap {
	transform-box: view-box; transform-origin: 50% 0.7px;
	transition: transform 0.45s cubic-bezier(0.7, 0, 0.2, 1);
}
.lm-mailto:hover .lm-mailto__flap,
.lm-mailto:focus-visible .lm-mailto__flap { transform: scaleY(-0.85); }
.lm-mailto__addr {
	white-space: nowrap;
	opacity: 0; transform: translateX(10px);
	clip-path: inset(0 0 0 100%);
	transition: opacity 0.3s var(--lm-ease), transform 0.45s cubic-bezier(0.7, 0, 0.2, 1), clip-path 0.45s cubic-bezier(0.7, 0, 0.2, 1);
}
.lm-mailto:hover .lm-mailto__addr,
.lm-mailto:focus-visible .lm-mailto__addr { opacity: 1; transform: none; clip-path: inset(0); }
.lm-contact__clients { display: grid; gap: 0; }
/* General enquiries and commercial representation: one block in the column, its two
   headed lists a line of air apart — the same rhythm as address and socials. */
.lm-contact__reps { display: grid; gap: 14px; }
.lm-contact__rep .lm-contact__label { margin-bottom: 0; }
.lm-contact__clients a { overflow-wrap: anywhere; }
.lm-contact__clients a:hover { text-decoration: underline; text-underline-offset: 3px; }
/* The credits are the one block that is a paragraph rather than a list, so they carry
   the most lines and set the height of the whole column: a point off body and off the
   leading is what lands the last line back on the gutter. */
.lm-contact__credits { color: inherit; font-size: calc(var(--lm-fs-body) - 1px); line-height: 1.05; }

.lm-contact__form { display: grid; gap: 26px; align-content: center; }
.lm-contact__form, .lm-contact__col { min-width: 0; }
.lm-contact__field input,
.lm-contact__field textarea {
	width: 100%;
	font: inherit;
	color: inherit;
	background: none;
	border: 0;
	padding: 0;
	resize: none;
	overflow: hidden;
}
.lm-contact__field input::placeholder,
.lm-contact__field textarea::placeholder { color: var(--lm-ink); opacity: 1; }

/**
 * No box around the fields.
 *
 * These are borderless, full-width type on a bare page, so the global :focus-visible
 * ring — 2px of currentColor, offset 3px — drew a black rectangle around the whole
 * field the instant it was clicked. Browsers always match :focus-visible on text
 * inputs, pointer or keyboard, so this fired every time and was the black box the
 * client saw while filling the form in. The caret is the affordance here instead:
 * it's the standard one for a text field and it doesn't draw anything the design
 * hasn't got. Every other control on the site keeps its ring.
 */
.lm-contact__field input:focus,
.lm-contact__field textarea:focus,
.lm-contact__field input:focus-visible,
.lm-contact__field textarea:focus-visible { outline: none; }

/* Chrome paints its own slab of colour behind an autofilled field and won't take a
   plain background declaration. A background-color transition it will never reach is
   the only way to hold it off, and -webkit-text-fill-color keeps the typed value the
   page's own ink rather than the browser's. */
.lm-contact__field input:-webkit-autofill,
.lm-contact__field input:-webkit-autofill:hover,
.lm-contact__field input:-webkit-autofill:focus,
.lm-contact__field textarea:-webkit-autofill {
	-webkit-text-fill-color: var(--lm-ink);
	caret-color: var(--lm-ink);
	transition: background-color 100000s ease-in-out 0s;
}
.lm-contact__field input {
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: clamp(24px, 2.6vw, 40px);
	line-height: 1.1;
}
.lm-contact__field textarea { font-size: clamp(15px, 1.2vw, 20px); line-height: 1.4; }
.lm-contact__send {
	justify-self: start;
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: clamp(15px, 1.2vw, 20px);
	text-transform: uppercase;
	text-decoration: underline; text-underline-offset: 4px;
}
.lm-contact__send[disabled] { opacity: 0.5; cursor: default; }
.lm-contact__status:empty { display: none; }
.lm-contact__status { color: var(--lm-mute); }

/* ------------------------------------------------------------- overlays --- */

.lm-scrim {
	position: fixed; inset: 0; z-index: 45;
	background: rgba(0, 0, 0, 0.4);
	opacity: 0; transition: opacity 0.3s var(--lm-ease);
}
.lm-scrim.is-open { opacity: 1; }

/**
 * About — the "Provino" variant the client chose, on white, after their mockup of About
 * Studio: rolling stills, the bio on the 26% guide, the services ruled off (Studio only),
 * the showreel alone, and LATEMILK cut by the bottom of the page.
 *
 * The panel is its own scroller (the page underneath is locked). The real bars are lifted
 * over it while it is open — html.lm-about-open, set by chrome.js — so the nav and the
 * cart keep working and "+ ABOUT" turns into "− ABOUT" in place. The ::before is the
 * white band those bars sit on, so nothing scrolls through the type.
 */
.lm-about {
	position: fixed; inset: 0; z-index: 60;
	overflow-y: auto; overscroll-behavior: contain;
	background: var(--lm-paper);
	color: var(--lm-ink);
	opacity: 0;
	transition: opacity 0.32s var(--lm-ease);
	--lm-about-head: var(--lm-topbar);
}
.lm-about.is-open { opacity: 1; }
.lm-about::before {
	content: ''; display: block;
	position: sticky; top: 0; z-index: 3;
	height: var(--lm-about-head);
	background: var(--lm-paper);
}
html.lm-about-open .lm-top,
html.lm-about-open .lm-mtop,
html.lm-about-open .lm-mbot { z-index: 65; }

.lm-about__reel { overflow: hidden; }
.lm-about__track { display: flex; width: max-content; animation: lm-about-roll var(--lm-roll, 70s) linear infinite; }
.lm-about__reel:hover .lm-about__track { animation-play-state: paused; }
.lm-about__track li { flex: none; width: clamp(180px, 15.5vw, 300px); margin-right: 4px; aspect-ratio: 3 / 2; background: #111; }
.lm-about__track img { width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) contrast(1.05); }
@keyframes lm-about-roll { to { transform: translateX(-50%); } }

/* Label on the left edge, content hung on the 26% guide — the bar's own grid. */
.lm-about__row {
	display: grid;
	grid-template-columns: var(--lm-g1) minmax(0, 1fr);
	padding: clamp(36px, 7vh, 80px) var(--lm-edge) 0 0;
}
.lm-about__label {
	padding-left: var(--lm-edge);
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: var(--lm-fs-nav); line-height: 1.2; letter-spacing: 0.01em;
	text-transform: uppercase;
	color: var(--lm-mute);
}
.lm-about__body {
	max-width: 46ch;
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: clamp(18px, 1.85vw, 32px);
	line-height: 1.08;
}
.lm-about__body p + p { margin-top: 0.7em; }

.lm-about__services {
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: clamp(30px, 3.4vw, 60px);
	line-height: 1;
	text-transform: uppercase;
	border-bottom: 1px solid var(--lm-mute);
}
.lm-about__services li { padding: 0.12em 0 0.06em; border-top: 1px solid var(--lm-mute); }

/* The showreel on the same guide as the words above it, and nothing else around it. */
.lm-about__showreel {
	position: relative;
	aspect-ratio: 16 / 9;
	background: #000;
	overflow: hidden;
}
.lm-about__reelrow { padding-top: clamp(48px, 9vh, 110px); }
.lm-about__showreel.is-playable { cursor: pointer; }
.lm-about__showreel img,
.lm-about__showreel video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.lm-about__showreel video { opacity: 0; transition: opacity 0.3s linear; }
.lm-about__showreel.is-running video { opacity: 1; }

.lm-about__mark {
	margin: clamp(56px, 12vh, 150px) 0 -0.25em;
	font-family: var(--lm-cond); font-stretch: 75%; font-weight: 700;
	font-size: 15vw; line-height: 0.8; letter-spacing: -0.02em; text-align: center;
	white-space: nowrap; overflow: hidden;
}
.lm-about--studio .lm-about__mark { font-size: 13.2vw; }
.lm-about__mark span { display: inline-block; }

@media (prefers-reduced-motion: reduce) {
	.lm-about__track { animation: none; }
}

/**
 * + into −. On the About buttons the upright turns down onto the crossbar as About
 * opens, and the whole mark makes a half turn with it, so it reads as one gesture; on
 * close it all runs backwards. The upright is a separate path (lmt_plus_icon).
 */
[data-lm-about-open] .lm-mark { transition: transform 0.5s cubic-bezier(0.65, 0, 0.25, 1); }
.lm-mark__v {
	transform-box: fill-box; transform-origin: center;
	transition: transform 0.5s cubic-bezier(0.65, 0, 0.25, 1);
}
html.lm-about-open [data-lm-about-open] .lm-mark { transform: rotate(180deg); }
html.lm-about-open [data-lm-about-open] .lm-mark__v { transform: rotate(90deg); }

/* Cart — a quarter of the screen from the right, black. */
.lm-cart {
	position: fixed; inset: 0 0 0 auto; z-index: 60;
	width: min(420px, 30vw);
	display: flex; flex-direction: column;
	padding: var(--lm-gut);
	background: var(--lm-ink); color: var(--lm-paper);
	transform: translateX(100%);
	transition: transform 0.36s var(--lm-ease);
	overflow-y: auto;
}
.lm-cart.is-open { transform: translateX(0); }

.lm-cart__head { display: flex; align-items: flex-start; justify-content: space-between; }
.lm-cart__title {
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: clamp(18px, 1.5vw, 24px); text-transform: uppercase;
	color: var(--lm-mute);
}
.lm-cart__close { font-size: 26px; line-height: 0.7; }

.lm-cart__items { flex: 1; display: grid; gap: 26px; align-content: start; margin-top: clamp(24px, 5vh, 60px); }
.lm-cart__row { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 16px; align-items: start; }
.lm-cart__row.is-busy { opacity: 0.5; }
.lm-cart__thumb { background: var(--lm-paper); aspect-ratio: 1 / 1; }
.lm-cart__thumb img { width: 100%; height: 100%; object-fit: contain; }
.lm-cart__info { display: grid; gap: 3px; }
.lm-cart__name { font-size: 1.05em; }
.lm-cart__variant { color: var(--lm-mute); font-size: 0.8em; text-transform: uppercase; }
.lm-cart__qty { display: flex; align-items: center; gap: 18px; margin-top: 10px; color: var(--lm-mute); }
/* Pushed to the far end of the row so it can't be hit while aiming for − */
.lm-cart__remove { margin-left: auto; display: inline-flex; padding: 4px; }
.lm-cart__remove:hover { color: var(--lm-paper); }
.lm-cart__empty { color: var(--lm-mute); }

.lm-cart__checkout {
	margin-top: 30px;
	font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
	font-size: clamp(15px, 1.2vw, 20px); text-transform: uppercase;
	text-align: center;
	text-decoration: underline; text-underline-offset: 4px;
}

/* Vimeo player */
.lm-player {
	position: fixed; inset: 0; z-index: 70;
	display: grid; place-items: center;
	background: rgba(0, 0, 0, 0.94);
}
/* Always white: the overlay is black whichever way the site's switch is thrown, so the
   X must not follow --lm-paper into black. Sits above the frame and clear of the notch. */
.lm-player__close {
	position: absolute; z-index: 2;
	top: calc(var(--lm-gut) + env(safe-area-inset-top));
	right: calc(var(--lm-gut) + env(safe-area-inset-right));
	font-size: 34px; line-height: 1; color: #fff;
	padding: 4px 10px;
}
/* Edge to edge: as wide as the screen allows, as tall as the screen allows, whichever
   runs out first. It used to stop at 94%, which on a phone held in portrait left a
   small letterboxed band in the middle of the black — the video now takes the full
   width there, and fills the screen the moment you turn the phone. */
.lm-player__frame {
	width: min(100vw, calc(100svh * 16 / 9));
	max-height: 100svh;
	aspect-ratio: 16 / 9;
}
.lm-player__frame iframe { width: 100%; height: 100%; border: 0; }

/* Empty states */
.lm-empty {
	min-height: 100svh;
	display: grid; place-content: center; justify-items: center; gap: 10px;
	color: var(--lm-paper);
	font-family: var(--lm-cond); font-stretch: 82%; text-transform: uppercase;
}
.lm-empty--light { color: var(--lm-ink); }
.lm-empty span { font-weight: 700; font-size: clamp(24px, 3vw, 44px); }
.lm-empty em { font-style: normal; color: var(--lm-mute); }

/* ------------------------------------------------------------ responsive -- */

@media (max-width: 900px) {
	.lm-top { display: none; }
	.lm-mtop { display: flex; }
	.lm-mbot { display: flex; }

	.lm-main { padding-bottom: calc(var(--lm-bar) + 12px); }

	/* Film: list only. The index and WATCH NOW are pointer affordances and the
	   design drops them here; tapping a row plays it. */
	.lm-film__deck {
		grid-template-columns: minmax(0, 1fr);
		align-content: center;
		padding-bottom: calc(var(--lm-bar) + 20px);
	}
	.lm-film__index, .lm-film__watch { display: none; }
	.lm-film__list { grid-column: 1; }

	/* Studio on phones (client note #42): only the list survives — the dock needs a
	   pointer to mean anything — and projects open from it. */
	.lm-studio { grid-template-columns: minmax(0, 1fr); padding: 0 var(--lm-gut); align-items: center; }
	.lm-studio__dock { display: none; }
	/* The row has to sit inside the same gutter on both sides. The year used to live in a
	   fixed 5ch cell with its text left-aligned in it, so it stopped roughly a character
	   short of the edge while the number on the left was flush against it — the table
	   read as pushed off-centre. Now the year takes only the width it needs and is set
	   against the right margin, so both ends land on the gutter. */
	.lm-studio__link { grid-template-columns: 3.2ch minmax(0, 1fr) minmax(0, 1fr) auto; gap: 10px; padding-left: 0; }
	.lm-studio__year { text-align: right; }

	/**
	 * Film on phones: the videos at their own shape, one after another, free scroll.
	 *
	 * It used to be full-bleed slides snapped one per screen, which cropped a 16:9 film
	 * to a 9:19 phone — the client wants the original format kept, so each clip now sits
	 * in its own 16:9 block and the caption reads underneath it rather than over it.
	 * With nothing cropped there is nothing for a gradient to rescue, so that goes too.
	 *
	 * No snap and no inner scroller: the page itself scrolls, which is what "scroll
	 * libero" means and what lets two clips share the screen while you move between them.
	 *
	 * The list-over-one-background composition needs a pointer to be readable at all, so
	 * it stays on the desktop.
	 */
	.lm-film__deck { display: none; }
	.lm-film__bg { display: none; }
	.lm-film { min-height: 0; }

	/* Here the ground is a colour again, not footage, so the wordmark's black/white
	   switch means something on Film too (client, 2026-10-08): inverted, the page goes
	   white with black captions. Desktop keeps opting out — its ground is the video. */
	:root[data-lm-theme="invert"] .lm--light { --lm-ink: #ffffff; --lm-paper: #000000; --lm-mute: #6f6f6f; background: #fff; color: #000; }
	:root[data-lm-theme="invert"] .lm--light ::selection { background: #000; color: #fff; }
	/* The bars blend by difference over Film, so they stay white to come out black on
	   the white page (and still read over the footage). */
	:root[data-lm-theme="invert"] .lm-page--film .lm-mtop,
	:root[data-lm-theme="invert"] .lm-page--film .lm-mbot { --lm-ink: #000000; --lm-paper: #ffffff; --lm-mute: #a6a6a6; }

	.lm-film__stack {
		display: block;
		margin: 0; list-style: none;
		/* Clear of the fixed wordmark: a 16:9 block is short enough that the bar would
		   otherwise cover a third of the first film. */
		padding: clamp(54px, 15vw, 76px) 0 0;
	}

	.lm-film__slide { position: relative; }
	.lm-film__slide + .lm-film__slide { margin-top: clamp(18px, 4vh, 32px); }

	/* aspect-ratio, not a viewport height: the box is the film's own shape, so `cover`
	   has nothing to crop and the frame arrives whole. The still and the film share it:
	   the still is always there, the film fades in over it once it is really playing
	   and drops back out when it is let go — never a black box in between. */
	.lm-film__slidemedia {
		position: relative;
		aspect-ratio: 16 / 9;
		background: #000;
		overflow: hidden;
	}
	.lm-film__slideposter,
	.lm-film__slidebg {
		position: absolute; inset: 0;
		width: 100%; height: 100%;
		object-fit: cover;
	}
	.lm-film__slidebg { opacity: 0; transition: opacity 0.25s linear; }
	.lm-film__slidebg.is-on { opacity: 1; }

	/* The caption is the tap target, and it sits under its film. */
	/* Two fixed columns under the film, both white, both the same size (client,
	   2026-10-08): the name in the left half, the track or content starting from the
	   centre of the line, so the two never run into each other and every caption lines
	   up with the next. A name too long for its half (the feat. lists) wraps there, in
	   balanced lines, and is never cut; a title too long for its half runs across on a
	   loop (film.js adds .is-run and a second copy) — until the script measures it, and
	   with reduced motion, it is cut with an ellipsis. No WATCH NOW — the whole slide
	   plays. */
	.lm-film__slide { cursor: pointer; }
	.lm-film__slidecta {
		display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: baseline;
		width: 100%; min-width: 0;
		text-align: left;
		color: var(--lm-paper);
		padding: 10px var(--lm-edge) 0;
		font-size: clamp(15px, 4.3vw, 22px); line-height: 1.1; /* two points down from 17–24px, 2026-10-08 */
	}
	/* Clear of the bars or not there at all (client, 2026-10-10). Where the browser can
	   drive an animation from the scroll, the caption's opacity follows the page frame
	   by frame instead of a script that hears about the scroll late: the view timeline
	   is inset to the bars' lettering (film.js measures it into the two variables), so
	   "contain" is exactly the stretch where the text sits clear of both, and its two
	   ends are the measured fade (film.js rewrites the keyframes so each end is ~24px
	   on that phone; the ones below are only the fallback). Outside it the fill holds
	   it at 0. film.js sets data-lm-cta="css" only when this is supported, and takes it
	   back if what the browser draws ever disagrees with the geometry. */
	html[data-lm-cta="css"] .lm-film__slidecta {
		animation: lm-film-cta linear both;
		animation-timeline: view(block var(--lm-cta-top, 50px) var(--lm-cta-bot, 70px));
		animation-range: contain 0% contain 100%;
	}
	@keyframes lm-film-cta {
		0%   { opacity: 0; }
		4%   { opacity: 1; }
		96%  { opacity: 1; }
		100% { opacity: 0; }
	}
	/* Arriving is timed, not measured (client, 2026-10-10: a pixel band is only as soft
	   as the thumb is slow, and a title popping in read as a bug). film.js adds .is-in
	   the moment a caption is clear of the type and its text fades in over half a
	   second; it drops it once the band above has the caption at 0, and the reset is
	   instant because nobody can see it. Gated on the script having run, so without it
	   the text is simply there. The first frames skip the fade: what's on screen at load
	   is not arriving. */
	html[data-lm-cta] .lm-film__slidecta:not(.is-in) > * { opacity: 0; }
	html[data-lm-cta-live] .lm-film__slidecta.is-in > * { transition: opacity 0.5s ease-out; }
	/* As written, like the desktop column above — one list, one voice. */
	.lm-film__slidename {
		font-family: var(--lm-cond); font-stretch: 82%; font-weight: 700;
		padding-right: 14px; /* air before the centre line when a long name fills its half */
		text-wrap: balance;
	}
	.lm-film__slidemeta {
		grid-column: 2; min-width: 0; overflow: hidden;
		white-space: nowrap;
		color: var(--lm-paper);
	}
	.lm-film__slidemetatext { display: block; overflow: hidden; text-overflow: ellipsis; }
	.lm-film__slidemeta.is-run {
		-webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 90%, transparent);
		        mask-image: linear-gradient(90deg, transparent, #000 5%, #000 90%, transparent);
	}
	.lm-film__slidemeta.is-run .lm-film__slidemetatext {
		display: inline-flex; overflow: visible; text-overflow: clip;
		animation: lm-film-run var(--lm-run-time, 12s) linear infinite;
	}
	.lm-film__slidemeta.is-run .lm-film__slidemetatext > span { padding-right: 3em; }
	@keyframes lm-film-run { to { transform: translateX(-50%); } }

	.lm-shop__card { flex-basis: 100%; max-width: 100%; }
	.lm-shop__grid { row-gap: 40px; }

	/* Shop on phones: the page itself never moves, only the products do (client,
	   2026-10-09). As a scrolling page it could open part-way down, with the cap half
	   under the wordmark. Now <main> is a fixed window between the two bars and the
	   products scroll inside it, two whole cards to a screen, one card per swipe and
	   always coming to rest on a card's top edge. The legal line is the last stop.
	   The checkout and the legal texts wear the shop's chrome (page.php) but are
	   documents: inside this window their only snap point was the legal line, so they
	   opened at the bottom and every swipe up was pulled back down (2026-10-12). The
	   window is the grid's alone. */
	html:has(body.lm-page--shop:not(.lm-page--doc, .lm-page--checkout)),
	body.lm-page--shop:not(.lm-page--doc, .lm-page--checkout) { overflow: hidden; overscroll-behavior: none; }
	.lm-page--shop:not(.lm-page--doc, .lm-page--checkout) .lm-main {
		--shop-top: calc(var(--lm-edge) * 2 + clamp(28px, 9vw, 40px) * 0.9 + 8px);
		--shop-bot: calc(var(--lm-bar) + env(safe-area-inset-bottom));
		position: fixed; top: var(--shop-top); bottom: var(--shop-bot); left: 0; right: 0;
		min-height: 0;
		padding: 0;
		overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
		scroll-snap-type: y mandatory;
		scrollbar-width: none;
	}
	.lm-page--shop:not(.lm-page--doc, .lm-page--checkout) .lm-main::-webkit-scrollbar { width: 0; height: 0; }
	.lm-page--shop .lm-shop { display: block; min-height: 0; padding: 0 var(--lm-gut); }
	.lm-page--shop .lm-shop__grid { display: block; }
	/* Each card is exactly half the window, so two always fit whole; the photo takes
	   whatever the type leaves and shrinks to stay inside it. */
	.lm-page--shop .lm-shop__card {
		height: calc((100vh - var(--shop-top) - var(--shop-bot)) / 2);
		height: calc((100dvh - var(--shop-top) - var(--shop-bot)) / 2);
		display: flex; flex-direction: column; justify-content: center;
		padding: 10px 0;
		scroll-snap-align: start; scroll-snap-stop: always;
	}
	.lm-page--shop .lm-shop__shot { flex: 1 1 auto; min-height: 0; width: 76%; aspect-ratio: auto; grid-template: minmax(0, 1fr) / minmax(0, 1fr); }
	.lm-page--shop .lm-shop__shot img { min-height: 0; }
	.lm-page--shop .lm-shop__name { margin-top: 8px; }
	.lm-page--shop .lm-buy { margin-top: 6px; }
	.lm-page--shop:not(.lm-page--doc, .lm-page--checkout) .lm-legal { scroll-snap-align: end; padding-top: 10px; }

	/* Contact on phones: the form sits in the middle of the screen, where the thumb is
	   and where the only thing you came to do lives; everything else spreads along the
	   bottom as a footer band, clear of the section bar. One screen, no scrolling. */
	.lm-contact {
		grid-template-columns: minmax(0, 1fr);
		grid-template-rows: 1fr auto;
		gap: clamp(24px, 4vh, 40px);
		padding: calc(var(--lm-gut) * 4.5) var(--lm-gut) calc(var(--lm-bar) + var(--lm-gut));
	}
	/* The section owns the full screen and keeps its own clearance for the bar; leaving
	   .lm-main's padding on top of that would push the footer into a scroll. */
	.lm-page--contact .lm-main { padding-bottom: 0; }
	.lm-contact__form { order: -1; gap: 20px; align-content: center; }
	.lm-contact__field input { font-size: clamp(26px, 8vw, 38px); }
	.lm-contact__field textarea { font-size: clamp(15px, 4.4vw, 18px); }
	.lm-contact__send { font-size: clamp(15px, 4.4vw, 18px); }
	/* E-MAIL can't be pinned to the corner here — the section bar and the footer band
	   own the bottom of the screen — so it joins SEND on its line, at the other end. */
	.lm-contact__form { grid-template-columns: minmax(0, 1fr) auto; }
	.lm-contact__form > * { grid-column: 1 / -1; }
	.lm-contact__form > .lm-contact__send { grid-column: 1; grid-row: 3; }
	.lm-contact__form > .lm-mailto { position: static; grid-column: 2; grid-row: 3; align-self: center; }
	/* No hover on a phone: the address would never show, so it doesn't take the room. */
	.lm-mailto__addr { display: none; }

	/* Two columns across the foot — address and socials on the left, the client list on
	   the right — with the credits ruled off underneath, spanning both. */
	.lm-contact__col {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		align-content: end;
		gap: clamp(14px, 2.4vh, 22px) clamp(14px, 5vw, 30px);
		padding-left: 0;
	}
	.lm-contact__col > .lm-contact__group { grid-column: 1; grid-row: 1; }
	.lm-contact__col > .lm-contact__block { grid-column: 2; grid-row: 1; }
	.lm-contact__credits { grid-column: 1 / -1; }
	.lm-contact__label { margin-bottom: 8px; }
	.lm-contact__group { gap: 10px; align-content: start; }

	/* About on phones: the mobile bars stay on top of it, so the white band behind the
	   top one is as tall as the big wordmark, and the foot keeps clear of the bottom bar. */
	.lm-about { --lm-about-head: calc(var(--lm-edge) * 2 + clamp(28px, 9vw, 40px) * 0.9); }
	/* …and the same white band under the bottom bar, so the showreel doesn't run behind
	   the section names. */
	.lm-about::after {
		content: ''; display: block;
		position: sticky; bottom: 0; z-index: 3;
		height: calc(var(--lm-bar) + env(safe-area-inset-bottom));
		background: var(--lm-paper);
	}
	.lm-about__track li { width: 44vw; }
	.lm-about__row { grid-template-columns: minmax(0, 1fr); gap: 14px; padding: clamp(32px, 6vh, 56px) var(--lm-edge) 0; }
	.lm-about__label { padding-left: 0; }
	.lm-about__body { font-size: clamp(19px, 5.6vw, 26px); }
	.lm-about__services { font-size: clamp(30px, 9.4vw, 44px); }
	.lm-about__reelrow { padding: clamp(36px, 7vh, 64px) 0 0; }
	.lm-about__reelrow > span { display: none; }

	/* Cart takes the whole screen. */
	.lm-cart { width: 100%; }

	.lm-landing__menu { flex-direction: column; align-items: center; gap: clamp(14px, 3vh, 26px); font-size: clamp(21px, 6.5vw, 30px); }
}

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 0.01ms !important;
		transition-duration: 0.01ms !important;
	}
	.lm-landing__video { opacity: 0 !important; }
}

/* Contact honeypot: off-screen rather than display:none, which some bots skip. */
.lm-contact__hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
