/* ==========================================================================
   Karen Ives — hand-written, mobile-first. Ports the design worked out
   across the Homepage/Work-grid/Piece-detail/About/Contact demo comps into
   the real theme. See the project plan for the design rationale behind
   each section; comments here focus on implementation specifics only.
   ========================================================================== */

:root {
	--paper: #ffffff;
	--ink: #151412;
	--ink-soft: #6f6a63;
	--line: #e7e2d9;
	--accent: #c23d2f;
	--font: "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
	--scrim: rgba(0, 0, 0, .7);
	--edge-x: clamp(1rem, 3vw, 2.4rem);
	--page-max: 100rem;
	--masonry-space: 8px;
	--admin-bg: #fdf6e3;
	--admin-line: #e8d9a0;
	--admin-ink: #7a6a2a;
}
@media (max-width: 479px) {
	:root { --edge-x: .5rem; }
}

*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
	margin: 0;
	background: var(--paper);
	color: var(--ink);
	font-family: var(--font);
	font-weight: 300;
	-webkit-font-smoothing: antialiased;
}
a { color: inherit; }
img { max-width: 100%; height: auto; }

/* Capped + centered so inner pages (header, pagehead, toolbar, grid, page
   content) read as one column instead of stretching edge to edge forever
   on very wide/ultrawide screens. Scoped away from .home: front-page.php's
   grid is deliberately full-bleed (width:100vw), which only lines up
   correctly if #main itself still starts at the true viewport edge there —
   capping it too would leave the grid overflowing #main's now-narrower,
   centered box. Everything with its own narrower max-width nested inside
   (.piecebody, .page-content) is unaffected — a smaller nested max-width
   just wins, same as always. */
