/* ============================================================
   layout.css — shared primitives
   ============================================================ */

[hidden] { display: none !important; }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* All sprite icons are stroked, never filled.

   This must be set on the REFERENCING element, not on `.sprite symbol path`:
   <use> clones the symbol into a shadow tree that an outside selector cannot
   reach, so those rules never applied and every icon rendered as a solid black
   blob. fill/stroke are inherited properties, so setting them here does cross
   the shadow boundary. */
.icon {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ------------------------------------------------------------
   Buttons — quiet, squared, light-weight to match the page
   ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.95em 1.9em;
  border: 1px solid var(--ink);
  border-radius: 0;
  font-size: var(--t-meta);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 260ms ease, color 260ms ease, border-color 260ms ease;
}
.btn:hover { text-decoration: none; }

.btn--solid { background: var(--ink); color: var(--paper); }
.btn--solid:hover, .btn--solid:focus-visible { background: transparent; color: var(--ink); }

.btn--ghost { background: transparent; color: var(--ink); }
.btn--ghost:hover, .btn--ghost:focus-visible { background: var(--ink); color: var(--paper); }

/* On the contact section, over photography */
.btn--light { background: #FFFFFF; color: var(--ink); border-color: #FFFFFF; }
.btn--light:hover, .btn--light:focus-visible { background: transparent; color: #FFFFFF; }
.btn--outline { background: transparent; color: #FFFFFF; border-color: rgba(255, 255, 255, 0.55); }
.btn--outline:hover, .btn--outline:focus-visible { background: #FFFFFF; color: var(--ink); border-color: #FFFFFF; }

/* ------------------------------------------------------------
   Reveal on entry.

   Content is visible by default; script adds the "not yet revealed" state.
   .js-reveal is armed pre-paint and reveal.js removes it again if
   IntersectionObserver is missing, so nothing can strand at opacity 0.
   Deliberately gentle — 12px, not 16 — because the page is dense now.
   ------------------------------------------------------------ */

.js-reveal [data-reveal] { opacity: 0; transform: translateY(12px); }
.js-reveal [data-reveal].is-in {
  opacity: 1;
  transform: none;
  transition: opacity 460ms var(--ease-out, ease), transform 460ms var(--ease-out, ease);
}

@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* ------------------------------------------------------------
   Gate entrance — CSS, not script, so the disclaimer text does not
   wait on a script load to become visible.
   ------------------------------------------------------------ */

@keyframes gate-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
.js-gate .gate__inner > * { opacity: 0; animation: gate-in 520ms ease forwards; }
.js-gate .gate__inner > *:nth-child(1) { animation-delay:  60ms; }
.js-gate .gate__inner > *:nth-child(2) { animation-delay: 130ms; }
.js-gate .gate__inner > *:nth-child(3) { animation-delay: 200ms; }
.js-gate .gate__inner > *:nth-child(4) { animation-delay: 270ms; }

@media (prefers-reduced-motion: reduce) {
  .js-gate .gate__inner > * { animation: none !important; opacity: 1 !important; transform: none !important; }
}

@media (max-width: 640px) {
  .btn { width: 100%; }
}
