@import "./tokens.css";

/* ===== Helvetica Now Display (Bold). Drop the woff2 into src/assets/fonts/
   as HelveticaNowDisplay-Bold.woff2 and it renders 1:1; fallback until then. */
@font-face {
  font-family: "Helvetica Now Display";
  src: url("/assets/fonts/HelveticaNowDisplay-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  /* optional (not swap): with the preload above, the font is ready at first
     paint, so it renders correctly with no fallback→brand swap flash. If it
     ever isn't ready in time, the fallback shows for that load WITHOUT a
     mid-view swap (no flash, no layout shift). */
  font-display: optional;
}

/* ============================================================= reset / base */
*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
html {
  -webkit-text-size-adjust: 100%;
  scrollbar-gutter: stable; /* always reserve the scrollbar — no shift/flick when the menu locks scroll */
}
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent; /* no grey tap flash on mobile */
  min-height: 100vh;
  transition: background-color var(--dur-fast) var(--ease-in-out),
    color var(--dur-fast) var(--ease-in-out); /* smooth light↔dark flip */
}
/* text selection: inverts with the theme (Leo's call: black bg / white text on light) */
::selection {
  background: var(--sel-bg);
  color: var(--sel-fg);
}
::-moz-selection {
  background: var(--sel-bg);
  color: var(--sel-fg);
}
img {
  display: block;
  max-width: 100%;
}
/* eleventy-img wraps output in <picture>; display:contents lets the inner <img>
   fill the card box for object-fit. */
picture {
  display: contents;
}
a {
  color: inherit;
  text-decoration: none;
}
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: fixed;
  top: var(--gap);
  left: var(--gap);
  z-index: 100;
  padding: 4px 10px;
  background: var(--pill-bg);
  color: var(--pill-fg);
  border-radius: var(--radius);
  font-weight: 700;
  font-size: var(--chrome-size);
  transform: translateY(-200%);
  transition: transform 0.2s;
}
.skip-link:focus {
  transform: none;
}

/* ================================================================== top bar */
.topbar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: var(--gap);
  pointer-events: none; /* let the white gaps pass clicks; pills re-enable */
}
.topbar__group {
  display: flex;
  gap: 3px;
}

.pill {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  height: var(--pill-h);
  padding: 0 var(--pill-pad-x);
  background: var(--pill-bg);
  color: var(--pill-fg);
  border-radius: var(--radius);
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 1;
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease-in-out),
    color var(--dur-fast) var(--ease-in-out);
}
/* breadcrumb pills (case pages): gray, non-interactive */
.pill--crumb {
  background: var(--label-bg);
  color: var(--label-fg);
}
/* MENU toggle stays inverted (active) while the panel is open — frame 28:116 */
.pill[data-menu-toggle][aria-expanded="true"] {
  background: var(--pill-fg);
  color: var(--pill-bg);
}
/* active nav pill: the current page's nav item turns gray (Figma 1:423) */
.topbar nav .pill[aria-current="page"] {
  background: var(--pill-active);
  color: var(--pill-bg);
}
/* theme toggle — icon-only square pill next to DELITO.CC */
.pill--theme {
  width: var(--pill-h);
  padding: 0;
  justify-content: center;
}
.theme-ico {
  display: block;
}

.clock {
  font-variant-numeric: tabular-nums;
}

/* mobile chrome: brand + MENU only. Clock and the INDEX/ARCHIVE/ABOUT/DELITO
   links hide (they live in the menu overlay); the MENU <button> stays. */
@media (max-width: 560px) {
  .topbar .clock {
    display: none;
  }
  .topbar nav a {
    display: none;
  }
  .topbar .pill--crumb {
    display: none;
  }
}

/* ============================================================== home / grid */
.home {
  /* outer margin = 10px; cards start at y=40 (bar 10 + pill 20 + 10 gap) */
  padding: 40px var(--gap) var(--gap);
}
/* 3-column masonry via CSS multicol. Cards flow top-to-bottom, each keeping its
   own aspect ratio (--ratio per project), so column heights vary = masonry. */
