/* ===========================================================================
   COMPONENTS — layout shell, header, menu, buttons, media, forms, footer
   Depends on tokens.css and base.css.
   ========================================================================= */

/* ---------------------------------------------------------------------------
   SHELL & GRID
   ------------------------------------------------------------------------ */
.pc-container {
  width: 100%;
  max-width: var(--pc-max-width);
  margin-inline: auto;
  padding-inline: var(--pc-page-x);
}

.pc-container--text {
  max-width: var(--pc-max-text);
}

.pc-grid {
  display: grid;
  grid-template-columns: repeat(var(--pc-grid-columns), minmax(0, 1fr));
  gap: var(--pc-grid-gap);
}

.pc-section {
  position: relative;
  padding-block: var(--pc-section-y);
}

.pc-section--tight {
  padding-block: var(--pc-section-y-tight);
}

.pc-section--flush-top {
  padding-block-start: 0;
}

/* Section header: folio number + label + rule, the magazine masthead device
   that repeats down every page. */
.pc-section-head {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: var(--pc-s-5);
  margin-block-end: var(--pc-s-9);
}

.pc-section-head__rule {
  height: var(--pc-hairline);
  background: var(--pc-line);
}

/* ---------------------------------------------------------------------------
   HEADER
   ------------------------------------------------------------------------ */
.pc-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--pc-z-header);
  height: var(--pc-header-h);
  display: flex;
  flex-direction: column;
  transition: background-color var(--pc-dur-base) var(--pc-ease),
    backdrop-filter var(--pc-dur-base) var(--pc-ease),
    transform var(--pc-dur-base) var(--pc-ease);
}

/* Solid backing appears once the hero has scrolled away, so the logo never
   sits on a busy video frame. */
.pc-header[data-scrolled='true'] {
  background-color: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-block-end: var(--pc-hairline) solid var(--pc-line);
}

/* Hidden while scrolling down, restored on scroll up. */
.pc-header[data-hidden='true'] {
  transform: translateY(-100%);
}

.pc-header__inner {
  width: 100%;
  height: var(--pc-header-row-h);
  max-width: var(--pc-max-width);
  margin-inline: auto;
  padding-inline: var(--pc-page-x);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pc-s-6);
}

/* ---------------------------------------------------------------------------
   UTILITY TOP BAR
   Email and phone on the left, social on the right. Display is driven by the
   same 760px breakpoint that sets --pc-topbar-h, so the bar and the height it
   reserves can never disagree.
   ------------------------------------------------------------------------ */
.pc-topbar {
  display: none;
  height: var(--pc-topbar-h);
  border-block-end: var(--pc-hairline) solid var(--pc-line);
  background-color: rgba(0, 0, 0, 0.55);
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-wide);
}

@media (min-width: 760px) {
  .pc-topbar {
    display: flex;
    align-items: center;
  }
}

.pc-topbar__inner {
  width: 100%;
  max-width: var(--pc-max-width);
  margin-inline: auto;
  padding-inline: var(--pc-page-x);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pc-s-6);
}

.pc-topbar__contact {
  display: flex;
  align-items: center;
  gap: var(--pc-s-6);
}

.pc-topbar__link,
.pc-topbar__social-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--pc-text-muted);
  text-decoration: none;
  transition: color var(--pc-dur-fast) var(--pc-ease);
}

.pc-topbar__link:hover,
.pc-topbar__link:focus-visible,
.pc-topbar__social-link:hover,
.pc-topbar__social-link:focus-visible {
  color: var(--pc-gold);
}

/* An email address is a literal string; uppercasing it misrepresents it. The
   surrounding bar is uppercase, so this has to opt out explicitly. */
.pc-topbar__link--email {
  text-transform: none;
  letter-spacing: var(--pc-ls-normal);
}

.pc-topbar__social {
  display: flex;
  align-items: center;
  gap: var(--pc-s-4);
  list-style: none;
  margin: 0;
  padding: 0;
}

.pc-topbar__social-link {
  padding: 0.25rem;
}

/* Icons ride slightly high beside uppercase type; nudge them onto the
   optical baseline rather than the metric one. */
.pc-icon {
  flex-shrink: 0;
  transform: translateY(-0.03em);
}

.pc-logo {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-shrink: 0;
}

/* ---------------------------------------------------------------------------
   THE LOCKUP
   The client's own artwork, inlined by partials/logo.php with the wordmark
   converted to outlines — so there is no font to substitute and nothing to
   clip. It is one SVG, sized entirely from --lockup-size: change that single
   value and the whole mark scales at brand proportions.

   The two wordmark lines take their colour from CSS rather than from the file:
   PRESTIGE inherits currentColor and COMMUNICATIONS takes --pc-gold. That is
   what lets the same markup read correctly on the black ground and on a
   .pc-invert cream section, where --pc-gold remaps to the darker --pc-gold-ink.
   ------------------------------------------------------------------------ */
.pc-lockup {
  --lockup-size: 1.9rem;

  display: inline-flex;
  align-items: center;
  font-size: var(--lockup-size);
  line-height: 1;
  flex-shrink: 0;
}

/* 1.42em is what the monogram measured when the wordmark was still HTML, and
   the mark spans the full height of the lockup's viewBox — so holding that
   number keeps the mark exactly the size it has always been on this site and
   lets the wordmark fall in around it at the artwork's own proportions. */
.pc-lockup__svg {
  height: 1.42em;
  width: calc(1.42em * (859.59 / 215.85));
  flex-shrink: 0;
  display: block;
}

.pc-lockup__name {
  fill: currentColor;
}

.pc-lockup__sub {
  fill: var(--pc-gold);
}

/* Monogram-only: the span is sized by its own rule (e.g. .pc-cta__mark sets a
   width) and the ratio keeps the glyph undistorted. */
.pc-lockup--mark {
  display: block;
  aspect-ratio: 163.19 / 215.85;
}

.pc-lockup--mark .pc-lockup__svg {
  height: 100%;
  width: 100%;
}

