/* ============================================================
   Boran Asena — portfolio
   Serif for the design half, mono for the engineering half.
   Vanilla CSS. No framework, no build step.
   ============================================================ */

:root {
  --bg:       #0b0b0c;
  --surface:  #141416;
  --raise:    #1c1c1f;
  --ink:      #f4f2ee;
  --muted:    #9a968d;
  --faint:    #605d57;
  --line:     #26262a;
  --accent:   #e8703a;

  --serif: "Instrument Serif", "Iowan Old Style", Georgia, serif;
  --sans:  "Inter", ui-sans-serif, -apple-system, "Segoe UI", sans-serif;
  --mono:  "JetBrains Mono", ui-monospace, "Cascadia Code", Consolas, monospace;

  --shell: 78rem;
  --pad:   1.5rem;

  --ease:  cubic-bezier(.22, .61, .28, 1);
}

@media (min-width: 60rem) { :root { --pad: 4rem; } }

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }
a   { color: inherit; text-decoration: none; }

::selection { background: var(--accent); color: #fff; }

.mono {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Film grain over everything. The tile repeats, so this only ever needs to
   cover the viewport — no overhang, no oversized compositing layer. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 9;
  pointer-events: none;
  opacity: 0.16;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  background-repeat: repeat;
}


/* ─────────────────────── Nav ─────────────────────── */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem var(--pad);
  background: rgba(11, 11, 12, 0.72);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transform: translateY(-100%);
  transition: transform .5s var(--ease), border-color .5s var(--ease);
}

.nav.is-visible {
  transform: none;
  border-bottom-color: var(--line);
}

.nav__name {
  font-family: var(--serif);
  font-size: 1.25rem;
}

.nav__cta {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.55rem 1rem;
  border: 1px solid var(--line);
  border-radius: 100px;
  transition: border-color .3s, color .3s;
}

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


/* ────────────────────── Hero ─────────────────────── */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 7rem var(--pad) 5rem;
  max-width: var(--shell);
  margin: 0 auto;
}

/* Warm bloom behind the headline. */
.hero::before {
  content: "";
  position: absolute;
  top: 30%; left: 15%;
  width: 45rem; height: 45rem;
  max-width: 90vw;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(232, 112, 58, .16), transparent 62%);
  pointer-events: none;
  z-index: -1;
}

/* Single column until there's genuinely room for the editor beside it. */
.hero__inner { display: grid; gap: 3.5rem; }

@media (min-width: 64rem) {
  .hero__inner {
    grid-template-columns: 1.05fr 0.95fr;
    align-items: center;
    gap: 4.5rem;
  }
}

/* Name as a signature above the statement. Mono, not serif — the headline
   already carries the serif, and mono ties to the engineering half. */
.hero__name {
  display: block;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 2.25rem;
}

.hero__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.75rem, 8.2vw, 7rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  margin: 0 0 2.25rem;
}

/* Two columns means less room — don't let the headline crowd the editor. */
@media (min-width: 64rem) {
  .hero__title { font-size: clamp(3.25rem, 4.6vw, 5rem); }
}

.hero__title em {
  font-style: italic;
  color: var(--accent);
}

/* Each line masked, then slid up on load. */
.line {
  display: block;
  overflow: hidden;
}

.line > span {
  display: block;
}

.js .line > span {
  transform: translateY(105%);
}

.js .hero.is-ready .line > span {
  transform: none;
  transition: transform 1.05s var(--ease);
}

.js .hero.is-ready .line:nth-child(2) > span { transition-delay: .09s; }
.js .hero.is-ready .line:nth-child(3) > span { transition-delay: .18s; }
.js .hero.is-ready .line:nth-child(4) > span { transition-delay: .27s; }

.hero__sub {
  max-width: 40ch;
  margin: 0;
  font-size: clamp(1rem, 1.6vw, 1.1875rem);
  color: var(--muted);
}

.js .hero__sub { opacity: 0; }
.js .hero.is-ready .hero__sub {
  opacity: 1;
  transition: opacity 1s var(--ease) .5s;
}