.home-grid {
  column-count: 3;
  column-gap: var(--gap);
}
.card {
  position: relative;
  display: block;
  break-inside: avoid;
  margin-bottom: var(--gap); /* vertical rhythm (column-gap is horizontal only) */
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--card);
  aspect-ratio: var(--ratio, 4 / 5); /* per-card; CMS dropdown sets this later */
}
.card img,
.card video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* archive masonry items — natural ratio (fit height, no crop), no link / no VIEW
   cursor (they're not .card). The --ratio is each photo's real IG dimensions. */
.archive-item {
  break-inside: avoid;
  margin-bottom: var(--gap);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--card);
  aspect-ratio: var(--ratio, 4 / 5);
}
.archive-item img,
.archive-item video {
  width: 100%;
  height: 100%;
  object-fit: cover; /* ratio == natural, so this fills exactly (no crop) */
  display: block;
}
@media (max-width: 900px) {
  .home-grid {
    column-count: 2;
  }
}
@media (max-width: 560px) {
  .home-grid {
    column-count: 1; /* mobile = one big scroll */
  }
}

/* ===== home-only footer (Figma 2016-16). Wordmark (left) + the CONTACT/SOCIAL/
   CREDITS columns (right) sit on one band, the columns vertically centred beside
   the wordmark; the copyright is pinned to the bottom-right corner. */
.home-footer {
  display: flex;
  align-items: flex-end; /* wordmark · columns · copyright all sit on the bottom line */
  justify-content: space-between;
  gap: 40px;
  padding: 150px var(--gap) var(--gap);
  color: var(--pill-bg);
}
.home-footer__brand {
  flex: 0 1 auto; /* the SVG scales, so let it shrink to fit (no overflow, no clip) */
  min-width: 0;
  width: clamp(280px, 46vw, 880px); /* big like the Figma; shrinks if the row is tight */
}
.home-footer__brand svg {
  display: block;
  width: 100%;
  height: auto;
}
.home-footer__brand svg path {
  fill: currentColor; /* follows the light/dark theme via the footer's color */
}
.home-footer__cols {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start; /* tags top-aligned, bottoms ~level with the baseline */
  gap: 28px;
}
.home-footer__copy {
  flex: 0 0 auto;
  font-weight: 700;
  font-size: var(--chrome-size);
  white-space: nowrap;
}
@media (max-width: 1340px) {
  /* the wordmark + 3 columns + copyright only fit on one line on wide screens;
     below this, stack: columns, wordmark, copyright */
  .home-footer {
    flex-direction: column;
    align-items: stretch;
    gap: 40px;
    padding: 96px var(--gap) var(--gap);
  }
  .home-footer__brand {
    order: 2;
    width: min(100%, 680px); /* SVG wordmark grows when stacked */
  }
  .home-footer__cols {
    order: 1;
    flex-wrap: wrap;
  }
  .home-footer__copy {
    order: 3;
  }
}

/* ============================================================= index (list) ==
   Figma 1:423 — a PROJETOS tag aligned with a project table (Nº | Cliente |
   Projeto | Categoria | Ano). Subgrid aligns the columns across every row; each
   row is a link with a full-row black hover bar. The table left-aligns under the
   clock pill, and the tag pill shares that same left edge. */
.index {
  /* left inset aligns the tag pill with the clock pill (its left edge, constant
     at the 13px brand width); the tag's text then sits over the Nº column. */
  padding: 51px var(--gap) var(--gap) var(--index-indent, 135px);
  color: var(--pill-bg);
}
.index__tag {
  display: inline-flex;
  align-items: center;
  height: var(--pill-h);
  margin-bottom: 20px;
  padding: 0 var(--pill-pad-x);
  background: var(--pill-active);
  color: var(--pill-bg);
  border-radius: var(--radius);
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 1;
}
.index-table {
  margin-left: var(--pill-pad-x); /* Nº column sits under the tag's TEXT */
  display: grid;
  grid-template-columns: max-content max-content max-content max-content max-content;
  column-gap: 44px;
  width: max-content;
  max-width: 100%;
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 17px;
}
.index-row {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  align-items: center;
  position: relative;
  z-index: 0;
  padding: 5px 0; /* ~27px row rhythm (Figma) */
  color: var(--pill-bg);
}
/* full-row hover bar — extends past the content to align with the tag pill */
.index-row::before {
  content: "";
  position: absolute;
  inset: 0 calc(-1 * var(--pill-pad-x));
  background: var(--pill-bg);
  border-radius: var(--radius);
  opacity: 0;
  z-index: -1;
}
.index-cell {
  white-space: nowrap;
}
@media (hover: hover) {
  .index-row:hover {
    color: var(--pill-fg);
  }
  .index-row:hover::before {
    opacity: 1;
  }
}
/* hover-preview pane. Default (/index/) = a large pane filling the right side
   (top→bottom, starting just past the table; index-preview.js sets `left`).
   Covers are stacked; the hovered row's cover hard-cuts in. */
