/* ==========================================================================
   Motion layer: veil, split headings, clip reveals, marquee, gallery,
   process line, quote banners, back-to-top, tilt and magnet hooks.
   ========================================================================== */

/* page veil: intro on first visit, quick fade between pages */
.veil {
  position: fixed; inset: 0; z-index: 300; background: var(--navy-950);
  display: none; place-items: center; pointer-events: none;
}
.js .veil { display: grid; }
.veil-mark {
  position: relative; font: 400 clamp(2.4rem, 6vw, 4.2rem) var(--display); letter-spacing: .22em; padding-left: .22em;
  color: var(--gold-300); opacity: 0;
}
.veil-mark::before {
  content: ''; position: absolute; inset: -30% -36%; border-radius: 50%;
  border: 1px solid rgba(201, 162, 76, .45); box-shadow: 0 0 0 10px rgba(201, 162, 76, .06);
}
/* The crest's entrance, the same 0.4 s effects.js used to play. There it had
   to wait for the animation library to download, and on a phone the navy
   sheet stood empty for seconds first; here it starts with the first paint and
   effects.js carries on from its end. A visit that has already seen the intro
   (html.veil-seen, set in base.html) gets the quick fade and no crest. */
@media (prefers-reduced-motion: no-preference) {
  .js:not(.veil-seen) .veil-mark { animation: veil-mark-in .4s cubic-bezier(.2, .7, .2, 1) both; }
}
@keyframes veil-mark-in {
  from { opacity: 0; transform: scale(.86); }
  to { opacity: 1; transform: scale(1); }
}

/* headings revealed word by word from behind a mask */
.is-split .w { display: inline-block; overflow: hidden; vertical-align: bottom; padding: 0 .06em .12em 0; margin: 0 -.06em -.12em 0; }
.is-split .wi { display: inline-block; transform: translateY(112%); will-change: transform; }

/* photographs wiped in from the left */
.js [data-clip] { clip-path: inset(0 100% 0 0); }
.img-frame-inner { width: 100%; height: 115%; will-change: transform; }
.img-frame-inner img { width: 100%; height: 100%; object-fit: cover; }

/* maxims marquee */
.marquee { overflow: hidden; padding: 1.15rem 0; border-block: 1px solid var(--line-dark); background: var(--navy-950); }
.marquee-track { display: flex; align-items: center; gap: 3.2rem; width: max-content; white-space: nowrap; will-change: transform; }
.marquee-track span { font: italic 500 1.35rem var(--serif); color: var(--gold-300); display: inline-flex; align-items: center; gap: 1rem; }
.marquee-track span i { font: 600 .68rem var(--sans); letter-spacing: .24em; text-transform: uppercase; font-style: normal; color: var(--muted); }
.marquee-track span::after { content: '✦'; color: var(--gold); font-style: normal; font-size: .75rem; margin-left: 2.2rem; }

/* pinned horizontal gallery */
.gallery { position: relative; height: 260vh; background: var(--navy-950); }
/* The height of the panel is a custom property because the pictures below are
   sized from it: adminbar.css shortens the panel by the bar, and the pictures
   have to shrink with it or the captions fall off the bottom edge. */
.gallery-sticky {
  --gallery-h: 100svh;
  position: sticky; top: 0; height: 100vh; height: var(--gallery-h); overflow: hidden;
  display: flex; flex-direction: column; justify-content: center; gap: 2.4rem; padding-top: 2rem;
}
.gallery-head { max-width: 40rem; }
.gallery-head h2 { margin-bottom: 0; }
.gallery-track { display: flex; gap: 1.6rem; padding-inline: 4vw; will-change: transform; }
/* The panel is pinned to one viewport and clips what will not fit, so the item
   cannot be sized from width alone: a wide but short screen - 1366x768 and
   1440x800, the two commonest laptops - made the 4:5 picture tall enough to push
   the caption out of sight. The height term caps it. 20.5rem is the head, the
   gap, the top padding and the caption, plus room to breathe below it; the 1.25
   converts picture height to width. In the height-bound regime the clearance
   below the caption is 20.5rem minus that furniture whatever the screen, so it
   is the one number to change if the captions sit too close to the edge. */
