/* Self-hosted fonts. The woff2 files come from the Fontsource packages (see
   src/lib/fonts.ts); the build copies them to /fonts/. Only the latin and
   latin-ext subsets: pt-BR needs nothing else. The unicode-range values are the
   ones Fontsource ships, so latin-ext is only fetched when a glyph needs it. */

@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url(/fonts/space-grotesk-latin-400-normal.woff2) format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url(/fonts/space-grotesk-latin-ext-400-normal.woff2) format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-display: swap;
  font-weight: 500;
  src: url(/fonts/space-grotesk-latin-500-normal.woff2) format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-display: swap;
  font-weight: 500;
  src: url(/fonts/space-grotesk-latin-ext-500-normal.woff2) format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url(/fonts/ibm-plex-mono-latin-400-normal.woff2) format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url(/fonts/ibm-plex-mono-latin-ext-400-normal.woff2) format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* Hallmark · genre: editorial · macrostructure: Split Studio · theme: brand-locked (dark #0F0E0C · lime #C9F24B · Space Grotesk + IBM Plex Mono)
 * mood: terminal · nav: N8 Terminal command · footer: Ft4 Dense typographic
 * H2 Split Diptych knobs: ratio=5/7, right=display, divider=negative-space
 * C3 Typographic link knobs: underline=solid, hover=colour-shift, arrow=↗
 * pre-emit critique: P5 H5 E4 S5 R4 V5
 * contrast: pass (40-41) · slop: pass (42-45) · tokens: pass (48) · mobile: pass (34, 49, 50-57)
 */

:root {
  /* Brand palette - unchanged. Accent marks live data and interaction, never decoration. */
  --bg: #0F0E0C;
  --fg: #ECE9E1;
  --muted: #8C867A;
  --accent: #C9F24B;
  --line: color-mix(in oklab, var(--fg) 12%, transparent);
  --line-2: color-mix(in oklab, var(--fg) 6%, transparent);
  /* Third reading step between --fg and --muted: running copy (lede and project
     descriptions) leaves the label grey without reaching the heading white.
     Resolves to #C6C2B9, 10.8:1 against --bg. */
  --fg-2: color-mix(in oklab, var(--fg) 62%, var(--muted));

  --grotesk: "Space Grotesk", system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, monospace;

  /* 4pt spacing scale */
  --space-3xs: 4px;
  --space-2xs: 8px;
  --space-xs: 12px;
  --space-sm: 16px;
  --space-md: 24px;
  --space-lg: 32px;
  --space-xl: 48px;
  --space-2xl: 72px;
  --space-3xl: 112px;

  /* Type scale */
  --text-xs: 12px;
  --text-sm: 13px;
  --text-md: 15px;
  --text-lg: 18px;
  --text-xl: clamp(24px, 3.2vw, 38px);
  --text-display: clamp(38px, 6.4vw, 82px);

  --ease-out: cubic-bezier(.2, .8, .2, 1);
  --dur-fast: 160ms;
  --dur-short: 220ms;
  --dur-mid: 320ms;

  --rule-hair: 1px;
  --gutter: clamp(22px, 5vw, 60px);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  -webkit-font-smoothing: antialiased;
  color-scheme: dark;
  overflow-x: clip;
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--grotesk);
  line-height: 1.45;
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: clip;
}