/* The header has to fit the lockup, the language switch and the menu button on
   a 320px screen, so the mark steps down twice on the way to phone size. */
.pc-header .pc-lockup {
  --lockup-size: 1.35rem;
}

@media (min-width: 420px) {
  .pc-header .pc-lockup {
    --lockup-size: 1.6rem;
  }
}

@media (min-width: 640px) {
  .pc-header .pc-lockup {
    --lockup-size: 2.15rem;
  }
}

.pc-footer .pc-lockup {
  --lockup-size: 2rem;
}

@media (min-width: 640px) {
  .pc-footer .pc-lockup {
    --lockup-size: 2.6rem;
  }
}

/* ---------------------------------------------------------------------------
   PRIMARY NAV (desktop)
   ------------------------------------------------------------------------ */
.pc-nav {
  display: none;
  align-items: center;
  gap: clamp(1.2rem, 2.4vw, 2.6rem);
}

@media (min-width: 1080px) {
  .pc-nav {
    display: flex;
  }
}

.pc-nav__link {
  position: relative;
  font-size: var(--pc-t-small);
  letter-spacing: var(--pc-ls-wide);
  text-transform: uppercase;
  color: var(--pc-text-muted);
  padding-block: 0.5rem;
  transition: color var(--pc-dur-fast) var(--pc-ease);
  white-space: nowrap;
}

.pc-nav__link::after {
  content: '';
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  width: 100%;
  height: var(--pc-hairline);
  background: var(--pc-gold);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform var(--pc-dur-base) var(--pc-ease);
}

.pc-nav__link:hover,
.pc-nav__link:focus-visible {
  color: var(--pc-text);
}

.pc-nav__link:hover::after,
.pc-nav__link:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

.pc-nav__link[aria-current='page'] {
  color: var(--pc-gold);
}

.pc-nav__link[aria-current='page']::after {
  transform: scaleX(1);
}

/* Services submenu ------------------------------------------------------- */
.pc-nav__group {
  position: relative;
}

.pc-nav__panel {
  position: absolute;
  inset-block-start: calc(100% + 1.1rem);
  inset-inline-start: -1.5rem;
  min-width: 17rem;
  padding: 1.4rem 1.5rem;
  background: rgba(6, 6, 6, 0.97);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: var(--pc-hairline) solid var(--pc-line);
  display: grid;
  gap: 0.35rem;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.6rem);
  transition: opacity var(--pc-dur-fast) var(--pc-ease),
    transform var(--pc-dur-fast) var(--pc-ease),
    visibility var(--pc-dur-fast);
}

.pc-nav__group:hover .pc-nav__panel,
.pc-nav__group:focus-within .pc-nav__panel,
.pc-nav__panel[data-open='true'] {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.pc-nav__panel a {
  display: block;
  padding: 0.55rem 0;
  font-size: var(--pc-t-small);
  color: var(--pc-text-muted);
  border-block-end: var(--pc-hairline) solid transparent;
  transition: color var(--pc-dur-fast) var(--pc-ease),
    border-color var(--pc-dur-fast) var(--pc-ease),
    padding-inline-start var(--pc-dur-fast) var(--pc-ease);
}

.pc-nav__panel a:hover,
.pc-nav__panel a:focus-visible {
  color: var(--pc-gold);
  border-block-end-color: var(--pc-line);
  padding-inline-start: 0.5rem;
}

.pc-nav__panel a[aria-current='page'] {
  color: var(--pc-gold);
  border-block-end-color: var(--pc-line-strong);
}

/* ---------------------------------------------------------------------------
   HEADER ACTIONS — language switch + menu toggle
   ------------------------------------------------------------------------ */
.pc-header__actions {
  display: flex;
  align-items: center;
  gap: var(--pc-s-5);
  flex-shrink: 0;
}

.pc-lang {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-wide);
  text-transform: uppercase;
}

.pc-lang__sep {
  color: var(--pc-line-strong);
  user-select: none;
}

.pc-lang__link {
  color: var(--pc-text-dim);
  padding: 0.35rem 0.15rem;
  transition: color var(--pc-dur-fast) var(--pc-ease);
  position: relative;
}

/* The visible glyphs are only 20-22 CSS px wide. That still satisfies WCAG 2.2
   SC 2.5.8, but by way of the spacing exception rather than by size, and on a
   phone it is a fiddly tap for the one control that changes language.
   Enlarging the hit area with a pseudo-element rather than padding keeps the
   header layout — and the measured spacing that earns the exception —
   untouched. The grown boxes still clear each other and the menu toggle. */
.pc-lang__link::after {
  content: '';
  position: absolute;
  inset: -0.35rem -0.45rem;
}

.pc-lang__link:hover,
.pc-lang__link:focus-visible {
  color: var(--pc-gold);
}

.pc-lang__link[aria-current='true'] {
  color: var(--pc-text);
}

.pc-menu-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-wide);
  text-transform: uppercase;
  color: var(--pc-text);
  padding: 0.5rem 0;
  min-height: 44px;
  flex-shrink: 0;
}

/* Below ~560px the word does not fit beside the lockup and the language
   switch. The label stays in the DOM — it is the button's accessible name —
   and is only hidden visually. */