.index-preview {
  position: fixed;
  top: 54px;
  left: 50%; /* JS refines this to the table's right edge */
  right: var(--gap);
  bottom: var(--gap);
  z-index: 5;
  pointer-events: none;
  border-radius: var(--radius);
  overflow: hidden;
}
.index-preview__item {
  position: absolute;
  inset: 0;
  opacity: 0; /* hard cut — no transition (Leo's call) */
}
.index-preview__item.is-active {
  opacity: 1;
}
.index-preview__item img,
.index-preview__item video {
  width: 100%;
  height: 100%;
  object-fit: cover; /* fill the pane (crop to fit) */
  display: block;
}
/* full-background variant (/index-teste, Figma 2017-37) — the hovered cover
   fills the whole viewport behind the list. */
.index--bg .index-preview {
  inset: 0;
  z-index: 0; /* behind the list */
  border-radius: 0;
}
.index--bg .index {
  position: relative;
  z-index: 1; /* list on top of the background */
}
@media (max-width: 1100px) {
  .index-preview {
    display: none; /* the big /index/ pane needs room; the list shows alone */
  }
  .index--bg .index-preview {
    display: block; /* but the full-bg variant keeps its cover on every size */
  }
}
@media (max-width: 560px) {
  /* phones: the 5-col table overflows — reflow each row to a stacked card:
     Nº on the left, Title on top, then client / category / year muted below. */
  .index {
    padding-left: var(--gap);
  }
  .index-table {
    display: block;
    margin-left: 0;
    width: auto;
    max-width: none;
  }
  .index-row {
    display: grid;
    grid-template-columns: 38px 1fr;
    column-gap: 12px;
    row-gap: 2px;
    align-items: baseline;
    padding: 11px 0;
  }
  .index-row::before {
    inset: 0 calc(-1 * var(--pill-pad-x)); /* keep the full-row hover bar */
  }
  .index-cell {
    white-space: normal;
  }
  .index-cell:nth-child(1) {
    grid-column: 1;
    grid-row: 1 / span 4;
  } /* Nº spans the stack */
  .index-cell:nth-child(3) {
    grid-column: 2;
    grid-row: 1;
  } /* Title — primary line */
  .index-cell:nth-child(2) {
    grid-column: 2;
    grid-row: 2;
  } /* Client */
  .index-cell:nth-child(4) {
    grid-column: 2;
    grid-row: 3;
  } /* Category */
  .index-cell:nth-child(5) {
    grid-column: 2;
    grid-row: 4;
  } /* Year */
  .index-cell:nth-child(2),
  .index-cell:nth-child(4),
  .index-cell:nth-child(5) {
    opacity: 0.5; /* the metadata reads as a muted secondary block */
  }
}

/* ===================================================================== about ==
   Figma 1:200. Photo left-aligns with the brand pill, its right edge meets the
   clock pill (about.js); EXPERIENCE/AWARDS top-right sit under MENU/ARCHIVE;
   CONTACT/SOCIAL/CREDITS footer bottom-left + copyright bottom-right. */
.tag {
  display: inline-flex;
  align-items: center;
  height: var(--pill-h);
  padding: 0 var(--pill-pad-x);
  background: var(--pill-active);
  color: var(--pill-bg);
  border-radius: var(--radius);
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 1;
}
.about {
  min-height: 100vh;
  padding: 40px var(--gap) var(--gap);
  color: var(--pill-bg);
  display: flex;
  flex-direction: column;
}
/* ONE grid: columns = image aspect ratios (set inline), so the lower blocks
   align to the images above. Images = row 1 (fixed band height, cover-crop). */
