/*
Theme Name: Portfolio
Description: Photography portfolio theme.
Update URI: false
Version: 1.0.0
Requires at least: 7.1
Requires PHP: 8.2
Text Domain: portfolio
*/

/* Spacing: 14, 28, 84. Other measures derive from them: 420 for flow photos
   (15 × 28), 672 for the phone breakpoint (24 × 28), the grid's eleven gaps
   (11 × 28). Type: 17, and 28 for a series title. Italic is reserved for
   <cite>, the title of a work. Colors: #000 and #fff. */

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

html { -webkit-text-size-adjust: 100%; }

/* hidden must beat the display: flex rules below: the arrows are hidden by
   this attribute, not by a class. */
[hidden] { display: none !important; }

body {
  margin: 0;
  padding: 28px 28px 84px;
  background: #fff;
  color: #000;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 17px;
  line-height: 1.45;
}

/* The page title exists for document structure, not for the eye. */
h1 {
  font: inherit;
  margin: 0;
}

p {
  margin: 0;
  text-wrap: pretty;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* No hover style: the pointer is enough to signal a link. */
a { color: #000; text-decoration: none; }

a, button { -webkit-tap-highlight-color: transparent; }

/* Controls are real buttons: a <span> is focusable but cannot be activated.
   Nothing distinguishes them from text. */
button {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

:focus-visible {
  outline: 1px solid #000;
  outline-offset: 2px;
}

/* The name is never underlined, even on the home page where it is current. A
   link in a text is, since nothing else distinguishes it. */
[aria-current="page"]:not(.nav__name),
.portfolio-text a,
.grid__item--text a {
  text-decoration: underline 1px;
  text-underline-offset: 0.25em;
}

.nav {
  margin: 0 0 84px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  column-gap: 28px;
}
.nav__right {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 28px;
}

/* hanging-punctuation only works in Safari. */
.portfolio-text,
.grid__item--text { hanging-punctuation: first allow-end last; }

.portfolio-text {
  max-width: 50ch;
  margin: 0 0 84px;
}
.portfolio-text p + p,
.portfolio-text p + address { margin-top: 28px; }

.portfolio-title {
  margin: 0 0 14px;
  font-size: 28px;
  line-height: 1.15;
  text-wrap: balance;
}
.years { margin: 0 0 28px; }

/* Italic is reserved for works, so <address> loses its default. */
address {
  font-style: inherit;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}
.next-series { margin-top: 84px; }

/* 84 below the last element is already the page's bottom padding. */
main > :last-child { margin-bottom: 0; }

/* Each column is as wide as the text column of other pages; without room,
   both shrink together. */
.portfolio-info {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 50ch));
  justify-content: space-between;
  column-gap: 28px;
  align-items: start;
}
.portfolio-info > .portfolio-text,
.portfolio-info > .portrait { margin: 0; }
.portrait img {
  display: block;
  width: 100%;
  height: auto;
}

@media (max-width: 672px) {
  .portfolio-info { display: block; }
  .portfolio-info > .portrait { margin-top: 84px; }
}

/* No photo is cropped: a height is set and the width follows its ratio, so
   format differences stay visible. */
.photo { display: block; cursor: zoom-in; }
.photo img { display: block; }

/* Flex, not grid: a grid would force equal widths and vary the heights. All
   photos are 420 high (their tag's height); a wider photo shrinks rather than
   overflow. */
.flow {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
}
.flow > li { max-width: 100%; }
.flow .photo img { max-width: 100%; height: auto; }

/* Rows are as tall as a column is wide: 100cqw, the .grid-wrapper width,
   minus eleven gaps, over twelve. A text longer than its rows stretches them.
   Items use .grid plus their class to outrank WordPress's block margins in
   the editor. */
.grid-wrapper { container-type: inline-size; }
.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: minmax(calc((100cqw - 308px) / 12), auto);
  gap: 28px;
  align-items: start;
}
.grid .grid__item {
  grid-column: var(--column) / span var(--column-span);
  grid-row: var(--row) / span var(--row-span);
  min-width: 0;
  margin: 0;
}
.grid .grid__item > .photo,
.grid .grid__item > .cover { width: 100%; }
.grid .grid__item img {
  display: block;
  width: 100%;
  height: auto;
}

.cover { display: block; }
.cover__title { display: block; margin-top: 14px; }

.grid .grid__item--text > * + * { margin-top: 28px; }
.grid .grid__item--text figure { margin: 0; }
.grid .grid__item--text iframe {
  display: block;
  width: 100%;
  border: 0;
}

/* On narrow screens: one column in reading order. Each photo keeps a share of
   the width and the side it leaned to; a text takes the full width. */
@media (max-width: 672px) {
  .grid {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
  .grid .grid__item:not(.grid__item--text) { width: calc(var(--narrow-share) * 100%); }
  .grid .grid__item--center { align-self: center; }
  .grid .grid__item--end { align-self: flex-end; }
  .grid .grid__item--text { align-self: stretch; }
}

/* <dialog> rather than a hidden overlay: the rest of the page truly leaves
   focus, screen readers and the Escape key, and the cursor returns where it
   was. */
.lightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 28px;
  border: 0;
  background: #000;
  color: #fff;
  cursor: zoom-out;
}
.lightbox :focus-visible { outline-color: #fff; }
.lightbox[open] {
  display: grid;
  place-content: safe center;
  gap: 28px;
}
.lightbox::backdrop { background: #000; }

/* Fades instead of vanishing. Without support, or with reduced motion, it
   appears at once. */
.lightbox,
.lightbox::backdrop {
  opacity: 0;
  transition: opacity .25s ease-out,
              display .25s allow-discrete,
              overlay .25s allow-discrete;
}
.lightbox[open],
.lightbox[open]::backdrop { opacity: 1; }
@starting-style {
  .lightbox[open],
  .lightbox[open]::backdrop { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .lightbox,
  .lightbox::backdrop { transition: none; }
}

/* As large as the screen allows, minus margins, the gap and two caption
   lines. --ratio, set by the script, gives the proportions before the file
   arrives; contain avoids cropping if they differ. */
.lightbox__photo {
  justify-self: center;
  width: auto;
  height: min(calc(100dvh - 84px - 2lh), calc((100vw - 56px) / var(--ratio, 1.5)));
  max-width: calc(100vw - 56px);
  aspect-ratio: var(--ratio, 1.5);
  object-fit: contain;
}

/* As wide as the photo, never under 28em or over the page, to fit a caption
   beside the arrows. Under a narrow image it overflows equally on both sides. */
.lightbox__footer {
  width: 0;
  min-width: max(100%, min(28em, 100vw - 56px));
  justify-self: center;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 28px;
}

/* On phones, when the caption would have under 14em beside the arrows, they
   wrap below. */
.lightbox__caption { flex: 1 1 14em; min-width: 0; }

/* The arrows keep their click area without thickening the line; the last
   aligns with the photo's edge. */
.lightbox__controls {
  display: flex;
  gap: 14px;
  flex: 0 0 auto;
  margin: -14px -14px -14px auto;
}
.lightbox__controls > * { padding: 14px; }
.lightbox__controls > :first-child { padding-left: 0; }