@media (max-width: 559px) {
  .pc-menu-toggle__label {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

@media (min-width: 1080px) {
  .pc-menu-toggle {
    display: none;
  }
}

/* Hamburger ⇄ close, morphing about a single point.
   Both bars are anchored to the vertical centre of the box and pushed apart by
   equal and opposite offsets, so "open" is simply offset 0 for both — they
   land on precisely the same line and the X crosses dead centre. Anchoring one
   bar to `top` and the other to `bottom` instead leaves their centres one
   hairline apart, because `bottom: 0` positions the bar's lower edge. */
.pc-menu-toggle__bars {
  --bar-gap: 0.3rem;

  position: relative;
  width: 1.4rem;
  height: calc(var(--bar-gap) * 2);
  flex-shrink: 0;
}

.pc-menu-toggle__bars span {
  position: absolute;
  inset-inline: 0;
  top: 50%;
  height: var(--pc-hairline);
  margin-block-start: calc(var(--pc-hairline) / -2);
  background: currentColor;
  transform-origin: 50% 50%;
  transition: transform var(--pc-dur-base) var(--pc-ease);
}

.pc-menu-toggle__bars span:first-child {
  transform: translateY(calc(var(--bar-gap) * -1));
}

.pc-menu-toggle__bars span:last-child {
  transform: translateY(var(--bar-gap));
}

.pc-menu-toggle[aria-expanded='true'] .pc-menu-toggle__bars span:first-child {
  transform: translateY(0) rotate(45deg);
}

.pc-menu-toggle[aria-expanded='true'] .pc-menu-toggle__bars span:last-child {
  transform: translateY(0) rotate(-45deg);
}

/* ---------------------------------------------------------------------------
   OVERLAY MENU (mobile / tablet)
   ------------------------------------------------------------------------ */
.pc-menu {
  position: fixed;
  inset: 0;
  z-index: var(--pc-z-menu);
  background: var(--pc-black);
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Top padding clears the header, which floats above this overlay so the
     close button stays visible and clickable. */
  padding: calc(var(--pc-header-h) + 2rem) var(--pc-page-x) 3rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  clip-path: inset(0 0 100% 0);
  visibility: hidden;
  /* visibility is a discrete property: with a plain duration it would flip
     halfway through the animation, and an element that is still
     visibility:hidden cannot receive focus — so opening the menu would fail to
     move focus into it. Instead it switches instantly on open, and is delayed
     until the wipe finishes on close. */
  transition: clip-path var(--pc-dur-slow) var(--pc-ease-out-slow),
    visibility 0s linear var(--pc-dur-slow);
}

.pc-menu[data-open='true'] {
  clip-path: inset(0 0 0 0);
  visibility: visible;
  transition: clip-path var(--pc-dur-slow) var(--pc-ease-out-slow),
    visibility 0s linear 0s;
}

.pc-menu__list {
  display: grid;
  gap: 0.2rem;
}

.pc-menu__item {
  overflow: hidden;
}

.pc-menu__link {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  font-family: var(--pc-font-display);
  font-size: clamp(2.1rem, 9vw, 4rem);
  line-height: 1.18;
  color: var(--pc-text);
  transform: translateY(105%);
  transition: transform var(--pc-dur-slow) var(--pc-ease-out-slow),
    color var(--pc-dur-fast) var(--pc-ease);
}

.pc-menu[data-open='true'] .pc-menu__link {
  transform: translateY(0);
}

.pc-menu__link:hover,
.pc-menu__link:focus-visible {
  color: var(--pc-gold);
}

/* Current page in the overlay menu. Colour alone would fail WCAG 1.4.1, so the
   gold rule carries the meaning and the colour reinforces it. */
.pc-menu__link[aria-current='page'] {
  color: var(--pc-gold);
}

.pc-menu__link[aria-current='page'] .pc-menu__index::after {
  content: '';
  display: inline-block;
  width: 1.6rem;
  height: var(--pc-hairline);
  background: var(--pc-gold);
  vertical-align: middle;
  margin-inline-start: 0.6rem;
}

.pc-menu__index {
  font-family: var(--pc-font-body);
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-wide);
  color: var(--pc-gold);
  flex-shrink: 0;
}

.pc-menu__foot {
  margin-block-start: auto;
  padding-block-start: 3rem;
  display: grid;
  gap: 0.6rem;
  font-size: var(--pc-t-small);
  color: var(--pc-text-muted);
}

/* ---------------------------------------------------------------------------
   BUTTONS
   ------------------------------------------------------------------------ */
.pc-btn {
  --btn-fg: var(--pc-text);
  --btn-line: var(--pc-line-strong);

  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  padding: 1.05rem 2.1rem;
  border: var(--pc-hairline) solid var(--btn-line);
  border-radius: var(--pc-radius);
  color: var(--btn-fg);
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-wide);
  text-transform: uppercase;
  line-height: 1;
  overflow: hidden;
  isolation: isolate;
  background: transparent;
  transition: color var(--pc-dur-base) var(--pc-ease),
    border-color var(--pc-dur-base) var(--pc-ease);
  /* Never smaller than the 44px WCAG 2.2 target minimum. */
  min-height: 44px;
}

/* Gold fill wipes up from the bottom on hover. */
.pc-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--pc-gradient-gold);
  transform: translateY(101%);
  transition: transform var(--pc-dur-base) var(--pc-ease);
}

.pc-btn:hover,
.pc-btn:focus-visible {
  color: #000;
  border-color: var(--pc-gold);
}

.pc-btn:hover::before,
.pc-btn:focus-visible::before {
  transform: translateY(0);
}

.pc-btn__arrow {
  transition: transform var(--pc-dur-base) var(--pc-ease);
}

.pc-btn:hover .pc-btn__arrow {
  transform: translateX(0.35rem);
}

.pc-btn--solid {
  background: var(--pc-gradient-gold);
  border-color: transparent;
  color: #000;
}

.pc-btn--solid::before {
  background: var(--pc-cream);
}

.pc-btn--solid:hover,
.pc-btn--solid:focus-visible {
  color: #000;
}

/* Text link with an animated rule — the workhorse CTA. */
.pc-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-wide);
  text-transform: uppercase;
  color: var(--pc-text);
  padding-block: 0.7rem;
  min-height: 44px;
  transition: color var(--pc-dur-fast) var(--pc-ease);
}

/* For links whose text is a literal that must not be transformed — an email
   address above all. `.pc-link` uppercases by default, which turns
   info@prestigecmn.gr into something the visitor never typed. */
.pc-link--lower {
  text-transform: none;
  letter-spacing: var(--pc-ls-normal);
  font-size: var(--pc-t-body);
}

.pc-link::after {
  content: '';
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  width: 100%;
  height: var(--pc-hairline);
  background: currentColor;
  transform-origin: left;
  transition: transform var(--pc-dur-base) var(--pc-ease);
}

.pc-link:hover,
.pc-link:focus-visible {
  color: var(--pc-gold);
}

