/*
 * The client-facing surface. Everything here is themed from the Client row via
 * --client-* custom properties set inline on <body>, so adding a client is a
 * database insert rather than a stylesheet edit.
 */

/* The theme is the client's decision, never the visitor's OS: repainting a
   white-labelled page to someone's dark mode pairs their accent with a
   background they never approved. */
.client[data-theme="dark"] {
  --ink: #ececec;
  --ink-soft: #a0a0a0;
  --rule: #333;
  --page: #141414;
  --panel: #1e1e1e;
}

.client[data-heading="serif"] { --font-heading: var(--font-serif); }

/* Client overrides land last so a half-filled brand kit still coheres. */
.client {
  background: var(--client-page, var(--page));
  color: var(--client-ink, var(--ink));
}

.client h1, .client h2, .client .hero__title { font-family: var(--font-heading); }

/* --- Hero ----------------------------------------------------------------- */

.hero {
  border-bottom: 3px solid var(--client-accent);
  background-color: var(--client-accent);
  background-size: cover;
  background-position: center;
}

.hero__inner {
  max-width: 1140px;
  margin: 0 auto;
  padding: 2.5rem 1.5rem;
  color: var(--client-accent-contrast);
}

/* The height tracks the width, and that is the whole point. A hero with a fixed
   height gets a wider and wider box as the viewport grows, and `cover` answers
   by scaling the image up and cropping more off the top and bottom — so St
   Patrick's church kept its spires in the preview iframe and lost them on a
   full-width monitor. Same page, cropped two different ways.

   With an aspect ratio the crop is identical at every width, which is what
   makes the preview trustworthy. min-height stops it collapsing to a strip on a
   phone, where the ratio would otherwise win; max-height stops a very wide
   monitor turning it into a billboard, and is the only width where some extra
   crop comes back. */
.hero--image {
  position: relative;

  /* width: 100% is load-bearing, not tidiness. With width left auto, the
     max-height clamp feeds back through the ratio and shrinks the *width* —
     the hero quietly stops being full-bleed and leaves a band of page colour
     down one side. A definite width means the ratio only ever decides height. */
  width: 100%;
  aspect-ratio: 25 / 9;
  min-height: 340px;
  max-height: 520px;

  /* Biased well above centre because the subject of a hero photograph is
     usually in its upper half — a church keeps its spires, a skyline its
     towers — and what gets given up is the foreground, which is the least of
     any of these pictures. It also decides what survives at the two sizes
     where the ratio cannot hold: very wide, where max-height clamps, and very
     narrow, where min-height does. 22% rather than centre is the difference
     between a complete church and a beheaded one on a wide monitor. */
  background-position: center 22%;
}

.hero--image .hero__inner {
  position: relative;
  height: 100%;
  padding: 2.5rem 1.5rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.6);
}

/* The scrim follows the text rather than washing the whole picture: heaviest
   behind the words, fading to nothing over the part of the photograph worth
   seeing. A flat top-to-bottom gradient does one of those jobs badly. */
.hero--image::before { content: ""; position: absolute; inset: 0; }

.hero--left.hero--image::before {
  background: linear-gradient(100deg,
    rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.5) 42%, rgba(0, 0, 0, 0.08) 78%, rgba(0, 0, 0, 0) 100%);
}

.hero--right.hero--image::before {
  background: linear-gradient(260deg,
    rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.5) 42%, rgba(0, 0, 0, 0.08) 78%, rgba(0, 0, 0, 0) 100%);
}

.hero--center.hero--image::before {
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0.55));
}

.hero--right .hero__inner { text-align: right; align-items: flex-end; }
.hero--center .hero__inner { text-align: center; align-items: center; }
.hero--right .hero__body, .hero--center .hero__body { margin-left: auto; }
.hero--center .hero__body { margin-right: auto; }

/* Sized against the band rather than in the abstract. The hero's height now
   tracks its width, so type set for the old fixed 395px strip was left looking
   stranded in a 520px one — these ceilings keep the title at roughly a tenth of
   the hero's height across the range, which is what reads as deliberate. The vw
   term measures the iframe when previewed, so the preview scales with it. */