.wrap { max-width: 1240px; margin: 0 auto; padding: 0 var(--gutter); }
.mono { font-family: var(--mono); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* Label register: every mono caption on the page shares this voice. */
.label {
  font-family: var(--mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---- N8 Terminal command nav ---------------------------------------- */

.nav {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2xs) var(--space-sm);
  padding: var(--space-md) 0;
  border-bottom: var(--rule-hair) solid var(--line);
  font-family: var(--mono); font-size: var(--text-sm);
  letter-spacing: 0.02em;
}
.nav__prompt { color: var(--accent); }
.nav__cmd { color: var(--fg); white-space: nowrap; }
/* The wordmark is a link (home) but does not take the dimmed tone of the flags. */
.nav a.nav__cmd { color: var(--fg); }
.nav a.nav__cmd:focus-visible { border-bottom-color: var(--accent); }
@media (hover: hover) and (pointer: fine) {
  .nav a.nav__cmd:hover { border-bottom-color: var(--accent); }
}
/* Keeps the caret welded to the last flag regardless of the flex gap. */
.nav__tail { display: inline-flex; align-items: center; gap: 0.3em; }
.nav a {
  color: var(--muted); text-decoration: none; white-space: nowrap;
  border-bottom: var(--rule-hair) solid transparent;
  transition: color var(--dur-short) var(--ease-out), border-color var(--dur-short) var(--ease-out);
}
.nav a:focus-visible { color: var(--fg); border-bottom-color: var(--accent); }
/* Fine pointers only: on touch, :hover latches after the tap and never clears. */
@media (hover: hover) and (pointer: fine) {
  .nav a:hover { color: var(--fg); border-bottom-color: var(--accent); }
}
.nav a:active { color: var(--accent); }

/* The blinking caret is allowed only in N8 - it signals "you would type next". */
.nav__caret {
  display: inline-block; width: 0.6em; height: 1.05em;
  background: var(--accent);
  /* steps(1, jump-none), not steps(2): a jump-end step function never reaches the
     interval's end value, so steps(2) cycled the caret through 1, 0.5, 0, 0.5 - a
     four-level pulse, not a terminal blink. jump-none holds each interval's start. */
  animation: blink 1.05s steps(1, jump-none) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* ---- Shared readout (key/value data, mono) --------------------------- */

.readout { font-family: var(--mono); font-size: var(--text-xs); }
.readout > div {
  display: flex; justify-content: space-between; gap: var(--space-sm);
  padding: var(--space-xs) 0;
}
.readout dt { color: var(--muted); letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
.readout dd { color: var(--fg); text-align: right; font-variant-numeric: tabular-nums; }
.readout dd .ac { color: var(--accent); }

/* Hero readout is the dense, ruled variant. */
.readout--ruled > div { border-top: var(--rule-hair) solid var(--line-2); }
.readout--ruled > div:first-child { border-top: none; }

/* ---- H2 Split diptych hero (5/7 - narrow proof left, display right) --- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--space-xl) var(--space-2xl);
  align-items: start;
  /* Bottom-heavy: pulls the hero into the index rhythm instead of floating it. */
  padding: clamp(48px, 7vw, 88px) 0 clamp(64px, 9vw, 120px);
}
/* Display leads the DOM (correct reading order); grid places it right on desktop. */
.hero__say { grid-column: 2; grid-row: 1; }
.hero .readout { grid-column: 1; grid-row: 1; }

.hero--solo { grid-template-columns: minmax(0, 1fr); max-width: 62ch; }
.hero--solo .hero__say { grid-column: 1; }

.hero h1 {
  font-size: var(--text-display); font-weight: 500;
  line-height: 1.02; letter-spacing: -0.03em;
  overflow-wrap: anywhere; min-width: 0;
}
.hero .lede {
  margin-top: var(--space-md);
  max-width: 54ch; color: var(--fg-2); font-size: var(--text-lg); line-height: 1.55;
}
.hero__err {
  margin-bottom: var(--space-md);
  font-family: var(--mono); font-size: var(--text-sm); line-height: 1.7; color: var(--muted);
}
.hero__err b { color: var(--accent); font-weight: 400; }
.hero__go { margin-top: var(--space-lg); }

/* ---- Split Studio project modules ------------------------------------ */

/* Divider between the hero and the index, in the .label register. */
.projects__title { display: block; margin-bottom: var(--space-sm); }
/* The nav's --projetos flag lands here; without this the h2 sits flush to the edge. */
#projetos { scroll-margin-top: var(--space-lg); }

.projects { list-style: none; }

.proj {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--space-md) var(--space-2xl);
  align-items: start;
  padding: clamp(32px, 4vw, 56px) 0;
  border-top: var(--rule-hair) solid var(--line);
}
.proj:last-child { border-bottom: var(--rule-hair) solid var(--line); }

/* Alternating diptych: the halves swap sides down the page.
   Alternation needs critical mass: under 4 modules the zigzag reads as accident,
   so Projects.astro only adds .projects--alt when the index has 4+ items. */
/* The explicit row is load-bearing, not cosmetic. Without it, on even modules
   .proj__say is pushed to column 2, the sparse auto-placement cursor moves past
   column 1, and .proj__side drops to row 2 - the module grows 153px and the
   alternation reads as misalignment. .hero already solves it the same way. */
.proj__say { grid-column: 1; grid-row: 1; }
.proj__side { grid-column: 2; grid-row: 1; }
.projects--alt .proj:nth-child(even) { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
.projects--alt .proj:nth-child(even) .proj__say { grid-column: 2; }
.projects--alt .proj:nth-child(even) .proj__side { grid-column: 1; }

/* One hover signal on the module: a scan line across its top rule.
   Asymmetric on purpose - the sweep in is deliberate, the retreat is snappy, so the
   line never trails the cursor on the way out. The base rule governs the exit. */
.proj::before {
  content: ""; position: absolute; inset-inline: 0; inset-block-start: -1px; height: var(--rule-hair);
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .proj:hover::before { transform: scaleX(1); transition-duration: var(--dur-mid); }
}
.proj:has(.link:focus-visible)::before { transform: scaleX(1); transition-duration: var(--dur-mid); }
.proj:has(.link:focus-visible) { outline: 2px solid var(--accent); outline-offset: 6px; }

.proj__n {
  display: block; margin-bottom: var(--space-2xs);
  font-family: var(--mono); font-size: var(--text-sm); color: var(--muted);
  font-variant-numeric: tabular-nums;
  transition: color var(--dur-short) var(--ease-out);
}
.proj:has(.link:focus-visible) .proj__n { color: var(--accent); }
@media (hover: hover) and (pointer: fine) {
  .proj:hover .proj__n { color: var(--accent); }
}

.proj__name {
  font-size: var(--text-xl); font-weight: 500; letter-spacing: -0.02em; line-height: 1.1;
  overflow-wrap: anywhere; min-width: 0;
}
.proj__desc {
  margin-top: var(--space-xs);
  max-width: 52ch; color: var(--fg-2); font-size: var(--text-md); line-height: 1.6;
}
.proj__go { margin-top: var(--space-md); }

/* ---- C3 Typographic link -------------------------------------------- */

.link {
  display: inline-flex; align-items: center; gap: var(--space-2xs);
  font-family: var(--mono); font-size: var(--text-sm); color: var(--fg);
  text-decoration: none; white-space: nowrap;
  padding-block: var(--space-2xs);
  border-bottom: var(--rule-hair) solid var(--line);
  transition: color var(--dur-short) var(--ease-out), border-color var(--dur-short) var(--ease-out);
}
.link svg {
  width: 14px; height: 14px; flex: none; stroke: currentColor; stroke-width: 2; fill: none;
  /* Same duration as the colour above: both land together on the same event. */
  transition: transform var(--dur-short) var(--ease-out);
}
.link:focus-visible { color: var(--accent); border-bottom-color: var(--accent); }
.link:focus-visible svg { transform: translate(3px, -3px); }
/* Back link travels left, not out. */
.link--back:focus-visible svg { transform: translateX(-3px); }
@media (hover: hover) and (pointer: fine) {
  .proj:hover .link, .link:hover { color: var(--accent); border-bottom-color: var(--accent); }
  .proj:hover .link svg, .link:hover svg { transform: translate(3px, -3px); }
  .link--back:hover svg { transform: translateX(-3px); }
}
.link:active { color: var(--fg); border-bottom-color: var(--fg); }
/* Index memory: what you already opened dims. The signal is the rule under the link,
   not its colour: a visited link's colour is not propagated to the icon's currentColor
   (browser privacy restriction), so dimming the text left the arrow bright behind it. */
.proj__go .link:visited { border-bottom-color: var(--line-2); }
.link:visited:focus-visible { border-bottom-color: var(--accent); }
@media (hover: hover) and (pointer: fine) {
  .proj:hover .link:visited,
  .link:visited:hover { border-bottom-color: var(--accent); }
}

/* The whole module is the hit target; the link keeps the accessible name. */
.proj__go .link::after { content: ""; position: absolute; inset: 0; }
/* Copyable data sits above the overlay: desc and readout stay selectable. */
.proj__desc, .proj .readout { position: relative; z-index: 1; }
.proj:active::before { transform: scaleX(1); }

/* ---- Contact: modulo de fechamento do indice -------------------------- */

/* Same grid and same vertical rhythm as the project modules: contact closes the
   index instead of floating after it. The top rule comes from the last .proj bottom
   border, so there is no margin and no border-top here. */
.contact {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--space-md) var(--space-2xl);
  align-items: start;
  padding: clamp(32px, 4vw, 56px) 0;
  border-bottom: var(--rule-hair) solid var(--line);
}
.contact__say { grid-column: 1; grid-row: 1; }
.contact__side { grid-column: 2; grid-row: 1; }
.contact__tag { display: block; margin-bottom: var(--space-2xs); }
.contact h2 {
  font-size: var(--text-xl); font-weight: 500;
  letter-spacing: -0.02em; line-height: 1.1;
}
.contact__desc {
  margin-top: var(--space-xs);
  max-width: 52ch; color: var(--fg-2); font-size: var(--text-md); line-height: 1.6;
}

/* ---- Ft4 Dense typographic colophon ---------------------------------- */

.colophon {
  margin-top: var(--space-3xl);
  padding: var(--space-lg) 0 var(--space-xl);
  border-top: var(--rule-hair) solid var(--line);
}
.colophon p {
  max-width: 74ch;
  font-family: var(--mono); font-size: var(--text-xs); line-height: 1.9; color: var(--muted);
}
.colophon a {
  color: var(--fg); text-decoration: none;
  border-bottom: var(--rule-hair) solid var(--line);
  transition: color var(--dur-short) var(--ease-out), border-color var(--dur-short) var(--ease-out);
}
.colophon a:focus-visible { color: var(--accent); border-bottom-color: var(--accent); }
@media (hover: hover) and (pointer: fine) {
  .colophon a:hover { color: var(--accent); border-bottom-color: var(--accent); }
}
.colophon a:active { color: var(--fg); }

/* ---- Reveal: opposite halves cross-fade, staggered (opacity only) ----- */

@keyframes fade { from { opacity: 0; } }

.proj__say, .proj__side {
  animation: fade var(--dur-mid) var(--ease-out) backwards;
}
.proj__say { animation-delay: calc(var(--i, 0) * 60ms); }
.proj__side { animation-delay: calc(90ms + var(--i, 0) * 60ms); }

/* ---- Responsive ------------------------------------------------------ */

/* 60rem: layout breakpoint. Diptychs collapse; halves stack in reading order. */
@media (max-width: 60rem) {
  .hero { grid-template-columns: minmax(0, 1fr); gap: var(--space-lg); }
  .hero__say, .hero .readout { grid-column: 1; grid-row: auto; }

  .proj,
  .projects--alt .proj:nth-child(even) { grid-template-columns: minmax(0, 1fr); gap: var(--space-md); }
  .proj__say,
  .projects--alt .proj:nth-child(even) .proj__say,
  .proj__side,
  .projects--alt .proj:nth-child(even) .proj__side { grid-column: 1; grid-row: auto; }

  .proj__side { padding-top: var(--space-2xs); border-top: var(--rule-hair) solid var(--line-2); }

  .contact { grid-template-columns: minmax(0, 1fr); gap: var(--space-md); }
  .contact__say, .contact__side { grid-column: 1; grid-row: auto; }
  /* Same divider rule that .proj__side gets at this breakpoint. */
  .contact__side { padding-top: var(--space-2xs); border-top: var(--rule-hair) solid var(--line-2); }
}

/* 40rem: typography breakpoint. Space becomes a luxury; hit targets stay 44px. */
@media (max-width: 40rem) {
  .nav { gap: var(--space-3xs) var(--space-xs); }
  .nav a { padding-block: var(--space-xs); }
  .readout > div { padding: var(--space-2xs) 0; }
  .link { padding-block: var(--space-xs); min-height: 44px; }
  .colophon p { line-height: 2; }
}

/* Reduced motion is less movement, not zero transition. Colour and border transitions
   displace nothing and help the eye follow a state change, so they stay. These two are
   the only transform transitions in the sheet; they are what has to go. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; }
  .proj::before, .link svg { transition: none !important; }
  .nav__caret { opacity: 1; }
}