body:not(.home) #main { max-width: var(--page-max); margin: 0 auto; }
@media (prefers-reduced-motion: reduce) {
	* { transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

.skip-link {
	position: absolute; left: -9999px; top: 0; z-index: 100;
	background: var(--ink); color: var(--paper); padding: .8rem 1.2rem;
}
.skip-link:focus { left: var(--edge-x); }
.screen-reader-text {
	position: absolute; width: 1px; height: 1px; overflow: hidden;
	clip: rect(1px, 1px, 1px, 1px); white-space: nowrap;
}
.skip-link.screen-reader-text:focus {
	position: fixed; width: auto; height: auto; clip: auto;
}

/* ---------- header: chop + hamburger + slide-down menu ---------- */
.mast {
	position: sticky; top: 0; z-index: 90; background: var(--paper);
	display: flex; align-items: center; justify-content: space-between; gap: 1rem;
	padding: .9rem var(--edge-x); border-bottom: 1px solid var(--line);
}
/* Capped + centered on inner pages so the chop and hamburger don't drift
   apart into a mostly-empty bar on very wide screens — scoped away from
   .home, whose header is absolutely overlaid on the full-bleed grid
   (inset:0 already spans it edge to edge; a max-width here would just
   fight that, and .home-grid's own 100vw math assumes #main starts at the
   true viewport edge — see the #main rule below). */
body:not(.home) .mast { max-width: var(--page-max); margin: 0 auto; }
.home .mast {
	position: absolute; inset: 0 0 auto 0; background: transparent; border-bottom: 0;
}

.chop {
	display: block; width: clamp(84px, 10.8vw, 120px); aspect-ratio: 600 / 309;
	background-color: var(--ink);
	-webkit-mask: url("../img/kives.png") center / contain no-repeat;
	        mask: url("../img/kives.png") center / contain no-repeat;
	flex: none; text-decoration: none;
}
.home .chop { background-color: var(--paper); }

/* 44x44 tap target; the drawn bars stay a slim 34px, centered inside it via
   left:50%/margin-left rather than left:0, so the hit area can grow without
   the icon getting visually heavier. */
.burger { position: relative; width: 44px; height: 44px; background: none; border: 0; padding: 0; flex: none; cursor: pointer; }
.burger span, .burger span::before, .burger span::after {
	content: ""; position: absolute; left: 50%; width: 34px; height: 4px; margin-left: -17px;
	background: var(--ink); transition: transform .18s ease, opacity .12s ease;
}
.home .burger span, .home .burger span::before, .home .burger span::after { background: var(--paper); }
.burger span { top: 50%; margin-top: -2px; }
.burger span::before { top: -12px; }
.burger span::after { top: 12px; }
.is-menu-open .burger span { transform: rotate(45deg); }
.is-menu-open .burger span::before { transform: translateY(12px); opacity: 0; }
.is-menu-open .burger span::after { transform: translateY(-12px) rotate(-90deg); }
.is-menu-open .mast { background: var(--paper); }
/* .is-menu-open and .home are both classes on <body> itself, not
   ancestor/descendant — "​.is-menu-open .home ..." (with a space) never
   matches anything, since .home would need to be a descendant of a
   .is-menu-open element, not the same element. Needs the compound
   (no-space) form to actually select anything. This silently broke the
   burger's color-switch entirely, and there was no equivalent rule for the
   chop at all — same bug, doubled. */
.home.is-menu-open .chop { background-color: var(--ink); }
.home.is-menu-open .burger span,
.home.is-menu-open .burger span::before,
.home.is-menu-open .burger span::after {
	background: var(--ink);
}

.slidemenu {
	position: fixed; inset: 0; z-index: 80; background: var(--paper);
	transform: translateY(-100%); transition: transform .5s cubic-bezier(.77, .2, .05, 1);
	display: flex; flex-direction: column; justify-content: center;
	padding: clamp(2rem, 8vw, 5rem) var(--edge-x); gap: clamp(.1rem, 1vw, .4rem);
}
/* Same cap + centering as .mast/#main, same .home exclusion — the menu's
   left inset (--edge-x) is meant to line up with the chop's, and on inner
   pages the chop now sits inside a capped, centered header. Left uncapped
   on .home, where the chop itself is still genuinely flush with the true
   viewport edge. inset:0 + max-width + margin:auto centers a fixed-position
   box the same way it would an absolute one (background still reads as
   full-screen either way, since body's own background is the same white). */
body:not(.home) .slidemenu { max-width: var(--page-max); margin: 0 auto; }
.is-menu-open .slidemenu { transform: translateY(0); }
/* wp_nav_menu() always wraps each item in a real <li> — list-style:none
   strips the bullet regardless of whether there's a <ul> parent at all. */
.slidemenu li { list-style: none; margin: 0; padding: 0; }
/* display:contents makes the <ul>'s own box disappear from layout while its
   <li> children still participate directly in .slidemenu's flex column —
   same visual result as the <li>s being direct flex children, but with a
   real, valid <ul> in the DOM (see parts/header.php) instead of orphaned
   <li>s with no list parent at all. */
.slidemenu ul { display: contents; list-style: none; margin: 0; padding: 0; }
.slidemenu a {
	font-weight: 400; color: var(--ink); text-decoration: none;
	font-size: clamp(1.6rem, 5vw, 2.6rem); letter-spacing: .02em; padding: .28em 0 .15em 0; width: max-content;
	border-bottom: 1px solid transparent; transition: border-color .15s;
}
.slidemenu a:hover, .slidemenu a:focus-visible { border-color: var(--ink); outline: none; }
.slidemenu .ext {
	margin-top: clamp(1rem, 4vw, 2.2rem); font-weight: 300;
	font-size: clamp(.8rem, 2.5vw, 1rem); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft);
}
/* Reuses karenives_contact_instagram (Settings -> Contact Gate) — same
   link the Contact page already shows, one place to manage it rather than
   a second copy here. Sized/muted to match .ext, not the big nav links
   above it — this is a plain, ungated social link, not a primary nav item
   (see the project plan's Contact page notes on why Instagram is never
   gated). */
.slidemenu .social {
	display: inline-block; margin-top: .5rem; font-weight: 300;
	font-size: clamp(.78rem, 2.3vw, .92rem); letter-spacing: .1em; text-transform: uppercase;
	color: var(--ink-soft); text-decoration: none; border-bottom: 1px solid transparent;
	padding: .28em 0; width: max-content; transition: border-color .15s;
}
.slidemenu .social:hover, .slidemenu .social:focus-visible { color: var(--ink); border-color: var(--ink); outline: none; }

/* ---------- homepage grid ---------- */
.home-grid {
	width: 100vw; height: 100dvh; display: grid; gap: 0;
	/* Mobile: first row a bit taller than the second (was an even 1fr 1fr). */
	grid-template-columns: 1fr 1fr; grid-template-rows: 1.2fr 1fr;
	grid-template-areas: "s0 s0" "s1 s2";
}
@media (min-width: 760px) {
	.home-grid {
		/* Column 1 (s0) a bit wider so the whole piece fits; 2 and 3 a bit
		   narrower to make room (was an even 1fr 1fr 1fr). Row 1 is "auto"
		   now instead of a fixed 64% — see .home-grid__cell--s2 img below,
		   which is what actually makes that row's height track the s2
		   image's own proportions rather than a hardcoded percentage. */
		grid-template-columns: 4fr 3fr 3fr; grid-template-rows: auto 1fr;
		grid-template-areas: "s0 s1 s2" "s0 s1 d0";
	}
}
.home-grid__cell { overflow: hidden; position: relative; }
.home-grid__cell--s0 { grid-area: s0; }
.home-grid__cell--s1 { grid-area: s1; }
.home-grid__cell--s2 { grid-area: s2; }
.home-grid__cell--d0 { grid-area: d0; display: none; }
@media (min-width: 760px) { .home-grid__cell--d0 { display: block; } }
.home-grid__cell img, .home-grid__cell a { display: block; width: 100%; height: 100%; }
.home-grid__cell img { object-fit: cover; object-position: center; }
/* s2 or (d0's) 64%-of-viewport row used to force-crop whatever image was
   here to a fixed box via object-fit:cover. Letting the image size itself
   (height:auto, no object-fit) means the row (now "auto" above) becomes
   exactly as tall as this image's own aspect ratio dictates at column 3's
   width — the whole piece shows, nothing cropped — and d0 automatically
   absorbs whatever vertical space is left via its own 1fr row, still
   cropping to fill exactly as before. Desktop only; mobile's s2 is a
   half-width tile that should still crop like the others. */
@media (min-width: 760px) {
	.home-grid__cell--s2 img { height: auto; object-fit: unset; }
}

/* ---------- page head: h1 + breadcrumbs ---------- */
.pagehead { padding: clamp(1.6rem, 4vw, 2.6rem) var(--edge-x) 1rem; }
/* On the grid pages, .pagehead holds only the breadcrumbs (the <h1> moved
   into .toolbar below, to sit on the same row/baseline as the category
   select) — .crumbs' own bottom margin already provides the visual gap
   before that row, so trim .pagehead's own padding-bottom here rather than
   stacking both and leaving too much air. Scoped with :has() so every other
   page's pagehead+h1 pairing (About, Contact, single piece) keeps its
   normal spacing untouched. */
.pagehead:has(+ .toolbar) { padding-bottom: .2rem; }
.crumbs {
	font-size: .8rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft);
	margin: 0 0 .8rem; display: flex; gap: .4rem; flex-wrap: wrap;
}
.crumbs a { text-decoration: none; color: var(--ink-soft); }
.crumbs a:hover { color: var(--ink); text-decoration: underline; }
h1 { font-weight: 400; font-size: clamp(1.7rem, 1.1rem + 2.6vw, 2.6rem); margin: 0 0 .3rem; letter-spacing: -.01em; }

