/* Small static-site additions around the original Twenty Twenty-Three UI.
   Source typography, colors, logo, media and WordPress block styles are loaded
   from the captured homepage head; rules here cover static masonry and details. */
:root {
  color-scheme: light;
  --theme-paper: #fff;
  --theme-ink: #000;
  --theme-muted: #666;
  --theme-line: #dedede;
  --layout-wide: 1280px;
  --layout-gutter: clamp(20px, 4vw, 56px);
}

*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; background: var(--theme-paper); }
img, video { display: block; max-width: 100%; }
img { height: auto; }

/* Rounded corners on every project and news image.
   The card wrappers clip their contents and sit on a #f5f5f5 placeholder
   background, so they carry the radius as well — rounding only the <img>
   would let that grey peek through at the corners. */
.project-card__image,
.news-card__image,
.gallery-item,
.gallery-item a,
.article-content img,
.vp-portfolio__items .vp-portfolio__item-img img,
.image-viewer img { border-radius: 5px; }
.skip-link:not(:focus) { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.skip-link:focus { position: fixed; z-index: 1000; inset: 12px auto auto 12px; padding: 10px 14px; color: #fff; background: #000; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.original-home-slogan { margin-block: 0 var(--wp--preset--spacing--40, 1.2em); font: inherit; line-height: inherit; letter-spacing: inherit; }

/* The captured footer carries an inline `line-height: 0` on its inner group,
   which its paragraphs inherit: every line box collapses to zero height, so on
   narrow screens the wrapped copyright lines draw on top of each other.
   An inherited value always loses to a rule on the element itself, so setting
   the leading on the paragraphs is enough — the markup stays as captured. */
footer.wp-block-template-part p { line-height: 1.5; }

/* Constrain the page container, site-wide: the video and the portfolio wall are
   full-bleed direct children of .wp-site-blocks, so constraining that one
   container is what keeps them off the viewport edges.
   Above the desktop breakpoint the measure is 80%.
   Selector is `body .wp-site-blocks`, not `body.home`: every generated page carries
   the copied `home` body class, so matching on it would be misleading and would
   silently stop applying if the per-page body classes are ever fixed. */
@media (min-width: 781px) {
  body .wp-site-blocks {
    width: 80%;
    margin-left: auto;
    margin-right: auto;
  }
}

/* Phones and tablets: the homepage used to run edge to edge here while the inner
   pages sat inside --layout-gutter. Put that gutter on the page container once,
   and let the inner page wrappers fill it — otherwise they would add a second
   gutter of their own and end up inset twice as far as the homepage. */
@media (max-width: 780px) {
  /* Halve the gutter on small screens. Redefining the token rather than each
     rule keeps every consumer in step — on phones .wp-site-blocks below is the
     only one left, since the inner page wrappers now fill it. */
  :root { --layout-gutter: clamp(10px, 2vw, 28px); }
  body .wp-site-blocks {
    width: calc(100% - 2 * var(--layout-gutter));
    margin-left: auto;
    margin-right: auto;
  }
}

/* Keep the source portfolio wall, rendered from the original Visual Portfolio markup. */
.vp-portfolio__preloader-wrap { display: none !important; }
.vp-portfolio__items-wrap, .vp-portfolio__filter-wrap, .vp-portfolio__layout-elements { opacity: 1 !important; visibility: visible !important; }
.vp-portfolio[data-vp-layout="masonry"] .vp-portfolio__items {
  column-count: 3;
  column-gap: 5px;
  margin: 0;
}
.vp-portfolio[data-vp-layout="masonry"] .vp-portfolio__item-wrap {
  display: inline-block;
  float: none !important;
  width: 100% !important;
  padding: 0 !important;
  margin: 0 0 5px !important;
  vertical-align: top;
  break-inside: avoid;
}
.vp-portfolio[data-vp-layout="masonry"] .vp-portfolio__item-wrap .vp-portfolio__item { margin: 0 !important; }
.vp-portfolio__items .vp-portfolio__item-img img { display: block; width: 100%; height: auto; }
.vp-portfolio__item-wrap[hidden] { display: none !important; }

/* Original site block typography remains in control; these rules only size custom route wrappers. */
.archive-page, .content-page, .article-page, .project-detail {
  width: min(100% - 2 * var(--layout-gutter), var(--layout-wide));
  margin-right: auto;
  margin-left: auto;
}
/* Phones: the gutter now lives on .wp-site-blocks (see the max-width:780px block
   above), so these wrappers fill it instead of insetting a second time.
   Declared after the rule above on purpose — same specificity, and the later
   rule has to be the one that wins. */
@media (max-width: 780px) {
  .archive-page, .content-page, .article-page, .project-detail {
    width: 100%;
    max-width: none;
  }
}
.archive-page { padding-top: clamp(40px, 7vw, 84px); padding-bottom: 90px; }
.page-intro { padding: 0 0 30px; }
.page-intro h1, .article-header h1, .project-overview h1 { margin: 0; color: var(--theme-ink); font-family: inherit; font-weight: inherit; line-height: 1.18; }
.page-intro h1 { font-size: clamp(30px, 4.5vw, 52px); }
.page-intro h1 span { font-size: .52em; }
.page-lede { max-width: 660px; margin: 14px 0 0; color: var(--theme-muted); }
.filter-bar { display: flex; flex-wrap: wrap; gap: 8px 22px; padding: 12px 0; margin: 0 0 24px; border-top: 1px solid var(--theme-line); border-bottom: 1px solid var(--theme-line); }
.filter-button { padding: 5px 0; border: 0; border-bottom: 1px solid transparent; background: transparent; color: var(--theme-muted); font: inherit; font-size: .82em; cursor: pointer; }
.filter-button:hover, .filter-button.is-active { border-bottom-color: var(--theme-ink); color: var(--theme-ink); }
.project-card[hidden] { display: none; }
.project-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 22px; }
.project-card { display: block; color: inherit; text-decoration: none; }
.project-card__image { display: block; overflow: hidden; aspect-ratio: 4 / 3; background: #f5f5f5; }
.project-card__image img { width: 100%; height: 100%; object-fit: cover; transition: transform .45s ease; }
.project-card:hover .project-card__image img { transform: scale(1.025); }
.project-card__meta { display: flex; flex-direction: column; gap: 3px; padding-top: 8px; }
.project-card .eyebrow { margin: 0; color: var(--theme-muted); font-size: .7em; }
.project-card__title { color: var(--theme-ink); }

.news-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 26px; }
.news-card { min-width: 0; }
.news-card__image { display: block; overflow: hidden; aspect-ratio: 4 / 3; background: #f5f5f5; }
.news-card__image img { width: 100%; height: 100%; object-fit: cover; }
.news-card__body { padding-top: 10px; }
.news-card time, .eyebrow { color: var(--theme-muted); font-size: .78em; }
.news-card h3 { margin: 6px 0; font: inherit; font-size: 1.05em; }
.news-card h3 a { color: inherit; text-decoration: none; }
.news-card p { color: var(--theme-muted); font-size: .9em; }
.text-link { color: inherit; text-decoration: none; }

/* The "← 返回…" links share one presentation; only their spacing differs by context. */
.back-link { display: inline-block; color: var(--theme-muted); font-size: .86em; text-decoration: none; }
.project-overview .back-link { margin-bottom: 22px; }
.article-header .back-link { margin-bottom: 16px; }
.page-intro .back-link { margin-bottom: 18px; }

/* One information layout across all 27 project details; original imagery stays intact. */
.project-detail { max-width: var(--layout-wide); padding-top: 30px; padding-bottom: 90px; }
/* Stacked on every breakpoint: the fact table sits below the title rather than
   beside it, so the heading gets the full measure and the table reads as a
   spec sheet underneath. */
.project-overview { display: grid; grid-template-columns: minmax(0, 1fr); gap: clamp(20px, 2.6vw, 34px); align-items: start; padding: 10px 0 34px; border-bottom: 1px solid var(--theme-line); margin-bottom: 34px; }
.project-overview__heading { min-width: 0; }
.project-overview .eyebrow { margin: 0 0 8px; letter-spacing: .08em; }
.project-overview h1 { overflow-wrap: anywhere; font-size: clamp(30px, 4.2vw, 54px); }
.project-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 26px; margin: 0; border-top: 1px solid var(--theme-line); }
.project-fact { display: grid; grid-template-columns: minmax(74px, .75fr) minmax(0, 1.25fr); gap: 10px; padding: 11px 0; border-bottom: 1px solid var(--theme-line); }
.project-fact dt { color: var(--theme-muted); font-size: .78em; }
.project-fact dd { margin: 0; font-size: .9em; overflow-wrap: anywhere; }
.project-video { margin: 0 auto 28px; }
.project-video figure { margin: 0; }
.project-video video { width: 100%; max-height: 76vh; object-fit: contain; background: #111; }
.project-gallery { columns: 3; column-gap: 5px; }
/* display:block, not inline-block: an inline-block sits in a line box whose
   strut adds ~4px below the baseline, so the 5px margin-bottom rendered as a
   9px vertical gap while the column-gap stayed 5px. Block boxes give the same
   5px row gap as the homepage masonry. */
.gallery-item { display: block; width: 100%; margin: 0 0 5px; background: #f5f5f5; break-inside: avoid; }
.gallery-item a { display: block; overflow: hidden; cursor: zoom-in; }
.gallery-item img { width: 100%; height: auto; }
.project-detail__return { padding-top: 28px; text-align: right; }
.content-page { max-width: min(100% - 2 * var(--layout-gutter), 1100px); padding-top: 20px; padding-bottom: 70px; }
.content-page h1.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.rich-content { overflow-wrap: anywhere; }
.article-page { max-width: min(100% - 2 * var(--layout-gutter), 900px); padding-top: 36px; padding-bottom: 80px; }
.article-header { padding-bottom: 22px; border-bottom: 1px solid var(--theme-line); margin-bottom: 30px; }
.article-header h1 { font-size: clamp(28px, 4vw, 44px); }
.article-content img, .article-content video { max-width: 100%; }
/* Full-window preview overlay.
   The <dialog> itself covers the viewport instead of being a boxed panel, so the
   frosted black backdrop is painted by the element. Relying on ::backdrop alone
   would make the blur share the patchy backdrop-filter support. */
.image-viewer {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: rgb(0 0 0 / .72);
  -webkit-backdrop-filter: blur(16px) saturate(120%);
  backdrop-filter: blur(16px) saturate(120%);
  overscroll-behavior: contain;
}
.image-viewer[open] { display: flex; align-items: center; justify-content: center; }
/* The dialog already paints the full-window scrim, so the backdrop must stay
   clear — otherwise the two layers stack to ~0.86 black and the frosted page
   behind becomes invisible. */
.image-viewer::backdrop { background: transparent; }
.image-viewer img {
  width: auto;
  max-width: min(92vw, 1600px);
  max-height: 88vh;
  margin: 0 auto;
  object-fit: contain;
  box-shadow: 0 24px 70px rgb(0 0 0 / .55);
}

/* Circular glass buttons shared by the close and prev/next controls.
   A dark base (not a white tint) keeps them legible over both dark and bright
   photo areas — a white-tinted button disappears against a bright image. */
.image-viewer__close,
.image-viewer__nav {
  position: absolute;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid rgb(255 255 255 / .3);
  border-radius: 50%;
  background: rgb(0 0 0 / .45);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 2px 14px rgb(0 0 0 / .35);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  transition: background .18s ease, border-color .18s ease, transform .18s ease;
}
.image-viewer__close:hover,
.image-viewer__nav:hover { border-color: rgb(255 255 255 / .6); background: rgb(0 0 0 / .68); }
.image-viewer__close:active,
.image-viewer__nav:active { transform: scale(.94); }
.image-viewer__close:focus-visible,
.image-viewer__nav:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.image-viewer svg { display: block; width: 20px; height: 20px; }
.image-viewer svg path { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.image-viewer__close { top: max(18px, env(safe-area-inset-top)); right: max(18px, env(safe-area-inset-right)); }
.image-viewer__nav { top: 50%; margin-top: -23px; }
.image-viewer__nav--prev { left: max(18px, env(safe-area-inset-left)); }
.image-viewer__nav--next { right: max(18px, env(safe-area-inset-right)); }
.image-viewer__nav[hidden] { display: none; }
.js-motion [data-reveal] { opacity: 0; transform: translateY(10px); transition: opacity .4s ease, transform .4s ease; }
.js-motion [data-reveal].is-visible { opacity: 1; transform: none; }

@media (max-width: 780px) {
  .vp-portfolio[data-vp-layout="masonry"] .vp-portfolio__items { column-count: 2; }
  .project-grid, .news-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .project-gallery { columns: 2; }
}
@media (max-width: 560px) {
  .vp-portfolio[data-vp-layout="masonry"] .vp-portfolio__items { column-count: 1; }
  .project-grid, .news-grid { grid-template-columns: 1fr; }
  .project-facts { grid-template-columns: 1fr; }
  .project-gallery { columns: 1; }
  .project-detail { padding-top: 15px; }
  /* Smaller tap targets on phones; swiping is the primary gesture here. */
  .image-viewer__close, .image-viewer__nav { width: 40px; height: 40px; }
  .image-viewer__nav { margin-top: -20px; }
  .image-viewer__close { top: max(12px, env(safe-area-inset-top)); right: max(12px, env(safe-area-inset-right)); }
  .image-viewer__nav--prev { left: max(10px, env(safe-area-inset-left)); }
  .image-viewer__nav--next { right: max(10px, env(safe-area-inset-right)); }
  .image-viewer svg { width: 18px; height: 18px; }
  .image-viewer img { max-width: 94vw; max-height: 80vh; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  .js-motion [data-reveal] { opacity: 1; transform: none; }
}