.pc-link:hover::after,
.pc-link:focus-visible::after {
  animation: pc-rule-sweep var(--pc-dur-base) var(--pc-ease);
}

@keyframes pc-rule-sweep {
  0% { transform: scaleX(1); transform-origin: right; }
  49% { transform: scaleX(0); transform-origin: right; }
  50% { transform: scaleX(0); transform-origin: left; }
  100% { transform: scaleX(1); transform-origin: left; }
}

.pc-btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pc-s-4);
  align-items: center;
}

/* ---------------------------------------------------------------------------
   FIGURES & BLUR-UP IMAGES
   ------------------------------------------------------------------------ */
.pc-figure {
  position: relative;
  overflow: hidden;
  background-color: var(--pc-surface-raised);
  background-size: cover;
  background-position: center;
}

.pc-figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--pc-dur-slow) var(--pc-ease),
    transform var(--pc-dur-cinematic) var(--pc-ease);
}

.pc-figure img.is-loaded,
.pc-figure img[data-loaded='true'] {
  opacity: 1;
}

/* Blur the tiny placeholder so the swap reads as a focus pull. */
.pc-figure::after {
  content: '';
  position: absolute;
  inset: 0;
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  opacity: 1;
  pointer-events: none;
  transition: opacity var(--pc-dur-slow) var(--pc-ease);
}

.pc-figure:has(img.is-loaded)::after,
.pc-figure:has(img[data-loaded='true'])::after {
  opacity: 0;
}

.pc-figure--4x5 { aspect-ratio: 4 / 5; }
.pc-figure--3x2 { aspect-ratio: 3 / 2; }
.pc-figure--16x9 { aspect-ratio: 16 / 9; }
.pc-figure--1x1 { aspect-ratio: 1 / 1; }

/* Slow zoom on hover for interactive cards. */
.pc-zoom-parent:hover .pc-figure img,
.pc-zoom-parent:focus-visible .pc-figure img {
  transform: scale(1.045);
}

/* ---------------------------------------------------------------------------
   SCROLL-DRIVEN VIDEO
   The <video> is scrubbed by JS (currentTime follows scroll progress), so it
   plays forward as you scroll down and reverses as you scroll up.
   ------------------------------------------------------------------------ */
.pc-scrollvideo {
  position: relative;
  /* Scroll distance the clip is mapped across; longer feels slower. */
  height: var(--sv-height, 320vh);
}

.pc-scrollvideo__sticky {
  position: sticky;
  inset-block-start: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  display: grid;
  place-items: center;
}

.pc-scrollvideo__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

/* The clip fills the pinned frame at every width — `object-fit: cover` above,
   with no narrow-viewport exception.

   There used to be an `object-fit: contain` override below 900px, on the
   grounds that cropping a 16:9 clip to a tall screen magnifies the centre so
   far that the monogram stops reading, and that the letterbox would be
   invisible against the black ground. The second half of that was measured and
   is false: the edge rows of both clips peak at luminance 250 (home) and 219
   (about), so the bar met the footage at a hard, visible seam — the letterbox
   announced itself instead of disappearing.

   Filling the frame is the deliberate call, made knowing the cost. Both clips
   push in as they scrub, and at their widest frame the glyph needs 42.2% of
   the frame width (about) and 35.3% (home) to stay whole — which a centred
   crop only affords down to a viewport aspect of 0.750. Below that the
   monogram is cropped at the sides on the way out of the clip. That is
   accepted: a hero that fills the screen is worth more here than a glyph that
   stays whole in its final second.

   Consequence to keep in mind when editing: the copy now sits over live
   footage at every width rather than over a black letterbox band, so any
   change to the clips, the crop or the scrim needs the contrast re-measured
   per docs/DESIGN-SYSTEM.md — worst local patch, never a frame average. */

/* Without scripting, or with motion reduced, the clip never scrubs and the two
   copy stages are read one after the other instead of crossfading. Release the
   pin so that stacked copy is never clipped by the 100vh overflow:hidden box.
   The media stays absolutely positioned on purpose — making it relative would
   hand it its own implicit grid row and push the copy out of the frame. */
.no-js .pc-scrollvideo,
.pc-scrollvideo[data-scroll-video-static] {
  height: auto;
}

.no-js .pc-scrollvideo__sticky,
.pc-scrollvideo[data-scroll-video-static] .pc-scrollvideo__sticky {
  position: static;
  height: auto;
  min-height: 100svh;
  padding-block: calc(var(--pc-header-h) + var(--pc-s-9)) var(--pc-s-9);
}

@media (prefers-reduced-motion: reduce) {
  .pc-scrollvideo {
    height: auto;
  }

  .pc-scrollvideo__sticky {
    position: static;
    height: auto;
    min-height: 100svh;
    padding-block: calc(var(--pc-header-h) + var(--pc-s-9)) var(--pc-s-9);
  }
}

.pc-scrollvideo__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--pc-scrim);
}

.pc-scrollvideo__content {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--pc-max-width);
  margin-inline: auto;
  padding-inline: var(--pc-page-x);
}

/* Contained (non-full-bleed) variant used where the footage is bright and
   would otherwise fight the text. */
.pc-scrollvideo--framed .pc-scrollvideo__sticky {
  display: grid;
  place-items: center;
  padding: var(--pc-page-x);
}

.pc-scrollvideo--framed .pc-scrollvideo__media {
  position: relative;
  inset: auto;
  width: min(100%, 1180px);
  height: auto;
  aspect-ratio: 16 / 9;
  border: var(--pc-hairline) solid var(--pc-line);
}

/* The sticky box is a grid. In the full-bleed variant the media is absolutely
   positioned, so the copy is the only grid item and centres correctly. Framing
   the clip makes it `position: relative` — at which point the poster, the
   <video> and the copy each claim their own implicit ROW, the box is
   `overflow: hidden` at 100vh, and the copy is pushed straight out of view.
   Pinning every child to the same cell puts them back on top of each other. */
.pc-scrollvideo--framed .pc-scrollvideo__sticky > * {
  grid-area: 1 / 1;
}