.gallery-item {
  flex: none; margin: 0;
  width: clamp(260px, min(30vw, calc((var(--gallery-h, 100svh) - 20.5rem) / 1.25)), 500px);
}
.gallery-item .gallery-img { position: relative; overflow: hidden; border-radius: 4px; aspect-ratio: 4 / 5; }
.gallery-item .gallery-img::after { content: ''; position: absolute; inset: 1rem; border: 1px solid rgba(201, 162, 76, .45); border-radius: 2px; pointer-events: none; }
.gallery-item img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.2s var(--ease); }
.gallery-item:hover img { transform: scale(1.06); }
.gallery-item figcaption { margin-top: 1rem; font: 600 .72rem var(--sans); letter-spacing: .22em; text-transform: uppercase; color: var(--gold); }
.gallery-item figcaption span { display: block; font: italic 500 1.1rem var(--serif); letter-spacing: 0; text-transform: none; color: var(--muted); margin-top: .25rem; }

/* how a matter proceeds */
.process { background: var(--paper); color: var(--ink); padding: clamp(5rem, 9vw, 8.5rem) 0; }
.process-grid { display: grid; grid-template-columns: .9fr 1.1fr; gap: clamp(2.5rem, 6vw, 5rem); align-items: start; }
.process-head { position: sticky; top: 7rem; }
.process-head .lead { color: var(--ink-2); }
.process-head .btn { margin-top: .6rem; }
.process-track { position: relative; padding-left: 3.4rem; }
.process-track::before { content: ''; position: absolute; left: .95rem; top: 0; bottom: 0; width: 1px; background: var(--line-light); }
.process-line { position: absolute; left: .95rem; top: 0; bottom: 0; width: 1px; background: var(--gold); transform-origin: top; transform: scaleY(0); }
.process-steps { list-style: none; margin: 0; padding: 0; counter-reset: s; }
.process-steps li { position: relative; padding: 1.5rem 0 1.7rem; border-bottom: 1px solid var(--line-light); opacity: .38; transition: opacity .6s; }
.process-steps li.is-on { opacity: 1; }
.process-steps li::before {
  counter-increment: s; content: counter(s, upper-roman);
  position: absolute; left: -3.4rem; top: 1.45rem; width: 1.9rem; height: 1.9rem; border-radius: 50%;
  display: grid; place-items: center; font: 400 .8rem var(--display); color: var(--gold-700);
  background: var(--paper); border: 1px solid var(--gold); transition: background .4s, color .4s, box-shadow .4s;
}
.process-steps li.is-on::before { background: var(--gold); color: var(--navy-950); box-shadow: 0 0 0 6px rgba(201, 162, 76, .18); }
.process-steps h3 { font-size: 1.5rem; margin-bottom: .4rem; color: var(--ink); }
.process-steps p { margin: 0; color: var(--ink-2); max-width: 34rem; }

/* full-bleed quote banner */
.banner { position: relative; min-height: 68vh; display: grid; place-items: center; overflow: hidden; background: var(--black); text-align: center; padding: 6rem 0; }
.banner-media { position: absolute; inset: -14% 0; will-change: transform; }
.banner-media img { width: 100%; height: 100%; object-fit: cover; }
.banner::after { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(7, 13, 26, .8), rgba(7, 13, 26, .5) 50%, rgba(7, 13, 26, .88)); }
.banner-copy { position: relative; z-index: 2; max-width: 54rem; }
.banner-quote { font: italic 500 clamp(1.9rem, 4.2vw, 3.4rem)/1.22 var(--serif); color: var(--ivory); margin: 0 0 1.4rem; }
.banner-quote em { font-style: normal; color: var(--gold-300); }
.banner-cite { display: inline-flex; align-items: center; gap: 1rem; font: 600 .72rem var(--sans); letter-spacing: .28em; text-transform: uppercase; color: var(--gold); margin: 0; }
.banner-cite::before, .banner-cite::after { content: ''; width: 2.4rem; height: 1px; background: var(--gold); }

/* ---- a maxim band that turns ------------------------------------------------
   Each picture is its own panel and the next one slides in over the last, so
   the change reads as a panel moving rather than a dissolve. The panels are
   positioned by transform alone, which means the markup works untouched with
   no JavaScript: the first is at rest, the rest are parked off-stage. */