/* ---------- Work grid toolbar + category select ---------- */
.toolbar {
	display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .8rem;
	padding: 0 var(--edge-x) 1.3rem; border-bottom: 1px solid var(--line);
}
/* The shared h1 rule's own bottom margin is for spacing it from whatever
   follows inside .pagehead elsewhere — here it's a flex item sharing a row
   with .filter, so that margin would just throw off the baseline match. */
.toolbar h1 { margin: 0; }
.filter { position: relative; }
.filter select {
	appearance: none; -webkit-appearance: none;
	font-family: var(--font); font-weight: 400; font-size: .88rem; letter-spacing: .04em;
	color: var(--ink); background: var(--paper); border: 1px solid var(--line); border-radius: 3px;
	padding: .55rem 2.1rem .55rem .9rem; cursor: pointer; min-width: 15rem;
}
.filter::after {
	content: ""; position: absolute; right: .9rem; top: 50%; width: 7px; height: 7px;
	border-right: 1.5px solid var(--ink-soft); border-bottom: 1.5px solid var(--ink-soft);
	transform: translateY(-70%) rotate(45deg); pointer-events: none;
}
.filter option.sep { color: var(--ink-soft); }

/* ---------- masonry grid ----------
   --cols columns at --masonry-space apart must sum to exactly 100%, so the
   single-tile width reserves room for (--cols - 1) gutters up front via
   calc() — mixing full-width percentages with a separate pixel gutter on
   top of them is what causes columns to stop fitting/overflow. */