.about-grid {
  display: grid;
  grid-auto-columns: 1fr; /* fallback when there are no images */
  column-gap: var(--gap);
  row-gap: 44px;
  align-items: start;
}
.about-img {
  grid-row: 1;
  height: clamp(240px, 40vh, 460px);
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--card);
}
.about-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* lower blocks, aligned to the image columns */
.about-bio {
  grid-column: 1 / 3; /* under images 1–2 */
  grid-row: 2;
  max-width: 13em; /* keep the bio a narrow readable column, like the Figma */
  font-weight: 700;
  font-size: clamp(22px, 1.93vw, 37px);
  line-height: 0.98;
  letter-spacing: -0.01em;
}
.about-meta {
  grid-column: 3; /* starts at image 3 */
  grid-row: 2;
  display: flex;
  gap: 28px;
}
.about-info {
  grid-column: 4; /* starts at image 4 */
  grid-row: 2;
  display: flex;
  flex-direction: column;
  gap: 26px;
}
/* shared column bits (also used by the home footer) */
.about-col .tag {
  margin-bottom: 18px;
}
.about-col__body {
  margin-left: var(--pill-pad-x); /* list aligns with the tag's text */
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 17px;
}
.about-col__body a,
.about-col__body span,
.about-col__body p {
  display: block;
}
.about-col__body a {
  text-decoration: underline;
  text-underline-offset: 2px;
}
.about-exp__item {
  margin-bottom: 0.85em;
}
.about-copy {
  margin-top: auto;
  padding-top: 56px;
  text-align: right;
  font-weight: 700;
  font-size: var(--chrome-size);
}
@media (max-width: 1100px) {
  /* stack: images, then bio, EXPERIENCE/AWARDS, SOCIAL/CREDITS/CONTACT */
  .about-grid {
    grid-template-columns: 1fr !important;
    row-gap: 40px;
  }
  .about-img,
  .about-bio,
  .about-meta,
  .about-info {
    grid-column: 1 !important;
    grid-row: auto !important;
  }
  .about-img {
    height: auto;
    aspect-ratio: var(--ratio); /* each image full-width at its natural ratio */
  }
  .about-meta {
    flex-wrap: wrap;
  }
  .about-bio {
    max-width: 620px;
  }
  .about-copy {
    text-align: left;
  }
}

/* ===================================================== hover interactions ===
   Only on real hover devices; reduced-motion drops the transforms below. */
@media (hover: hover) {
  /* pills (nav + brand) invert */
  a.pill:hover,
  button.pill:hover {
    background: var(--pill-fg);
    color: var(--pill-bg);
  }
  /* card: label inverts + arrow slides in (no image zoom — Leo's call) */
  .card:hover .card__label {
    background: var(--pill-bg);
    color: var(--pill-fg);
  }
  .card:hover .lbl-arrow {
    max-width: 2ch;
    margin-left: 0.35em;
    opacity: 1;
  }
}

/* ====================================================== custom "VIEW" cursor =
   Activated by cursor.js only on desktop + motion-allowed; otherwise the native
   cursor stays and this element is never shown. */
.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 90;
  pointer-events: none;
  display: none;
  will-change: transform;
}
.has-cursor .cursor {
  display: block;
}
.cursor__dot {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);
  width: 12px;
  height: 12px;
  padding: 0;
  border-radius: var(--radius); /* rounded square, same corner as the site (was a circle) */
  background: var(--pill-bg);
  display: grid;
  place-items: center;
  white-space: nowrap;
  transition: width var(--dur-fast) var(--ease-out), height var(--dur-fast) var(--ease-out),
    padding var(--dur-fast) var(--ease-out);
}
/* VIEW state = a pill button, matching the nav pills (height/padding/radius/colors).
   FIXED width (fits "VIEW") — not auto — so width animates in step with height +
   padding (auto isn't transitionable, so it would snap while the rest eased). */
.cursor[data-state="view"] .cursor__dot {
  width: 56px;
  height: var(--pill-h);
  padding: 0 var(--pill-pad-x);
}
.cursor__view {
  color: var(--pill-fg);
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 1;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.cursor[data-state="view"] .cursor__view {
  opacity: 1;
}
/* hide the native cursor while the custom one is active */
.has-cursor,
.has-cursor a,
.has-cursor button {
  cursor: none;
}

.card__label {
  position: absolute;
  left: var(--gap);
  bottom: var(--gap);
  display: inline-flex;
  align-items: center;
  height: var(--pill-h);
  padding: 0 var(--pill-pad-x);
  background: var(--label-bg);
  color: var(--label-fg);
  border-radius: var(--radius);
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 1;
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease-in-out),
    color var(--dur-fast) var(--ease-in-out);
}
/* arrow slides in on card hover; takes no space at rest (1:1 preserved) */
.lbl-arrow {
  display: inline-block;
  overflow: hidden;
  max-width: 0;
  margin-left: 0;
  opacity: 0;
  transition: max-width var(--dur-fast) var(--ease-out),
    margin-left var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}