/* The framed clip runs bright — molten gold against black. Averaged over the
   copy band it measures a comfortable 9.8:1, but that average is a lie: where a
   line of type crosses one of the bright gold columns the local patch peaks at
   188/255, i.e. 1.7:1, well under the 3:1 AA floor for large text.

   So the scrim is not decorative here. It is an ellipse sized to the copy
   block, darkest where the type sits and fading out so the gold still reads at
   the edges of the frame. At 62-78% black the worst patch lands above 8:1.
   Measure with the crop in docs/DESIGN-SYSTEM.md before weakening it. */
.pc-scrollvideo--framed .pc-scrollvideo__scrim {
  background: radial-gradient(
    ellipse 58% 34% at 50% 50%,
    rgba(0, 0, 0, 0.78) 0%,
    rgba(0, 0, 0, 0.62) 55%,
    rgba(0, 0, 0, 0.15) 100%
  );
}

/* ---------------------------------------------------------------------------
   SCROLL PROGRESS HAIRLINE
   ------------------------------------------------------------------------ */
.pc-progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  z-index: var(--pc-z-banner);
  pointer-events: none;
  background: var(--pc-gradient-gold);
  transform: scaleX(0);
  transform-origin: left;
}

/* ---------------------------------------------------------------------------
   CUSTOM CURSOR (fine pointers only — never on touch)
   ------------------------------------------------------------------------ */
.pc-cursor {
  display: none;
}

@media (hover: hover) and (pointer: fine) {
  .pc-cursor {
    display: block;
    position: fixed;
    inset-block-start: 0;
    inset-inline-start: 0;
    z-index: var(--pc-z-cursor);
    width: 26px;
    height: 26px;
    margin: -13px 0 0 -13px;
    border: var(--pc-hairline) solid var(--pc-gold);
    border-radius: 50%;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--pc-dur-fast) var(--pc-ease),
      width var(--pc-dur-fast) var(--pc-ease),
      height var(--pc-dur-fast) var(--pc-ease),
      margin var(--pc-dur-fast) var(--pc-ease),
      background-color var(--pc-dur-fast) var(--pc-ease);
    will-change: transform;
  }

  .pc-cursor[data-active='true'] {
    opacity: 1;
  }

  .pc-cursor[data-hover='true'] {
    width: 62px;
    height: 62px;
    margin: -31px 0 0 -31px;
    background-color: rgba(191, 155, 92, 0.14);
  }
}

@media (prefers-reduced-motion: reduce) {
  .pc-cursor {
    display: none;
  }
}

/* ---------------------------------------------------------------------------
   FORMS
   ------------------------------------------------------------------------ */
.pc-form {
  display: grid;
  gap: var(--pc-s-6);
}

.pc-fieldset {
  border: 0;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--pc-s-5);
}

.pc-fieldset__legend {
  font-family: var(--pc-font-display);
  font-size: var(--pc-t-d4);
  color: var(--pc-text);
  padding: 0;
  margin-block-end: var(--pc-s-2);
}

.pc-field {
  display: grid;
  gap: 0.55rem;
}

.pc-field__label {
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-wide);
  text-transform: uppercase;
  color: var(--pc-text-muted);
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
}

.pc-field__marker {
  font-size: 0.62rem;
  letter-spacing: var(--pc-ls-wide);
  color: var(--pc-text-dim);
  text-transform: uppercase;
}

.pc-field__control {
  width: 100%;
  padding: 1rem 0;
  min-height: 48px;
  background: transparent;
  border: 0;
  border-block-end: var(--pc-hairline) solid var(--pc-line-strong);
  border-radius: var(--pc-radius);
  color: var(--pc-text);
  font-size: var(--pc-t-body);
  transition: border-color var(--pc-dur-fast) var(--pc-ease);
  appearance: none;
}

.pc-field__control::placeholder {
  color: var(--pc-text-dim);
  opacity: 1;
}

.pc-field__control:hover {
  border-block-end-color: var(--pc-gold);
}

.pc-field__control:focus-visible {
  outline: none;
  border-block-end-color: var(--pc-gold-light);
  box-shadow: 0 1px 0 0 var(--pc-gold-light);
}

textarea.pc-field__control {
  min-height: 9rem;
  resize: vertical;
  line-height: var(--pc-lh-normal);
}

select.pc-field__control {
  cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--pc-gold) 50%),
    linear-gradient(135deg, var(--pc-gold) 50%, transparent 50%);
  background-position: calc(100% - 14px) center, calc(100% - 8px) center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-inline-end: 2rem;
}

select.pc-field__control option {
  background: #0a0a0a;
  color: var(--pc-text);
}

.pc-field__help {
  font-size: 0.78rem;
  color: var(--pc-text-dim);
}

.pc-field__error {
  font-size: 0.8rem;
  color: #ffb4a8; /* 8.4:1 on black — readable, unmistakably an error */
  display: flex;
  align-items: center;
  gap: 0.45rem;
}

.pc-field[data-invalid='true'] .pc-field__control {
  border-block-end-color: #ffb4a8;
}

/* Checkbox / radio ------------------------------------------------------- */
.pc-check {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.85rem;
  align-items: start;
  cursor: pointer;
  font-size: var(--pc-t-small);
  color: var(--pc-text-muted);
  line-height: var(--pc-lh-normal);
  padding-block: 0.35rem;
}

.pc-check input {
  appearance: none;
  width: 1.15rem;
  height: 1.15rem;
  margin: 0.2rem 0 0;
  border: var(--pc-hairline) solid var(--pc-line-strong);
  background: transparent;
  display: grid;
  place-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: border-color var(--pc-dur-fast) var(--pc-ease),
    background-color var(--pc-dur-fast) var(--pc-ease);
}

.pc-check input[type='radio'] {
  border-radius: 50%;
}

.pc-check input::before {
  content: '';
  width: 0.55rem;
  height: 0.55rem;
  transform: scale(0);
  transition: transform var(--pc-dur-fast) var(--pc-ease);
  background: #000;
}