.gridwrap { padding: 0 var(--edge-x) 2rem; }
.masonry { position: relative; --cols: 2; }
.tile-sizer { height: 0; margin: 0; padding: 0; }
.tile-sizer, .tile { width: calc((100% - (var(--cols) - 1) * var(--masonry-space)) / var(--cols)); }
.tile.is-wide { width: calc((100% - (var(--cols) - 1) * var(--masonry-space)) / var(--cols) * 2 + var(--masonry-space)); }
@media (min-width: 480px) { .masonry { --cols: 3; } }
@media (min-width: 800px) { .masonry { --cols: 4; } }
@media (min-width: 1200px) { .masonry { --cols: 5; } }

.tile {
	position: absolute; display: block; overflow: hidden; background: transparent;
	text-decoration: none; outline-offset: 2px; margin-bottom: var(--masonry-space);
}
.tile img { display: block; width: 100%; height: auto; }

/* caption: title only, hover/keyboard-focus reveal, never on touch — a tap
   just opens the piece, the title lives on that page. Kept in the DOM
   (only visually hidden) so it's still announced to assistive tech. */
.cap {
	position: absolute; left: .6rem; bottom: .6rem; display: inline-block;
	background: var(--scrim); color: #f6f3ee;
	padding: .35rem .6rem; border-radius: 2px;
	font-weight: 500; font-size: .78rem; letter-spacing: .01em; line-height: 1.2;
	max-width: calc(100% - 1.2rem);
	opacity: 0; transform: translateY(4px);
	transition: opacity .18s, transform .18s; pointer-events: none;
}
.tile:focus-visible .cap { opacity: 1; transform: none; }
@media (hover: hover) and (pointer: fine) {
	.tile:hover .cap { opacity: 1; transform: none; }
}

/* ---------- load more / sentinel ---------- */
.loadmore { display: flex; flex-direction: column; align-items: center; gap: .6rem; padding: 2.2rem 0 3rem; }
.loadmore button {
	border: 1px solid var(--ink); background: var(--paper); color: var(--ink);
	font-size: .76rem; letter-spacing: .12em; text-transform: uppercase; padding: .7rem 1.6rem;
	border-radius: 999px; transition: background .15s, color .15s; cursor: pointer;
}
.loadmore button:hover { background: var(--ink); color: var(--paper); }
.loadmore button[disabled] { opacity: .35; cursor: default; pointer-events: none; }
.loadmore small { color: var(--ink-soft); font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; }

/* ---------- piece-detail gallery ---------- */
.gallerywrap { padding: 0 var(--edge-x); margin-top: 1.4rem; }
@media (max-width: 759px) { .gallerywrap { margin-top: 0; } }
.hero { display: block; text-align: center; background: var(--line); border-radius: 2px; overflow: hidden; }
.hero img { display: block; width: 100%; height: auto; max-height: 70vh; object-fit: contain; margin: 0 auto; background: #f4f2ee; }
.subrow { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--masonry-space); margin-top: var(--masonry-space); }
.subrow button.thumb-btn {
	display: block; height: 180px; border-radius: 2px; overflow: hidden; background: #f4f2ee;
	cursor: pointer; flex: none; border: 0; padding: 0;
}
@media (max-width: 479px) { .subrow button.thumb-btn { height: 20vw; } }
.subrow img { display: block; height: 100%; width: auto; }
.subrow button.thumb-btn.is-active { outline: 2px solid var(--ink); outline-offset: 2px; }