/* ====================================================================== case
   Hero (full-width) -> organic gallery (6-col grid, per-image span+ratio) ->
   outro (big description + metadata, lower-right) -> NEXT PROJECT pill. */
.case {
  padding-top: 40px; /* clear the fixed bar (bar 30 + 10 gap) */
}
/* gallery = stacked rows authored in the CMS (full / duo / trio). Each row is a
   grid of equal-width cells with a uniform height = the TALLEST item's natural
   ratio. Only that item (.is-lead, computed in projects.js) carries the
   aspect-ratio and so sizes the row track; the others have no intrinsic height,
   stretch to fill the track, and cover-crop — flush rows, no holes. */
.gallery {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  padding: var(--gap) var(--gap) 0;
}
.gallery-row {
  display: grid;
  grid-template-columns: repeat(var(--cols, 1), 1fr);
  gap: var(--gap);
  /* align-items: stretch (default) = every cell fills the row height */
}
.gallery__item {
  position: relative;
  min-height: 0;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--card);
}
.gallery__item.is-lead {
  aspect-ratio: var(--ratio, 4 / 3); /* the tallest item sets the row height */
}
.gallery__item img,
.gallery__item video {
  position: absolute; /* fill the cell (which = the row height) and crop */
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Outro (Figma 1:310) — a spacer column (1fr) pushes everything into the right
   half: title (name + year) above, big description below it (col 2), credits
   column to its right (col 3), top-aligned with the description. */
.case-outro {
  display: grid;
  grid-template-columns: 1fr minmax(0, 28vw) auto;
  grid-template-rows: auto auto;
  align-items: start;
  column-gap: 56px;
  row-gap: 20px;
  padding: 44px var(--gap) 0;
  color: var(--text);
}
.case-title {
  grid-column: 2;
  grid-row: 1;
  align-self: end;
  font-weight: 700;
  font-size: clamp(20px, 2.1vw, 30px);
  letter-spacing: -0.01em;
}
.case-desc {
  grid-column: 2;
  grid-row: 2;
  font-weight: 700;
  font-size: clamp(26px, 3vw, 40px);
  line-height: 0.98;
  letter-spacing: -0.01em;
}
/* credits = stacked block (Category / Client / Year), in the far-right column */
.case-meta {
  grid-column: 3;
  grid-row: 2; /* top-aligned with the description (below the title) */
  align-self: start;
  width: 210px;
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 1.12;
}
.case-meta a {
  text-decoration: underline;
  text-underline-offset: 2px;
}
.case-meta .meta-block + .meta-block {
  margin-top: 1.2em;
}

.next-project {
  display: flex;
  justify-content: flex-end;
  padding: 96px var(--gap) var(--gap) var(--gap); /* flush right (10px) — aligns with credits */
}

@media (max-width: 900px) {
  /* stack: title, description, credits — full width */
  .case-outro {
    display: flex;
    flex-direction: column;
    gap: 22px;
    padding: 40px var(--gap) 0;
  }
  .case-meta {
    width: auto;
  }
  .next-project {
    padding: 56px var(--gap) var(--gap);
  }
}
/* phones: every row goes linear (one full-width image per line). Each item gets
   its OWN ratio back (not just the lead) so images show in full, no crop.
   (Tablet keeps the authored 1/2/3 columns via --cols.) */
@media (max-width: 560px) {
  .gallery-row {
    grid-template-columns: 1fr;
  }
  .gallery__item {
    aspect-ratio: var(--ratio, 4 / 3);
  }
}

/* ===== reveal (Fase 1 motion). Hide via opacity ONLY while motion is enabled
   (set pre-paint by the head script) — no flash, and content stays visible with
   no JS or under reduced-motion. GSAP animates the translate + final state. */
.motion [data-reveal] {
  opacity: 0;
}
.motion-failsafe [data-reveal] {
  opacity: 1 !important;
  transform: none !important;
}

/* ============================================================= menu (panel) ===
   Gray dropdown panel anchored top-right (frame 28:116): big black nav links,
   BRAGA© + a small footer row at the bottom. Toggled by the MENU pill. */
.menu {
  position: fixed;
  top: 54px; /* 24px gap below the bar (pills end at ~30px); was 40 */
  right: var(--gap); /* fallback / mobile — JS aligns left+width to the nav on desktop */
  z-index: 80;
  /* query container: nav + brand are sized in cqi (% of this panel's width),
     so the type scales with the panel and the logo spans exactly edge-to-edge. */
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 18px;
  width: 340px;
  max-width: calc(100vw - 2 * var(--gap));
  height: 472px;
  max-height: calc(100vh - 64px); /* keep a ~10px bottom gap below the 54px top */
  padding: 5px; /* tight box per the prototype (cqi type rescales to fill it) */
  background: var(--label-bg);
  border-radius: var(--radius);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition: opacity 0.3s var(--ease-out), transform 0.3s var(--ease-out), visibility 0s 0.3s;
}
.menu[data-open="true"] {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.3s var(--ease-out), transform 0.3s var(--ease-out), visibility 0s 0s;
}
.menu__nav {
  display: flex;
  flex-direction: column;
}
/* big nav — sized so the widest word (ARCHIVE) nearly spans the panel width */
.menu__nav a {
  color: var(--pill-bg);
  font-weight: 700;
  font-size: 22cqi;
  line-height: 0.98;
  letter-spacing: -0.03em;
  width: max-content;
  transition: opacity 0.25s var(--ease-out);
}
.menu[data-hovering="true"] .menu__nav a:not(:hover) {
  opacity: 0.4;
}
/* BRAGA© spans the full panel width (cqi tuned to the measured glyph advance) */
.menu__brand {
  display: block;
  width: 100%;
  color: #fff;
  font-weight: 700;
  font-size: 23.5cqi;
  line-height: 0.74;
  letter-spacing: -0.005em;
  white-space: nowrap;
}
.menu__foot {
  display: flex;
  justify-content: space-between;
  gap: var(--gap);
  margin-top: 9px;
  color: #fff;
  font-weight: 700;
  font-size: 9px;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ===================================================== preloader (home intro)
   Project covers HARD-CUT-switch ("frames trocando") on a solid theme bg, then
   settle to the solid bg, then preloader.js hands off to a gentle, delayed,
   staggered entrance of the home. Covers come from _data/preloader.js. Hidden
   instantly on repeat visits / reduced motion via .pl-skip (pre-paint, base.njk). */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 300; /* above topbar (100) / menu / cursor */
  background: var(--bg); /* the solid bg the switching settles onto */
  overflow: hidden;
}
.pl-skip .preloader {
  display: none !important;
}
.preloader__frame {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0; /* JS hard-cuts opacity 0/1 (no transition) */
  will-change: opacity;
}
.preloader__frame img,
.preloader__frame video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* edge mini-text frame (ref layout), in the site's chrome style but smaller.
   Rides in mix-blend difference so it inverts against whatever cover is showing;
   JS fades the whole frame out on the settle. Lines reveal via the .mask. */
.preloader__chrome {
  position: absolute;
  inset: 0;
  z-index: 2;
  color: #fff;
  mix-blend-mode: difference;
  font-family: var(--font);
  font-weight: 700;
  text-transform: uppercase;
  font-size: 10px; /* small (ref corner texts) — back to the smaller size */
  letter-spacing: normal; /* but keep the site's tracking (none) */
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.5s ease;
}
.plx {
  position: absolute;
}
.plx--tl {
  top: var(--gap);
  left: var(--gap);
}
.plx--tc {
  top: var(--gap);
  left: 50%;
  transform: translateX(-50%);
}
.plx--tr {
  top: var(--gap);
  right: var(--gap);
}
.plx--pct {
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%);
}
.plx--loc {
  bottom: var(--gap);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
}
.mask {
  display: block;
  overflow: hidden; /* the reveal mask */
}
.mask__in {
  display: inline-block;
  transform: translateY(110%); /* hidden below the mask until GSAP reveals it */
  will-change: transform;
}
/* While the intro runs, keep the home hidden so it can drift in afterwards.
   !important + later source order beats both .motion and the .motion-failsafe
   safety rule; preloader.js drops .pl-running and animates the entrance itself. */