.pc-check input[type='radio']::before {
  border-radius: 50%;
}

.pc-check input:checked {
  background: var(--pc-gold);
  border-color: var(--pc-gold);
}

.pc-check input:checked::before {
  transform: scale(1);
}

.pc-check:hover input {
  border-color: var(--pc-gold);
}

.pc-check-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr));
  gap: 0.4rem 1.5rem;
}

/* Form feedback ---------------------------------------------------------- */
.pc-notice {
  padding: var(--pc-s-6);
  border: var(--pc-hairline) solid var(--pc-line-strong);
  display: grid;
  gap: 0.6rem;
}

.pc-notice--success {
  border-color: var(--pc-gold);
}

.pc-notice--error {
  border-color: #ffb4a8;
}

.pc-notice__title {
  font-family: var(--pc-font-display);
  font-size: var(--pc-t-d3);
  color: var(--pc-text);
}

/* Honeypot — hidden from humans, offered to bots. Not display:none, which
   many bots detect and skip. */
.pc-trap {
  position: absolute !important;
  left: -9999px !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
}

/* ---------------------------------------------------------------------------
   COOKIE BANNER
   ------------------------------------------------------------------------ */
.pc-cookie {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--pc-z-banner);
  background: rgba(6, 6, 6, 0.97);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-block-start: var(--pc-hairline) solid var(--pc-line);
  padding: var(--pc-s-6) 0;
  transform: translateY(110%);
  transition: transform var(--pc-dur-slow) var(--pc-ease-out-slow);
}

.pc-cookie[data-open='true'] {
  transform: translateY(0);
}

.pc-cookie__inner {
  display: grid;
  gap: var(--pc-s-5);
  align-items: center;
}

@media (min-width: 900px) {
  .pc-cookie__inner {
    grid-template-columns: 1fr auto;
    gap: var(--pc-s-8);
  }
}

.pc-cookie__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pc-s-3);
}

/* ---------------------------------------------------------------------------
   FOOTER
   ------------------------------------------------------------------------ */
.pc-footer {
  position: relative;
  border-block-start: var(--pc-hairline) solid var(--pc-line);
  padding-block: var(--pc-s-11) var(--pc-s-7);
}

.pc-footer__grid {
  display: grid;
  gap: var(--pc-s-9);
  grid-template-columns: 1fr;
}

@media (min-width: 720px) {
  .pc-footer__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1080px) {
  .pc-footer__grid {
    grid-template-columns: 1.6fr repeat(3, 1fr);
    gap: var(--pc-s-8);
  }
}

.pc-footer__col h2 {
  font-family: var(--pc-font-body);
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-label);
  text-transform: uppercase;
  color: var(--pc-gold);
  margin-block-end: var(--pc-s-5);
  font-weight: var(--pc-weight-medium);
}

.pc-footer__col ul {
  display: grid;
  gap: 0.15rem;
}

.pc-footer__col a {
  display: inline-block;
  padding-block: 0.4rem;
  font-size: var(--pc-t-small);
  color: var(--pc-text-muted);
  transition: color var(--pc-dur-fast) var(--pc-ease),
    transform var(--pc-dur-fast) var(--pc-ease);
}

.pc-footer__col a:hover,
.pc-footer__col a:focus-visible {
  color: var(--pc-gold);
  transform: translateX(0.3rem);
}

/* ---------------------------------------------------------------------------
   FOOTER ADDRESS BLOCK
   The whole address is one link to Google Maps rather than an embedded map:
   on a phone it hands off to the native Maps app, and it keeps the strict CSP
   and the cookie policy's no-third-party claim intact. See config/app.php.
   ------------------------------------------------------------------------ */
.pc-footer__address {
  font-style: normal;
  margin-block-start: var(--pc-s-6);
}

.pc-footer__maplink {
  display: inline-flex;
  align-items: flex-start;
  gap: 0.6rem;
  font-size: var(--pc-t-body);
  line-height: var(--pc-lh-normal);
  color: var(--pc-text-muted);
  transition: color var(--pc-dur-fast) var(--pc-ease);
}

.pc-footer__maplink .pc-icon {
  margin-block-start: 0.25em;
  color: var(--pc-gold);
}

.pc-footer__maplink:hover,
.pc-footer__maplink:focus-visible {
  color: var(--pc-text);
}

.pc-footer__contactline {
  margin-block-start: var(--pc-s-3);
}

/* The department inbox sits under its label rather than beside it, so a long
   address never forces the column wider than its grid track. */
.pc-footer__inbox {
  display: block;
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-normal);
  color: var(--pc-text-dim);
}

.pc-footer__social a {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}

.pc-footer__bottom {
  margin-block-start: var(--pc-s-10);
  padding-block-start: var(--pc-s-6);
  border-block-start: var(--pc-hairline) solid var(--pc-line-soft);
  display: flex;
  flex-wrap: wrap;
  gap: var(--pc-s-4) var(--pc-s-7);
  align-items: center;
  justify-content: space-between;
  font-size: 0.78rem;
  color: var(--pc-text-dim);
}

.pc-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pc-s-5);
}

.pc-footer__legal a {
  color: var(--pc-text-dim);
  padding-block: 0.3rem;
  transition: color var(--pc-dur-fast) var(--pc-ease);
}

.pc-footer__legal a:hover,
.pc-footer__legal a:focus-visible {
  color: var(--pc-gold);
}

/* ---------------------------------------------------------------------------
   PAGE TRANSITION
   Navigation crossfades <main> — there is no overlay panel.

   An earlier version wiped a full-screen curtain across the viewport. It was
   removed rather than repaired: its resting position was a CSS
   `transform: translateY(100%)`, and GSAP composes `yPercent` on top of an
   existing transform rather than replacing it, so the whole animation sat one
   viewport-height low. The cover ran harmlessly off-screen, the content swap
   was therefore never hidden, and the *exit* leg was the part that actually
   covered the screen — producing a blank panel that slid up after the new page
   was already visible. Keep transforms for this out of CSS if it is ever
   reinstated: let GSAP own them outright.
   ------------------------------------------------------------------------ */