.hero__title {
  display: inline-block;
  font-size: clamp(1.9rem, 4.4vw, 3.75rem);
  /* 800 where the face has it — Kepler and most system stacks do. A display
     line at 700 next to 400 body is a smaller step than it looks at size. */
  font-weight: 800;
  line-height: 1.08;
  /* Tracking that suits 16px is loose at 60px. Negative at display size is what
     makes a headline read as one shape rather than a row of letters. */
  letter-spacing: -0.025em;
  color: inherit;
  text-decoration: none;
  text-wrap: balance;
}

/* The tagline is a subhead, not more body copy. Weight and a little tracking
   separate it from the sentence below without another size step, which is what
   was missing — title, tagline and body all read as one block. */
.hero__tagline {
  margin: 0.7rem 0 0;
  font-size: clamp(1.05rem, 1.6vw, 1.45rem);
  font-weight: 500;
  letter-spacing: 0.005em;
  /* Not opacity. Fading white text over a photograph greys it and drops the
     contrast the scrim was there to protect; a solid near-white keeps the
     hierarchy without the wash. */
  color: rgba(255, 255, 255, 0.95);
}

.hero--image .hero__tagline { color: rgba(255, 255, 255, 0.95); }
.hero--image .hero__body { color: rgba(255, 255, 255, 0.88); }

.hero__body {
  margin: 0.9rem 0 0;
  max-width: 58ch;
  font-size: clamp(0.95rem, 1.2vw, 1.1rem);
  line-height: 1.5;
}

/* A mark above the name. Bigger than it was, because 72px was sized for a logo
   that stood in for the title; as a mark sitting over one it can afford
   presence. */
.hero__logo { display: inline-block; margin-bottom: 0.85rem; }
.hero__logo img {
  max-height: 96px;
  max-width: min(100%, 320px);
  width: auto;
  display: block;
  /* Most client logos are drawn for white. Over a photograph they need the same
     lift the text gets, or they sit flat against it. */
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.45));
}

/* A sideways fade needs width to work. On a phone there isn't any, so the
   gradient goes vertical and the text returns to the left — an angled scrim at
   400px just darkens everything unevenly. Must come after the alignment rules:
   same specificity, so source order decides. */
@media (max-width: 700px) {
  .hero--image::before,
  .hero--left.hero--image::before,
  .hero--right.hero--image::before {
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.62));
  }

  .hero--right .hero__inner,
  .hero--center .hero__inner { text-align: left; align-items: flex-start; }
  .hero--right .hero__body, .hero--center .hero__body { margin-left: 0; }
  .hero--image .hero__inner { padding: 3.5rem 1.25rem; min-height: 240px; }
}

/* --- Layout --------------------------------------------------------------- */

.client__main { max-width: 1140px; margin: 0 auto; padding: 2.5rem 1.5rem 3rem; }

.client__footer {
  padding: 1.5rem;
  border-top: 1px solid var(--rule);
  color: var(--ink-soft);
  font-size: 0.85rem;
  text-align: center;
}

/* A separator drawn in CSS rather than typed between the two items, so it
   appears only when there are in fact two — a client who hides the platform
   credit gets "Log in" alone, not a stray bullet in front of it. */
.client__footerline {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}

.client__footerline > * + *::before {
  content: "·";
  margin-right: 0.6rem;
  opacity: 0.6;
}

/* --- Video cards ---------------------------------------------------------- */

/* auto-fit rather than a fixed three, so a client with two videos doesn't leave
   a ragged hole. */
.grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}

@media (min-width: 900px) {
  .grid { grid-template-columns: repeat(3, 1fr); }
}

.card__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  color: inherit;
  text-decoration: none;
  border: 1px solid var(--rule);
  border-radius: 10px;
  overflow: hidden;
  background: var(--page);
  transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;
}

.card__link:hover, .card__link:focus-visible {
  border-color: var(--client-accent);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.09);
}

/* A 16:9 box so a missing thumbnail doesn't change the layout. */
.card__thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--panel);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.card__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card__placeholder { font-size: 1.75rem; color: var(--ink-soft); opacity: 0.5; }