.pl-running [data-reveal] {
  opacity: 0 !important;
  transform: translateY(20px) !important;
}
.pl-running .topbar {
  opacity: 0;
}

/* ===== dev credit: the cursor becomes the crest ========================
   Hovering "Leal" swaps the custom cursor's dot for the gold crest, which
   spins on its own vertical axis like a coin for as long as the pointer is
   over the link. Reuses the existing cursor follower and its states, so there
   is only ever one thing tracking the pointer.
   No fallback outside the custom cursor is intentional: it only runs on a fine
   pointer with motion allowed, which is exactly where this flourish belongs. */
.cursor__crest {
  position: absolute;
  top: 0;
  left: 0;
  width: 58px;
  height: 58px;
  max-width: none; /* the global img{max-width:100%} would squash it */
  margin: -29px 0 0 -29px; /* centred on the pointer */
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
  will-change: transform;
}
.cursor[data-state="crest"] .cursor__dot {
  opacity: 0; /* the crest replaces the dot, it doesn't sit beside it */
}
.cursor[data-state="crest"] .cursor__crest {
  opacity: 1;
  /* perspective goes in the transform: .cursor is driven by GSAP's own
     transforms, so a parent perspective would fight it. */
  animation: leal-crest-spin 9s linear infinite;
}
/* Measured, not guessed: a zero-thickness plane collapses to 0px twice per
   turn and no transform prevents it — the normal only keeps a component
   toward the viewer if the spin axis IS the view axis, and then it stops
   reading as a flip. Tilting on X does nothing: at the crossing the normal
   lies along the tilt axis itself (verified, still 0px at 90deg).

   So instead of hiding the pass-through, we spend less time in it. Constant
   angular speed is the problem: projected width goes as cos(angle), so it
   barely moves face-on and then snaps through the edge. Easing per quarter
   inverts that — slow where the lions are readable, quick through the blind
   spot. Velocity matches at every keyframe (max at 90/270, zero at 0/180),
   so there is no corner in the motion. Measured over a 9s turn, time spent
   under 12px wide: 1150ms linear, 550ms here. Sharper curves reach 250ms but
   the motion turns into hang-and-whip, which is unsmooth in a different way.

   Perspective at 820px rather than 560: on a 58px object the tighter value
   ballooned the near edge against the far one and read as wobble. */