/* ───────────────────── Hero editor ───────────────────── */

/* Below 64rem this sits under the copy; above, it takes the right column.
   Opaque rather than blurred — one less compositing layer, same look. */
.editor {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #141416;
  box-shadow: 0 40px 80px -40px rgba(0, 0, 0, .9);
  overflow: hidden;
}

.editor__bar {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.85rem 1.1rem;
  border-bottom: 1px solid var(--line);
  background: rgba(28, 28, 31, .6);
}

.editor__dots { display: flex; gap: 0.4rem; }

.editor__dots i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--line);
}

.editor__dots i:first-child { background: #3a2a22; }

.editor__file { color: var(--faint); }

.editor__code {
  margin: 0;
  padding: 1.25rem 1.1rem 1.5rem;
  font-family: var(--mono);
  /* Small enough that the longest line clears a 375px screen without
     sideways scrolling; grows once there's room. */
  font-size: 0.625rem;
  line-height: 1.9;
  /* Reserve the finished height so the hero can't jump while typing. */
  min-height: 15rem;
  white-space: pre;
  overflow-x: auto;
  scrollbar-width: none;
}

@media (min-width: 64rem) {
  .editor__code {
    padding: 1.5rem 1.4rem 1.75rem;
    font-size: 0.8125rem;
    line-height: 1.85;
    min-height: 17.5rem;
  }
}

.editor__code::-webkit-scrollbar { display: none; }

.editor__code .kw  { color: var(--accent); }
.editor__code .fn  { color: #e6e3dd; }
.editor__code .str { color: #93c58a; }
.editor__code .num { color: #d9a15b; }
.editor__code .com { color: var(--faint); font-style: italic; }
.editor__code .pun,
.editor__code .id  { color: var(--muted); }

.editor__caret {
  display: inline-block;
  width: 7px;
  height: 1.05em;
  vertical-align: -0.18em;
  background: var(--accent);
  animation: blink 1.05s steps(1) infinite;
}

@keyframes blink { 50% { opacity: 0; } }

.hero__cue {
  position: absolute;
  left: var(--pad);
  bottom: 2.5rem;
  display: flex;
  align-items: center;
  gap: 1rem;
  color: var(--faint);
  transition: color .3s;
}

.hero__cue:hover { color: var(--ink); }

.hero__cue-line {
  width: 3.5rem;
  height: 1px;
  background: currentColor;
  transform-origin: left;
  animation: cue 2.4s var(--ease) infinite;
}

@keyframes cue {
  0%, 100% { transform: scaleX(.4); opacity: .5; }
  50%      { transform: scaleX(1);  opacity: 1; }
}


/* ───────────────────── Intro ─────────────────────── */

main { position: relative; z-index: 2; }

.intro {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 4rem var(--pad) 8rem;
  display: grid;
  gap: 2.5rem;
  align-items: start;
}

/* ── Base = mobile: single column, centred ──
   ALL base rules live here, before the media queries. Every override below
   has the same specificity, so it only wins by coming later in source order.
   Keeping base and overrides separated this way is what prevents the paragraph
   from re-centring on desktop. */
.intro__portrait {
  margin: 0 auto;
  max-width: 13rem;
}

.intro__portrait img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: 4px;
  filter: grayscale(1) contrast(1.05);
  transition: filter .6s var(--ease);
  background: var(--raise);
}

.intro__portrait img:hover { filter: none; }

.intro__body { text-align: center; }

.intro__body p {
  margin: 0 auto;               /* centre the constrained block on mobile */
  font-family: var(--serif);
  font-size: clamp(1.4rem, 2.7vw, 2.125rem);
  line-height: 1.3;
  max-width: 24ch;
}

/* ── 40rem up: two columns, left-aligned ── */
@media (min-width: 40rem) {
  .intro {
    grid-template-columns: 13rem 1fr;
    gap: 4rem;
    padding-bottom: 12rem;
  }
  .intro__portrait { margin: 0; }
  .intro__body { text-align: left; }
  .intro__body p { margin: 0; max-width: 30ch; }
}

/* ── Desktop: bigger portrait ── */
@media (min-width: 64rem) {
  .intro {
    grid-template-columns: 19rem 1fr;
    gap: 5rem;
  }
  .intro__portrait { max-width: 19rem; }
}

.intro__meta {
  margin-top: 2rem !important;
  font-family: var(--mono) !important;
  font-size: 0.6875rem !important;
  line-height: 1.6 !important;
  color: var(--faint);
  max-width: none !important;
}


/* ────────────────────── Work ─────────────────────── */

.work {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--pad) 8rem;
}

@media (min-width: 60rem) { .work { padding-bottom: 12rem; } }

.work__head {
  padding-top: 3rem;
  border-top: 1px solid var(--line);
  margin-bottom: 3.5rem;
}

.work__index {
  display: block;
  color: var(--accent);
  margin-bottom: 1.75rem;
}

.work__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.25rem, 5.5vw, 4.25rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 1.5rem;
}

.work__lede {
  margin: 0;
  max-width: 46ch;
  color: var(--muted);
  font-size: 1.0625rem;
}

.work__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 2.5rem;
  margin: 2.5rem 0 0;
  padding-top: 2rem;
  border-top: 1px solid var(--line);
}