/* ---------- content grid: specs + admin box ---------- */
.piecebody { padding: 1.8rem var(--edge-x) 3rem; display: grid; gap: 2.2rem; grid-template-columns: 1fr; max-width: 64rem; margin: 0 auto; }
@media (min-width: 760px) {
	/* The admin box is a PHP-conditional element (only rendered at all for
	   logged-in editors), not a JS-hidden one — so for everyone else this
	   selects correctly back down to one column instead of reserving a
	   20rem track for an aside that was never in the markup. */
	.piecebody:has(.adminbox) { grid-template-columns: minmax(0, 1fr) 20rem; }
	.piecebody:not(:has(.adminbox)) { grid-template-columns: 1fr; }
}

.piece-description { font-size: .95rem; line-height: 1.6; margin: 0 0 1.6rem; max-width: 34rem; }
.piece-description p { margin: 0 0 1em; }
.piece-description p:last-child { margin-bottom: 0; }

/* Real <table>, left its natural width — a table's own column-sizing
   shrinks the label column to its content, which a fixed CSS Grid track
   can't do; and it's centered with plain margin:auto now that its width
   is intrinsic rather than forced to fill the container. */
.specs { margin: 0 auto; border-collapse: collapse; border-top: 1px solid var(--line); }
.specs th, .specs td {
	padding: .4rem 0; border-bottom: 1px solid var(--line); font-weight: 400;
	vertical-align: baseline; text-align: left;
}
.specs th {
	font-size: .7rem; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-soft);
	text-align: right; padding-right: 1.2rem;
}
.specs td { font-size: .9rem; line-height: 1.45; color: var(--ink); }
.specs td a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--line); }
.specs td a:hover { border-color: var(--ink); }
.specs td .empty { font-style: italic; color: var(--ink-soft); }