.banner-stage { position: absolute; inset: 0; overflow: hidden; }
.banner-slider .banner-media { position: absolute; inset: -14% 0; }
.banner-panel { position: absolute; inset: 0; transform: translateX(100%); will-change: transform; }
.banner-panel.is-on { transform: none; }
/* a hairline of gold on the leading edge, shown only while a panel is moving */
.banner-panel::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: -2px; width: 2px; z-index: 1;
  background: linear-gradient(180deg, transparent, var(--gold), transparent);
  opacity: 0; transition: opacity .18s;
}
.banner-panel.is-sliding::before { opacity: .85; }

/* the sets of words are stacked on one another, one lit at a time. They are
   aligned to the top of the cell so the maxims do not shuffle up and down as
   the band turns: the Latin runs to one line under some and three under
   others, and the eye reads that movement as a fault. */
.banner-slider .banner-copy { display: grid; align-items: start; }
.banner-words { grid-area: 1 / 1; margin: 0; opacity: 0; }
.banner-words.is-on { opacity: 1; }
/* one Latin line runs to seventy characters, so it has to be able to wrap -
   the flanking rules keep their length while the words between them give */
.banner-slider .banner-cite { max-width: 100%; letter-spacing: .2em; }
.banner-slider .banner-cite::before, .banner-slider .banner-cite::after { flex: none; }
.banner-source {
  margin: .85rem 0 0; font: 400 .72rem/1.5 var(--sans);
  letter-spacing: .04em; color: rgba(240, 236, 228, .5);
}

.banner-nav {
  position: absolute; z-index: 3; left: 0; right: 0; bottom: 2.1rem;
  display: flex; gap: .55rem; justify-content: center;
}
.banner-dot {
  -webkit-appearance: none; appearance: none; border: 0; background: none;
  width: 3.1rem; padding: .85rem .25rem; cursor: pointer;
}
.banner-dot-rule {
  display: block; position: relative; height: 2px; overflow: hidden;
  background: rgba(240, 236, 228, .26); transition: background .25s;
}
.banner-dot:hover .banner-dot-rule, .banner-dot.is-on .banner-dot-rule { background: rgba(240, 236, 228, .42); }
.banner-dot-fill {
  position: absolute; inset: 0; background: var(--gold);
  transform: scaleX(0); transform-origin: left;
}
.banner-dot.is-on .banner-dot-fill { transform: scaleX(1); }
.banner-dot:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .banner-panel { transition: none; }
  .banner-panel::before { display: none; }
}

@media (max-width: 720px) {
  .banner-nav { bottom: 1.3rem; gap: .4rem; }
  /* the rule is 2px, so the padding is what a thumb actually lands on */
  .banner-dot { width: 2.2rem; padding: 1.25rem .2rem; }
  /* a 16:9 picture in a tall band is cropped to a sliver of its middle, which
     on these panels is the part the light was kept out of. Let the band sit
     closer to the shape of the picture and drop the parallax allowance, so
     what shows on a phone is a slab of the scene rather than its darkest inch. */
  .banner.banner-slider { min-height: 0; padding: 4.4rem 0 5.4rem; }
  .banner-slider .banner-media { inset: 0; }
  .banner-slider .banner-cite { font-size: .64rem; letter-spacing: .14em; gap: .6rem; }
  .banner-slider .banner-cite::before, .banner-slider .banner-cite::after { width: 1.3rem; }
  .banner-source { font-size: .68rem; margin-top: .7rem; }
}

/* back to top */
.totop {
  position: fixed; right: 1.6rem; bottom: 1.6rem; z-index: 100; width: 48px; height: 48px; border-radius: 50%;
  border: 1px solid var(--gold); background: rgba(7, 13, 26, .85); color: var(--gold-300);
  cursor: pointer; display: grid; place-items: center; opacity: 0; transform: translateY(16px); pointer-events: none;
  transition: opacity .35s, transform .35s var(--ease), background .3s, color .3s;
}
.totop svg { width: 18px; height: 18px; }
.totop.is-visible { opacity: 1; transform: none; pointer-events: auto; }
.totop:hover { background: var(--gold); color: var(--navy-950); }