.work__meta dt { color: var(--faint); margin-bottom: 0.4rem; }
.work__meta dd { margin: 0; color: var(--ink); text-transform: none; letter-spacing: 0.02em; }


/* ──────────────────── Carousel ───────────────────── */

.carousel { position: relative; }

.carousel__track {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  outline: none;
  /* Bleed to the screen edge on mobile so it reads as swipeable. */
  margin: 0 calc(var(--pad) * -1);
  padding: 0 var(--pad);
}

.carousel__track::-webkit-scrollbar { display: none; }

.slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  margin: 0;
}

.slide img {
  width: 100%;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, .9);
  /* Sources land between 2.07 and 2.14 after cropping. Pinning the ratio
     keeps every slide the same height so the carousel never jumps; anchoring
     to the top means the trim comes off the bottom, never off the nav. */
  aspect-ratio: 2.1 / 1;
  object-fit: cover;
  object-position: top center;
}

.slide figcaption {
  display: flex;
  gap: 1rem;
  margin-top: 1.5rem;
  font-size: 0.9375rem;
  color: var(--muted);
  max-width: 52ch;
}

.slide figcaption .mono { color: var(--faint); flex: none; padding-top: .28rem; }

.carousel__ui {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  margin-top: 2.5rem;
}

.carousel__dots { display: flex; gap: 0.5rem; }

.carousel__dots button {
  width: 2rem;
  height: 2px;
  padding: 0;
  border: 0;
  background: var(--line);
  cursor: pointer;
  transition: background .4s var(--ease);
}

.carousel__dots button.is-active { background: var(--accent); }

.carousel__arrows { display: flex; gap: 0.5rem; }

.carousel__arrows button {
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: none;
  color: var(--ink);
  font-size: 0.9rem;
  cursor: pointer;
  transition: border-color .3s, color .3s, opacity .3s;
}

.carousel__arrows button:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.carousel__arrows button:disabled { opacity: .25; cursor: default; }


/* ──────────────────── More note ──────────────────── */

.more {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--pad) 8rem;
}

@media (min-width: 60rem) { .more { padding-bottom: 12rem; } }

.more p {
  margin: 0;
  max-width: 34ch;
  font-family: var(--serif);
  font-size: clamp(1.375rem, 2.6vw, 2rem);
  line-height: 1.3;
  color: var(--muted);
}


/* ──────────────────── Design grid ────────────────── */
/* Deliberately quieter than the case studies above: mono label and a plain
   lede, no serif display heading, smaller images. It supports, not competes. */

.design {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 3rem var(--pad) 8rem;
  border-top: 1px solid var(--line);
}

@media (min-width: 60rem) { .design { padding-bottom: 12rem; } }

.design__label { display: block; color: var(--accent); margin-bottom: 2rem; }