[data-page-fade] {
  transition: opacity var(--pc-dur-fast) var(--pc-ease);
}

/* Belt and braces for the reveal system.
   No stylesheet ever hides a [data-reveal] element — the hidden state is set
   by JS immediately before its tween is built, so a page without scripting
   renders complete. This rule guarantees that even if a reveal is interrupted
   mid-flight (a script error, a killed ScrollTrigger during a page
   transition), nothing is left stranded invisible. */
.no-js [data-reveal],
.no-js [data-reveal] *,
.no-js [data-parallax],
.no-js [data-drift] {
  opacity: 1 !important;
  transform: none !important;
  clip-path: none !important;
  visibility: visible !important;
}

/* Overlay-menu footer: the email keeps its literal casing, and the map link
   aligns its pin with the text baseline. */
.pc-menu__email {
  text-transform: none;
}

.pc-menu__foot a {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

/* ---------------------------------------------------------------------------
   PORTRAIT SCROLL-VIDEO
   The client's activation reel is a 1080x1920 phone clip. Forced through the
   framed variant's 16:9 box it would show as two black bars with a slot of
   footage between them, so the portrait frame is sized off the viewport
   HEIGHT instead — the constraint that actually binds for a 9:16 clip inside
   a 100vh pinned box.
   ------------------------------------------------------------------------ */
.pc-scrollvideo--portrait .pc-scrollvideo__media {
  width: auto;
  height: min(100%, 76vh);
  max-width: 100%;
  aspect-ratio: 9 / 16;
}

/* No copy sits over this clip — the client removed the statement that used to.
   The framed scrim exists to hold contrast for overlaid text; with no text it
   would just be an unexplained dark patch in the middle of the frame. A light
   vignette is all that is wanted, to seat the clip against the page. */
.pc-scrollvideo--portrait .pc-scrollvideo__scrim {
  background: radial-gradient(
    ellipse 74% 74% at 50% 50%,
    rgba(0, 0, 0, 0) 0%,
    rgba(0, 0, 0, 0) 62%,
    rgba(0, 0, 0, 0.35) 100%
  );
}

/* ---------------------------------------------------------------------------
   NATIVE-RATIO FIGURE
   The portfolio masonry keeps each photograph's real proportions; the ratio
   arrives as an inline aspect-ratio from config/images.php, so this only has
   to not impose one of its own.
   ------------------------------------------------------------------------ */
.pc-figure--auto {
  width: 100%;
  height: auto;
}

/* ---------------------------------------------------------------------------
   LIGHTBOX
   Built by assets/js/modules/lightbox.js. Every tile is a real <a> to the
   full-size JPEG, so this is an enhancement over a working link, never the
   only way to reach the picture.
   ------------------------------------------------------------------------ */
.pc-lightbox {
  position: fixed;
  inset: 0;
  z-index: var(--pc-z-lightbox, 400);
  display: grid;
  place-items: center;
  opacity: 0;
  transition: opacity var(--pc-dur-base) var(--pc-ease);
}

.pc-lightbox[data-open='true'] {
  opacity: 1;
}

.pc-lightbox__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.94);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.pc-lightbox__figure {
  position: relative;
  margin: 0;
  padding: clamp(1rem, 4vw, 3rem);
  max-width: 100%;
  max-height: 100%;
  display: grid;
  place-items: center;
}

.pc-lightbox__img {
  max-width: min(100%, 1120px);
  /* Leaves room for the counter and the close control at every height. */
  max-height: calc(100svh - 8rem);
  width: auto;
  height: auto;
  object-fit: contain;
  transform: scale(0.985);
  transition: transform var(--pc-dur-base) var(--pc-ease);
}

.pc-lightbox[data-open='true'] .pc-lightbox__img {
  transform: scale(1);
}

.pc-lightbox__counter {
  position: absolute;
  inset-block-end: clamp(1rem, 3vh, 2rem);
  inset-inline: 0;
  text-align: center;
  font-size: var(--pc-t-label);
  letter-spacing: var(--pc-ls-wide);
  color: var(--pc-text-dim);
}

.pc-lightbox__btn {
  position: absolute;
  display: grid;
  place-items: center;
  /* 48px: comfortably over the 44px minimum target, on a black backdrop where
     the control has no other affordance. */
  width: 48px;
  height: 48px;
  border: var(--pc-hairline) solid var(--pc-line);
  background: rgba(0, 0, 0, 0.5);
  color: var(--pc-text);
  cursor: pointer;
  transition: color var(--pc-dur-fast) var(--pc-ease),
    border-color var(--pc-dur-fast) var(--pc-ease);
}

.pc-lightbox__btn:hover,
.pc-lightbox__btn:focus-visible {
  color: var(--pc-gold);
  border-color: var(--pc-line-strong);
}

.pc-lightbox__btn--close {
  inset-block-start: clamp(0.75rem, 2vh, 1.5rem);
  inset-inline-end: clamp(0.75rem, 2vw, 1.5rem);
}

.pc-lightbox__btn--prev {
  inset-inline-start: clamp(0.75rem, 2vw, 1.5rem);
  inset-block-start: 50%;
  transform: translateY(-50%);
}

.pc-lightbox__btn--next {
  inset-inline-end: clamp(0.75rem, 2vw, 1.5rem);
  inset-block-start: 50%;
  transform: translateY(-50%);
}

/* The page behind must not scroll while the overlay is up. Lenis is stopped in
   JS as well; this covers the native path when Lenis is not running. */
[data-lightbox='open'] body {
  overflow: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .pc-lightbox,
  .pc-lightbox__img {
    transition: none;
  }
}