@keyframes leal-crest-spin {
  0% {
    transform: perspective(820px) rotateY(0deg);
    animation-timing-function: cubic-bezier(0.6, 0, 1, 1); /* accelerate into the edge */
  }
  25% {
    transform: perspective(820px) rotateY(90deg);
    animation-timing-function: cubic-bezier(0, 0, 0.4, 1); /* settle back face-on */
  }
  50% {
    transform: perspective(820px) rotateY(180deg);
    animation-timing-function: cubic-bezier(0.6, 0, 1, 1);
  }
  75% {
    transform: perspective(820px) rotateY(270deg);
    animation-timing-function: cubic-bezier(0, 0, 0.4, 1);
  }
  100% {
    transform: perspective(820px) rotateY(360deg);
  }
}

/* ===== 404 ===============================================================
   Uses the site's own vocabulary: the big numeral echoes the footer wordmark
   (the one place the site goes big), the line under it is chrome-sized, and
   the way back is a nav pill. Centred on both axes (Leo's call) — the one
   screen in the site that does, since it's a dead end with nothing to scan. */
.notfound {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: 18px;
  padding: 40px var(--gap) var(--gap);
}
.notfound__big {
  font-weight: 700;
  /* same scaling idea as .home-footer__brand: big on desktop, never overflows */
  font-size: clamp(96px, 28vw, 420px);
  line-height: 0.82;
  letter-spacing: -0.02em;
  color: var(--text);
}
.notfound__msg {
  font-weight: 700;
  font-size: var(--chrome-size);
  line-height: 1.3;
  color: var(--text);
  max-width: 34ch;
}
.notfound__back {
  pointer-events: auto;
}