.design__lede {
  margin: 0 0 3rem;
  max-width: 52ch;
  color: var(--muted);
  font-size: 1.0625rem;
}

/* Default (no JS): CSS multi-column. Works, but can leave a balancing gap
   when a tall image won't fit — so script.js upgrades this to true masonry,
   placing each image in the shortest column. Each image keeps its native
   ratio; nothing is hard-cropped into a box. */
.gallery {
  columns: 1;
  column-gap: 1rem;
}

@media (min-width: 40rem) { .gallery { columns: 2; column-gap: 1.25rem; } }
@media (min-width: 64rem) { .gallery { columns: 3; } }

.gallery figure {
  margin: 0 0 1rem;
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}

@media (min-width: 40rem) { .gallery figure { margin-bottom: 1.25rem; } }

/* JS masonry: real columns, each filled independently — no interior gaps. */
.gallery.is-masonry {
  columns: auto;
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}

@media (min-width: 40rem) { .gallery.is-masonry { gap: 1.25rem; } }

.gallery__col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

@media (min-width: 40rem) { .gallery__col { gap: 1.25rem; } }

.gallery.is-masonry figure { margin: 0; }

.gallery img {
  width: 100%;
  height: auto;
  border-radius: 6px;
  border: 1px solid var(--line);
}


/* ─────────────────── What I build ────────────────── */

.build {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 3rem var(--pad) 8rem;
  border-top: 1px solid var(--line);
}

@media (min-width: 60rem) { .build { padding-bottom: 12rem; } }

.build__label { display: block; color: var(--accent); margin-bottom: 3rem; }

.build__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}

@media (min-width: 40rem) { .build__grid { grid-template-columns: 1fr 1fr; } }

.build__grid li {
  background: var(--bg);
  padding: 2.5rem 2rem;
  transition: background .4s var(--ease);
}

.build__grid li:hover { background: var(--surface); }

.build__grid .mono { color: var(--faint); }

.build__grid h3 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.75rem;
  margin: 1rem 0 0.75rem;
}

.build__grid p {
  margin: 0;
  color: var(--muted);
  font-size: 0.9375rem;
  max-width: 34ch;
}


/* ───────────────────── Contact ───────────────────── */

.contact {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 3rem var(--pad) 6rem;
  border-top: 1px solid var(--line);
}

.contact__label { display: block; color: var(--accent); margin-bottom: 2.5rem; }

.contact__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.75rem, 8vw, 6rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  margin: 0 0 3.5rem;
}

.contact__list { list-style: none; margin: 0; padding: 0; }

.contact__list li { border-top: 1px solid var(--line); }
.contact__list li:last-child { border-bottom: 1px solid var(--line); }

.contact__list a {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 2rem;
  padding: 1.75rem 0;
  transition: color .3s, padding-left .4s var(--ease);
}

.contact__list a:hover { color: var(--accent); padding-left: 1rem; }

/* Narrow screens: label always on its own line, so long and short
   values can't align differently from each other. */
.contact__list span {
  flex: 0 0 100%;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

.contact__list a:hover span { color: var(--accent); }

@media (min-width: 40rem) {
  .contact__list span { flex: 0 0 8rem; }
}

.contact__list em {
  font-family: var(--serif);
  font-style: normal;
  font-size: clamp(1.375rem, 3.4vw, 2.25rem);
  line-height: 1.1;
}


/* ────────────────────── Footer ───────────────────── */

.foot {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 2.5rem var(--pad) 3rem;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  color: var(--faint);
  border-top: 1px solid var(--line);
}


/* ─────────────── Reveal on scroll ────────────────── */
/* .js is set inline in <head> so nothing hides without JS. */

.js .reveal {
  opacity: 0;
  transform: translateY(24px);
}

.js .reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity .9s var(--ease), transform .9s var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal,
  .js .line > span,
  .js .hero__sub { opacity: 1; transform: none; }
  .js .reveal.is-in { transition: none; }
  .hero__cue-line { animation: none; }
  .carousel__track { scroll-behavior: auto; }
}