/* ---------------------------------------------------------------------------
   PRELOADER
   The stacked lockup on black, once per browser session. CSS owns every stage
   of it including the exit, so the veil retires whether or not app.js ever
   evaluates; modules/preloader.js only watches. See partials/preloader.php.

   Nothing here applies until boot-gate.js sets data-preloader="run" on <html>,
   which is what keeps a scripting-less visit free of a curtain that has
   nothing left to draw it back.

   THE TIMELINE, in one place. Every number below is a multiple of --pl-t, so
   the whole sequence retimes from that single value.

       60ms   mark        resolves out of a soft defocus, 0.86 -> 1
      200ms   glow        gold bloom rises behind it
      320ms   PRESTIGE    letters lift and fade in, 26ms apart
      480ms   rule        hairline draws outward from the centre
      540ms   COMMUNI...  glyphs fade in, 15ms apart
      900ms   art         brightness catch — the mark takes the light
     1120ms   stage       lifts away
     1150ms   veil        wipes off the top, revealing the hero first
     1670ms   ends

   The brightness catch is deliberately a filter rather than a swept gradient
   band: brightness leaves black at black, so it lights the artwork and only
   the artwork, with no bar tracking across the empty ground.
   ------------------------------------------------------------------------ */
.pc-preloader {
  display: none;
}

[data-preloader='run'] .pc-preloader {
  /* Retime the whole sequence from here: 0.8 makes it brisk, 1.3 stately. */
  --pl-t: 1;
  /* And resize it from here. The rule under the lockup is derived from this
     rather than clamped separately, so the two cannot drift apart. */
  --pl-art-w: clamp(11rem, 25vw, 19rem);

  position: fixed;
  inset: 0;
  z-index: var(--pc-z-preloader);
  display: grid;
  place-items: center;
  background: var(--pc-black);
  animation: pc-pl-veil calc(520ms * var(--pl-t)) var(--pc-ease)
    calc(1150ms * var(--pl-t)) forwards;
}

.pc-preloader__glow {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(140vw, 64rem);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background: radial-gradient(
    circle,
    rgba(191, 155, 92, 0.17) 0%,
    rgba(191, 155, 92, 0.06) 36%,
    transparent 68%
  );
  opacity: 0;
  animation: pc-pl-glow calc(700ms * var(--pl-t)) var(--pc-ease-out-slow)
    calc(200ms * var(--pl-t)) forwards;
}

.pc-preloader__stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(1.25rem, 2.6vw, 2.25rem);
  animation: pc-pl-stage calc(400ms * var(--pl-t)) var(--pc-ease-in-out)
    calc(1120ms * var(--pl-t)) forwards;
}

.pc-preloader__art {
  width: var(--pl-art-w);
  height: auto;
  display: block;
  animation: pc-pl-catch calc(320ms * var(--pl-t)) var(--pc-ease-in-out)
    calc(900ms * var(--pl-t)) both;
}

/* fill-box so the scale pivots on the mark's own centre rather than on the
   whole 791x579 canvas, which would swing it across the frame as it grows. */
.pc-preloader__mark {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  opacity: 0;
  animation: pc-pl-mark calc(600ms * var(--pl-t)) var(--pc-ease-out-slow)
    calc(60ms * var(--pl-t)) forwards;
}

.pc-preloader__name {
  fill: var(--pc-cream);
}

.pc-preloader__sub {
  fill: var(--pc-gold);
}

/* --i is written onto each glyph by partials/preloader.php, so one rule stages
   an entire line and the stagger is a single number to tune. */
.pc-preloader__letter {
  transform-box: fill-box;
  opacity: 0;
  animation: pc-pl-letter calc(620ms * var(--pl-t)) var(--pc-ease-out-slow)
    calc((320ms + var(--i) * 26ms) * var(--pl-t)) forwards;
}

.pc-preloader__glyph {
  opacity: 0;
  animation: pc-pl-glyph calc(520ms * var(--pl-t)) var(--pc-ease)
    calc((540ms + var(--i) * 15ms) * var(--pl-t)) forwards;
}

.pc-preloader__rule {
  /* Just under the width COMMUNICATIONS sets — it reads as the lockup's own
     baseline rather than as a separate graphic element. */
  width: calc(var(--pl-art-w) * 0.36);
  height: var(--pc-hairline);
  background: var(--pc-gradient-gold);
  transform: scaleX(0);
  animation: pc-pl-rule calc(560ms * var(--pl-t)) var(--pc-ease-out-slow)
    calc(480ms * var(--pl-t)) forwards;
}

@keyframes pc-pl-mark {
  from { opacity: 0; transform: scale(0.86); filter: blur(6px); }
  to   { opacity: 1; transform: scale(1);    filter: blur(0); }
}

@keyframes pc-pl-glow {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes pc-pl-letter {
  from { opacity: 0; transform: translateY(38%); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes pc-pl-glyph {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes pc-pl-rule {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes pc-pl-catch {
  0%   { filter: brightness(1); }
  45%  { filter: brightness(1.42); }
  100% { filter: brightness(1); }
}

@keyframes pc-pl-stage {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-1.75rem); }
}

/* inset() from the top, so the hero is uncovered first rather than last.
   The final frame is what makes the veil genuinely gone — visibility, not just
   a transparent layer still swallowing clicks. */
@keyframes pc-pl-veil {
  from { clip-path: inset(0 0 0 0);    visibility: visible; }
  99%  { clip-path: inset(100% 0 0 0); visibility: visible; }
  to   { clip-path: inset(100% 0 0 0); visibility: hidden; }
}

/* Reduced motion: nothing travels, nothing scales, nothing blurs. The lockup
   is simply present for a beat and the veil fades. Same guarantee, 0.7s. */
@media (prefers-reduced-motion: reduce) {
  [data-preloader='run'] .pc-preloader {
    animation: pc-pl-veil-fade 420ms linear 260ms forwards;
  }

  .pc-preloader__glow,
  .pc-preloader__stage,
  .pc-preloader__art,
  .pc-preloader__mark,
  .pc-preloader__letter,
  .pc-preloader__glyph,
  .pc-preloader__rule {
    animation: none;
    opacity: 1;
    transform: none;
    filter: none;
  }

  @keyframes pc-pl-veil-fade {
    from { opacity: 1; visibility: visible; }
    99%  { opacity: 0; visibility: visible; }
    to   { opacity: 0; visibility: hidden; }
  }
}