.adminbox { background: var(--admin-bg); border: 1px solid var(--admin-line); border-radius: 4px; padding: 0 1.1rem 1.1rem; align-self: start; }
.adminbox .eyebrow {
	font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: var(--admin-ink);
	font-weight: 500; padding: 1rem 0 .8rem;
}
.adminbox .specs { border-top-color: var(--admin-line); }
.adminbox .specs th, .adminbox .specs td { border-bottom-color: var(--admin-line); }
.adminbox .specs th { color: var(--admin-ink); opacity: .8; }
.adminbox .specs td { color: #4a3f14; }

/* ---------- prev/next ---------- */
.piecenav {
	display: flex; justify-content: space-between; gap: 1rem; padding: 1.4rem var(--edge-x) 2.4rem;
	border-top: 1px solid var(--line); max-width: 64rem; margin: 0 auto;
}
.piecenav a { font-size: .78rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft); text-decoration: none; }
.piecenav a:hover { color: var(--ink); }
.piecenav .next { margin-left: auto; text-align: right; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- contact page ---------- */
/* Same mismatch as the old About-page bug: .piecebody centers itself
   (margin:0 auto) and adds its own top padding, but .pagehead's h1 above it
   has neither — on Contact specifically the centering left-shifts the body
   off the h1's left edge, and the padding-top adds an unwanted gap between
   them. Overriding both here rather than touching the shared .piecebody
   rule, which single-art_portfolio.php's specs+adminbox layout still needs
   as-is. */
.contact-body { max-width: 34rem; margin: 0; padding-top: 0; }
.contact-notice { padding: .8rem 1rem; border-radius: 3px; font-size: .9rem; }
.contact-notice--ok { background: #eef6ea; color: #2f5d24; }
.contact-notice--error { background: #fbeceb; color: #8a2c22; }

/* Honeypot: off-screen, not display:none (some bots specifically skip
   display:none fields), and the markup also carries aria-hidden +
   tabindex="-1" so screen-reader and keyboard users never encounter it —
   a honeypot has to be invisible to every legitimate user, not just
   sighted ones. */
.hp-field { position: absolute; left: -9999px; top: -9999px; }

.contact-form { display: flex; flex-direction: column; gap: 1rem; margin: 2rem 0; }
.contact-form .field { display: flex; flex-direction: column; gap: .3rem; margin: 0; }
.contact-form label { font-size: .78rem; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-soft); }
.contact-form input, .contact-form textarea {
	font-family: var(--font); font-size: 1rem; padding: .6rem .7rem;
	border: 1px solid var(--line); border-radius: 3px; background: var(--paper); color: var(--ink);
}
.contact-form button, .contact-direct button {
	align-self: flex-start; border: 1px solid var(--ink); background: var(--paper); color: var(--ink);
	font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; padding: .7rem 1.6rem;
	border-radius: 999px; cursor: pointer; transition: background .15s, color .15s;
}
.contact-form button:hover, .contact-direct button:hover { background: var(--ink); color: var(--paper); }

.contact-direct h2 { font-size: 1.1rem; }
.contact-instagram { margin-top: 1rem; }
.contact-instagram a { color: var(--ink); }

#contact-gate-dialog { border: 0; border-radius: 4px; padding: 1.6rem; max-width: 22rem; width: 90vw; }
#contact-gate-dialog::backdrop { background: rgba(10, 9, 8, .55); }
#contact-gate-form { position: relative; }
/* The question/fields live in their own flex column, separate from the
   close button above — a lockout only ever replaces .gate-body's content
   (see contact.js showLocked()), so the close button (present in "use
   contact form" mode) is never at risk of being wiped out along with it. */
.gate-body { display: flex; flex-direction: column; gap: .8rem; }
.gate-intro { font-size: .82rem; color: var(--ink-soft); margin: 0; }
.gate-question { font-size: 1rem; margin: 0 1.6rem 0 0; }
.dialog-close {
	position: absolute; top: -.4rem; right: -.4rem; width: 32px; height: 32px; border-radius: 50%;
	border: 0; background: var(--line); cursor: pointer; font-size: 1.2rem; line-height: 1;
}
#gate-answer {
	font-family: var(--font); font-size: 1rem; padding: .6rem .7rem;
	border: 1px solid var(--line); border-radius: 3px;
}
.gate-result { color: var(--accent); font-size: .82rem; margin: 0; }
.gate-locked { font-size: 1rem; margin: 0; }

/* ---------- plain page content (About, etc.) ----------
   Deliberately its own class, not .piecebody: that one is display:grid with
   a uniform row-gap, built for the single-piece page's two-item
   (specs + admin box) layout. Reused as a generic wrapper here, it put a
   full 2.2rem gap between *every* top-level block — every paragraph, every
   heading, every entire resume section — and its max-width+margin:auto
   centering put the body text at a different left edge than .pagehead's h1,
   which has no such constraint. Normal block flow, left-aligned to match. */
.page-featured-image { padding: 0 var(--edge-x); margin-top: 1rem; }
.page-featured-image img { display: block; width: 100%; height: auto; border-radius: 2px; }

.page-content {
	padding: 1.8rem var(--edge-x) 3rem;
	/* max-width: 42rem; */
}
.page-content > * + * { margin-top: 1.2rem; }
.page-content h2 { margin-top: 2.4rem; }
.page-content h3 { margin-top: 1.8rem; margin-bottom: .2rem; }

/* ---------- WordPress core alignment classes ----------
   WP core ships zero CSS for these — every theme has to define its own, or
   an editor's "align left"/"wide"/"full" choice on an image/gallery block
   just does nothing on the front end.

   alignwide/alignfull deliberately skip the classic "centered content,
   margin-left: calc(50% - 50vw)" breakout trick that assumes a centered
   container — .page-content isn't centered here (see its comment above:
   left-aligned with --edge-x padding, no margin:auto), it sits flush
   against the viewport's left edge already. So instead: alignwide just
   grows rightward from the same left edge as normal text, capped at
   --wp--style--global--wide-size (theme.json's wideSize, output onto every
   page automatically by core — kept as the single source of truth rather
   than a second hardcoded 64rem here); alignfull cancels the --edge-x
   inset to reach x=0 and sets width:100vw. Both degrade to "same as
   unaligned content" on a narrow viewport, nothing extra needed for that. */
.alignleft { float: left; margin: .3rem 1.4rem .8rem 0; }
.alignright { float: right; margin: .3rem 0 .8rem 1.4rem; }
.aligncenter { display: block; margin-left: auto; margin-right: auto; }
.alignnone { margin: 1rem 0; }
.alignwide {
	width: calc(100vw - 2 * var(--edge-x));
	max-width: var(--wp--style--global--wide-size, 64rem);
}
.alignfull {
	/* #main's own effective width, not the true viewport — now that #main
	   itself is capped at --page-max on wide screens (see the #main rule
	   above), "full bleed" should mean the edges of the page's own column,
	   not the physical screen beyond it. min() matches #main's rendered
	   width in both regimes: 100vw below the cap, --page-max above it. */
	width: min(100vw, var(--page-max));
	margin-left: calc(-1 * var(--edge-x));
}
.alignfull img { width: 100%; }

/* Classic <figure>/[caption]-shortcode markup — some migrated piece
   descriptions/pages may still carry this rather than the block editor's
   own figure/figcaption, which needs no extra CSS (figure/figcaption are
   already sane block defaults). */
.wp-caption { max-width: 100%; }
.wp-caption img { display: block; width: 100%; height: auto; }
.wp-caption-text, .wp-element-caption {
	font-size: .78rem; color: var(--ink-soft); margin-top: .5rem;
}

/* Real semantic <dl class="type2"> markup carried over as-is from the old
   site's resume content — dl{display:grid} lets dt/dd pairs auto-place
   into label/value rows in DOM order (dt, dd, dt, dd, ...) without needing
   a wrapper per pair, the same hanging-indent effect the old site had. */
.type2 { display: grid; grid-template-columns: auto 1fr; gap: .15rem 1.2rem; margin: 0; }
.type2 dt { color: var(--ink-soft); font-size: .88rem; white-space: nowrap; padding-top: .1rem; }
.type2 dd { margin: 0 0 .9rem; line-height: 1.5; }
.type2 dd b { font-weight: 500; }

/* ---------- piece overlay (grid → dialog, see app.js) ---------- */
#piece-overlay {
	position: relative; border: 0; border-radius: 4px; padding: 0;
	width: min(64rem, 92vw); max-height: 88vh; max-width: 92vw;
}
#piece-overlay::backdrop { background: rgba(10, 9, 8, .7); }
#piece-overlay #piece-overlay-body { overflow-y: auto; max-height: 88vh; }
/* position:absolute (not fixed) so these sit relative to the dialog's own
   box — fixed positioned them relative to the viewport instead, which put
   them over the dark backdrop outside the white dialog entirely on any
   screen wider than the dialog itself. */