/* header that steps aside while reading, returns on scroll up */
.nav { transition: background .4s, padding .4s, border-color .4s, transform .45s var(--ease); }
.nav.is-hidden { transform: translateY(-100%); }

/* tilt targets */
.card, .area, .result, .quote, .side-card { transform-style: preserve-3d; }
.is-tilting { transition: transform .12s linear, box-shadow .4s var(--ease), border-color .4s !important; }

@media (max-width: 1100px) {
  .process-grid { grid-template-columns: 1fr; }
  .process-head { position: static; }
}
@media (max-width: 900px) {
  .gallery { height: auto; padding: 5rem 0; }
  .gallery-sticky { position: static; height: auto; overflow: visible; }
  .gallery-track { overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: 1rem; transform: none !important; }
  .gallery-item { scroll-snap-align: start; width: 76vw; }
  .banner { min-height: 56vh; }
  .totop { right: 1rem; bottom: 1rem; }
}
@media (prefers-reduced-motion: reduce) {
  .js [data-clip] { clip-path: none; }
  .is-split .wi { transform: none; }
  .marquee-track { transform: none !important; }
  .process-steps li { opacity: 1; }
  .nav.is-hidden { transform: none; }
  .js .veil { display: none; }
}

/* practice tiles: photographs with the maxim, name and a summary that rises on hover */
.tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.3rem; }
.tile {
  position: relative; display: block; aspect-ratio: 4 / 5; overflow: hidden; border-radius: 4px;
  background: var(--navy-950); color: var(--ivory); text-decoration: none; isolation: isolate;
}
.js .tile { clip-path: inset(100% 0 0 0); }
.tile-media { position: absolute; inset: 0; z-index: 0; }
.tile-media img { width: 100%; height: 100%; object-fit: cover; transform: scale(1.04); transition: transform 1.4s var(--ease); will-change: transform; }
.tile::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, rgba(7, 13, 26, .05) 0%, rgba(7, 13, 26, .25) 45%, rgba(7, 13, 26, .92) 100%);
  transition: background .6s;
}
.tile:hover::after { background: linear-gradient(180deg, rgba(7, 13, 26, .2) 0%, rgba(7, 13, 26, .55) 40%, rgba(7, 13, 26, .96) 100%); }
.tile:hover .tile-media img { transform: scale(1.12); }
.tile-frame {
  position: absolute; inset: 1rem; z-index: 2; pointer-events: none; border-radius: 2px;
  border: 1px solid rgba(201, 162, 76, .45); transition: inset .6s var(--ease), border-color .6s;
}
.tile:hover .tile-frame { inset: .65rem; border-color: rgba(228, 199, 123, .85); }
.tile-body { position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; padding: 2rem 2rem 1.9rem; display: flex; flex-direction: column; }
.tile-maxim { font: italic 600 1.05rem var(--serif); color: var(--gold-300); margin-bottom: .5rem; }
.tile-name { font: 400 1.65rem/1.15 var(--display); color: var(--ivory); margin-bottom: .6rem; }
.tile-text {
  font-size: .96rem; line-height: 1.55; color: rgba(244, 239, 228, .82);
  max-height: 0; opacity: 0; overflow: hidden; transform: translateY(10px);
  transition: max-height .6s var(--ease), opacity .45s, transform .6s var(--ease);
}
.tile:hover .tile-text, .tile:focus-visible .tile-text { max-height: 9rem; opacity: 1; transform: none; }
.tile-link { display: inline-flex; align-items: center; gap: .5rem; margin-top: .9rem; font: 700 .72rem var(--sans); letter-spacing: .2em; text-transform: uppercase; color: var(--gold-300); }
.tile-link i { font-style: normal; transition: transform .3s var(--ease); }
.tile:hover .tile-link i { transform: translateX(6px); }
@media (max-width: 1100px) { .tiles { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px) { .tiles { grid-template-columns: 1fr; } .tile { aspect-ratio: 4 / 4.4; } }
@media (hover: none) { .tile-text { max-height: 9rem; opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .js .tile { clip-path: none; } .tile-media img, .tile-frame, .tile-text { transition: none; } }