.card__duration {
  position: absolute;
  right: 0.5rem;
  bottom: 0.5rem;
  padding: 0.1rem 0.4rem;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.75);
  color: #fff;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.card__body { padding: 0.9rem 1rem 1.1rem; }
.card__title { margin: 0; font-size: 1.05rem; line-height: 1.35; }
.card__blurb { margin: 0.4rem 0 0; color: var(--ink-soft); font-size: 0.9rem; }

/* --- One video ------------------------------------------------------------ */

.video__title { font-size: 1.5rem; margin: 0 0 1rem; }

/* Intrinsic ratio keeps the iframe responsive without JS. */
.video__frame {
  position: relative;
  padding-bottom: 56.25%;
  height: 0;
  overflow: hidden;
  background: #000;
  border-radius: var(--radius);

  /* A poster is usually the client's own title card in the client's own
     colours, so the player can sit cream-on-cream against the page and lose its
     edges entirely — St Patrick's does exactly that. The hairline ring is the
     part that fixes it: a soft shadow alone still leaves the boundary a guess,
     while a 1px ring says where the video stops whatever the poster looks like.
     The long shadow beneath then lifts it off the page. */
  box-shadow:
    0 0 0 1px rgb(0 0 0 / 0.12),
    0 2px 6px rgb(0 0 0 / 0.16),
    0 20px 50px -12px rgb(0 0 0 / 0.45);
}

.video__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video__description { margin-top: 1.5rem; }
.video__back { margin-top: 2rem; }

/* --- Stats a client's own audience sees ----------------------------------- */

.stats__caveat {
  background: var(--panel);
  border-left: 3px solid var(--client-accent);
  padding: 1rem 1.25rem;
  font-size: 0.92rem;
}

.stats__caveat h2 { margin: 0 0 0.5rem; font-size: 1rem; }
.stats__caveat p { margin: 0 0 0.5rem; }

/* --- The passcode gate ---------------------------------------------------- */

.unlock { max-width: 420px; margin: 3rem auto; padding: 0 1.5rem; }
.unlock h1 { font-size: 1.4rem; margin-bottom: 0.75rem; }
.unlock .errors { margin-bottom: 1rem; }

/* --- Filters and sections -------------------------------------------------- */

/* One row above the grid, scrolling sideways rather than wrapping into a block
   that pushes the videos below the fold on a phone. */
.filters {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 0 0 1.75rem;
}

.filters__chip {
  display: inline-block;
  padding: 0.35rem 0.8rem;
  border: 1px solid color-mix(in srgb, var(--client-accent) 35%, transparent);
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  color: var(--client-accent);
  background: transparent;
}

.filters__chip:hover { border-color: var(--client-accent); }

.filters__chip.is-on {
  background: var(--client-accent);
  border-color: var(--client-accent);
  color: var(--client-accent-contrast);
}

.section + .section { margin-top: 2.75rem; }

.section__title {
  font-size: 1.15rem;
  margin: 0 0 1rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
}

/* Names the set on a category page. Quiet on purpose — it is orientation, not a
   heading competing with the hero above it. */
.setname {
  margin: 0 0 1.25rem;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--client-accent) 75%, var(--ink));
}

/* --- About panel ----------------------------------------------------------- */

/* The sidebar is a column only when there is something in it and room for it.
   A client who has written no About gets exactly the single-column page they
   had before, with no empty gutter to explain. */
.withaside { display: grid; gap: 2rem; align-items: start; }

@media (min-width: 900px) {
  .withaside { grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); }
  .withaside--none { grid-template-columns: minmax(0, 1fr); }
}

/* On a phone it reads first and then gets out of the way — someone who arrived
   to watch something should not scroll past an essay to reach it, so it stays
   short by being a panel rather than a page. */
.aside {
  background: var(--panel);
  border-left: 3px solid var(--client-accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 1.15rem 1.25rem;
}

.aside__title {
  margin: 0 0 0.6rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--client-accent) 78%, var(--ink));
}

.aside__body { font-size: 0.95rem; line-height: 1.6; }
.aside__body p { margin: 0 0 0.75rem; }
.aside__body p:last-child { margin-bottom: 0; }