#piece-overlay .overlay-toolbar { position: absolute; top: 12px; right: 12px; z-index: 10; display: flex; gap: .5rem; }
#piece-overlay .overlay-share,
#piece-overlay .overlay-close {
	border: 0; background: var(--line); color: var(--ink); cursor: pointer;
	display: inline-flex; align-items: center; justify-content: center;
	width: 40px; height: 40px; border-radius: 50%;
}
#piece-overlay .overlay-close { font-size: 1.3rem; line-height: 1; }
#piece-overlay .overlay-share:hover,
#piece-overlay .overlay-close:hover { background: #d9d3c6; }

/* "Copied!" confirmation — a CSS-only tooltip driven by a data attribute
   JS toggles, rather than swapping the button's content (which would have
   nowhere to put text now that it's icon-only). */
#piece-overlay .overlay-share { position: relative; }
#piece-overlay .overlay-share[data-copied]::after {
	content: attr(data-copied-text);
	position: absolute; bottom: 100%; right: 0; margin-bottom: 8px;
	background: var(--ink); color: #fff; white-space: nowrap;
	font-size: .68rem; letter-spacing: .03em; padding: .35rem .6rem; border-radius: 4px;
}
/* The fragment has no .pagehead at all in overlay mode (see
   piece-content.php) — this just reserves room so its first real element
   (the gallery, or the specs table on a piece with no images) doesn't sit
   underneath the toolbar buttons. */
#piece-overlay #piece-overlay-body > :first-child { margin-top: 2.4rem; }

/* The overlay's own <h1> lives in .pagehead, up near the photos — this is
   a second, valid-HTML title (h2, never a second h1) repeated beside the
   meta table lower down, so the title is still visible once a viewer has
   scrolled past the gallery inside the dialog's scroll area. */
.overlay-title { font-size: 1.3rem; font-weight: 400; letter-spacing: -.01em; margin: 0 0 1rem; }
